aboutsummaryrefslogtreecommitdiffstats
path: root/matrix-room.c
diff options
context:
space:
mode:
Diffstat (limited to 'matrix-room.c')
-rw-r--r--matrix-room.c235
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);
+}
+