From 6940875e6e16c6e4034b2c59a8748a1ac1bca305 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Sun, 29 Dec 2019 17:55:15 -0800 Subject: [PATCH] GUACAMOLE-249: Refactor away old stream.h and guac_rdp_stream. --- src/protocols/rdp/Makefile.am | 8 +- .../rdpdr/rdpdr-fs-messages-file-info.c | 3 +- .../rdp/channels/rdpdr/rdpdr-fs-messages.c | 3 +- src/protocols/rdp/channels/rdpdr/rdpdr.c | 94 ---- src/protocols/rdp/channels/rdpdr/rdpdr.h | 6 - src/protocols/rdp/download.c | 242 +++++++++ src/protocols/rdp/download.h | 70 +++ src/protocols/rdp/fs.c | 3 +- src/protocols/rdp/ls.c | 127 +++++ src/protocols/rdp/ls.h | 66 +++ src/protocols/rdp/rdp.c | 1 - src/protocols/rdp/stream.c | 479 ------------------ src/protocols/rdp/stream.h | 184 ------- src/protocols/rdp/upload.c | 241 +++++++++ src/protocols/rdp/upload.h | 73 +++ src/protocols/rdp/user.c | 2 +- 16 files changed, 832 insertions(+), 770 deletions(-) create mode 100644 src/protocols/rdp/download.c create mode 100644 src/protocols/rdp/download.h create mode 100644 src/protocols/rdp/ls.c create mode 100644 src/protocols/rdp/ls.h delete mode 100644 src/protocols/rdp/stream.c delete mode 100644 src/protocols/rdp/stream.h create mode 100644 src/protocols/rdp/upload.c create mode 100644 src/protocols/rdp/upload.h diff --git a/src/protocols/rdp/Makefile.am b/src/protocols/rdp/Makefile.am index 3ba1da05..19c81c2e 100644 --- a/src/protocols/rdp/Makefile.am +++ b/src/protocols/rdp/Makefile.am @@ -57,6 +57,7 @@ libguac_client_rdp_la_SOURCES = \ channels/rdpsnd/rdpsnd.c \ client.c \ color.c \ + download.c \ error.c \ fs.c \ gdi.c \ @@ -65,6 +66,7 @@ libguac_client_rdp_la_SOURCES = \ keyboard/decompose.c \ keyboard/keyboard.c \ keyboard/keymap.c \ + ls.c \ plugins/channels.c \ plugins/ptr-string.c \ pointer.c \ @@ -72,8 +74,8 @@ libguac_client_rdp_la_SOURCES = \ rdp.c \ resolution.c \ settings.c \ - stream.c \ unicode.c \ + upload.c \ user.c noinst_HEADERS = \ @@ -96,6 +98,7 @@ noinst_HEADERS = \ channels/rdpsnd/rdpsnd.h \ client.h \ color.h \ + download.h \ error.h \ fs.h \ gdi.h \ @@ -104,6 +107,7 @@ noinst_HEADERS = \ keyboard/decompose.h \ keyboard/keyboard.h \ keyboard/keymap.h \ + ls.h \ plugins/channels.h \ plugins/guacai/guacai-messages.h \ plugins/guacai/guacai.h \ @@ -113,8 +117,8 @@ noinst_HEADERS = \ rdp.h \ resolution.h \ settings.h \ - stream.h \ unicode.h \ + upload.h \ user.h libguac_client_rdp_la_CFLAGS = \ diff --git a/src/protocols/rdp/channels/rdpdr/rdpdr-fs-messages-file-info.c b/src/protocols/rdp/channels/rdpdr/rdpdr-fs-messages-file-info.c index 509087b8..0e4f66f0 100644 --- a/src/protocols/rdp/channels/rdpdr/rdpdr-fs-messages-file-info.c +++ b/src/protocols/rdp/channels/rdpdr/rdpdr-fs-messages-file-info.c @@ -20,6 +20,7 @@ #include "config.h" #include "channels/rdpdr/rdpdr-fs-messages-file-info.h" #include "channels/rdpdr/rdpdr.h" +#include "download.h" #include "fs.h" #include "unicode.h" @@ -158,7 +159,7 @@ void guac_rdpdr_fs_process_set_rename_info(guac_rdp_common_svc* svc, return; /* Initiate download, pretend move succeeded */ - guac_rdpdr_start_download(svc, device, file->absolute_path); + guac_client_for_owner(svc->client, guac_rdp_download_to_user, file->absolute_path); output_stream = guac_rdpdr_new_io_completion(device, iorequest->completion_id, STATUS_SUCCESS, 4); diff --git a/src/protocols/rdp/channels/rdpdr/rdpdr-fs-messages.c b/src/protocols/rdp/channels/rdpdr/rdpdr-fs-messages.c index 4fede57a..8475ce94 100644 --- a/src/protocols/rdp/channels/rdpdr/rdpdr-fs-messages.c +++ b/src/protocols/rdp/channels/rdpdr/rdpdr-fs-messages.c @@ -24,6 +24,7 @@ #include "channels/rdpdr/rdpdr-fs-messages.h" #include "channels/rdpdr/rdpdr-messages.h" #include "channels/rdpdr/rdpdr.h" +#include "download.h" #include "fs.h" #include "unicode.h" @@ -224,7 +225,7 @@ void guac_rdpdr_fs_process_close(guac_rdp_common_svc* svc, /* If file was written to, and it's in the \Download folder, start stream */ if (file->bytes_written > 0 && strncmp(file->absolute_path, "\\Download\\", 10) == 0) { - guac_rdpdr_start_download(svc, device, file->absolute_path); + guac_client_for_owner(svc->client, guac_rdp_download_to_user, file->absolute_path); guac_rdp_fs_delete((guac_rdp_fs*) device->data, iorequest->file_id); } diff --git a/src/protocols/rdp/channels/rdpdr/rdpdr.c b/src/protocols/rdp/channels/rdpdr/rdpdr.c index 39da918d..d0d3143e 100644 --- a/src/protocols/rdp/channels/rdpdr/rdpdr.c +++ b/src/protocols/rdp/channels/rdpdr/rdpdr.c @@ -26,7 +26,6 @@ #include "plugins/channels.h" #include "rdp.h" #include "settings.h" -#include "stream.h" #include #include @@ -135,99 +134,6 @@ wStream* guac_rdpdr_new_io_completion(guac_rdpdr_device* device, } -/** - * Callback invoked on the current connection owner (if any) when a file - * download is being initiated using the magic "Download" folder. - * - * @param owner - * The guac_user that is the owner of the connection, or NULL if the - * connection owner has left. - * - * @param data - * The full absolute path to the file that should be downloaded. - * - * @return - * The stream allocated for the file download, or NULL if the download has - * failed to start. - */ -static void* guac_rdpdr_download_to_owner(guac_user* owner, void* data) { - - /* Do not bother attempting the download if the owner has left */ - if (owner == NULL) - return NULL; - - guac_client* client = owner->client; - guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; - guac_rdp_fs* filesystem = rdp_client->filesystem; - - /* Ignore download if filesystem has been unloaded */ - if (filesystem == NULL) - return NULL; - - /* Attempt to open requested file */ - char* path = (char*) data; - int file_id = guac_rdp_fs_open(filesystem, path, - FILE_READ_DATA, 0, FILE_OPEN, 0); - - /* If file opened successfully, start stream */ - if (file_id >= 0) { - - guac_rdp_stream* rdp_stream; - const char* basename; - - int i; - char c; - - /* Associate stream with transfer status */ - guac_stream* stream = guac_user_alloc_stream(owner); - stream->data = rdp_stream = malloc(sizeof(guac_rdp_stream)); - stream->ack_handler = guac_rdp_download_ack_handler; - rdp_stream->type = GUAC_RDP_DOWNLOAD_STREAM; - rdp_stream->download_status.file_id = file_id; - rdp_stream->download_status.offset = 0; - - /* Get basename from absolute path */ - i=0; - basename = path; - do { - - c = path[i]; - if (c == '/' || c == '\\') - basename = &(path[i+1]); - - i++; - - } while (c != '\0'); - - guac_user_log(owner, GUAC_LOG_DEBUG, "%s: Initiating download " - "of \"%s\"", __func__, path); - - /* Begin stream */ - guac_protocol_send_file(owner->socket, stream, - "application/octet-stream", basename); - guac_socket_flush(owner->socket); - - /* Download started successfully */ - return stream; - - } - - /* Download failed */ - guac_user_log(owner, GUAC_LOG_ERROR, "Unable to download \"%s\"", path); - return NULL; - -} - -void guac_rdpdr_start_download(guac_rdp_common_svc* svc, - guac_rdpdr_device* device, char* path) { - - guac_client* client = svc->client; - - /* Initiate download to the owner of the connection */ - guac_client_for_owner(client, guac_rdpdr_download_to_owner, path); - -} - void guac_rdpdr_process_connect(guac_rdp_common_svc* svc) { /* Get data from client */ diff --git a/src/protocols/rdp/channels/rdpdr/rdpdr.h b/src/protocols/rdp/channels/rdpdr/rdpdr.h index 19f178d0..fc494ee1 100644 --- a/src/protocols/rdp/channels/rdpdr/rdpdr.h +++ b/src/protocols/rdp/channels/rdpdr/rdpdr.h @@ -212,12 +212,6 @@ typedef struct guac_rdpdr { wStream* guac_rdpdr_new_io_completion(guac_rdpdr_device* device, int completion_id, int status, int size); -/** - * Begins streaming the given file to the user via a Guacamole file stream. - */ -void guac_rdpdr_start_download(guac_rdp_common_svc* svc, - guac_rdpdr_device* device, char* path); - /** * Initializes device redirection support (file transfer, printing, etc.) for * RDP and handling of the RDPDR channel. If failures occur, messages noting diff --git a/src/protocols/rdp/download.c b/src/protocols/rdp/download.c new file mode 100644 index 00000000..34f7e2e6 --- /dev/null +++ b/src/protocols/rdp/download.c @@ -0,0 +1,242 @@ +/* + * 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 "config.h" +#include "client.h" +#include "download.h" +#include "fs.h" +#include "ls.h" +#include "rdp.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +int guac_rdp_download_ack_handler(guac_user* user, guac_stream* stream, + char* message, guac_protocol_status status) { + + guac_client* client = user->client; + guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; + guac_rdp_download_status* download_status = (guac_rdp_download_status*) stream->data; + + /* Get filesystem, return error if no filesystem */ + guac_rdp_fs* fs = rdp_client->filesystem; + if (fs == NULL) { + guac_protocol_send_ack(user->socket, stream, "FAIL (NO FS)", + GUAC_PROTOCOL_STATUS_SERVER_ERROR); + guac_socket_flush(user->socket); + return 0; + } + + /* If successful, read data */ + if (status == GUAC_PROTOCOL_STATUS_SUCCESS) { + + /* Attempt read into buffer */ + char buffer[4096]; + int bytes_read = guac_rdp_fs_read(fs, + download_status->file_id, + download_status->offset, buffer, sizeof(buffer)); + + /* If bytes read, send as blob */ + if (bytes_read > 0) { + download_status->offset += bytes_read; + guac_protocol_send_blob(user->socket, stream, + buffer, bytes_read); + } + + /* If EOF, send end */ + else if (bytes_read == 0) { + guac_protocol_send_end(user->socket, stream); + guac_user_free_stream(user, stream); + free(download_status); + } + + /* Otherwise, fail stream */ + else { + guac_user_log(user, GUAC_LOG_ERROR, + "Error reading file for download"); + guac_protocol_send_end(user->socket, stream); + guac_user_free_stream(user, stream); + free(download_status); + } + + guac_socket_flush(user->socket); + + } + + /* Otherwise, return stream to user */ + else + guac_user_free_stream(user, stream); + + return 0; + +} + +int guac_rdp_download_get_handler(guac_user* user, guac_object* object, + char* name) { + + guac_client* client = user->client; + guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; + + /* Get filesystem, ignore request if no filesystem */ + guac_rdp_fs* fs = rdp_client->filesystem; + if (fs == NULL) + return 0; + + /* Attempt to open file for reading */ + int file_id = guac_rdp_fs_open(fs, name, GENERIC_READ, 0, FILE_OPEN, 0); + if (file_id < 0) { + guac_user_log(user, GUAC_LOG_INFO, "Unable to read file \"%s\"", + name); + return 0; + } + + /* Get opened file */ + guac_rdp_fs_file* file = guac_rdp_fs_get_file(fs, file_id); + if (file == NULL) { + guac_client_log(fs->client, GUAC_LOG_DEBUG, + "%s: Successful open produced bad file_id: %i", + __func__, file_id); + return 0; + } + + /* If directory, send contents of directory */ + if (file->attributes & FILE_ATTRIBUTE_DIRECTORY) { + + /* Create stream data */ + guac_rdp_ls_status* ls_status = malloc(sizeof(guac_rdp_ls_status)); + ls_status->fs = fs; + ls_status->file_id = file_id; + guac_strlcpy(ls_status->directory_name, name, + sizeof(ls_status->directory_name)); + + /* Allocate stream for body */ + guac_stream* stream = guac_user_alloc_stream(user); + stream->ack_handler = guac_rdp_ls_ack_handler; + stream->data = ls_status; + + /* Init JSON object state */ + guac_common_json_begin_object(user, stream, + &ls_status->json_state); + + /* Associate new stream with get request */ + guac_protocol_send_body(user->socket, object, stream, + GUAC_USER_STREAM_INDEX_MIMETYPE, name); + + } + + /* Otherwise, send file contents */ + else { + + /* Create stream data */ + guac_rdp_download_status* download_status = malloc(sizeof(guac_rdp_download_status)); + download_status->file_id = file_id; + download_status->offset = 0; + + /* Allocate stream for body */ + guac_stream* stream = guac_user_alloc_stream(user); + stream->data = download_status; + stream->ack_handler = guac_rdp_download_ack_handler; + + /* Associate new stream with get request */ + guac_protocol_send_body(user->socket, object, stream, + "application/octet-stream", name); + + } + + guac_socket_flush(user->socket); + return 0; +} + +void* guac_rdp_download_to_user(guac_user* user, void* data) { + + /* Do not bother attempting the download if the user has left */ + if (user == NULL) + return NULL; + + guac_client* client = user->client; + guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; + guac_rdp_fs* filesystem = rdp_client->filesystem; + + /* Ignore download if filesystem has been unloaded */ + if (filesystem == NULL) + return NULL; + + /* Attempt to open requested file */ + char* path = (char*) data; + int file_id = guac_rdp_fs_open(filesystem, path, + FILE_READ_DATA, 0, FILE_OPEN, 0); + + /* If file opened successfully, start stream */ + if (file_id >= 0) { + + guac_rdp_download_status* download_status; + const char* basename; + + int i; + char c; + + /* Associate stream with transfer status */ + guac_stream* stream = guac_user_alloc_stream(user); + stream->data = download_status = malloc(sizeof(guac_rdp_download_status)); + stream->ack_handler = guac_rdp_download_ack_handler; + download_status->file_id = file_id; + download_status->offset = 0; + + /* Get basename from absolute path */ + i=0; + basename = path; + do { + + c = path[i]; + if (c == '/' || c == '\\') + basename = &(path[i+1]); + + i++; + + } while (c != '\0'); + + guac_user_log(user, GUAC_LOG_DEBUG, "%s: Initiating download " + "of \"%s\"", __func__, path); + + /* Begin stream */ + guac_protocol_send_file(user->socket, stream, + "application/octet-stream", basename); + guac_socket_flush(user->socket); + + /* Download started successfully */ + return stream; + + } + + /* Download failed */ + guac_user_log(user, GUAC_LOG_ERROR, "Unable to download \"%s\"", path); + return NULL; + +} + diff --git a/src/protocols/rdp/download.h b/src/protocols/rdp/download.h new file mode 100644 index 00000000..c15dba67 --- /dev/null +++ b/src/protocols/rdp/download.h @@ -0,0 +1,70 @@ +/* + * 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. + */ + +#ifndef GUAC_RDP_DOWNLOAD_H +#define GUAC_RDP_DOWNLOAD_H + +#include "config.h" +#include "common/json.h" + +#include +#include +#include + +#include + +/** + * The transfer status of a file being downloaded. + */ +typedef struct guac_rdp_download_status { + + /** + * The file ID of the file being downloaded. + */ + int file_id; + + /** + * The current position within the file. + */ + uint64_t offset; + +} guac_rdp_download_status; + +/** + * Handler for acknowledgements of receipt of data related to file downloads. + */ +guac_user_ack_handler guac_rdp_download_ack_handler; + +/** + * Handler for get messages. In context of downloads and the filesystem exposed + * via the Guacamole protocol, get messages request the body of a file within + * the filesystem. + */ +guac_user_get_handler guac_rdp_download_get_handler; + +/** + * Callback for guac_client_for_user() and similar functions which initiates a + * file download to a specific user if that user is still connected. The path + * for the file to be downloaded must be passed as the arbitrary data parameter + * for the function invoking this callback. + */ +guac_user_callback guac_rdp_download_to_user; + +#endif + diff --git a/src/protocols/rdp/fs.c b/src/protocols/rdp/fs.c index 7dd3ea7a..8b48b945 100644 --- a/src/protocols/rdp/fs.c +++ b/src/protocols/rdp/fs.c @@ -19,8 +19,9 @@ #include "config.h" +#include "download.h" #include "fs.h" -#include "stream.h" +#include "upload.h" #include #include diff --git a/src/protocols/rdp/ls.c b/src/protocols/rdp/ls.c new file mode 100644 index 00000000..f008da90 --- /dev/null +++ b/src/protocols/rdp/ls.c @@ -0,0 +1,127 @@ +/* + * 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 "config.h" +#include "client.h" +#include "fs.h" +#include "ls.h" +#include "rdp.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +int guac_rdp_ls_ack_handler(guac_user* user, guac_stream* stream, + char* message, guac_protocol_status status) { + + int blob_written = 0; + const char* filename; + + guac_rdp_ls_status* ls_status = (guac_rdp_ls_status*) stream->data; + + /* If unsuccessful, free stream and abort */ + if (status != GUAC_PROTOCOL_STATUS_SUCCESS) { + guac_rdp_fs_close(ls_status->fs, ls_status->file_id); + guac_user_free_stream(user, stream); + free(ls_status); + return 0; + } + + /* While directory entries remain */ + while ((filename = guac_rdp_fs_read_dir(ls_status->fs, + ls_status->file_id)) != NULL + && !blob_written) { + + char absolute_path[GUAC_RDP_FS_MAX_PATH]; + + /* Skip current and parent directory entries */ + if (strcmp(filename, ".") == 0 || strcmp(filename, "..") == 0) + continue; + + /* Concatenate into absolute path - skip if invalid */ + if (!guac_rdp_fs_append_filename(absolute_path, + ls_status->directory_name, filename)) { + + guac_user_log(user, GUAC_LOG_DEBUG, + "Skipping filename \"%s\" - filename is invalid or " + "resulting path is too long", filename); + + continue; + } + + /* Attempt to open file to determine type */ + int file_id = guac_rdp_fs_open(ls_status->fs, absolute_path, + GENERIC_READ, 0, FILE_OPEN, 0); + if (file_id < 0) + continue; + + /* Get opened file */ + guac_rdp_fs_file* file = guac_rdp_fs_get_file(ls_status->fs, file_id); + if (file == NULL) { + guac_user_log(user, GUAC_LOG_DEBUG, "%s: Successful open produced " + "bad file_id: %i", __func__, file_id); + return 0; + } + + /* Determine mimetype */ + const char* mimetype; + if (file->attributes & FILE_ATTRIBUTE_DIRECTORY) + mimetype = GUAC_USER_STREAM_INDEX_MIMETYPE; + else + mimetype = "application/octet-stream"; + + /* Write entry */ + blob_written |= guac_common_json_write_property(user, stream, + &ls_status->json_state, absolute_path, mimetype); + + guac_rdp_fs_close(ls_status->fs, file_id); + + } + + /* Complete JSON and cleanup at end of directory */ + if (filename == NULL) { + + /* Complete JSON object */ + guac_common_json_end_object(user, stream, &ls_status->json_state); + guac_common_json_flush(user, stream, &ls_status->json_state); + + /* Clean up resources */ + guac_rdp_fs_close(ls_status->fs, ls_status->file_id); + free(ls_status); + + /* Signal of stream */ + guac_protocol_send_end(user->socket, stream); + guac_user_free_stream(user, stream); + + } + + guac_socket_flush(user->socket); + return 0; + +} + diff --git a/src/protocols/rdp/ls.h b/src/protocols/rdp/ls.h new file mode 100644 index 00000000..21a2eb85 --- /dev/null +++ b/src/protocols/rdp/ls.h @@ -0,0 +1,66 @@ +/* + * 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. + */ + +#ifndef GUAC_RDP_LS_H +#define GUAC_RDP_LS_H + +#include "config.h" +#include "common/json.h" + +#include +#include +#include + +#include + +/** + * The current state of a directory listing operation. + */ +typedef struct guac_rdp_ls_status { + + /** + * The filesystem associated with the directory being listed. + */ + guac_rdp_fs* fs; + + /** + * The file ID of the directory being listed. + */ + int file_id; + + /** + * The absolute path of the directory being listed. + */ + char directory_name[GUAC_RDP_FS_MAX_PATH]; + + /** + * The current state of the JSON directory object being written. + */ + guac_common_json_state json_state; + +} guac_rdp_ls_status; + +/** + * Handler for ack messages received due to receipt of a "body" or "blob" + * instruction associated with a directory list operation. + */ +guac_user_ack_handler guac_rdp_ls_ack_handler; + +#endif + diff --git a/src/protocols/rdp/rdp.c b/src/protocols/rdp/rdp.c index 7e91ef11..5a6fb875 100644 --- a/src/protocols/rdp/rdp.c +++ b/src/protocols/rdp/rdp.c @@ -39,7 +39,6 @@ #include "pointer.h" #include "print-job.h" #include "rdp.h" -#include "stream.h" #ifdef ENABLE_COMMON_SSH #include "common-ssh/sftp.h" diff --git a/src/protocols/rdp/stream.c b/src/protocols/rdp/stream.c deleted file mode 100644 index 71fd0357..00000000 --- a/src/protocols/rdp/stream.c +++ /dev/null @@ -1,479 +0,0 @@ -/* - * 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 "config.h" -#include "client.h" -#include "fs.h" -#include "rdp.h" -#include "stream.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -/** - * Writes the given filename to the given upload path, sanitizing the filename - * and translating the filename to the root directory. - * - * @param filename - * The filename to sanitize and move to the root directory. - * - * @param path - * A pointer to a buffer which should receive the sanitized path. The - * buffer must hav at least GUAC_RDP_FS_MAX_PATH bytes available. - */ -static void __generate_upload_path(const char* filename, char* path) { - - int i; - - /* Add initial backslash */ - *(path++) = '\\'; - - for (i=1; iclient; - guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; - - int file_id; - guac_rdp_stream* rdp_stream; - char file_path[GUAC_RDP_FS_MAX_PATH]; - - /* Get filesystem, return error if no filesystem */ - guac_rdp_fs* fs = rdp_client->filesystem; - if (fs == NULL) { - guac_protocol_send_ack(user->socket, stream, "FAIL (NO FS)", - GUAC_PROTOCOL_STATUS_SERVER_ERROR); - guac_socket_flush(user->socket); - return 0; - } - - /* Translate name */ - __generate_upload_path(filename, file_path); - - /* Open file */ - file_id = guac_rdp_fs_open(fs, file_path, GENERIC_WRITE, 0, - FILE_OVERWRITE_IF, 0); - if (file_id < 0) { - guac_protocol_send_ack(user->socket, stream, "FAIL (CANNOT OPEN)", - GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN); - guac_socket_flush(user->socket); - return 0; - } - - /* Init upload status */ - rdp_stream = malloc(sizeof(guac_rdp_stream)); - rdp_stream->type = GUAC_RDP_UPLOAD_STREAM; - rdp_stream->upload_status.offset = 0; - rdp_stream->upload_status.file_id = file_id; - stream->data = rdp_stream; - stream->blob_handler = guac_rdp_upload_blob_handler; - stream->end_handler = guac_rdp_upload_end_handler; - - guac_protocol_send_ack(user->socket, stream, "OK (STREAM BEGIN)", - GUAC_PROTOCOL_STATUS_SUCCESS); - guac_socket_flush(user->socket); - return 0; - -} - -int guac_rdp_upload_blob_handler(guac_user* user, guac_stream* stream, - void* data, int length) { - - int bytes_written; - guac_rdp_stream* rdp_stream = (guac_rdp_stream*) stream->data; - - /* Get filesystem, return error if no filesystem 0*/ - guac_client* client = user->client; - guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; - guac_rdp_fs* fs = rdp_client->filesystem; - if (fs == NULL) { - guac_protocol_send_ack(user->socket, stream, "FAIL (NO FS)", - GUAC_PROTOCOL_STATUS_SERVER_ERROR); - guac_socket_flush(user->socket); - return 0; - } - - /* Write entire block */ - while (length > 0) { - - /* Attempt write */ - bytes_written = guac_rdp_fs_write(fs, - rdp_stream->upload_status.file_id, - rdp_stream->upload_status.offset, - data, length); - - /* On error, abort */ - if (bytes_written < 0) { - guac_protocol_send_ack(user->socket, stream, - "FAIL (BAD WRITE)", - GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN); - guac_socket_flush(user->socket); - return 0; - } - - /* Update counters */ - rdp_stream->upload_status.offset += bytes_written; - data += bytes_written; - length -= bytes_written; - - } - - guac_protocol_send_ack(user->socket, stream, "OK (DATA RECEIVED)", - GUAC_PROTOCOL_STATUS_SUCCESS); - guac_socket_flush(user->socket); - return 0; - -} - -int guac_rdp_upload_end_handler(guac_user* user, guac_stream* stream) { - - guac_client* client = user->client; - guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; - guac_rdp_stream* rdp_stream = (guac_rdp_stream*) stream->data; - - /* Get filesystem, return error if no filesystem */ - guac_rdp_fs* fs = rdp_client->filesystem; - if (fs == NULL) { - guac_protocol_send_ack(user->socket, stream, "FAIL (NO FS)", - GUAC_PROTOCOL_STATUS_SERVER_ERROR); - guac_socket_flush(user->socket); - return 0; - } - - /* Close file */ - guac_rdp_fs_close(fs, rdp_stream->upload_status.file_id); - - /* Acknowledge stream end */ - guac_protocol_send_ack(user->socket, stream, "OK (STREAM END)", - GUAC_PROTOCOL_STATUS_SUCCESS); - guac_socket_flush(user->socket); - - free(rdp_stream); - return 0; - -} - -int guac_rdp_download_ack_handler(guac_user* user, guac_stream* stream, - char* message, guac_protocol_status status) { - - guac_client* client = user->client; - guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; - guac_rdp_stream* rdp_stream = (guac_rdp_stream*) stream->data; - - /* Get filesystem, return error if no filesystem */ - guac_rdp_fs* fs = rdp_client->filesystem; - if (fs == NULL) { - guac_protocol_send_ack(user->socket, stream, "FAIL (NO FS)", - GUAC_PROTOCOL_STATUS_SERVER_ERROR); - guac_socket_flush(user->socket); - return 0; - } - - /* If successful, read data */ - if (status == GUAC_PROTOCOL_STATUS_SUCCESS) { - - /* Attempt read into buffer */ - char buffer[4096]; - int bytes_read = guac_rdp_fs_read(fs, - rdp_stream->download_status.file_id, - rdp_stream->download_status.offset, buffer, sizeof(buffer)); - - /* If bytes read, send as blob */ - if (bytes_read > 0) { - rdp_stream->download_status.offset += bytes_read; - guac_protocol_send_blob(user->socket, stream, - buffer, bytes_read); - } - - /* If EOF, send end */ - else if (bytes_read == 0) { - guac_protocol_send_end(user->socket, stream); - guac_user_free_stream(user, stream); - free(rdp_stream); - } - - /* Otherwise, fail stream */ - else { - guac_user_log(user, GUAC_LOG_ERROR, - "Error reading file for download"); - guac_protocol_send_end(user->socket, stream); - guac_user_free_stream(user, stream); - free(rdp_stream); - } - - guac_socket_flush(user->socket); - - } - - /* Otherwise, return stream to user */ - else - guac_user_free_stream(user, stream); - - return 0; - -} - -int guac_rdp_ls_ack_handler(guac_user* user, guac_stream* stream, - char* message, guac_protocol_status status) { - - int blob_written = 0; - const char* filename; - - guac_rdp_stream* rdp_stream = (guac_rdp_stream*) stream->data; - - /* If unsuccessful, free stream and abort */ - if (status != GUAC_PROTOCOL_STATUS_SUCCESS) { - guac_rdp_fs_close(rdp_stream->ls_status.fs, - rdp_stream->ls_status.file_id); - guac_user_free_stream(user, stream); - free(rdp_stream); - return 0; - } - - /* While directory entries remain */ - while ((filename = guac_rdp_fs_read_dir(rdp_stream->ls_status.fs, - rdp_stream->ls_status.file_id)) != NULL - && !blob_written) { - - char absolute_path[GUAC_RDP_FS_MAX_PATH]; - - /* Skip current and parent directory entries */ - if (strcmp(filename, ".") == 0 || strcmp(filename, "..") == 0) - continue; - - /* Concatenate into absolute path - skip if invalid */ - if (!guac_rdp_fs_append_filename(absolute_path, - rdp_stream->ls_status.directory_name, filename)) { - - guac_user_log(user, GUAC_LOG_DEBUG, - "Skipping filename \"%s\" - filename is invalid or " - "resulting path is too long", filename); - - continue; - } - - /* Attempt to open file to determine type */ - int file_id = guac_rdp_fs_open(rdp_stream->ls_status.fs, absolute_path, - GENERIC_READ, 0, FILE_OPEN, 0); - if (file_id < 0) - continue; - - /* Get opened file */ - guac_rdp_fs_file* file = guac_rdp_fs_get_file(rdp_stream->ls_status.fs, - file_id); - if (file == NULL) { - guac_client_log(rdp_stream->ls_status.fs->client, GUAC_LOG_DEBUG, - "%s: Successful open produced bad file_id: %i", - __func__, file_id); - return 0; - } - - /* Determine mimetype */ - const char* mimetype; - if (file->attributes & FILE_ATTRIBUTE_DIRECTORY) - mimetype = GUAC_USER_STREAM_INDEX_MIMETYPE; - else - mimetype = "application/octet-stream"; - - /* Write entry */ - blob_written |= guac_common_json_write_property(user, stream, - &rdp_stream->ls_status.json_state, absolute_path, mimetype); - - guac_rdp_fs_close(rdp_stream->ls_status.fs, file_id); - - } - - /* Complete JSON and cleanup at end of directory */ - if (filename == NULL) { - - /* Complete JSON object */ - guac_common_json_end_object(user, stream, - &rdp_stream->ls_status.json_state); - guac_common_json_flush(user, stream, - &rdp_stream->ls_status.json_state); - - /* Clean up resources */ - guac_rdp_fs_close(rdp_stream->ls_status.fs, - rdp_stream->ls_status.file_id); - free(rdp_stream); - - /* Signal of stream */ - guac_protocol_send_end(user->socket, stream); - guac_user_free_stream(user, stream); - - } - - guac_socket_flush(user->socket); - return 0; - -} - -int guac_rdp_download_get_handler(guac_user* user, guac_object* object, - char* name) { - - guac_client* client = user->client; - guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; - - /* Get filesystem, ignore request if no filesystem */ - guac_rdp_fs* fs = rdp_client->filesystem; - if (fs == NULL) - return 0; - - /* Attempt to open file for reading */ - int file_id = guac_rdp_fs_open(fs, name, GENERIC_READ, 0, FILE_OPEN, 0); - if (file_id < 0) { - guac_user_log(user, GUAC_LOG_INFO, "Unable to read file \"%s\"", - name); - return 0; - } - - /* Get opened file */ - guac_rdp_fs_file* file = guac_rdp_fs_get_file(fs, file_id); - if (file == NULL) { - guac_client_log(fs->client, GUAC_LOG_DEBUG, - "%s: Successful open produced bad file_id: %i", - __func__, file_id); - return 0; - } - - /* If directory, send contents of directory */ - if (file->attributes & FILE_ATTRIBUTE_DIRECTORY) { - - /* Create stream data */ - guac_rdp_stream* rdp_stream = malloc(sizeof(guac_rdp_stream)); - rdp_stream->type = GUAC_RDP_LS_STREAM; - rdp_stream->ls_status.fs = fs; - rdp_stream->ls_status.file_id = file_id; - guac_strlcpy(rdp_stream->ls_status.directory_name, name, - sizeof(rdp_stream->ls_status.directory_name)); - - /* Allocate stream for body */ - guac_stream* stream = guac_user_alloc_stream(user); - stream->ack_handler = guac_rdp_ls_ack_handler; - stream->data = rdp_stream; - - /* Init JSON object state */ - guac_common_json_begin_object(user, stream, - &rdp_stream->ls_status.json_state); - - /* Associate new stream with get request */ - guac_protocol_send_body(user->socket, object, stream, - GUAC_USER_STREAM_INDEX_MIMETYPE, name); - - } - - /* Otherwise, send file contents */ - else { - - /* Create stream data */ - guac_rdp_stream* rdp_stream = malloc(sizeof(guac_rdp_stream)); - rdp_stream->type = GUAC_RDP_DOWNLOAD_STREAM; - rdp_stream->download_status.file_id = file_id; - rdp_stream->download_status.offset = 0; - - /* Allocate stream for body */ - guac_stream* stream = guac_user_alloc_stream(user); - stream->data = rdp_stream; - stream->ack_handler = guac_rdp_download_ack_handler; - - /* Associate new stream with get request */ - guac_protocol_send_body(user->socket, object, stream, - "application/octet-stream", name); - - } - - guac_socket_flush(user->socket); - return 0; -} - -int guac_rdp_upload_put_handler(guac_user* user, guac_object* object, - guac_stream* stream, char* mimetype, char* name) { - - guac_client* client = user->client; - guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; - - /* Get filesystem, return error if no filesystem */ - guac_rdp_fs* fs = rdp_client->filesystem; - if (fs == NULL) { - guac_protocol_send_ack(user->socket, stream, "FAIL (NO FS)", - GUAC_PROTOCOL_STATUS_SERVER_ERROR); - guac_socket_flush(user->socket); - return 0; - } - - /* Open file */ - int file_id = guac_rdp_fs_open(fs, name, GENERIC_WRITE, 0, - FILE_OVERWRITE_IF, 0); - - /* Abort on failure */ - if (file_id < 0) { - guac_protocol_send_ack(user->socket, stream, "FAIL (CANNOT OPEN)", - GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN); - guac_socket_flush(user->socket); - return 0; - } - - /* Init upload stream data */ - guac_rdp_stream* rdp_stream = malloc(sizeof(guac_rdp_stream)); - rdp_stream->type = GUAC_RDP_UPLOAD_STREAM; - rdp_stream->upload_status.offset = 0; - rdp_stream->upload_status.file_id = file_id; - - /* Allocate stream, init for file upload */ - stream->data = rdp_stream; - stream->blob_handler = guac_rdp_upload_blob_handler; - stream->end_handler = guac_rdp_upload_end_handler; - - /* Acknowledge stream creation */ - guac_protocol_send_ack(user->socket, stream, "OK (STREAM BEGIN)", - GUAC_PROTOCOL_STATUS_SUCCESS); - guac_socket_flush(user->socket); - return 0; -} - diff --git a/src/protocols/rdp/stream.h b/src/protocols/rdp/stream.h deleted file mode 100644 index 3317c6cb..00000000 --- a/src/protocols/rdp/stream.h +++ /dev/null @@ -1,184 +0,0 @@ -/* - * 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. - */ - -#ifndef GUAC_RDP_STREAM_H -#define GUAC_RDP_STREAM_H - -#include "config.h" -#include "common/json.h" - -#include -#include -#include - -#include - -/** - * The transfer status of a file being downloaded. - */ -typedef struct guac_rdp_download_status { - - /** - * The file ID of the file being downloaded. - */ - int file_id; - - /** - * The current position within the file. - */ - uint64_t offset; - -} guac_rdp_download_status; - -/** - * Structure which represents the current state of an upload. - */ -typedef struct guac_rdp_upload_status { - - /** - * The overall offset within the file that the next write should - * occur at. - */ - int offset; - - /** - * The ID of the file being written to. - */ - int file_id; - -} guac_rdp_upload_status; - -/** - * The current state of a directory listing operation. - */ -typedef struct guac_rdp_ls_status { - - /** - * The filesystem associated with the directory being listed. - */ - guac_rdp_fs* fs; - - /** - * The file ID of the directory being listed. - */ - int file_id; - - /** - * The absolute path of the directory being listed. - */ - char directory_name[GUAC_RDP_FS_MAX_PATH]; - - /** - * The current state of the JSON directory object being written. - */ - guac_common_json_state json_state; - -} guac_rdp_ls_status; - -/** - * All available stream types. - */ -typedef enum guac_rdp_stream_type { - - /** - * An in-progress file upload. - */ - GUAC_RDP_UPLOAD_STREAM, - - /** - * An in-progress file download. - */ - GUAC_RDP_DOWNLOAD_STREAM, - - /** - * An in-progress stream of a directory listing. - */ - GUAC_RDP_LS_STREAM - -} guac_rdp_stream_type; - -/** - * Variable-typed stream data. - */ -typedef struct guac_rdp_stream { - - /** - * The type of this stream. - */ - guac_rdp_stream_type type; - - /** - * The file upload status. Only valid for GUAC_RDP_UPLOAD_STREAM. - */ - guac_rdp_upload_status upload_status; - - /** - * The file upload status. Only valid for GUAC_RDP_DOWNLOAD_STREAM. - */ - guac_rdp_download_status download_status; - - /** - * The directory list status. Only valid for GUAC_RDP_LS_STREAM. - */ - guac_rdp_ls_status ls_status; - -} guac_rdp_stream; - -/** - * Handler for inbound files related to file uploads. - */ -guac_user_file_handler guac_rdp_upload_file_handler; - -/** - * Handler for stream data related to file uploads. - */ -guac_user_blob_handler guac_rdp_upload_blob_handler; - -/** - * Handler for end-of-stream related to file uploads. - */ -guac_user_end_handler guac_rdp_upload_end_handler; - -/** - * Handler for acknowledgements of receipt of data related to file downloads. - */ -guac_user_ack_handler guac_rdp_download_ack_handler; - -/** - * Handler for ack messages received due to receipt of a "body" or "blob" - * instruction associated with a directory list operation. - */ -guac_user_ack_handler guac_rdp_ls_ack_handler; - -/** - * Handler for get messages. In context of downloads and the filesystem exposed - * via the Guacamole protocol, get messages request the body of a file within - * the filesystem. - */ -guac_user_get_handler guac_rdp_download_get_handler; - -/** - * Handler for put messages. In context of uploads and the filesystem exposed - * via the Guacamole protocol, put messages request write access to a file - * within the filesystem. - */ -guac_user_put_handler guac_rdp_upload_put_handler; - -#endif - diff --git a/src/protocols/rdp/upload.c b/src/protocols/rdp/upload.c new file mode 100644 index 00000000..24295ec5 --- /dev/null +++ b/src/protocols/rdp/upload.c @@ -0,0 +1,241 @@ +/* + * 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 "config.h" +#include "client.h" +#include "fs.h" +#include "rdp.h" +#include "upload.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/** + * Writes the given filename to the given upload path, sanitizing the filename + * and translating the filename to the root directory. + * + * @param filename + * The filename to sanitize and move to the root directory. + * + * @param path + * A pointer to a buffer which should receive the sanitized path. The + * buffer must hav at least GUAC_RDP_FS_MAX_PATH bytes available. + */ +static void __generate_upload_path(const char* filename, char* path) { + + int i; + + /* Add initial backslash */ + *(path++) = '\\'; + + for (i=1; iclient; + guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; + + int file_id; + char file_path[GUAC_RDP_FS_MAX_PATH]; + + /* Get filesystem, return error if no filesystem */ + guac_rdp_fs* fs = rdp_client->filesystem; + if (fs == NULL) { + guac_protocol_send_ack(user->socket, stream, "FAIL (NO FS)", + GUAC_PROTOCOL_STATUS_SERVER_ERROR); + guac_socket_flush(user->socket); + return 0; + } + + /* Translate name */ + __generate_upload_path(filename, file_path); + + /* Open file */ + file_id = guac_rdp_fs_open(fs, file_path, GENERIC_WRITE, 0, + FILE_OVERWRITE_IF, 0); + if (file_id < 0) { + guac_protocol_send_ack(user->socket, stream, "FAIL (CANNOT OPEN)", + GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN); + guac_socket_flush(user->socket); + return 0; + } + + /* Init upload status */ + guac_rdp_upload_status* upload_status = malloc(sizeof(guac_rdp_upload_status)); + upload_status->offset = 0; + upload_status->file_id = file_id; + stream->data = upload_status; + stream->blob_handler = guac_rdp_upload_blob_handler; + stream->end_handler = guac_rdp_upload_end_handler; + + guac_protocol_send_ack(user->socket, stream, "OK (STREAM BEGIN)", + GUAC_PROTOCOL_STATUS_SUCCESS); + guac_socket_flush(user->socket); + return 0; + +} + +int guac_rdp_upload_blob_handler(guac_user* user, guac_stream* stream, + void* data, int length) { + + int bytes_written; + guac_rdp_upload_status* upload_status = (guac_rdp_upload_status*) stream->data; + + /* Get filesystem, return error if no filesystem 0*/ + guac_client* client = user->client; + guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; + guac_rdp_fs* fs = rdp_client->filesystem; + if (fs == NULL) { + guac_protocol_send_ack(user->socket, stream, "FAIL (NO FS)", + GUAC_PROTOCOL_STATUS_SERVER_ERROR); + guac_socket_flush(user->socket); + return 0; + } + + /* Write entire block */ + while (length > 0) { + + /* Attempt write */ + bytes_written = guac_rdp_fs_write(fs, upload_status->file_id, + upload_status->offset, data, length); + + /* On error, abort */ + if (bytes_written < 0) { + guac_protocol_send_ack(user->socket, stream, + "FAIL (BAD WRITE)", + GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN); + guac_socket_flush(user->socket); + return 0; + } + + /* Update counters */ + upload_status->offset += bytes_written; + data += bytes_written; + length -= bytes_written; + + } + + guac_protocol_send_ack(user->socket, stream, "OK (DATA RECEIVED)", + GUAC_PROTOCOL_STATUS_SUCCESS); + guac_socket_flush(user->socket); + return 0; + +} + +int guac_rdp_upload_end_handler(guac_user* user, guac_stream* stream) { + + guac_client* client = user->client; + guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; + guac_rdp_upload_status* upload_status = (guac_rdp_upload_status*) stream->data; + + /* Get filesystem, return error if no filesystem */ + guac_rdp_fs* fs = rdp_client->filesystem; + if (fs == NULL) { + guac_protocol_send_ack(user->socket, stream, "FAIL (NO FS)", + GUAC_PROTOCOL_STATUS_SERVER_ERROR); + guac_socket_flush(user->socket); + return 0; + } + + /* Close file */ + guac_rdp_fs_close(fs, upload_status->file_id); + + /* Acknowledge stream end */ + guac_protocol_send_ack(user->socket, stream, "OK (STREAM END)", + GUAC_PROTOCOL_STATUS_SUCCESS); + guac_socket_flush(user->socket); + + free(upload_status); + return 0; + +} + +int guac_rdp_upload_put_handler(guac_user* user, guac_object* object, + guac_stream* stream, char* mimetype, char* name) { + + guac_client* client = user->client; + guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; + + /* Get filesystem, return error if no filesystem */ + guac_rdp_fs* fs = rdp_client->filesystem; + if (fs == NULL) { + guac_protocol_send_ack(user->socket, stream, "FAIL (NO FS)", + GUAC_PROTOCOL_STATUS_SERVER_ERROR); + guac_socket_flush(user->socket); + return 0; + } + + /* Open file */ + int file_id = guac_rdp_fs_open(fs, name, GENERIC_WRITE, 0, + FILE_OVERWRITE_IF, 0); + + /* Abort on failure */ + if (file_id < 0) { + guac_protocol_send_ack(user->socket, stream, "FAIL (CANNOT OPEN)", + GUAC_PROTOCOL_STATUS_CLIENT_FORBIDDEN); + guac_socket_flush(user->socket); + return 0; + } + + /* Init upload stream data */ + guac_rdp_upload_status* upload_status = malloc(sizeof(guac_rdp_upload_status)); + upload_status->offset = 0; + upload_status->file_id = file_id; + + /* Allocate stream, init for file upload */ + stream->data = upload_status; + stream->blob_handler = guac_rdp_upload_blob_handler; + stream->end_handler = guac_rdp_upload_end_handler; + + /* Acknowledge stream creation */ + guac_protocol_send_ack(user->socket, stream, "OK (STREAM BEGIN)", + GUAC_PROTOCOL_STATUS_SUCCESS); + guac_socket_flush(user->socket); + return 0; +} + diff --git a/src/protocols/rdp/upload.h b/src/protocols/rdp/upload.h new file mode 100644 index 00000000..b7563ffe --- /dev/null +++ b/src/protocols/rdp/upload.h @@ -0,0 +1,73 @@ +/* + * 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. + */ + +#ifndef GUAC_RDP_UPLOAD_H +#define GUAC_RDP_UPLOAD_H + +#include "config.h" +#include "common/json.h" + +#include +#include +#include + +#include + +/** + * Structure which represents the current state of an upload. + */ +typedef struct guac_rdp_upload_status { + + /** + * The overall offset within the file that the next write should + * occur at. + */ + int offset; + + /** + * The ID of the file being written to. + */ + int file_id; + +} guac_rdp_upload_status; + +/** + * Handler for inbound files related to file uploads. + */ +guac_user_file_handler guac_rdp_upload_file_handler; + +/** + * Handler for stream data related to file uploads. + */ +guac_user_blob_handler guac_rdp_upload_blob_handler; + +/** + * Handler for end-of-stream related to file uploads. + */ +guac_user_end_handler guac_rdp_upload_end_handler; + +/** + * Handler for put messages. In context of uploads and the filesystem exposed + * via the Guacamole protocol, put messages request write access to a file + * within the filesystem. + */ +guac_user_put_handler guac_rdp_upload_put_handler; + +#endif + diff --git a/src/protocols/rdp/user.c b/src/protocols/rdp/user.c index a22d138b..b070b4a2 100644 --- a/src/protocols/rdp/user.c +++ b/src/protocols/rdp/user.c @@ -24,7 +24,7 @@ #include "input.h" #include "rdp.h" #include "settings.h" -#include "stream.h" +#include "upload.h" #include "user.h" #ifdef ENABLE_COMMON_SSH