| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| #include "channels/rdpdr/rdpdr.h" |
| #include "channels/rdpdr/rdpdr-fs.h" |
| #include "channels/rdpdr/rdpdr-messages.h" |
| #include "channels/rdpdr/rdpdr-printer.h" |
| #include "rdp.h" |
| #include "settings.h" |
| |
| #include <freerdp/channels/rdpdr.h> |
| #include <freerdp/freerdp.h> |
| #include <freerdp/settings.h> |
| #include <guacamole/client.h> |
| #include <winpr/stream.h> |
| |
| #include <stdlib.h> |
| |
| void guac_rdpdr_process_receive(guac_rdp_common_svc* svc, |
| wStream* input_stream) { |
| |
| int component; |
| int packet_id; |
| |
| /* |
| * Check that device redirection stream contains at least 4 bytes |
| * (UINT16 + UINT16). |
| */ |
| if (Stream_GetRemainingLength(input_stream) < 4) { |
| guac_client_log(svc->client, GUAC_LOG_WARNING, "Device redirection " |
| "channel PDU header does not contain the expected number of " |
| "bytes. Device redirection may not function as expected."); |
| return; |
| } |
| |
| /* Read header */ |
| Stream_Read_UINT16(input_stream, component); |
| Stream_Read_UINT16(input_stream, packet_id); |
| |
| /* Core component */ |
| if (component == RDPDR_CTYP_CORE) { |
| |
| /* Dispatch handlers based on packet ID */ |
| switch (packet_id) { |
| |
| case PAKID_CORE_SERVER_ANNOUNCE: |
| guac_rdpdr_process_server_announce(svc, input_stream); |
| break; |
| |
| case PAKID_CORE_CLIENTID_CONFIRM: |
| guac_rdpdr_process_clientid_confirm(svc, input_stream); |
| break; |
| |
| case PAKID_CORE_DEVICE_REPLY: |
| guac_rdpdr_process_device_reply(svc, input_stream); |
| break; |
| |
| case PAKID_CORE_DEVICE_IOREQUEST: |
| guac_rdpdr_process_device_iorequest(svc, input_stream); |
| break; |
| |
| case PAKID_CORE_SERVER_CAPABILITY: |
| guac_rdpdr_process_server_capability(svc, input_stream); |
| break; |
| |
| case PAKID_CORE_USER_LOGGEDON: |
| guac_rdpdr_process_user_loggedon(svc, input_stream); |
| break; |
| |
| default: |
| guac_client_log(svc->client, GUAC_LOG_DEBUG, "Ignoring " |
| "RDPDR core packet with unexpected ID: 0x%04x", |
| packet_id); |
| |
| } |
| |
| } /* end if core */ |
| |
| /* Printer component */ |
| else if (component == RDPDR_CTYP_PRN) { |
| |
| /* Dispatch handlers based on packet ID */ |
| switch (packet_id) { |
| |
| case PAKID_PRN_CACHE_DATA: |
| guac_rdpdr_process_prn_cache_data(svc, input_stream); |
| break; |
| |
| case PAKID_PRN_USING_XPS: |
| guac_rdpdr_process_prn_using_xps(svc, input_stream); |
| break; |
| |
| default: |
| guac_client_log(svc->client, GUAC_LOG_DEBUG, "Ignoring RDPDR " |
| "printer packet with unexpected ID: 0x%04x", |
| packet_id); |
| |
| } |
| |
| } /* end if printer */ |
| |
| else |
| guac_client_log(svc->client, GUAC_LOG_DEBUG, "Ignoring packet for " |
| "unknown RDPDR component: 0x%04x", component); |
| |
| } |
| |
| wStream* guac_rdpdr_new_io_completion(guac_rdpdr_device* device, |
| int completion_id, int status, int size) { |
| |
| wStream* output_stream = Stream_New(NULL, 16+size); |
| |
| /* Write header */ |
| Stream_Write_UINT16(output_stream, RDPDR_CTYP_CORE); |
| Stream_Write_UINT16(output_stream, PAKID_CORE_DEVICE_IOCOMPLETION); |
| |
| /* Write content */ |
| Stream_Write_UINT32(output_stream, device->device_id); |
| Stream_Write_UINT32(output_stream, completion_id); |
| Stream_Write_UINT32(output_stream, status); |
| |
| return output_stream; |
| |
| } |
| |
| void guac_rdpdr_process_connect(guac_rdp_common_svc* svc) { |
| |
| /* Get data from client */ |
| guac_client* client = svc->client; |
| guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; |
| |
| guac_rdpdr* rdpdr = (guac_rdpdr*) calloc(1, sizeof(guac_rdpdr)); |
| svc->data = rdpdr; |
| |
| /* Register printer if enabled */ |
| if (rdp_client->settings->printing_enabled) |
| guac_rdpdr_register_printer(svc, rdp_client->settings->printer_name); |
| |
| /* Register drive if enabled */ |
| if (rdp_client->settings->drive_enabled) |
| guac_rdpdr_register_fs(svc, rdp_client->settings->drive_name); |
| |
| } |
| |
| void guac_rdpdr_process_terminate(guac_rdp_common_svc* svc) { |
| |
| guac_rdpdr* rdpdr = (guac_rdpdr*) svc->data; |
| if (rdpdr == NULL) |
| return; |
| |
| int i; |
| |
| for (i=0; i<rdpdr->devices_registered; i++) { |
| guac_rdpdr_device* device = &(rdpdr->devices[i]); |
| guac_client_log(svc->client, GUAC_LOG_DEBUG, "Unloading device %i " |
| "(%s)", device->device_id, device->device_name); |
| device->free_handler(svc, device); |
| } |
| |
| free(rdpdr); |
| |
| } |
| |
| |
| void guac_rdpdr_load_plugin(rdpContext* context) { |
| |
| guac_client* client = ((rdp_freerdp_context*) context)->client; |
| |
| /* Load support for RDPDR */ |
| if (guac_rdp_common_svc_load_plugin(context, "rdpdr", |
| CHANNEL_OPTION_COMPRESS_RDP, guac_rdpdr_process_connect, |
| guac_rdpdr_process_receive, guac_rdpdr_process_terminate)) { |
| guac_client_log(client, GUAC_LOG_WARNING, "Support for the RDPDR " |
| "channel (device redirection) could not be loaded. Drive " |
| "redirection and printing will not work. Sound MAY not work."); |
| } |
| |
| } |
| |