diff --git a/src/common/clipboard.c b/src/common/clipboard.c index 4452bee7..eb2f5480 100644 --- a/src/common/clipboard.c +++ b/src/common/clipboard.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -37,6 +38,8 @@ guac_common_clipboard* guac_common_clipboard_alloc(int size) { clipboard->length = 0; clipboard->available = size; + pthread_mutex_init(&(clipboard->lock), NULL); + return clipboard; } @@ -108,18 +111,37 @@ static void* __send_user_clipboard(guac_user* user, void* data) { } void guac_common_clipboard_send(guac_common_clipboard* clipboard, guac_client* client) { + + pthread_mutex_lock(&(clipboard->lock)); + guac_client_log(client, GUAC_LOG_DEBUG, "Broadcasting clipboard to all connected users."); guac_client_foreach_user(client, __send_user_clipboard, clipboard); guac_client_log(client, GUAC_LOG_DEBUG, "Broadcast of clipboard complete."); + + pthread_mutex_unlock(&(clipboard->lock)); + } -void guac_common_clipboard_reset(guac_common_clipboard* clipboard, const char* mimetype) { +void guac_common_clipboard_reset(guac_common_clipboard* clipboard, + const char* mimetype) { + + pthread_mutex_lock(&(clipboard->lock)); + + /* Clear clipboard contents */ clipboard->length = 0; - strncpy(clipboard->mimetype, mimetype, sizeof(clipboard->mimetype)-1); + + /* Assign given mimetype */ + strncpy(clipboard->mimetype, mimetype, sizeof(clipboard->mimetype) - 1); + clipboard->mimetype[sizeof(clipboard->mimetype) - 1] = '\0'; + + pthread_mutex_unlock(&(clipboard->lock)); + } void guac_common_clipboard_append(guac_common_clipboard* clipboard, const char* data, int length) { + pthread_mutex_lock(&(clipboard->lock)); + /* Truncate data to available length */ int remaining = clipboard->available - clipboard->length; if (remaining < length) @@ -131,5 +153,7 @@ void guac_common_clipboard_append(guac_common_clipboard* clipboard, const char* /* Update length */ clipboard->length += length; + pthread_mutex_unlock(&(clipboard->lock)); + } diff --git a/src/common/common/clipboard.h b/src/common/common/clipboard.h index 5ebb2617..c129a7c3 100644 --- a/src/common/common/clipboard.h +++ b/src/common/common/clipboard.h @@ -23,6 +23,7 @@ #include "config.h" #include +#include /** * The maximum number of bytes to send in an individual blob when @@ -35,6 +36,13 @@ */ typedef struct guac_common_clipboard { + /** + * Lock which restricts simultaneous access to the clipboard, guaranteeing + * ordered modifications to the clipboard and that changes to the clipboard + * are not allowed while the clipboard is being broadcast to all users. + */ + pthread_mutex_t lock; + /** * The mimetype of the contained clipboard data. */ diff --git a/src/protocols/ssh/client.c b/src/protocols/ssh/client.c index 47b5978f..25e8e5bc 100644 --- a/src/protocols/ssh/client.c +++ b/src/protocols/ssh/client.c @@ -20,6 +20,7 @@ #include "config.h" #include "client.h" +#include "common/clipboard.h" #include "common/recording.h" #include "common-ssh/sftp.h" #include "ssh.h" @@ -42,6 +43,9 @@ int guac_client_init(guac_client* client) { guac_ssh_client* ssh_client = calloc(1, sizeof(guac_ssh_client)); client->data = ssh_client; + /* Init clipboard */ + ssh_client->clipboard = guac_common_clipboard_alloc(GUAC_SSH_CLIPBOARD_MAX_LENGTH); + /* Set handlers */ client->join_handler = guac_ssh_user_join_handler; client->free_handler = guac_ssh_client_free_handler; @@ -106,6 +110,7 @@ int guac_ssh_client_free_handler(guac_client* client) { guac_ssh_settings_free(ssh_client->settings); /* Free client structure */ + guac_common_clipboard_free(ssh_client->clipboard); free(ssh_client); guac_common_ssh_uninit(); diff --git a/src/protocols/ssh/client.h b/src/protocols/ssh/client.h index a759e817..551cb7a5 100644 --- a/src/protocols/ssh/client.h +++ b/src/protocols/ssh/client.h @@ -22,6 +22,11 @@ #include +/** + * The maximum number of bytes to allow within the clipboard. + */ +#define GUAC_SSH_CLIPBOARD_MAX_LENGTH 262144 + /** * Handler which is invoked when the SSH client needs to be disconnected (if * connected) and freed. This can happen if initialization fails, or all users diff --git a/src/protocols/ssh/clipboard.c b/src/protocols/ssh/clipboard.c index 0e236325..72e1ca01 100644 --- a/src/protocols/ssh/clipboard.c +++ b/src/protocols/ssh/clipboard.c @@ -19,6 +19,7 @@ #include "config.h" #include "clipboard.h" +#include "common/clipboard.h" #include "ssh.h" #include "terminal/terminal.h" @@ -32,7 +33,7 @@ int guac_ssh_clipboard_handler(guac_user* user, guac_stream* stream, /* Clear clipboard and prepare for new data */ guac_client* client = user->client; guac_ssh_client* ssh_client = (guac_ssh_client*) client->data; - guac_terminal_clipboard_reset(ssh_client->term, mimetype); + guac_common_clipboard_reset(ssh_client->clipboard, mimetype); /* Set handlers for clipboard stream */ stream->blob_handler = guac_ssh_clipboard_blob_handler; @@ -47,7 +48,7 @@ int guac_ssh_clipboard_blob_handler(guac_user* user, guac_stream* stream, /* Append new data */ guac_client* client = user->client; guac_ssh_client* ssh_client = (guac_ssh_client*) client->data; - guac_terminal_clipboard_append(ssh_client->term, data, length); + guac_common_clipboard_append(ssh_client->clipboard, data, length); return 0; } diff --git a/src/protocols/ssh/ssh.c b/src/protocols/ssh/ssh.c index f98f2228..a1162813 100644 --- a/src/protocols/ssh/ssh.c +++ b/src/protocols/ssh/ssh.c @@ -206,7 +206,7 @@ void* ssh_client_thread(void* data) { } /* Create terminal */ - ssh_client->term = guac_terminal_create(client, + ssh_client->term = guac_terminal_create(client, ssh_client->clipboard, settings->font_name, settings->font_size, settings->resolution, settings->width, settings->height, settings->color_scheme, settings->backspace); diff --git a/src/protocols/ssh/ssh.h b/src/protocols/ssh/ssh.h index 0a89a2d1..cb5d8bc5 100644 --- a/src/protocols/ssh/ssh.h +++ b/src/protocols/ssh/ssh.h @@ -22,6 +22,7 @@ #include "config.h" +#include "common/clipboard.h" #include "common/recording.h" #include "common-ssh/sftp.h" #include "common-ssh/ssh.h" @@ -89,6 +90,11 @@ typedef struct guac_ssh_client { */ pthread_mutex_t term_channel_lock; + /** + * The current clipboard contents. + */ + guac_common_clipboard* clipboard; + /** * The terminal which will render all output from the SSH client. */ diff --git a/src/protocols/telnet/client.c b/src/protocols/telnet/client.c index 3f64f1c2..1c6a2ec8 100644 --- a/src/protocols/telnet/client.c +++ b/src/protocols/telnet/client.c @@ -42,6 +42,9 @@ int guac_client_init(guac_client* client) { guac_telnet_client* telnet_client = calloc(1, sizeof(guac_telnet_client)); client->data = telnet_client; + /* Init clipboard */ + telnet_client->clipboard = guac_common_clipboard_alloc(GUAC_TELNET_CLIPBOARD_MAX_LENGTH); + /* Init telnet client */ telnet_client->socket_fd = -1; telnet_client->naws_enabled = 0; @@ -89,6 +92,7 @@ int guac_telnet_client_free_handler(guac_client* client) { if (telnet_client->settings != NULL) guac_telnet_settings_free(telnet_client->settings); + guac_common_clipboard_free(telnet_client->clipboard); free(telnet_client); return 0; diff --git a/src/protocols/telnet/client.h b/src/protocols/telnet/client.h index 8921a9f4..47d21f28 100644 --- a/src/protocols/telnet/client.h +++ b/src/protocols/telnet/client.h @@ -29,6 +29,11 @@ #include +/** + * The maximum number of bytes to allow within the clipboard. + */ +#define GUAC_TELNET_CLIPBOARD_MAX_LENGTH 262144 + /** * Free handler. Required by libguac and called when the guac_client is * disconnected and must be cleaned up. diff --git a/src/protocols/telnet/clipboard.c b/src/protocols/telnet/clipboard.c index 08d28b11..b663b2c3 100644 --- a/src/protocols/telnet/clipboard.c +++ b/src/protocols/telnet/clipboard.c @@ -19,6 +19,7 @@ #include "config.h" #include "clipboard.h" +#include "common/clipboard.h" #include "telnet.h" #include "terminal/terminal.h" @@ -32,7 +33,7 @@ int guac_telnet_clipboard_handler(guac_user* user, guac_stream* stream, /* Clear clipboard and prepare for new data */ guac_client* client = user->client; guac_telnet_client* telnet_client = (guac_telnet_client*) client->data; - guac_terminal_clipboard_reset(telnet_client->term, mimetype); + guac_common_clipboard_reset(telnet_client->clipboard, mimetype); /* Set handlers for clipboard stream */ stream->blob_handler = guac_telnet_clipboard_blob_handler; @@ -47,7 +48,7 @@ int guac_telnet_clipboard_blob_handler(guac_user* user, guac_stream* stream, /* Append new data */ guac_client* client = user->client; guac_telnet_client* telnet_client = (guac_telnet_client*) client->data; - guac_terminal_clipboard_append(telnet_client->term, data, length); + guac_common_clipboard_append(telnet_client->clipboard, data, length); return 0; } diff --git a/src/protocols/telnet/telnet.c b/src/protocols/telnet/telnet.c index e902c3b3..93abdd4d 100644 --- a/src/protocols/telnet/telnet.c +++ b/src/protocols/telnet/telnet.c @@ -478,6 +478,7 @@ void* guac_telnet_client_thread(void* data) { /* Create terminal */ telnet_client->term = guac_terminal_create(client, + telnet_client->clipboard, settings->font_name, settings->font_size, settings->resolution, settings->width, settings->height, settings->color_scheme, settings->backspace); diff --git a/src/protocols/telnet/telnet.h b/src/protocols/telnet/telnet.h index 24ad4a8f..b68470fe 100644 --- a/src/protocols/telnet/telnet.h +++ b/src/protocols/telnet/telnet.h @@ -21,6 +21,7 @@ #define GUAC_TELNET_H #include "config.h" +#include "common/clipboard.h" #include "common/recording.h" #include "settings.h" #include "terminal/terminal.h" @@ -66,6 +67,11 @@ typedef struct guac_telnet_client { */ int echo_enabled; + /** + * The current clipboard contents. + */ + guac_common_clipboard* clipboard; + /** * The terminal which will render all output from the telnet client. */ diff --git a/src/terminal/terminal.c b/src/terminal/terminal.c index 5ae2ee3d..18f35119 100644 --- a/src/terminal/terminal.c +++ b/src/terminal/terminal.c @@ -502,6 +502,7 @@ static void guac_terminal_parse_color_scheme(guac_client* client, } guac_terminal* guac_terminal_create(guac_client* client, + guac_common_clipboard* clipboard, const char* font_name, int font_size, int dpi, int width, int height, const char* color_scheme, const int backspace) { @@ -582,6 +583,7 @@ guac_terminal* guac_terminal_create(guac_client* client, /* Init terminal state */ term->current_attributes = default_char.attributes; term->default_char = default_char; + term->clipboard = clipboard; /* Set pixel size */ term->width = width; @@ -632,9 +634,6 @@ guac_terminal* guac_terminal_create(guac_client* client, term->current_cursor = GUAC_TERMINAL_CURSOR_BLANK; guac_common_cursor_set_blank(term->cursor); - /* Allocate clipboard */ - term->clipboard = guac_common_clipboard_alloc(GUAC_TERMINAL_CLIPBOARD_MAX_LENGTH); - /* Start terminal thread */ if (pthread_create(&(term->thread), NULL, guac_terminal_thread, (void*) term)) { @@ -682,9 +681,6 @@ void guac_terminal_free(guac_terminal* term) { /* Free buffer */ guac_terminal_buffer_free(term->buffer); - /* Free clipboard */ - guac_common_clipboard_free(term->clipboard); - /* Free scrollbar */ guac_terminal_scrollbar_free(term->scrollbar); @@ -2028,14 +2024,6 @@ void guac_terminal_scroll_handler(guac_terminal_scrollbar* scrollbar, int value) } -void guac_terminal_clipboard_reset(guac_terminal* term, const char* mimetype) { - guac_common_clipboard_reset(term->clipboard, mimetype); -} - -void guac_terminal_clipboard_append(guac_terminal* term, const void* data, int length) { - guac_common_clipboard_append(term->clipboard, data, length); -} - int guac_terminal_sendf(guac_terminal* term, const char* format, ...) { int written; diff --git a/src/terminal/terminal/terminal.h b/src/terminal/terminal/terminal.h index 6094c393..616568a4 100644 --- a/src/terminal/terminal/terminal.h +++ b/src/terminal/terminal/terminal.h @@ -58,11 +58,6 @@ */ #define GUAC_TERMINAL_WHEEL_SCROLL_AMOUNT 3 -/** - * The maximum number of bytes to allow within the clipboard. - */ -#define GUAC_TERMINAL_CLIPBOARD_MAX_LENGTH 262144 - /** * The name of the color scheme having black foreground and white background. */ @@ -443,7 +438,10 @@ struct guac_terminal { guac_terminal_cursor_type current_cursor; /** - * The current contents of the clipboard. + * The current contents of the clipboard. This clipboard instance is + * maintained externally (will not be freed when this terminal is freed) + * and will be updated both internally by the terminal and externally + * through received clipboard instructions. */ guac_common_clipboard* clipboard; @@ -461,6 +459,13 @@ struct guac_terminal { * @param client * The client to which the terminal will be rendered. * + * @param clipboard + * The guac_common_clipboard which will contain the current clipboard + * state. It is expected that this clipboard instance will be updated + * both internally by the terminal and externally through received + * clipboard instructions. This clipboard will not be automatically + * freed when this terminal is freed. + * * @param font_name * The name of the font to use when rendering glyphs. * @@ -493,6 +498,7 @@ struct guac_terminal { * which renders all text to the given client. */ guac_terminal* guac_terminal_create(guac_client* client, + guac_common_clipboard* clipboard, const char* font_name, int font_size, int dpi, int width, int height, const char* color_scheme, const int backspace); @@ -592,17 +598,6 @@ int guac_terminal_send_mouse(guac_terminal* term, guac_user* user, */ void guac_terminal_scroll_handler(guac_terminal_scrollbar* scrollbar, int value); -/** - * Clears the current clipboard contents and sets the mimetype for future - * contents. - */ -void guac_terminal_clipboard_reset(guac_terminal* term, const char* mimetype); - -/** - * Appends the given data to the current clipboard. - */ -void guac_terminal_clipboard_append(guac_terminal* term, const void* data, int length); - /** * Replicates the current display state to a user that has just joined the * connection. All instructions necessary to replicate state are sent over the