diff options
-rw-r--r-- | doc/protocols | 17 | ||||
-rw-r--r-- | src/mds-clipboard.c | 367 | ||||
-rw-r--r-- | src/mds-clipboard.h | 65 |
3 files changed, 439 insertions, 10 deletions
diff --git a/doc/protocols b/doc/protocols index e008c28..1a251af 100644 --- a/doc/protocols +++ b/doc/protocols @@ -142,23 +142,28 @@ Conditionally required header: Length Length of the message Conditionally required header: Size - Available and optional if: `Action: set-size` + Required if: `Action: set-size` The maximum number of elements in the clipstack -Conditionally optional header: Index - Available and optional if: `Action: read` - The index of the item in the clipstack, starting at 0 - Conditionally required header: Client ID Your ID, provided by `ID assignment` in response to `Command: assign-id`. - Required if: `Action: add` but not `Time to live: forever` + Required if: `Action: add` and a header starting with + `Time to live: until-death` + Required if: `Action: read` + Required if: `Action: get-size` + +Conditionally optional header: Index + Available and optional if: `Action: read` + The index of the item in the clipstack, starting at 0 Conditionally optional header: Time to live Available and optional if: `Action: add` The number of seconds the entry should be available before it is removed by the server, or: until-death: remove entry when the client closes + until-death #: remove entry when the client closes, + or # seconds have elapsed forever: never remove it (default) The server will always remove the entry when: 1) it is at the bottom of the clipstack and a new diff --git a/src/mds-clipboard.c b/src/mds-clipboard.c index 212b039..7032b81 100644 --- a/src/mds-clipboard.c +++ b/src/mds-clipboard.c @@ -66,6 +66,12 @@ server_characteristics_t server_characteristics = */ #define CLIPITEM_AUTOPURGE_UPON_CLOCK 2 +/** + * Delete entry when the client closes or when a + * point in time has elapsed, or when needed + */ +#define CLIPITEM_AUTOPURGE_UPON_DEATH_OR_CLOCK 3 + /** * A clipboard entry @@ -162,9 +168,10 @@ int initialise_server(void) const char* const message = "Command: intercept\n" "Message ID: 0\n" - "Length: 19\n" + "Length: 33\n" "\n" - "Command: clipboard\n"; + "Command: clipboard\n" + "Client closed\n"; if (full_send(message, strlen(message))) return 1; @@ -400,8 +407,7 @@ int master_loop(void) int r = mds_message_read(&received, socket_fd); if (r == 0) { - r = 0; /* FIXME */ - if (r == 0) + if (r = handle_message(), r == 0) continue; } @@ -473,3 +479,356 @@ int full_send(const char* message, size_t length) return 0; } + +/** + * Handle the received message + * + * @return Zero on success, -1 on error + */ +int handle_message(void) +{ + /* Fetch message headers. */ + + const char* recv_client_id = "0:0"; + const char* recv_message_id = NULL; + const char* recv_length = NULL; + const char* recv_level = NULL; + const char* recv_action = NULL; + const char* recv_size = NULL; + const char* recv_index = "0"; + const char* recv_time_to_live = "forever"; + const char* recv_client_closed = NULL; + size_t i; + int level; + +#define __get_header(storage, header) \ + (startswith(received.headers[i], header)) \ + storage = received.headers[i] + strlen(header) + + for (i = 0; i < received.header_count; i++) + { + if __get_header(recv_client_id, "Client ID: "); + else if __get_header(recv_message_id, "Message ID: "); + else if __get_header(recv_length, "Length: "); + else if __get_header(recv_action, "Action: "); + else if __get_header(recv_level, "Level: "); + else if __get_header(recv_size, "Size: "); + else if __get_header(recv_index, "Index: "); + else if __get_header(recv_time_to_live, "Time to live: "); + else if __get_header(recv_client_closed, "Client closed: "); + else + continue; + } + +#undef __get_header + + + /* Validate headers and take appropriate action. */ + + if (recv_message_id == NULL) + { + eprint("received message with ID, ignoring, master server is misbehaving."); + return 0; + } + + if (recv_client_closed) + { + if (strequals(recv_client_closed, "0:0")) + return 0; + return clipboard_death(recv_client_closed); + } + + if (recv_action == NULL) + { + eprint("received message without any action, ignoring."); + return 0; + } + if (recv_level == NULL) + { + eprint("received message without specified clipboard level, ignoring."); + return 0; + } + level = atoi(recv_level); + if ((level < 0) || (CLIPBOARD_LEVELS <= level)) + { + eprint("received message without invalid clipboard level, ignoring."); + return 0; + } + if (strequals(recv_client_id, "0:0")) + if (strequals(recv_message_id, "read") || strequals(recv_message_id, "get-size")) + { + eprint("received information request from an anonymous client, ignoring."); + return 0; + } + + if (strequals(recv_message_id, "add")) + { + if (recv_length == NULL) + { + eprint("received request for adding a clipboard entry but did not receive any content, ignoring."); + return 0; + } + if ((strequals(recv_client_id, "0:0")) && startswith(recv_time_to_live, "until-death")) + { + eprint("received request new clipboard entry with autopurge upon" + " client close from an anonymous client, ignoring."); + return 0; + } + return clipboard_add(level, recv_time_to_live, recv_client_id); + } + else if (strequals(recv_message_id, "read")) + return clipboard_read(level, (size_t)atoll(recv_index), recv_client_id, recv_message_id); + else if (strequals(recv_message_id, "clear")) + return clipboard_clear(level); + else if (strequals(recv_message_id, "set-size")) + { + if (recv_size == NULL) + { + eprint("received request for clipboard resizing without a new size, ignoring."); + return 0; + } + return clipboard_set_size(level, (size_t)atoll(recv_size)); + } + else if (strequals(recv_message_id, "get-size")) + return clipboard_get_size(level, recv_client_id, recv_message_id); + + eprint("received message with invalid action, ignoring."); + return 0; +} + + +/** + * Remove old entries form a clipstack + * + * @param level The clipboard level + * @param client_id The ID of the client that has newly closed, `NULL` if none + * @return Zero on success, -1 on error + */ +static int clipboard_purge(int level, const char* client_id) +{ + /* TODO */ + + return 0; +} + + +/** + * Remove entries in the clipboard added by a client + * + * @param recv_client_id The ID of the client + * @return Zero on success, -1 on error + */ +int clipboard_death(const char* recv_client_id) +{ + int i; + for (i = 0; i < CLIPBOARD_LEVELS; i++) + if (clipboard_purge(i, recv_client_id)) + return -1; + return 0; +} + + +/** + * Add a new entry to the clipboard + * + * @param level The clipboard level + * @param time_to_live When the entry should be removed + * @param recv_client_id The ID of the client + * @return Zero on success, -1 on error + */ +int clipboard_add(int level, const char* time_to_live, const char* recv_client_id) +{ + if (clipboard_purge(level, NULL)) + return -1; + + /* TODO */ + + return 0; +} + + +/** + * Read an entry to the clipboard + * + * @param level The clipboard level + * @param index The index of the clipstack element + * @param recv_client_id The ID of the client + * @param recv_message_id The message ID of the received message + * @return Zero on success, -1 on error + */ +int clipboard_read(int level, size_t index, const char* recv_client_id, const char* recv_message_id) +{ + char* message = NULL; + clipitem_t* clip = NULL; + size_t n; + + if (clipboard_purge(level, NULL)) + return -1; + + if (clipboard_used[level] == 0) + { + n = strlen("To: %s\n" + "In response to: %s\n" + "Message ID: %" PRIi32 "\n" + "\n"); + n += strlen(recv_client_id) + strlen(recv_message_id) + 10; + + fail_if (xmalloc(message, n, char)); + + sprintf(message, + "To: %s\n" + "In response to: %s\n" + "Message ID: %" PRIi32 "\n" + "\n", + recv_client_id, recv_message_id, message_id); + + goto send; + } + + if (index >= clipboard_used[level]) + index = clipboard_used[level] - 1; + + clip = clipboard[level] + index; + + n = strlen("To: %s\n" + "In response to: %s\n" + "Message ID: %" PRIi32 "\n" + "Length: %lu\n" + "\n"); + n += strlen(recv_client_id) + strlen(recv_message_id) + 10 + 3 * sizeof(size_t); + + fail_if (xmalloc(message, n, char)); + + sprintf(message, + "To: %s\n" + "In response to: %s\n" + "Message ID: %" PRIi32 "\n" + "Length: %lu\n" + "\n", + recv_client_id, recv_message_id, message_id, clip->length); + + send: + message_id = message_id == INT32_MAX ? 0 : (message_id + 1); + fail_if (full_send(message, strlen(message))); + if (clip != NULL) + fail_if (full_send(clip->content, clip->length)); + + free(message); + return 0; + pfail: + xperror(*argv); + return -1; +} + + +/** + * Clear a clipstack + * + * @param level The clipboard level + * @return Zero on success, -1 on error + */ +int clipboard_clear(int level) +{ + size_t i; + for (i = 0; i < clipboard_used[level]; i++) + { + if (clipboard[level][i].autopurge == CLIPITEM_AUTOPURGE_NEVER) + free(clipboard[level][i].content); + else + wipe_and_free(clipboard[level][i].content, clipboard[level][i].length); + } + clipboard_used[level] = 0; + return 0; +} + + +/** + * Resize a clipstack + * + * @param level The clipboard level + * @param size The new clipstack size + * @return Zero on success, -1 on error + */ +int clipboard_set_size(int level, size_t size) +{ + size_t i; + if (clipboard_purge(level, NULL)) + return -1; + + if (size < clipboard_size[level]) + { + size_t old_used = clipboard_used[level]; + if (size < old_used) + { + clipboard_used[level] = size; + for (i = size; i < old_used; i++) + if (clipboard[level][i].autopurge == CLIPITEM_AUTOPURGE_NEVER) + free(clipboard[level][i].content), clipboard[level][i].content = NULL; + else + wipe_and_free(clipboard[level][i].content, clipboard[level][i].length); + } + } + + if (size != clipboard_size[level]) + { + clipitem_t* old = clipboard[level]; + if (xrealloc(clipboard[level], size, clipitem_t)) + { + clipboard[level] = old; + goto pfail; + } + clipboard_size[level] = size; + } + + return 0; + pfail: + xperror(*argv); + return -1; +} + + +/** + * Get the size of a clipstack and how many entries it contains + * + * @param level The clipboard level + * @param recv_client_id The ID of the client + * @param recv_message_id The message ID of the received message + * @return Zero on success, -1 on error + */ +int clipboard_get_size(int level, const char* recv_client_id, const char* recv_message_id) +{ + char* message = NULL; + size_t n; + if (clipboard_purge(level, NULL)) + return -1; + + n = strlen("To: %s\n" + "In response to: %s\n" + "Message ID: %" PRIi32 "\n" + "Size: %lu\n" + "Used: %lu\n" + "\n"); + n += strlen(recv_client_id) + strlen(recv_message_id) + 10 + 2 * 3 * sizeof(size_t); + + fail_if (xmalloc(message, n, char)); + sprintf(message, + "To: %s\n" + "In response to: %s\n" + "Message ID: %" PRIi32 "\n" + "Size: %lu\n" + "Used: %lu\n" + "\n", + recv_client_id, recv_message_id, message_id, clipboard_size[level], clipboard_used[level]); + + message_id = message_id == INT32_MAX ? 0 : (message_id + 1); + + fail_if (full_send(message, strlen(message))); + + free(message); + return 0; + pfail: + xperror(*argv); + free(message); + return -1; +} + diff --git a/src/mds-clipboard.h b/src/mds-clipboard.h index 5fd2b93..c84ad01 100644 --- a/src/mds-clipboard.h +++ b/src/mds-clipboard.h @@ -21,6 +21,8 @@ #include "mds-base.h" +#include <stddef.h> + /** * Send a full message even if interrupted @@ -31,6 +33,69 @@ */ int full_send(const char* message, size_t length); +/** + * Handle the received message + * + * @return Zero on success, -1 on error + */ +int handle_message(void); + +/** + * Remove entries in the clipboard added by a client + * + * @param recv_client_id The ID of the client + * @return Zero on success, -1 on error + */ +int clipboard_death(const char* recv_client_id); + +/** + * Add a new entry to the clipboard + * + * @param level The clipboard level + * @param time_to_live -- + * @param recv_client_id The ID of the client + * @return Zero on success, -1 on error + */ +int clipboard_add(int level, const char* time_to_live, const char* recv_client_id); + +/** + * Read an entry to the clipboard + * + * @param level The clipboard level + * @param index The index of the clipstack element + * @param recv_client_id The ID of the client + * @param recv_message_id The message ID of the received message + * @return Zero on success, -1 on error + */ +int clipboard_read(int level, size_t index, const char* recv_client_id, const char* recv_message_id); + +/** + * Clear a clipstack + * + * @param level The clipboard level + * @return Zero on success, -1 on error + */ +int clipboard_clear(int level); + +/** + * Resize a clipstack + * + * @param level The clipboard level + * @param size The new clipstack size + * @return Zero on success, -1 on error + */ +int clipboard_set_size(int level, size_t size); + +/** + * Get the size of a clipstack and how many entries it contains + * + * @param level The clipboard level + * @param recv_client_id The ID of the client + * @param recv_message_id The message ID of the received message + * @return Zero on success, -1 on error + */ +int clipboard_get_size(int level, const char* recv_client_id, const char* recv_message_id); + #endif |