cscg24-guacamole

CSCG 2024 Challenge 'Guacamole Mashup'
git clone https://git.sinitax.com/sinitax/cscg24-guacamole
Log | Files | Refs | sfeed.txt

rdpdr-printer.c (8114B)


      1/*
      2 * Licensed to the Apache Software Foundation (ASF) under one
      3 * or more contributor license agreements.  See the NOTICE file
      4 * distributed with this work for additional information
      5 * regarding copyright ownership.  The ASF licenses this file
      6 * to you under the Apache License, Version 2.0 (the
      7 * "License"); you may not use this file except in compliance
      8 * with the License.  You may obtain a copy of the License at
      9 *
     10 *   http://www.apache.org/licenses/LICENSE-2.0
     11 *
     12 * Unless required by applicable law or agreed to in writing,
     13 * software distributed under the License is distributed on an
     14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
     15 * KIND, either express or implied.  See the License for the
     16 * specific language governing permissions and limitations
     17 * under the License.
     18 */
     19
     20#include "channels/rdpdr/rdpdr-printer.h"
     21#include "channels/rdpdr/rdpdr.h"
     22#include "print-job.h"
     23#include "rdp.h"
     24#include "unicode.h"
     25
     26#include <freerdp/channels/rdpdr.h>
     27#include <freerdp/settings.h>
     28#include <guacamole/client.h>
     29#include <guacamole/unicode.h>
     30#include <winpr/nt.h>
     31#include <winpr/stream.h>
     32
     33#include <stdlib.h>
     34
     35void guac_rdpdr_process_print_job_create(guac_rdp_common_svc* svc,
     36        guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
     37        wStream* input_stream) {
     38
     39    guac_client* client = svc->client;
     40    guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
     41
     42    /* Log creation of print job */
     43    guac_client_log(client, GUAC_LOG_INFO, "Print job created");
     44
     45    /* Create print job */
     46    rdp_client->active_job = guac_client_for_owner(client,
     47            guac_rdp_print_job_alloc, NULL);
     48
     49    /* Respond with success */
     50    wStream* output_stream = guac_rdpdr_new_io_completion(device,
     51            iorequest->completion_id, STATUS_SUCCESS, 4);
     52
     53    Stream_Write_UINT32(output_stream, 0); /* fileId */
     54    guac_rdp_common_svc_write(svc, output_stream);
     55
     56}
     57
     58void guac_rdpdr_process_print_job_write(guac_rdp_common_svc* svc,
     59        guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
     60        wStream* input_stream) {
     61
     62    guac_client* client = svc->client;
     63    guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
     64    guac_rdp_print_job* job = (guac_rdp_print_job*) rdp_client->active_job;
     65
     66    unsigned char* buffer;
     67    int length;
     68    int status;
     69
     70    /* Verify that Stream contains at least 32 bytes (UINT32 + 8 + 20) */
     71    if (Stream_GetRemainingLength(input_stream) < 32) {
     72        guac_client_log(client, GUAC_LOG_WARNING, "Print job write stream does "
     73                "not contain the expected number of bytes. Printer redirection "
     74                "may not work as expected.");
     75        return;
     76    }
     77    
     78    /* Read buffer of print data */
     79    Stream_Read_UINT32(input_stream, length);
     80    Stream_Seek(input_stream, 8);  /* Offset */
     81    Stream_Seek(input_stream, 20); /* Padding */
     82    buffer = Stream_Pointer(input_stream);
     83
     84    /* Verify the stream has at least length number of bytes remaining. */
     85    if (Stream_GetRemainingLength(input_stream) < length) {
     86        guac_client_log(client, GUAC_LOG_WARNING, "Print job write stream does "
     87                "not contain the expected number of bytes. Printer redirection "
     88                "may not work as expected.");
     89        return;
     90    }
     91    
     92    /* Write data only if job exists, translating status for RDP */
     93    if (job != NULL && (length = guac_rdp_print_job_write(job,
     94                    buffer, length)) >= 0) {
     95        status = STATUS_SUCCESS;
     96    }
     97
     98    /* Report device offline if write fails */
     99    else {
    100        status = STATUS_DEVICE_OFF_LINE;
    101        length = 0;
    102    }
    103
    104    wStream* output_stream = guac_rdpdr_new_io_completion(device,
    105            iorequest->completion_id, status, 5);
    106
    107    Stream_Write_UINT32(output_stream, length);
    108    Stream_Write_UINT8(output_stream, 0); /* Padding */
    109
    110    guac_rdp_common_svc_write(svc, output_stream);
    111
    112}
    113
    114void guac_rdpdr_process_print_job_close(guac_rdp_common_svc* svc,
    115        guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
    116        wStream* input_stream) {
    117
    118    guac_client* client = svc->client;
    119    guac_rdp_client* rdp_client = (guac_rdp_client*) client->data;
    120    guac_rdp_print_job* job = (guac_rdp_print_job*) rdp_client->active_job;
    121
    122    /* End print job */
    123    if (job != NULL) {
    124        guac_rdp_print_job_free(job);
    125        rdp_client->active_job = NULL;
    126    }
    127
    128    wStream* output_stream = guac_rdpdr_new_io_completion(device,
    129            iorequest->completion_id, STATUS_SUCCESS, 4);
    130
    131    Stream_Write_UINT32(output_stream, 0); /* Padding */
    132    guac_rdp_common_svc_write(svc, output_stream);
    133
    134    /* Log end of print job */
    135    guac_client_log(client, GUAC_LOG_INFO, "Print job closed");
    136
    137}
    138
    139void guac_rdpdr_device_printer_iorequest_handler(guac_rdp_common_svc* svc,
    140        guac_rdpdr_device* device, guac_rdpdr_iorequest* iorequest,
    141        wStream* input_stream) {
    142
    143    switch (iorequest->major_func) {
    144
    145        /* Print job create */
    146        case IRP_MJ_CREATE:
    147            guac_rdpdr_process_print_job_create(svc, device, iorequest, input_stream);
    148            break;
    149
    150        /* Printer job write */
    151        case IRP_MJ_WRITE:
    152            guac_rdpdr_process_print_job_write(svc, device, iorequest, input_stream);
    153            break;
    154
    155        /* Printer job close */
    156        case IRP_MJ_CLOSE:
    157            guac_rdpdr_process_print_job_close(svc, device, iorequest, input_stream);
    158            break;
    159
    160        /* Log unknown */
    161        default:
    162            guac_client_log(svc->client, GUAC_LOG_ERROR, "Unknown printer "
    163                    "I/O request function: 0x%x/0x%x", iorequest->major_func,
    164                    iorequest->minor_func);
    165
    166    }
    167
    168}
    169
    170void guac_rdpdr_device_printer_free_handler(guac_rdp_common_svc* svc,
    171        guac_rdpdr_device* device) {
    172
    173    Stream_Free(device->device_announce, 1);
    174
    175}
    176
    177void guac_rdpdr_register_printer(guac_rdp_common_svc* svc, char* printer_name) {
    178
    179    guac_rdpdr* rdpdr = (guac_rdpdr*) svc->data;
    180    int id = rdpdr->devices_registered++;
    181
    182    /* Get new device */
    183    guac_rdpdr_device* device = &(rdpdr->devices[id]);
    184
    185    /* Init device */
    186    device->device_id   = id;
    187    device->device_name = printer_name;
    188    int device_name_len = guac_utf8_strlen(device->device_name);
    189    device->device_type = RDPDR_DTYP_PRINT;
    190    device->dos_name = "PRN1\0\0\0\0";
    191    
    192    /* Set up device announce stream */
    193    int prt_name_len = (device_name_len + 1) * 2;
    194    device->device_announce_len = 44 + prt_name_len
    195            + GUAC_PRINTER_DRIVER_LENGTH;
    196    device->device_announce = Stream_New(NULL, device->device_announce_len);
    197    
    198    /* Write common information. */
    199    Stream_Write_UINT32(device->device_announce, device->device_type);
    200    Stream_Write_UINT32(device->device_announce, device->device_id);
    201    Stream_Write(device->device_announce, device->dos_name, 8);
    202    
    203    /* DeviceDataLength */
    204    Stream_Write_UINT32(device->device_announce, 24 +  prt_name_len + GUAC_PRINTER_DRIVER_LENGTH);
    205    
    206    /* Begin printer-specific information */
    207    Stream_Write_UINT32(device->device_announce,
    208              RDPDR_PRINTER_ANNOUNCE_FLAG_DEFAULTPRINTER
    209            | RDPDR_PRINTER_ANNOUNCE_FLAG_NETWORKPRINTER); /* Printer flags */
    210    Stream_Write_UINT32(device->device_announce, 0); /* Reserved - must be 0. */
    211    Stream_Write_UINT32(device->device_announce, 0); /* PnPName Length - ignored. */
    212    Stream_Write_UINT32(device->device_announce, GUAC_PRINTER_DRIVER_LENGTH);
    213    Stream_Write_UINT32(device->device_announce, prt_name_len);
    214    Stream_Write_UINT32(device->device_announce, 0); /* CachedFields length. */
    215    
    216    Stream_Write(device->device_announce, GUAC_PRINTER_DRIVER, GUAC_PRINTER_DRIVER_LENGTH);
    217    guac_rdp_utf8_to_utf16((const unsigned char*) device->device_name,
    218            device_name_len + 1, (char*) Stream_Pointer(device->device_announce),
    219            prt_name_len);
    220    Stream_Seek(device->device_announce, prt_name_len);
    221
    222    /* Set handlers */
    223    device->iorequest_handler = guac_rdpdr_device_printer_iorequest_handler;
    224    device->free_handler      = guac_rdpdr_device_printer_free_handler;
    225
    226}
    227