diff options
author | Richard van der Hoff <richard@matrix.org> | 2015-10-30 17:10:15 +0000 |
---|---|---|
committer | Richard van der Hoff <richard@matrix.org> | 2015-10-30 17:10:15 +0000 |
commit | 2ed95f49507a2fa528df6eeacdf8954df473a75e (patch) | |
tree | 87a44f0a7ab85ebce37735d891b975b1f7272ffe | |
parent | e354189a87d01942a0abe782583f0b30eb7f8839 (diff) | |
download | purple-matrix-2ed95f49507a2fa528df6eeacdf8954df473a75e.tar.gz |
Refactor the statetable out to a separate file
Apart from generally being cleaner, we're going to want to use the statetable
in isolation of rooms, to handle invites.
-rw-r--r-- | Makefile | 5 | ||||
-rw-r--r-- | matrix-event.c | 53 | ||||
-rw-r--r-- | matrix-event.h | 52 | ||||
-rw-r--r-- | matrix-room.c | 260 | ||||
-rw-r--r-- | matrix-statetable.c | 147 | ||||
-rw-r--r-- | matrix-statetable.h | 79 |
6 files changed, 400 insertions, 196 deletions
@@ -17,9 +17,12 @@ DATA_ROOT_DIR_PURPLE = $(shell $(PKG_CONFIG) --variable=datarootdir purple) # generate .d files when compiling CPPFLAGS+=-MMD -OBJECTS=libmatrix.o matrix-api.o matrix-connection.o matrix-json.o \ +OBJECTS=libmatrix.o matrix-api.o matrix-connection.o \ + matrix-event.o \ + matrix-json.o \ matrix-room.o \ matrix-roommembers.o \ + matrix-statetable.o \ matrix-sync.o TARGET=libmatrix.so diff --git a/matrix-event.c b/matrix-event.c new file mode 100644 index 0000000..64f05e2 --- /dev/null +++ b/matrix-event.c @@ -0,0 +1,53 @@ +/* + * matrix-event.c + * + * + * Copyright (c) Openmarket UK Ltd 2015 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + + +#include "matrix-event.h" + +#include <glib.h> + +#include <json-glib/json-glib.h> + +/** + * 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. + */ +MatrixRoomEvent *matrix_event_new(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; +} + + +void matrix_event_free(MatrixRoomEvent *event) +{ + if(event->content) + json_object_unref(event->content); + g_free(event->txn_id); + g_free(event->event_type); + g_free(event); +} diff --git a/matrix-event.h b/matrix-event.h new file mode 100644 index 0000000..6a5df9b --- /dev/null +++ b/matrix-event.h @@ -0,0 +1,52 @@ +/* + * matrix-event.h + * + * + * Copyright (c) Openmarket UK Ltd 2015 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#ifndef MATRIX_EVENT_H_ +#define MATRIX_EVENT_H_ + +#include <glib.h> + +struct _JsonObject; + +typedef struct _MatrixRoomEvent { + /* for outgoing events, our made-up transaction id. NULL for incoming + * events. + */ + gchar *txn_id; + gchar *event_type; + struct _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. + */ +MatrixRoomEvent *matrix_event_new(const gchar *event_type, + struct _JsonObject *content); + +void matrix_event_free(MatrixRoomEvent *event); + + +#endif /* MATRIX_EVENT_H_ */ diff --git a/matrix-room.c b/matrix-room.c index 30c20c7..58d249b 100644 --- a/matrix-room.c +++ b/matrix-room.c @@ -27,8 +27,25 @@ #include "libmatrix.h" #include "matrix-api.h" +#include "matrix-event.h" #include "matrix-json.h" #include "matrix-roommembers.h" +#include "matrix-statetable.h" + + +static gchar *_get_room_name(MatrixConnectionData *conn, + PurpleConversation *conv); + +static MatrixConnectionData *_get_connection_data_from_conversation( + PurpleConversation *conv) +{ + return conv->account->gc->proto_data; +} + +/****************************************************************************** + * + * conversation data + */ /* * identifiers for purple_conversation_get/set_data @@ -47,121 +64,56 @@ #define PURPLE_CONV_MEMBER_TABLE "member_table" -static MatrixConnectionData *_get_connection_data_from_conversation( - PurpleConversation *conv) -{ - return conv->account->gc->proto_data; -} - -/****************************************************************************** - * - * Members - */ - /** * Get the member table for a room */ -MatrixRoomMemberTable *matrix_room_get_member_table(PurpleConversation *conv) +static MatrixRoomMemberTable *matrix_room_get_member_table( + PurpleConversation *conv) { return purple_conversation_get_data(conv, PURPLE_CONV_MEMBER_TABLE); } -/****************************************************************************** - * - * 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. + * Get the state table for a room */ -static MatrixRoomEvent *_alloc_room_event(const gchar *event_type, - JsonObject *content) +static MatrixRoomStateEventTable *matrix_room_get_state_table( + PurpleConversation *conv) { - MatrixRoomEvent *event; - event = g_new0(MatrixRoomEvent, 1); - event->content = json_object_ref(content); - event->event_type = g_strdup(event_type); - return event; + return purple_conversation_get_data(conv, PURPLE_CONV_DATA_STATE); } -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 */ -/* 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 - * MatrixRoomEvent. - * - */ -typedef GHashTable MatrixRoomStateEventTable; - - -static void _update_room_alias(PurpleConversation *conv); - -/** - * create a new, empty, state table - */ -static MatrixRoomStateEventTable *_create_state_table() -{ - return g_hash_table_new_full(g_str_hash, g_str_equal, g_free, - (GDestroyNotify) g_hash_table_destroy); -} - /** - * Get the state table for a room - */ -MatrixRoomStateEventTable *matrix_room_get_state_table(PurpleConversation *conv) -{ - return purple_conversation_get_data(conv, PURPLE_CONV_DATA_STATE); -} - - -/** - * look up a particular bit of state + * Update the name of the room in the buddy list (which in turn will update it + * in the chat window) * - * @returns null if this key ies not known + * @param conv: conversation info */ -static MatrixRoomEvent *matrix_room_get_state_event( - MatrixRoomStateEventTable *state_table, const gchar *event_type, - const gchar *state_key) +static void _update_room_alias(PurpleConversation *conv) { - GHashTable *tmp; + gchar *room_name; + MatrixConnectionData *conn = _get_connection_data_from_conversation(conv); + PurpleChat *chat = purple_blist_find_chat(conv->account, conv->name); - tmp = (GHashTable *) g_hash_table_lookup(state_table, event_type); - if(tmp == NULL) - return NULL; + /* we know there should be a buddy list entry for this room */ + g_assert(chat != NULL); - return (MatrixRoomEvent *)g_hash_table_lookup(tmp, state_key); + room_name = _get_room_name(conn, conv); + purple_blist_alias_chat(chat, room_name); + g_free(room_name); } /** - * Called when there is a change to the member list + * Called when there is a change to the member list. Tells the MemberTable + * about it. */ static void _on_member_change(PurpleConversation *conv, const gchar *member_user_id, MatrixRoomEvent *new_state) @@ -181,10 +133,11 @@ static void _on_member_change(PurpleConversation *conv, * old_state may be NULL to indicate addition of a state * key. */ -static void _on_state_update(PurpleConversation *conv, - const gchar *event_type, const gchar *state_key, - MatrixRoomEvent *old_state, MatrixRoomEvent *new_state) +static void _on_state_update(const gchar *event_type, + const gchar *state_key, MatrixRoomEvent *old_state, + MatrixRoomEvent *new_state, gpointer user_data) { + PurpleConversation *conv = user_data; g_assert(new_state != NULL); if(strcmp(event_type, "m.room.member") == 0) { @@ -196,55 +149,19 @@ static void _on_state_update(PurpleConversation *conv, } } -/** - * Update the state table on a room - */ -void matrix_room_handle_state_event(PurpleConversation *conv, +void matrix_room_handle_state_event(struct _PurpleConversation *conv, const gchar *event_id, JsonObject *json_event_obj) { - const gchar *event_type, *state_key; - JsonObject *json_content_obj; - MatrixRoomEvent *event, *old_event; - MatrixRoomStateEventTable *state_table; - GHashTable *state_table_entry; - - event_type = matrix_json_object_get_string_member( - json_event_obj, "type"); - state_key = matrix_json_object_get_string_member( - json_event_obj, "state_key"); - json_content_obj = matrix_json_object_get_object_member( - json_event_obj, "content"); - - if(event_type == NULL || state_key == NULL || json_content_obj == NULL) { - purple_debug_warning("matrixprpl", "event missing fields"); - return; - } - - 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_event); - g_hash_table_insert(state_table, g_strdup(event_type), - state_table_entry); - old_event = NULL; - } else { - old_event = g_hash_table_lookup(state_table_entry, - state_key); - } - - _on_state_update(conv, event_type, state_key, old_event, event); - - g_hash_table_insert(state_table_entry, g_strdup(state_key), event); + MatrixRoomStateEventTable *state_table = matrix_room_get_state_table(conv); + matrix_statetable_update(state_table, event_id, json_event_obj, + _on_state_update, conv); } /** * figure out the best name for a room based on its members list * - * @returns a string which should be freedd + * @returns a string which should be freed */ static gchar *_get_room_name_from_members(MatrixConnectionData *conn, PurpleConversation *conv) @@ -264,9 +181,8 @@ static gchar *_get_room_name_from_members(MatrixConnectionData *conn, } if(members == NULL) { - /* nobody else here. Self-chat or an invitation. TODO: improve this - */ - return g_strdup("invitation"); + /* nobody else here! */ + return NULL; } member1 = matrix_roommembers_get_displayname_for_member( @@ -289,79 +205,33 @@ static gchar *_get_room_name_from_members(MatrixConnectionData *conn, return res; } + /** * figure out the best name for a room * * @returns a string which should be freed */ -static char *_get_room_name(MatrixConnectionData *conn, +static gchar *_get_room_name(MatrixConnectionData *conn, PurpleConversation *conv) { - GHashTable *tmp; - MatrixRoomEvent *event; - const gchar *tmpname = NULL; - MatrixRoomStateEventTable *state_table; - - state_table = matrix_room_get_state_table(conv); - - /* start by looking for the official room name */ - event = matrix_room_get_state_event(state_table, "m.room.name", ""); - if(event != NULL) { - tmpname = matrix_json_object_get_string_member( - event->content, "name"); - if(tmpname != NULL) { - return g_strdup(tmpname); - } - } - + MatrixRoomStateEventTable *state_table = matrix_room_get_state_table(conv); + gchar *res; - /* look for an alias */ - tmp = (GHashTable *) g_hash_table_lookup(state_table, "m.room.aliases"); - if(tmp != NULL) { - GHashTableIter iter; - gpointer key, value; - - g_hash_table_iter_init(&iter, tmp); - while(g_hash_table_iter_next(&iter, &key, &value)) { - MatrixRoomEvent *event = value; - JsonArray *array = matrix_json_object_get_array_member( - event->content, "aliases"); - if(array != NULL && json_array_get_length(array) > 0) { - tmpname = matrix_json_array_get_string_element(array, 0); - if(tmpname != NULL) { - return g_strdup(tmpname); - } - } - } - } + /* first try to pick a name based on the official name / alias */ + res = matrix_statetable_get_room_alias(state_table); + if (res) + return res; /* look for room members, and pick a name based on that */ - return _get_room_name_from_members(conn, conv); -} + res = _get_room_name_from_members(conn, conv); + if (res) + return res; + /* failing all else, just use the room id */ + return g_strdup(conv -> name); -/** - * Update the name of the room in the buddy list (which in turn will update it - * in the chat window) - * - * @param conv: conversation info - */ -static void _update_room_alias(PurpleConversation *conv) -{ - gchar *room_name; - MatrixConnectionData *conn = _get_connection_data_from_conversation(conv); - PurpleChat *chat = purple_blist_find_chat(conv->account, conv->name); - - /* we know there should be a buddy list entry for this room */ - g_assert(chat != NULL); - - room_name = _get_room_name(conn, conv); - purple_blist_alias_chat(chat, room_name); - g_free(room_name); } - - /****************************************************************************** * * event queue handling @@ -393,7 +263,7 @@ static void _event_send_complete(MatrixConnectionData *account, gpointer user_da event_queue = _get_event_queue(conv); event = event_queue -> data; - _free_room_event(event); + matrix_event_free(event); event_queue = g_list_remove(event_queue, event); purple_conversation_set_data(conv, PURPLE_CONV_DATA_EVENT_QUEUE, @@ -474,7 +344,7 @@ static void _enqueue_event(PurpleConversation *conv, const gchar *event_type, GList *event_queue; MatrixApiRequestData *active_send; - event = _alloc_room_event(event_type, event_content); + event = matrix_event_new(event_type, event_content); event->txn_id = g_strdup_printf("%"G_GINT64_FORMAT"%"G_GUINT32_FORMAT, g_get_monotonic_time(), g_random_int()); @@ -602,7 +472,7 @@ PurpleConversation *matrix_room_create_conversation( conv = serv_got_joined_chat(pc, g_str_hash(room_id), room_id); /* set our data on it */ - state_table = _create_state_table(); + state_table = matrix_statetable_new(); member_table = matrix_roommembers_new_table(); purple_conversation_set_data(conv, PURPLE_CONV_DATA_EVENT_QUEUE, NULL); purple_conversation_set_data(conv, PURPLE_CONV_DATA_ACTIVE_SEND, NULL); @@ -651,7 +521,7 @@ void matrix_room_leave_chat(PurpleConversation *conv) event_queue = _get_event_queue(conv); if(event_queue != NULL) { - g_list_free_full(event_queue, (GDestroyNotify)_free_room_event); + g_list_free_full(event_queue, (GDestroyNotify)matrix_event_free); purple_conversation_set_data(conv, PURPLE_CONV_DATA_EVENT_QUEUE, NULL); } } diff --git a/matrix-statetable.c b/matrix-statetable.c new file mode 100644 index 0000000..f90fb97 --- /dev/null +++ b/matrix-statetable.c @@ -0,0 +1,147 @@ +/* + * matrix-statetable.c + * + * + * Copyright (c) Openmarket UK Ltd 2015 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + + +#include "matrix-statetable.h" + +#include "debug.h" + +#include "matrix-event.h" +#include "matrix-json.h" + + +/** + * create a new, empty, state table + */ +MatrixRoomStateEventTable *matrix_statetable_new() +{ + return g_hash_table_new_full(g_str_hash, g_str_equal, g_free, + (GDestroyNotify) g_hash_table_destroy); +} + +/** + * look up a particular bit of state + * + * @returns null if this key ies not known + */ +MatrixRoomEvent *matrix_statetable_get_event( + MatrixRoomStateEventTable *state_table, const gchar *event_type, + const gchar *state_key) +{ + GHashTable *tmp; + + tmp = (GHashTable *) g_hash_table_lookup(state_table, event_type); + if(tmp == NULL) + return NULL; + + return (MatrixRoomEvent *)g_hash_table_lookup(tmp, state_key); +} + + +/** + * Update the state table on a room + */ +void matrix_statetable_update(MatrixRoomStateEventTable *state_table, + const gchar *event_id, JsonObject *json_event_obj, + MatrixStateUpdateCallback callback, gpointer user_data) +{ + const gchar *event_type, *state_key; + JsonObject *json_content_obj; + MatrixRoomEvent *event, *old_event; + GHashTable *state_table_entry; + + event_type = matrix_json_object_get_string_member( + json_event_obj, "type"); + state_key = matrix_json_object_get_string_member( + json_event_obj, "state_key"); + json_content_obj = matrix_json_object_get_object_member( + json_event_obj, "content"); + + if(event_type == NULL || state_key == NULL || json_content_obj == NULL) { + purple_debug_warning("matrixprpl", "event missing fields"); + return; + } + + event = matrix_event_new(event_type, json_content_obj); + + 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)matrix_event_free); + g_hash_table_insert(state_table, g_strdup(event_type), + state_table_entry); + old_event = NULL; + } else { + old_event = g_hash_table_lookup(state_table_entry, + state_key); + } + + if(callback) { + callback(event_type, state_key, old_event, event, user_data); + } + + g_hash_table_insert(state_table_entry, g_strdup(state_key), event); +} + + +/** + * If the room has an official name, or an alias, return it + * + * @returns a string which should be freed + */ +gchar *matrix_statetable_get_room_alias(MatrixRoomStateEventTable *state_table) +{ + GHashTable *tmp; + MatrixRoomEvent *event; + const gchar *tmpname = NULL; + + /* start by looking for the official room name */ + event = matrix_statetable_get_event(state_table, "m.room.name", ""); + if(event != NULL) { + tmpname = matrix_json_object_get_string_member( + event->content, "name"); + if(tmpname != NULL) { + return g_strdup(tmpname); + } + } + + /* look for an alias */ + tmp = (GHashTable *) g_hash_table_lookup(state_table, "m.room.aliases"); + if(tmp != NULL) { + GHashTableIter iter; + gpointer key, value; + + g_hash_table_iter_init(&iter, tmp); + while(g_hash_table_iter_next(&iter, &key, &value)) { + MatrixRoomEvent *event = value; + JsonArray *array = matrix_json_object_get_array_member( + event->content, "aliases"); + if(array != NULL && json_array_get_length(array) > 0) { + tmpname = matrix_json_array_get_string_element(array, 0); + if(tmpname != NULL) { + return g_strdup(tmpname); + } + } + } + } + + return NULL; +} diff --git a/matrix-statetable.h b/matrix-statetable.h new file mode 100644 index 0000000..d5ab324 --- /dev/null +++ b/matrix-statetable.h @@ -0,0 +1,79 @@ +/* + * matrix-statetable.h + * + * + * Copyright (c) Openmarket UK Ltd 2015 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#ifndef MATRIX_STATETABLE_H_ +#define MATRIX_STATETABLE_H_ + +#include <glib.h> + +struct _MatrixRoomEvent; +struct _JsonObject; + + +/* 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 + * MatrixRoomEvent. + * + */ +typedef GHashTable MatrixRoomStateEventTable; + + +/** + * The type of a function which can be passed into matrix_statetable_update + * to be called to handle an update + */ +typedef void (*MatrixStateUpdateCallback)(const gchar *event_type, + const gchar *state_key, struct _MatrixRoomEvent *old_state, + struct _MatrixRoomEvent *new_state, gpointer user_data); + + +/** + * create a new, empty, state table + */ +MatrixRoomStateEventTable *matrix_statetable_new(); + + +/** + * look up a particular bit of state + * + * @returns null if this key ies not known + */ +struct _MatrixRoomEvent *matrix_statetable_get_event( + MatrixRoomStateEventTable *state_table, const gchar *event_type, + const gchar *state_key); + + +/** + * Update a state table with a new state event + */ +void matrix_statetable_update(MatrixRoomStateEventTable *state_table, + const gchar *event_id, struct _JsonObject *json_event_obj, + MatrixStateUpdateCallback callback, gpointer user_data); + + +/** + * If the room has an official name, or an alias, return it + * + * @returns a string which should be freed + */ +gchar *matrix_statetable_get_room_alias(MatrixRoomStateEventTable *state_table); + +#endif /* MATRIX_STATETABLE_H_ */ |