diff options
Diffstat (limited to 'matrix-room.c')
-rw-r--r-- | matrix-room.c | 235 |
1 files changed, 210 insertions, 25 deletions
diff --git a/matrix-room.c b/matrix-room.c index d41fe15..e5c95e6 100644 --- a/matrix-room.c +++ b/matrix-room.c @@ -26,11 +26,62 @@ #include "debug.h" #include "libmatrix.h" +#include "matrix-api.h" #include "matrix-json.h" -/* identifiers for purple_conversation_get/set_data */ +/* + * identifiers for purple_conversation_get/set_data + */ + +/* a MatrixRoomStateEventTable * - see below */ #define PURPLE_CONV_DATA_STATE "state" +/* a GList of MatrixRoomEvents */ +#define PURPLE_CONV_DATA_EVENT_QUEUE "queue" + +/* PurpleUtilFetchUrlData * */ +#define PURPLE_CONV_DATA_ACTIVE_SEND "active_send" + +/****************************************************************************** + * + * Events + */ + +typedef struct _MatrixRoomEvent { + /* for outgoing events, our made-up transaction id. NULL for incoming + * events. + */ + gchar *txn_id; + gchar *event_type; + JsonObject *content; +} MatrixRoomEvent; + +/** + * Allocate a new MatrixRoomEvent. + * + * @param event_type the type of the event. this is copied into the event + * @param content the content of the event. This is used direct, but the + * reference count is incremented. + */ +static MatrixRoomEvent *_alloc_room_event(const gchar *event_type, + JsonObject *content) +{ + MatrixRoomEvent *event; + event = g_new0(MatrixRoomEvent, 1); + event->content = json_object_ref(content); + event->event_type = g_strdup(event_type); + return event; +} + +static void _free_room_event(MatrixRoomEvent *event) +{ + if(event->content) + json_object_unref(event->content); + g_free(event->txn_id); + g_free(event->event_type); + g_free(event); +} + /****************************************************************************** * * room state handling @@ -38,16 +89,11 @@ /* The state event table is a hashtable which maps from event type to * another hashtable, which maps from state key to content, which is itself a - * MatrixRoomStateEvent. + * MatrixRoomEvent. * */ typedef GHashTable MatrixRoomStateEventTable; -typedef struct _MatrixRoomStateEvent { - JsonObject *content; -} MatrixRoomStateEvent; - - /** * create a new, empty, state table */ @@ -57,12 +103,6 @@ static MatrixRoomStateEventTable *_create_state_table() (GDestroyNotify) g_hash_table_destroy); } -static void _free_room_state_event(MatrixRoomStateEvent *event) -{ - if(event->content) - json_object_unref(event->content); - g_free(event); -} /** * Get the state table for a room @@ -80,19 +120,17 @@ void matrix_room_update_state_table(PurpleConversation *conv, const gchar *event_type, const gchar *state_key, JsonObject *json_content_obj) { - MatrixRoomStateEvent *event; + MatrixRoomEvent *event; MatrixRoomStateEventTable *state_table; GHashTable *state_table_entry; - event = g_new0(MatrixRoomStateEvent, 1); - event->content = json_content_obj; - json_object_ref(event->content); + event = _alloc_room_event(event_type, json_content_obj); state_table = matrix_room_get_state_table(conv); state_table_entry = g_hash_table_lookup(state_table, event_type); if(state_table_entry == NULL) { state_table_entry = g_hash_table_new_full(g_str_hash, g_str_equal, - g_free, (GDestroyNotify)_free_room_state_event); + g_free, (GDestroyNotify)_free_room_event); g_hash_table_insert(state_table, g_strdup(event_type), state_table_entry); } @@ -105,7 +143,7 @@ void matrix_room_update_state_table(PurpleConversation *conv, * * @returns null if this key ies not known */ -static MatrixRoomStateEvent *matrix_room_get_state_event( +static MatrixRoomEvent *matrix_room_get_state_event( MatrixRoomStateEventTable *state_table, const gchar *event_type, const gchar *state_key) { @@ -115,7 +153,7 @@ static MatrixRoomStateEvent *matrix_room_get_state_event( if(tmp == NULL) return NULL; - return (MatrixRoomStateEvent *)g_hash_table_lookup(tmp, state_key); + return (MatrixRoomEvent *)g_hash_table_lookup(tmp, state_key); } /** @@ -124,7 +162,7 @@ static MatrixRoomStateEvent *matrix_room_get_state_event( static const char *matrix_room_get_name(MatrixRoomStateEventTable *state_table) { GHashTable *tmp; - MatrixRoomStateEvent *event; + MatrixRoomEvent *event; /* start by looking for the official room name */ event = matrix_room_get_state_event(state_table, "m.room.name", ""); @@ -132,7 +170,6 @@ static const char *matrix_room_get_name(MatrixRoomStateEventTable *state_table) const gchar *tmpname = matrix_json_object_get_string_member( event->content, "name"); if(tmpname != NULL) { - purple_debug_info("matrixprpl", "got room name %s\n", tmpname); return tmpname; } } @@ -144,14 +181,12 @@ static const char *matrix_room_get_name(MatrixRoomStateEventTable *state_table) g_hash_table_iter_init(&iter, tmp); gpointer key, value; while(g_hash_table_iter_next(&iter, &key, &value)) { - MatrixRoomStateEvent *event = value; + MatrixRoomEvent *event = value; JsonArray *array = matrix_json_object_get_array_member( event->content, "aliases"); if(array != NULL && json_array_get_length(array) > 0) { const gchar *tmpname = matrix_json_array_get_string_element(array, 0); if(tmpname != NULL) { - purple_debug_info("matrixprpl", "got room alias %s\n", - tmpname); return tmpname; } } @@ -164,6 +199,128 @@ static const char *matrix_room_get_name(MatrixRoomStateEventTable *state_table) } +/****************************************************************************** + * + * event queue handling + */ +static void _send_queued_event(PurpleConversation *conv); + +/** + * Get the state table for a room + */ +static GList *_get_event_queue(PurpleConversation *conv) +{ + return purple_conversation_get_data(conv, PURPLE_CONV_DATA_EVENT_QUEUE); +} + +static void _event_send_complete(MatrixAccount *account, gpointer user_data, + JsonNode *json_root) +{ + PurpleConversation *conv = user_data; + JsonObject *response_object; + const gchar *event_id; + GList *event_queue; + MatrixRoomEvent *event; + + response_object = matrix_json_node_get_object(json_root); + event_id = matrix_json_object_get_string_member(response_object, + "event_id"); + purple_debug_info("matrixprpl", "Successfully sent event id %s\n", + event_id); + + event_queue = _get_event_queue(conv); + event = event_queue -> data; + _free_room_event(event); + event_queue = g_list_remove(event_queue, event); + + purple_conversation_set_data(conv, PURPLE_CONV_DATA_EVENT_QUEUE, + event_queue); + + if(event_queue) { + _send_queued_event(conv); + } else { + purple_conversation_set_data(conv, PURPLE_CONV_DATA_ACTIVE_SEND, + NULL); + } +} + + +/** + * Unable to send event to homeserver + */ +void _event_send_error(MatrixAccount *ma, gpointer user_data, + const gchar *error_message) +{ + PurpleConversation *conv = user_data; + matrix_api_error(ma, user_data, error_message); + purple_conversation_set_data(conv, PURPLE_CONV_DATA_ACTIVE_SEND, NULL); + + /* for now, we leave the message queued. We should consider retrying. */ +} + +/** + * homeserver gave non-200 on event send. + */ +void _event_send_bad_response(MatrixAccount *ma, gpointer user_data, + int http_response_code, JsonNode *json_root) +{ + PurpleConversation *conv = user_data; + matrix_api_bad_response(ma, user_data, http_response_code, json_root); + purple_conversation_set_data(conv, PURPLE_CONV_DATA_ACTIVE_SEND, NULL); + + /* for now, we leave the message queued. We should consider retrying. */ +} + +static void _send_queued_event(PurpleConversation *conv) +{ + PurpleUtilFetchUrlData *fetch_data; + MatrixAccount *acct; + MatrixRoomEvent *event; + + acct = purple_connection_get_protocol_data(conv->account->gc); + event = _get_event_queue(conv) -> data; + g_assert(event != NULL); + + purple_debug_info("matrixprpl", "Sending %s with txn id %s\n", + event->event_type, event->txn_id); + + fetch_data = matrix_api_send(acct, conv->name, event->event_type, + event->txn_id, event->content, _event_send_complete, + _event_send_error, _event_send_bad_response, conv); + + purple_conversation_set_data(conv, PURPLE_CONV_DATA_ACTIVE_SEND, + fetch_data); +} + + +static void _enqueue_event(PurpleConversation *conv, const gchar *event_type, + JsonObject *event_content) +{ + MatrixRoomEvent *event; + GList *event_queue; + PurpleUtilFetchUrlData *active_send; + + event = _alloc_room_event(event_type, event_content); + event->txn_id = g_strdup_printf("%"G_GINT64_FORMAT"%"G_GUINT32_FORMAT, + g_get_monotonic_time(), g_random_int()); + + event_queue = _get_event_queue(conv); + event_queue = g_list_append(event_queue, event); + purple_conversation_set_data(conv, PURPLE_CONV_DATA_EVENT_QUEUE, + event_queue); + + purple_debug_info("matrixprpl", "Enqueued %s with txn id %s\n", + event_type, event->txn_id); + + active_send = purple_conversation_get_data(conv, + PURPLE_CONV_DATA_ACTIVE_SEND); + if(active_send != NULL) { + purple_debug_info("matrixprpl", "Event send is already in progress\n"); + } else { + _send_queued_event(conv); + } + +} /*****************************************************************************/ @@ -221,6 +378,9 @@ PurpleConversation *matrix_room_get_or_create_conversation( /* set our data on it */ state_table = _create_state_table(); purple_conversation_set_data(conv, PURPLE_CONV_DATA_STATE, state_table); + + purple_conversation_set_data(conv, PURPLE_CONV_DATA_EVENT_QUEUE, NULL); + purple_conversation_set_data(conv, PURPLE_CONV_DATA_ACTIVE_SEND, NULL); return conv; } @@ -232,6 +392,7 @@ PurpleConversation *matrix_room_get_or_create_conversation( void matrix_room_leave_chat(PurpleConversation *conv) { MatrixRoomStateEventTable *state_table; + GList *event_queue; /* TODO: actually tell the server that we are leaving the chat, and only * destroy the memory structures once we get a response from that. @@ -241,6 +402,12 @@ void matrix_room_leave_chat(PurpleConversation *conv) state_table = matrix_room_get_state_table(conv); g_hash_table_destroy(state_table); purple_conversation_set_data(conv, PURPLE_CONV_DATA_STATE, NULL); + + event_queue = _get_event_queue(conv); + if(event_queue != NULL) { + g_list_free_full(event_queue, (GDestroyNotify)_free_room_event); + purple_conversation_set_data(conv, PURPLE_CONV_DATA_EVENT_QUEUE, NULL); + } } @@ -282,3 +449,21 @@ void matrix_room_update_buddy_list(PurpleConversation *conv) room_name = matrix_room_get_name(matrix_room_get_state_table(conv)); purple_blist_alias_chat(chat, room_name); } + + +/** + * Send a message in a room + */ +void matrix_room_send_message(struct _PurpleConversation *conv, + const gchar *message) +{ + JsonObject *content; + + content = json_object_new(); + json_object_set_string_member(content, "msgtype", "m.text"); + json_object_set_string_member(content, "body", message); + + _enqueue_event(conv, "m.room.message", content); + json_object_unref(content); +} + |