From a6b46a0ea0b1c695376ab3e724edc7b4bdf08631 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Fri, 10 Jul 2015 15:54:44 -0700 Subject: [PATCH] GUAC-1171: Add and document SFTP functionality within common SSH. --- src/common-ssh/guac_sftp.c | 689 ++++++++++++++++++++++++++++++++++++- src/common-ssh/guac_sftp.h | 139 +++++++- src/protocols/ssh/client.c | 3 - src/protocols/ssh/client.h | 16 - 4 files changed, 808 insertions(+), 39 deletions(-) diff --git a/src/common-ssh/guac_sftp.c b/src/common-ssh/guac_sftp.c index a2524eaf..54cfa804 100644 --- a/src/common-ssh/guac_sftp.c +++ b/src/common-ssh/guac_sftp.c @@ -20,22 +20,697 @@ * THE SOFTWARE. */ +#include "guac_sftp.h" #include "guac_ssh.h" #include #include +#include +#include #include -guac_object* guac_common_ssh_create_sftp_filesystem(guac_client* client, - const char* name, LIBSSH2_SESSION* session) { +#include +#include +#include +#include - /* STUB */ - return NULL; +/** + * Concatenates the given filename with the given path, separating the two + * with a single forward slash. The full result must be no more than + * GUAC_COMMON_SSH_SFTP_MAX_PATH bytes long, counting null terminator. + * + * @param fullpath + * The buffer to store the result within. This buffer must be at least + * GUAC_COMMON_SSH_SFTP_MAX_PATH bytes long. + * + * @param path + * The path to append the filename to. + * + * @param filename + * The filename to append to the path. + * + * @return + * Non-zero if the filename is valid and was successfully appended to the + * path, zero otherwise. + */ +static int guac_ssh_append_filename(char* fullpath, const char* path, + const char* filename) { + + int i; + + /* Disallow "." as a filename */ + if (strcmp(filename, ".") == 0) + return 0; + + /* Disallow ".." as a filename */ + if (strcmp(filename, "..") == 0) + return 0; + + /* Copy path, append trailing slash */ + for (i=0; i 0 && path[i-1] != '/') + fullpath[i++] = '/'; + break; + } + + /* Copy character if not end of string */ + fullpath[i] = c; + + } + + /* Append filename */ + for (; idata; + + /* Attempt write */ + if (libssh2_sftp_write(file, data, length) == length) { + guac_client_log(client, GUAC_LOG_DEBUG, "%i bytes written", length); + guac_protocol_send_ack(client->socket, stream, "SFTP: OK", + GUAC_PROTOCOL_STATUS_SUCCESS); + guac_socket_flush(client->socket); + } + + /* Inform of any errors */ + else { + guac_client_log(client, GUAC_LOG_INFO, "Unable to write to file"); + guac_protocol_send_ack(client->socket, stream, "SFTP: Write failed", + GUAC_PROTOCOL_STATUS_SERVER_ERROR); + guac_socket_flush(client->socket); + } + + return 0; + +} + +/** + * Handler for end messages which terminate an inbound SFTP data transfer + * (upload). The data associated with the given stream is expected to be a + * pointer to an open LIBSSH2_SFTP_HANDLE for the file to which the data + * has been written and which should now be closed. + * + * @param client + * The client receiving the end message. + * + * @param stream + * The Guacamole protocol stream associated with the received end message. + * + * @return + * Zero if the file is closed successfully, or non-zero on error. + */ +static int guac_common_ssh_sftp_end_handler(guac_client* client, + guac_stream* stream) { + + /* Pull file from stream */ + LIBSSH2_SFTP_HANDLE* file = (LIBSSH2_SFTP_HANDLE*) stream->data; + + /* Attempt to close file */ + if (libssh2_sftp_close(file) == 0) { + guac_client_log(client, GUAC_LOG_DEBUG, "File closed"); + guac_protocol_send_ack(client->socket, stream, "SFTP: OK", + GUAC_PROTOCOL_STATUS_SUCCESS); + guac_socket_flush(client->socket); + } + else { + guac_client_log(client, GUAC_LOG_INFO, "Unable to close file"); + guac_protocol_send_ack(client->socket, stream, "SFTP: Close failed", + GUAC_PROTOCOL_STATUS_SERVER_ERROR); + guac_socket_flush(client->socket); + } + + return 0; + +} + +int guac_common_ssh_sftp_handle_file_stream(guac_object* filesystem, + guac_stream* stream, char* mimetype, char* filename) { + + guac_common_ssh_sftp_data* sftp_data = + (guac_common_ssh_sftp_data*) filesystem->data; + + guac_client* client = sftp_data->ssh_session->client; + + char fullpath[GUAC_COMMON_SSH_SFTP_MAX_PATH]; + LIBSSH2_SFTP_HANDLE* file; + + /* Concatenate filename with path */ + if (!guac_ssh_append_filename(fullpath, sftp_data->upload_path, + filename)) { + + guac_client_log(client, GUAC_LOG_DEBUG, + "Filename \"%s\" is invalid or resulting path is too long", + filename); + + /* Abort transfer - invalid filename */ + guac_protocol_send_ack(client->socket, stream, + "SFTP: Illegal filename", + GUAC_PROTOCOL_STATUS_CLIENT_BAD_REQUEST); + + guac_socket_flush(client->socket); + return 0; + } + + /* Open file via SFTP */ + file = libssh2_sftp_open(sftp_data->sftp_session, fullpath, + LIBSSH2_FXF_WRITE | LIBSSH2_FXF_CREAT | LIBSSH2_FXF_TRUNC, + S_IRUSR | S_IWUSR); + + /* Inform of status */ + if (file != NULL) { + + guac_client_log(client, GUAC_LOG_DEBUG, + "File \"%s\" opened", + fullpath); + + guac_protocol_send_ack(client->socket, stream, "SFTP: File opened", + GUAC_PROTOCOL_STATUS_SUCCESS); + guac_socket_flush(client->socket); + } + else { + guac_client_log(client, GUAC_LOG_INFO, "Unable to open file \"%s\": %s", + fullpath, libssh2_sftp_last_error(sftp_data->sftp_session)); + guac_protocol_send_ack(client->socket, stream, "SFTP: Open failed", + GUAC_PROTOCOL_STATUS_RESOURCE_NOT_FOUND); + guac_socket_flush(client->socket); + } + + /* Set handlers for file stream */ + stream->blob_handler = guac_common_ssh_sftp_blob_handler; + stream->end_handler = guac_common_ssh_sftp_end_handler; + + /* Store file within stream */ + stream->data = file; + return 0; + +} + +/** + * Handler for ack messages which continue an outbound SFTP data transfer + * (download), signalling the current status and requesting additional data. + * The data associated with the given stream is expected to be a pointer to an + * open LIBSSH2_SFTP_HANDLE for the file from which the data is to be read. + * + * @param client + * The client receiving the ack message. + * + * @param stream + * The Guacamole protocol stream associated with the received ack message. + * + * @param message + * An arbitrary human-readable message describing the nature of the + * success or failure denoted by the ack message. + * + * @param status + * The status code associated with the ack message, which may indicate + * success or an error. + * + * @return + * Zero if the file is read from successfully, or non-zero on error. + */ +static int guac_common_ssh_sftp_ack_handler(guac_client* client, + guac_stream* stream, char* message, guac_protocol_status status) { + + /* Pull file from stream */ + LIBSSH2_SFTP_HANDLE* file = (LIBSSH2_SFTP_HANDLE*) stream->data; + + /* If successful, read data */ + if (status == GUAC_PROTOCOL_STATUS_SUCCESS) { + + /* Attempt read into buffer */ + char buffer[4096]; + int bytes_read = libssh2_sftp_read(file, buffer, sizeof(buffer)); + + /* If bytes read, send as blob */ + if (bytes_read > 0) { + guac_protocol_send_blob(client->socket, stream, + buffer, bytes_read); + + guac_client_log(client, GUAC_LOG_DEBUG, "%i bytes sent to client", + bytes_read); + + } + + /* If EOF, send end */ + else if (bytes_read == 0) { + guac_client_log(client, GUAC_LOG_DEBUG, "File sent"); + guac_protocol_send_end(client->socket, stream); + guac_client_free_stream(client, stream); + } + + /* Otherwise, fail stream */ + else { + guac_client_log(client, GUAC_LOG_INFO, "Error reading file"); + guac_protocol_send_end(client->socket, stream); + guac_client_free_stream(client, stream); + } + + guac_socket_flush(client->socket); + + } + + /* Otherwise, return stream to client */ + else + guac_client_free_stream(client, stream); + + return 0; +} + +guac_stream* guac_common_ssh_sftp_download_file(guac_object* filesystem, + char* filename) { + + guac_common_ssh_sftp_data* sftp_data = + (guac_common_ssh_sftp_data*) filesystem->data; + + guac_client* client = sftp_data->ssh_session->client; + + guac_stream* stream; + LIBSSH2_SFTP_HANDLE* file; + + /* Attempt to open file for reading */ + file = libssh2_sftp_open(sftp_data->sftp_session, filename, + LIBSSH2_FXF_READ, 0); + if (file == NULL) { + guac_client_log(client, GUAC_LOG_INFO, + "Unable to read file \"%s\": %s", + filename, libssh2_sftp_last_error(sftp_data->sftp_session)); + return NULL; + } + + /* Allocate stream */ + stream = guac_client_alloc_stream(client); + stream->ack_handler = guac_common_ssh_sftp_ack_handler; + stream->data = file; + + /* Send stream start, strip name */ + filename = basename(filename); + guac_protocol_send_file(client->socket, stream, + "application/octet-stream", filename); + guac_socket_flush(client->socket); + + guac_client_log(client, GUAC_LOG_DEBUG, "Sending file \"%s\"", filename); + return stream; + +} + +void guac_common_ssh_sftp_set_upload_path(guac_object* filesystem, + const char* path) { + + guac_common_ssh_sftp_data* sftp_data = + (guac_common_ssh_sftp_data*) filesystem->data; + + guac_client* client = sftp_data->ssh_session->client; + + /* Ignore requests which exceed maximum-allowed path */ + int length = strnlen(path, GUAC_COMMON_SSH_SFTP_MAX_PATH)+1; + if (length > GUAC_COMMON_SSH_SFTP_MAX_PATH) { + guac_client_log(client, GUAC_LOG_ERROR, + "Submitted path exceeds limit of %i bytes", + GUAC_COMMON_SSH_SFTP_MAX_PATH); + return; + } + + /* Copy path */ + memcpy(sftp_data->upload_path, path, length); + guac_client_log(client, GUAC_LOG_DEBUG, "Upload path set to \"%s\"", path); + +} + +/** + * Handler for ack messages received due to receipt of a "body" or "blob" + * instruction associated with a SFTP directory list operation. + * + * @param client + * The client receiving the ack message. + * + * @param stream + * The Guacamole protocol stream associated with the received ack message. + * + * @param message + * An arbitrary human-readable message describing the nature of the + * success or failure denoted by this ack message. + * + * @param status + * The status code associated with this ack message, which may indicate + * success or an error. + * + * @return + * Zero on success, non-zero on error. + */ +static int guac_common_ssh_sftp_ls_ack_handler(guac_client* client, + guac_stream* stream, char* message, guac_protocol_status status) { + + int bytes_read; + int blob_written = 0; + + char filename[GUAC_COMMON_SSH_SFTP_MAX_PATH]; + LIBSSH2_SFTP_ATTRIBUTES attributes; + + guac_common_ssh_sftp_ls_state* list_state = + (guac_common_ssh_sftp_ls_state*) stream->data; + + guac_common_ssh_sftp_data* sftp_data = list_state->sftp_data; + + LIBSSH2_SFTP* sftp = sftp_data->sftp_session; + + /* If unsuccessful, free stream and abort */ + if (status != GUAC_PROTOCOL_STATUS_SUCCESS) { + libssh2_sftp_closedir(list_state->directory); + guac_client_free_stream(client, stream); + free(list_state); + return 0; + } + + /* While directory entries remain */ + while ((bytes_read = libssh2_sftp_readdir(list_state->directory, + filename, sizeof(filename), &attributes)) > 0 + && !blob_written) { + + char absolute_path[GUAC_COMMON_SSH_SFTP_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_ssh_append_filename(absolute_path, + list_state->directory_name, filename)) { + + guac_client_log(client, GUAC_LOG_DEBUG, + "Skipping filename \"%s\" - filename is invalid or " + "resulting path is too long", filename); + + continue; + } + + /* Stat explicitly if symbolic link (might point to directory) */ + if (LIBSSH2_SFTP_S_ISLNK(attributes.permissions)) + libssh2_sftp_stat(sftp, absolute_path, &attributes); + + /* Determine mimetype */ + const char* mimetype; + if (LIBSSH2_SFTP_S_ISDIR(attributes.permissions)) + mimetype = GUAC_CLIENT_STREAM_INDEX_MIMETYPE; + else + mimetype = "application/octet-stream"; + + /* Write entry */ + blob_written |= guac_common_json_write_property(client, stream, + &list_state->json_state, absolute_path, mimetype); + + } + + /* Complete JSON and cleanup at end of directory */ + if (bytes_read <= 0) { + + /* Complete JSON object */ + guac_common_json_end_object(client, stream, &list_state->json_state); + guac_common_json_flush(client, stream, &list_state->json_state); + + /* Clean up resources */ + libssh2_sftp_closedir(list_state->directory); + free(list_state); + + /* Signal of stream */ + guac_protocol_send_end(client->socket, stream); + guac_client_free_stream(client, stream); + + } + + guac_socket_flush(client->socket); + return 0; + +} + +/** + * Handler for get messages. In context of SFTP and the filesystem exposed via + * the Guacamole protocol, get messages request the body of a file within the + * filesystem. + * + * @param client + * The client receiving the get message. + * + * @param object + * The Guacamole protocol object associated with the get request itself. + * + * @param name + * The name of the input stream (file) being requested. + * + * @return + * Zero on success, non-zero on error. + */ +static int guac_common_ssh_sftp_get_handler(guac_client* client, + guac_object* object, char* name) { + + guac_common_ssh_sftp_data* sftp_data = + (guac_common_ssh_sftp_data*) object->data; + + LIBSSH2_SFTP* sftp = sftp_data->sftp_session; + LIBSSH2_SFTP_ATTRIBUTES attributes; + + /* Attempt to read file information */ + if (libssh2_sftp_stat(sftp, name, &attributes)) { + guac_client_log(client, GUAC_LOG_INFO, "Unable to read file \"%s\"", + name); + return 0; + } + + /* If directory, send contents of directory */ + if (LIBSSH2_SFTP_S_ISDIR(attributes.permissions)) { + + /* Open as directory */ + LIBSSH2_SFTP_HANDLE* dir = libssh2_sftp_opendir(sftp, name); + if (dir == NULL) { + guac_client_log(client, GUAC_LOG_INFO, + "Unable to read directory \"%s\": %s", + name, libssh2_sftp_last_error(sftp)); + return 0; + } + + /* Init directory listing state */ + guac_common_ssh_sftp_ls_state* list_state = + malloc(sizeof(guac_common_ssh_sftp_ls_state)); + + list_state->directory = dir; + list_state->sftp_data = sftp_data; + strncpy(list_state->directory_name, name, + sizeof(list_state->directory_name)); + + /* Allocate stream for body */ + guac_stream* stream = guac_client_alloc_stream(client); + stream->ack_handler = guac_common_ssh_sftp_ls_ack_handler; + stream->data = list_state; + + /* Init JSON object state */ + guac_common_json_begin_object(client, stream, &list_state->json_state); + + /* Associate new stream with get request */ + guac_protocol_send_body(client->socket, object, stream, + GUAC_CLIENT_STREAM_INDEX_MIMETYPE, name); + + } + + /* Otherwise, send file contents */ + else { + + /* Open as normal file */ + LIBSSH2_SFTP_HANDLE* file = libssh2_sftp_open(sftp, name, + LIBSSH2_FXF_READ, 0); + if (file == NULL) { + guac_client_log(client, GUAC_LOG_INFO, + "Unable to read file \"%s\": %s", + name, libssh2_sftp_last_error(sftp)); + return 0; + } + + /* Allocate stream for body */ + guac_stream* stream = guac_client_alloc_stream(client); + stream->ack_handler = guac_common_ssh_sftp_ack_handler; + stream->data = file; + + /* Associate new stream with get request */ + guac_protocol_send_body(client->socket, object, stream, + "application/octet-stream", name); + + } + + guac_socket_flush(client->socket); + return 0; +} + +/** + * Handler for put messages. In context of SFTP and the filesystem exposed via + * the Guacamole protocol, put messages request write access to a file within + * the filesystem. + * + * @param client + * The client receiving the put message. + * + * @param object + * The Guacamole protocol object associated with the put request itself. + * + * @param stream + * The Guacamole protocol stream along which the client will be sending + * file data. + * + * @param mimetype + * The mimetype of the data being send along the stream. + * + * @param name + * The name of the input stream (file) being requested. + * + * @return + * Zero on success, non-zero on error. + */ +static int guac_common_ssh_sftp_put_handler(guac_client* client, + guac_object* object, guac_stream* stream, char* mimetype, char* name) { + + guac_common_ssh_sftp_data* sftp_data = + (guac_common_ssh_sftp_data*) object->data; + + LIBSSH2_SFTP* sftp = sftp_data->sftp_session; + + /* Open file via SFTP */ + LIBSSH2_SFTP_HANDLE* file = libssh2_sftp_open(sftp, name, + LIBSSH2_FXF_WRITE | LIBSSH2_FXF_CREAT | LIBSSH2_FXF_TRUNC, + S_IRUSR | S_IWUSR); + + /* Acknowledge stream if successful */ + if (file != NULL) { + guac_client_log(client, GUAC_LOG_DEBUG, "File \"%s\" opened", name); + guac_protocol_send_ack(client->socket, stream, "SFTP: File opened", + GUAC_PROTOCOL_STATUS_SUCCESS); + } + + /* Abort on failure */ + else { + guac_client_log(client, GUAC_LOG_INFO, "Unable to open file \"%s\": %s", + name, libssh2_sftp_last_error(sftp)); + guac_protocol_send_ack(client->socket, stream, "SFTP: Open failed", + GUAC_PROTOCOL_STATUS_RESOURCE_NOT_FOUND); + } + + /* Set handlers for file stream */ + stream->blob_handler = guac_common_ssh_sftp_blob_handler; + stream->end_handler = guac_common_ssh_sftp_end_handler; + + /* Store file within stream */ + stream->data = file; + + guac_socket_flush(client->socket); + return 0; +} + +guac_object* guac_common_ssh_create_sftp_filesystem( + guac_common_ssh_session* session, + const char* name) { + + guac_client* client = session->client; + + /* Allocate data for SFTP session */ + guac_common_ssh_sftp_data* sftp_data = + malloc(sizeof(guac_common_ssh_sftp_data)); + + /* Associate SSH session with SFTP data */ + sftp_data->ssh_session = session; + + /* Initially upload files to current directory */ + strcpy(sftp_data->upload_path, "."); + + /* Request SFTP */ + sftp_data->sftp_session = libssh2_sftp_init(session->session); + if (sftp_data->sftp_session == NULL) { + guac_client_abort(client, GUAC_PROTOCOL_STATUS_UPSTREAM_ERROR, + "Unable to start SFTP session."); + return NULL; + } + + /* Init filesystem */ + guac_object* filesystem = guac_client_alloc_object(client); + filesystem->get_handler = guac_common_ssh_sftp_get_handler; + filesystem->put_handler = guac_common_ssh_sftp_put_handler; + filesystem->data = sftp_data; + + /* Send filesystem to client */ + guac_protocol_send_filesystem(client->socket, filesystem, "/"); + guac_socket_flush(client->socket); + + /* Return allocated filesystem */ + return filesystem; + +} + +void guac_common_ssh_destroy_sftp_filesystem(guac_object* filesystem) { + + guac_common_ssh_sftp_data* sftp_data = + (guac_common_ssh_sftp_data*) filesystem->data; + + /* Shutdown SFTP session */ + libssh2_sftp_shutdown(sftp_data->sftp_session); + + /* Clean up the SFTP filesystem object */ + guac_client_free_object(sftp_data->ssh_session->client, filesystem); + + /* Disconnect SSH session corresponding to the SFTP session */ + guac_common_ssh_destroy_session(sftp_data->ssh_session); + } diff --git a/src/common-ssh/guac_sftp.h b/src/common-ssh/guac_sftp.h index ee6f3885..13511115 100644 --- a/src/common-ssh/guac_sftp.h +++ b/src/common-ssh/guac_sftp.h @@ -23,9 +23,69 @@ #ifndef GUAC_COMMON_SSH_SFTP_H #define GUAC_COMMON_SSH_SFTP_H +#include "guac_json.h" +#include "guac_ssh.h" + #include #include #include +#include + +/** + * Maximum number of bytes per path. + */ +#define GUAC_COMMON_SSH_SFTP_MAX_PATH 2048 + +/** + * Data associated with an SFTP-driven filesystem object. + */ +typedef struct guac_common_ssh_sftp_data { + + /** + * The distinct SSH session used for SFTP. + */ + guac_common_ssh_session* ssh_session; + + /** + * SFTP session, used for file transfers. + */ + LIBSSH2_SFTP* sftp_session; + + /** + * The path files will be sent to, if uploaded directly via a "file" + * instruction. + */ + char upload_path[GUAC_COMMON_SSH_SFTP_MAX_PATH]; + +} guac_common_ssh_sftp_data; + +/** + * The current state of a directory listing operation. + */ +typedef struct guac_common_ssh_sftp_ls_state { + + /** + * Data associated with the current SFTP session. + */ + guac_common_ssh_sftp_data* sftp_data; + + /** + * Reference to the directory currently being listed over SFTP. This + * directory must already be open from a call to libssh2_sftp_opendir(). + */ + LIBSSH2_SFTP_HANDLE* directory; + + /** + * The absolute path of the directory being listed. + */ + char directory_name[GUAC_COMMON_SSH_SFTP_MAX_PATH]; + + /** + * The current state of the JSON directory object being written. + */ + guac_common_json_state json_state; + +} guac_common_ssh_sftp_ls_state; /** * Creates a new Guacamole filesystem object which provides access to files @@ -33,35 +93,88 @@ * will no longer be used, it must be explicitly destroyed with * guac_common_ssh_destroy_sftp_filesystem(). * - * @param client - * The Guacamole client which will be associated with the new filesystem - * object. + * @param session + * The session to use to provide SFTP. * * @param name * The name to send as the name of the filesystem. * - * @param session - * The session to use to provide SFTP. - * * @return * A new Guacamole filesystem object, already configured to use SFTP for * uploading and downloading files. */ -guac_object* guac_common_ssh_create_sftp_filesystem(guac_client* client, - const char* name, LIBSSH2_SESSION* session); +guac_object* guac_common_ssh_create_sftp_filesystem( + guac_common_ssh_session* session, + const char* name); /** * Destroys the given filesystem object, disconnecting from SFTP and freeing * and associated resources. * - * @param client - * The client associated with the filesystem object. - * * @param object * The filesystem object to destroy. */ -void guac_common_ssh_destroy_sftp_filesystem(guac_client* client, - guac_object* filesystem); +void guac_common_ssh_destroy_sftp_filesystem(guac_object* filesystem); + +/** + * Initiates an SFTP file download to the user via the Guacamole "file" + * instruction. The download will be automatically monitored and continued + * after this function terminates in response to "ack" instructions received by + * the client. + * + * @param filesystem + * The filesystem containing the file to be downloaded. + * + * @param filename + * The filename of the file to download, relative to the given filesystem. + * + * @return + * The file stream created for the file download, already configured to + * properly handle "ack" responses, etc. from the client. + */ +guac_stream* guac_common_ssh_sftp_download_file(guac_object* filesystem, + char* filename); + +/** + * Handles an incoming stream from a Guacamole "file" instruction, saving the + * contents of that stream to the file having the given name within the + * upload directory set by guac_common_ssh_sftp_set_upload_path(). + * + * @param filesystem + * The filesystem that should receive the uploaded file. + * + * @param stream + * The stream through which the uploaded file data will be received. + * + * @param mimetype + * The mimetype of the data being received. + * + * @param filename + * The filename of the file to write to. This filename will always be taken + * relative to the upload path set by + * guac_common_ssh_sftp_set_upload_path(). + * + * @return + * Zero if the incoming stream has been handled successfully, non-zero on + * failure. + */ +int guac_common_ssh_sftp_handle_file_stream(guac_object* filesystem, + guac_stream* stream, char* mimetype, char* filename); + +/** + * Set the destination directory for future uploads submitted via + * guac_common_ssh_sftp_handle_file_stream(). This function has no bearing + * on the destination directories of files uploaded with "put" instructions. + * + * @param filesystem + * The filesystem to set the upload path of. + * + * @param path + * The path to use for future uploads submitted via the + * guac_common_ssh_sftp_handle_file_stream() function. + */ +void guac_common_ssh_sftp_set_upload_path(guac_object* filesystem, + const char* path); #endif diff --git a/src/protocols/ssh/client.c b/src/protocols/ssh/client.c index 63c5f2c8..d231fa7f 100644 --- a/src/protocols/ssh/client.c +++ b/src/protocols/ssh/client.c @@ -159,10 +159,7 @@ int guac_client_init(guac_client* client, int argc, char** argv) { /* Parse SFTP enable */ client_data->enable_sftp = strcmp(argv[IDX_ENABLE_SFTP], "true") == 0; - client_data->sftp_session = NULL; - client_data->sftp_ssh_session = NULL; client_data->sftp_filesystem = NULL; - strcpy(client_data->sftp_upload_path, "."); #ifdef ENABLE_SSH_AGENT client_data->enable_agent = strcmp(argv[IDX_ENABLE_AGENT], "true") == 0; diff --git a/src/protocols/ssh/client.h b/src/protocols/ssh/client.h index 40f97ab2..99d3257b 100644 --- a/src/protocols/ssh/client.h +++ b/src/protocols/ssh/client.h @@ -114,27 +114,11 @@ typedef struct ssh_guac_client_data { */ guac_common_ssh_session* session; - /** - * The distinct SSH session used for SFTP. - */ - guac_common_ssh_session* sftp_ssh_session; - - /** - * SFTP session, used for file transfers. - */ - LIBSSH2_SFTP* sftp_session; - /** * The filesystem object exposed for the SFTP session. */ guac_object* sftp_filesystem; - /** - * The path files will be sent to, if uploaded directly via a "file" - * instruction. - */ - char sftp_upload_path[GUAC_SFTP_MAX_PATH]; - /** * SSH terminal channel, used by the SSH client thread. */