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