diff options
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | matrix-room.c | 386 | ||||
-rw-r--r-- | matrix-room.h | 35 | ||||
-rw-r--r-- | matrix-roommembers.c | 316 | ||||
-rw-r--r-- | matrix-roommembers.h | 110 | ||||
-rw-r--r-- | matrix-sync.c | 17 |
6 files changed, 549 insertions, 316 deletions
@@ -19,6 +19,7 @@ CPPFLAGS+=-MMD OBJECTS=libmatrix.o matrix-api.o matrix-connection.o matrix-json.o \ matrix-room.o \ + matrix-roommembers.o \ matrix-sync.o TARGET=libmatrix.so diff --git a/matrix-room.c b/matrix-room.c index 485c657..edd29ac 100644 --- a/matrix-room.c +++ b/matrix-room.c @@ -28,6 +28,7 @@ #include "libmatrix.h" #include "matrix-api.h" #include "matrix-json.h" +#include "matrix-roommembers.h" /* * identifiers for purple_conversation_get/set_data @@ -57,27 +58,6 @@ static MatrixConnectionData *_get_connection_data_from_conversation( * Members */ -/* The MatrixRoomMemberTable is a hashtable from userid to MatrixRoomMember * - */ -typedef GHashTable MatrixRoomMemberTable; - -#define MATRIX_ROOM_MEMBERSHIP_NONE 0 -#define MATRIX_ROOM_MEMBERSHIP_JOIN 1 -#define MATRIX_ROOM_MEMBERSHIP_INVITE 2 -#define MATRIX_ROOM_MEMBERSHIP_LEAVE 3 - - -typedef struct _MatrixRoomMember { - /* the displayname we gave to purple */ - gchar *current_displayname; - - /* the current room membership */ - int membership; - - /* the displayname from the state table */ - const gchar *state_displayname; -} MatrixRoomMember; - /** * Get the member table for a room */ @@ -87,214 +67,6 @@ MatrixRoomMemberTable *matrix_room_get_member_table(PurpleConversation *conv) } -/** - * calculate the displayname for the given member - * - * @returns a string, which should be freed - */ -static gchar *_calculate_displayname_for_member(const gchar *member_user_id, - const MatrixRoomMember *member) -{ - if(member->state_displayname != NULL) { - return g_strdup(member->state_displayname); - } else { - return g_strdup(member_user_id); - } -} - - -static int _parse_membership(const gchar *membership) -{ - if(membership == NULL) - return MATRIX_ROOM_MEMBERSHIP_NONE; - - if(strcmp(membership, "join") == 0) - return MATRIX_ROOM_MEMBERSHIP_JOIN; - if(strcmp(membership, "leave") == 0) - return MATRIX_ROOM_MEMBERSHIP_LEAVE; - if(strcmp(membership, "invite") == 0) - return MATRIX_ROOM_MEMBERSHIP_INVITE; - return MATRIX_ROOM_MEMBERSHIP_NONE; -} - -static MatrixRoomMember *_new_member(MatrixRoomMemberTable *table, - const gchar *userid) -{ - MatrixRoomMember *mem = g_new0(MatrixRoomMember, 1); - g_hash_table_insert(table, g_strdup(userid), mem); - return mem; -} - -static void _free_member(MatrixRoomMember *member) -{ - g_assert(member != NULL); - g_free(member->current_displayname); - member->current_displayname = NULL; - g_free(member); -} - -static MatrixRoomMember *_lookup_member(MatrixRoomMemberTable *table, - const gchar *userid) -{ - return g_hash_table_lookup(table, userid); -} - - -static MatrixRoomMemberTable *_new_member_table() -{ - return g_hash_table_new_full(g_str_hash, g_str_equal, g_free, - (GDestroyNotify) _free_member); -} - - -static void _free_member_table(MatrixRoomMemberTable *table) -{ - g_hash_table_destroy(table); -} - - -static void _on_member_joined(PurpleConversation *conv, - const gchar *member_user_id, MatrixRoomMember *member) -{ - PurpleConvChat *chat = PURPLE_CONV_CHAT(conv); - gchar *displayname; - - g_assert(member->current_displayname == NULL); - displayname = _calculate_displayname_for_member(member_user_id, member); - - purple_conv_chat_add_user(chat, displayname, NULL, - PURPLE_CBFLAGS_NONE, TRUE); - member->current_displayname = displayname; -} - -static void _on_member_changed_displayname(PurpleConversation *conv, - const gchar *member_user_id, MatrixRoomMember *member) -{ - PurpleConvChat *chat = PURPLE_CONV_CHAT(conv); - gchar *old_displayname, *new_displayname; - - old_displayname = member->current_displayname; - g_assert(old_displayname != NULL); - new_displayname = _calculate_displayname_for_member(member_user_id, member); - - purple_conv_chat_rename_user(chat, old_displayname, new_displayname); - g_free(old_displayname); - member->current_displayname = new_displayname; -} - - -static void _on_member_left(PurpleConversation *conv, - const gchar *member_user_id, MatrixRoomMember *member) -{ - PurpleConvChat *chat = PURPLE_CONV_CHAT(conv); - gchar *old_displayname; - - old_displayname = member->current_displayname; - g_assert(old_displayname != NULL); - purple_conv_chat_remove_user(chat, old_displayname, NULL); - g_free(old_displayname); - member->current_displayname = NULL; -} - - -static void _update_member(PurpleConversation *conv, - MatrixRoomMemberTable *member_table, const gchar *member_user_id, - const gchar *new_displayname, const gchar *new_membership, - gboolean suppress_state_update_notifications) -{ - const gchar *old_displayname = NULL; - MatrixRoomMember *member; - int old_membership_val = MATRIX_ROOM_MEMBERSHIP_NONE, - new_membership_val = _parse_membership(new_membership); - - member = _lookup_member(member_table, member_user_id); - - if(member != NULL) { - old_displayname = member -> state_displayname; - old_membership_val = member -> membership; - } - - if(!member) { - member = _new_member(member_table, member_user_id); - } - member->membership = new_membership_val; - member->state_displayname = new_displayname; - - if(suppress_state_update_notifications) - return; - - purple_debug_info("matrixprpl", "Room %s: member %s change %i->%i, " - "%s->%s\n", conv -> name, member_user_id, - old_membership_val, new_membership_val, - old_displayname, new_displayname); - - - if(new_membership_val == MATRIX_ROOM_MEMBERSHIP_JOIN) { - if(old_membership_val != MATRIX_ROOM_MEMBERSHIP_JOIN) { - _on_member_joined(conv, member_user_id, member); - } else if(g_strcmp0(old_displayname, new_displayname) != 0) { - _on_member_changed_displayname(conv, member_user_id, member); - } - } else { - if(old_membership_val == MATRIX_ROOM_MEMBERSHIP_JOIN) { - _on_member_left(conv, member_user_id, member); - } - } -} - - -static void _init_user_list(PurpleConversation *conv) -{ - GHashTableIter iter; - gpointer key, value; - PurpleConvChat *chat = PURPLE_CONV_CHAT(conv); - MatrixRoomMemberTable *member_table = matrix_room_get_member_table(conv); - GList *users = NULL, *flags = NULL; - - purple_debug_info("matrixprpl", "Doing initial member population of %s\n", - conv->name); - - g_hash_table_iter_init (&iter, member_table); - while (g_hash_table_iter_next (&iter, &key, &value)) { - const gchar *user_id = key; - gchar *displayname; - MatrixRoomMember *member = value; - - if(member->membership != MATRIX_ROOM_MEMBERSHIP_JOIN) - continue; - - g_assert(member->current_displayname == NULL); - displayname = _calculate_displayname_for_member(user_id, member); - - users = g_list_prepend(users, displayname); - flags = g_list_prepend(users, GINT_TO_POINTER(PURPLE_CBFLAGS_NONE)); - member->current_displayname = displayname; - } - - purple_conv_chat_add_users(chat, users, NULL, flags, FALSE); -} - -/** - * Returns a list of user ids. Free the list, but not the string pointers. - */ -static GList *_get_active_members(MatrixRoomMemberTable *member_table) -{ - GHashTableIter iter; - gpointer key, value; - GList *members = NULL; - - g_hash_table_iter_init (&iter, member_table); - while (g_hash_table_iter_next (&iter, &key, &value)) { - const gchar *user_id = key; - MatrixRoomMember *member = value; - - if(member->membership == MATRIX_ROOM_MEMBERSHIP_JOIN) - members = g_list_prepend(members, (gpointer)user_id); - } - return members; -} - - /****************************************************************************** * * Events @@ -347,6 +119,9 @@ static void _free_room_event(MatrixRoomEvent *event) */ typedef GHashTable MatrixRoomStateEventTable; + +static void _update_room_alias(PurpleConversation *conv); + /** * create a new, empty, state table */ @@ -389,22 +164,14 @@ static MatrixRoomEvent *matrix_room_get_state_event( * Called when there is a change to the member list */ static void _on_member_change(PurpleConversation *conv, - const gchar *member_user_id, - MatrixRoomEvent *old_state, MatrixRoomEvent *new_state, - gboolean suppress_state_update_notifications) + const gchar *member_user_id, MatrixRoomEvent *new_state) { MatrixRoomMemberTable *member_table; - const gchar *new_displayname, *new_membership; member_table = matrix_room_get_member_table(conv); - new_displayname = matrix_json_object_get_string_member( - new_state->content, "displayname"); - new_membership = matrix_json_object_get_string_member( - new_state->content, "membership"); - - _update_member(conv, member_table, member_user_id, new_displayname, - new_membership, suppress_state_update_notifications); + matrix_roommembers_update_member(member_table, member_user_id, + new_state->content); } @@ -416,22 +183,24 @@ static void _on_member_change(PurpleConversation *conv, */ static void _on_state_update(PurpleConversation *conv, const gchar *event_type, const gchar *state_key, - MatrixRoomEvent *old_state, MatrixRoomEvent *new_state, - gboolean suppress_state_update_notifications) + MatrixRoomEvent *old_state, MatrixRoomEvent *new_state) { g_assert(new_state != NULL); - if(strcmp(event_type, "m.room.member") == 0) - _on_member_change(conv, state_key, old_state, new_state, - suppress_state_update_notifications); + if(strcmp(event_type, "m.room.member") == 0) { + _on_member_change(conv, state_key, new_state); + } + else if(strcmp(event_type, "m.room.alias") == 0 || + strcmp(event_type, "m.room.room_name") == 0) { + _update_room_alias(conv); + } } /** * Update the state table on a room */ void matrix_room_handle_state_event(PurpleConversation *conv, - const gchar *event_id, JsonObject *json_event_obj, - gboolean suppress_state_update_notifications) + const gchar *event_id, JsonObject *json_event_obj) { const gchar *event_type, *state_key; JsonObject *json_content_obj; @@ -466,8 +235,7 @@ void matrix_room_handle_state_event(PurpleConversation *conv, state_key); } - _on_state_update(conv, event_type, state_key, old_event, event, - suppress_state_update_notifications); + _on_state_update(conv, event_type, state_key, old_event, event); g_hash_table_insert(state_table_entry, g_strdup(state_key), event); } @@ -487,7 +255,7 @@ static gchar *_get_room_name_from_members(MatrixConnectionData *conn, MatrixRoomMemberTable *member_table; member_table = matrix_room_get_member_table(conv); - members = _get_active_members(member_table); + members = matrix_roommembers_get_active_members(member_table); /* remove ourselves from the list */ tmp = g_list_find_custom(members, conn->user_id, (GCompareFunc)strcmp); @@ -501,16 +269,16 @@ static gchar *_get_room_name_from_members(MatrixConnectionData *conn, return g_strdup("invitation"); } - member1 = _lookup_member(member_table, members->data) -> - current_displayname; + member1 = matrix_roommembers_get_displayname_for_member( + member_table, members->data); if(members->next == NULL) { /* one other person */ res = g_strdup(member1); } else if(members->next->next == NULL) { /* two other people */ - gchar *member2 = _lookup_member(member_table, members->next->data) -> - current_displayname; + const gchar *member2 = matrix_roommembers_get_displayname_for_member( + member_table, members->next->data); res = g_strdup_printf(_("%s and %s"), member1, member2); } else { int nmembers = g_list_length(members); @@ -526,7 +294,7 @@ static gchar *_get_room_name_from_members(MatrixConnectionData *conn, * * @returns a string which should be freed */ -static char *matrix_room_get_name(MatrixConnectionData *conn, +static char *_get_room_name(MatrixConnectionData *conn, PurpleConversation *conv) { GHashTable *tmp; @@ -572,6 +340,28 @@ static char *matrix_room_get_name(MatrixConnectionData *conn, } +/** + * 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 @@ -717,7 +507,6 @@ void matrix_room_handle_timeline_event(PurpleConversation *conv, const gchar *room_id, *msg_body; PurpleMessageFlags flags; const gchar *sender_display_name; - MatrixRoomMember *sender = NULL; room_id = conv->name; @@ -762,15 +551,14 @@ void matrix_room_handle_timeline_event(PurpleConversation *conv, return; } - if(sender_id != NULL) { + if(sender_id == NULL) { + sender_display_name = "<unknown>"; + } else { MatrixRoomMemberTable *member_table = matrix_room_get_member_table(conv); - sender = _lookup_member(member_table, sender_id); - } - if(sender != NULL && sender->current_displayname != NULL) { - sender_display_name = sender->current_displayname; - } else { - sender_display_name = sender_id; + + sender_display_name = matrix_roommembers_get_displayname_for_member( + member_table, sender_id); } flags = PURPLE_MESSAGE_RECV; @@ -787,7 +575,7 @@ PurpleConversation *matrix_room_create_conversation( { PurpleConversation *conv; MatrixRoomStateEventTable *state_table; - GHashTable *member_table; + MatrixRoomMemberTable *member_table; purple_debug_info("matrixprpl", "New room %s\n", room_id); @@ -796,7 +584,7 @@ PurpleConversation *matrix_room_create_conversation( /* set our data on it */ state_table = _create_state_table(); - member_table = _new_member_table(); + 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); purple_conversation_set_data(conv, PURPLE_CONV_DATA_STATE, state_table); @@ -815,7 +603,7 @@ void matrix_room_leave_chat(PurpleConversation *conv) { MatrixRoomStateEventTable *state_table; GList *event_queue; - GHashTable *member_table; + MatrixRoomMemberTable *member_table; /* TODO: actually tell the server that we are leaving the chat, and only * destroy the memory structures once we get a response from that. @@ -827,7 +615,7 @@ void matrix_room_leave_chat(PurpleConversation *conv) purple_conversation_set_data(conv, PURPLE_CONV_DATA_STATE, NULL); member_table = matrix_room_get_member_table(conv); - _free_member_table(member_table); + matrix_roommembers_free_table(member_table); purple_conversation_set_data(conv, PURPLE_CONV_MEMBER_TABLE, NULL); event_queue = _get_event_queue(conv); @@ -838,31 +626,59 @@ void matrix_room_leave_chat(PurpleConversation *conv) } -/** - * 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 _set_room_alias(PurpleConversation *conv) +static void _update_user_list(PurpleConversation *conv, + gboolean announce_arrivals) { - gchar *room_name; - MatrixConnectionData *conn = _get_connection_data_from_conversation(conv); - PurpleChat *chat = purple_blist_find_chat(conv->account, conv->name); + PurpleConvChat *chat = PURPLE_CONV_CHAT(conv); + MatrixRoomMemberTable *table = matrix_room_get_member_table(conv); + GList *names = NULL, *flags = NULL, *oldnames = NULL; + gboolean updated = FALSE; - /* we know there should be a buddy list entry for this room */ - g_assert(chat != NULL); + purple_debug_info("matrixprpl", "Updating members in %s\n", + conv->name); - room_name = matrix_room_get_name(conn, conv); - purple_blist_alias_chat(chat, room_name); - g_free(room_name); + matrix_roommembers_get_new_members(table, &names, &flags); + if(names) { + purple_conv_chat_add_users(chat, names, NULL, flags, announce_arrivals); + g_list_free(names); + g_list_free(flags); + names = NULL; + flags = NULL; + updated = TRUE; + } + + matrix_roommembers_get_renamed_members(table, &oldnames, &names); + if(names) { + GList *name1 = names, *oldname1 = oldnames; + while(name1 && oldname1) { + purple_conv_chat_rename_user(chat, oldname1->data, name1->data); + name1 = g_list_next(name1); + oldname1 = g_list_next(oldname1); + } + g_list_free_full(oldnames, (GDestroyNotify)g_free); + g_list_free(names); + names = NULL; + oldnames = NULL; + updated = TRUE; + } + + matrix_roommembers_get_left_members(table, &names); + if(names) { + purple_conv_chat_remove_users(chat, names, NULL); + g_list_free_full(names, (GDestroyNotify)g_free); + names = NULL; + updated = TRUE; + } + + if(updated) + _update_room_alias(conv); } -void matrix_room_handle_initial_state(PurpleConversation *conv) +void matrix_room_complete_state_update(PurpleConversation *conv, + gboolean announce_arrivals) { - _init_user_list(conv); - _set_room_alias(conv); + _update_user_list(conv, announce_arrivals); } diff --git a/matrix-room.h b/matrix-room.h index 2ac29eb..90978d1 100644 --- a/matrix-room.h +++ b/matrix-room.h @@ -36,10 +36,11 @@ struct _PurpleConversation; struct _PurpleConnection; /** - * @param conn connection data for the account * @param conv conversation info */ -void matrix_room_handle_initial_state(struct _PurpleConversation *conv); +void matrix_room_complete_state_update(struct _PurpleConversation *conv, + gboolean announce_arrivals); + /** * Create a new conversation for the given room @@ -53,38 +54,32 @@ struct _PurpleConversation *matrix_room_create_conversation( */ void matrix_room_leave_chat(struct _PurpleConversation *conv); + /** - * handle a single received timeline event for a room (such as a message) + * Update the state table on a room, based on a received state event * * @param conv info on the room * @param event_id id of the event * @param json_event_obj the event object. */ -void matrix_room_handle_timeline_event(struct _PurpleConversation *conv, - const gchar *event_id, JsonObject *json_event_obj); - -/** - * Send a message in a room - */ -void matrix_room_send_message(struct _PurpleConversation *conv, - const gchar *message); -/************************************************************************* - * - * Room state handling - */ +void matrix_room_handle_state_event(struct _PurpleConversation *conv, + const gchar *event_id, JsonObject *json_event_obj); /** - * Update the state table on a room, based on a received state event + * handle a single received timeline event for a room (such as a message) * * @param conv info on the room * @param event_id id of the event * @param json_event_obj the event object. */ +void matrix_room_handle_timeline_event(struct _PurpleConversation *conv, + const gchar *event_id, JsonObject *json_event_obj); -void matrix_room_handle_state_event(struct _PurpleConversation *conv, - const gchar *event_id, JsonObject *json_event_obj, - gboolean suppress_state_update_notifications); - +/** + * Send a message in a room + */ +void matrix_room_send_message(struct _PurpleConversation *conv, + const gchar *message); #endif diff --git a/matrix-roommembers.c b/matrix-roommembers.c new file mode 100644 index 0000000..a437796 --- /dev/null +++ b/matrix-roommembers.c @@ -0,0 +1,316 @@ +/* + * matrix-roommember.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-roommembers.h" + +#include <stdlib.h> +#include <string.h> + +#include "debug.h" + +#include "matrix-json.h" + +/****************************************************************************** + * + * Individual members + */ + +typedef struct _MatrixRoomMember { + gchar *userid; + + /* the displayname we gave to purple */ + gchar *current_displayname; + + /* the current room membership */ + int membership; + + /* the displayname from the state table */ + const gchar *state_displayname; +} MatrixRoomMember; + + +/** + * calculate the displayname for the given member + * + * @returns a string, which should be freed + */ +static gchar *_calculate_displayname_for_member(const MatrixRoomMember *member) +{ + if(member->state_displayname != NULL) { + return g_strdup(member->state_displayname); + } else { + return g_strdup(member->userid); + } +} + + +static int _parse_membership(const gchar *membership) +{ + if(membership == NULL) + return MATRIX_ROOM_MEMBERSHIP_NONE; + + if(strcmp(membership, "join") == 0) + return MATRIX_ROOM_MEMBERSHIP_JOIN; + if(strcmp(membership, "leave") == 0) + return MATRIX_ROOM_MEMBERSHIP_LEAVE; + if(strcmp(membership, "invite") == 0) + return MATRIX_ROOM_MEMBERSHIP_INVITE; + return MATRIX_ROOM_MEMBERSHIP_NONE; +} + +static MatrixRoomMember *_new_member(const gchar *userid) +{ + MatrixRoomMember *mem = g_new0(MatrixRoomMember, 1); + mem->userid = g_strdup(userid); + return mem; +} + +static void _free_member(MatrixRoomMember *member) +{ + g_assert(member != NULL); + g_free(member->userid); + member->userid = NULL; + g_free(member->current_displayname); + member->current_displayname = NULL; + g_free(member); +} + + +/****************************************************************************** + * + * member table + */ + +struct _MatrixRoomMemberTable { + GHashTable *hash_table; + GSList *new_members; + GSList *left_members; + GSList *renamed_members; +}; + +MatrixRoomMemberTable *matrix_roommembers_new_table() +{ + MatrixRoomMemberTable *table; + table = g_new0(MatrixRoomMemberTable, 1); + table -> hash_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, + (GDestroyNotify) _free_member); + return table; +} + + +void matrix_roommembers_free_table(MatrixRoomMemberTable *table) +{ + g_hash_table_destroy(table->hash_table); + table->hash_table = NULL; + g_free(table); +} + + +static MatrixRoomMember *_lookup_member(MatrixRoomMemberTable *table, + const gchar *userid) +{ + return g_hash_table_lookup(table->hash_table, userid); +} + + +#if 0 +static void _on_member_changed_displayname(PurpleConversation *conv, + const gchar *member_user_id, MatrixRoomMember *member) +{ + PurpleConvChat *chat = PURPLE_CONV_CHAT(conv); + gchar *old_displayname, *new_displayname; + + old_displayname = member->current_displayname; + g_assert(old_displayname != NULL); + new_displayname = _calculate_displayname_for_member(member_user_id, member); + + purple_conv_chat_rename_user(chat, old_displayname, new_displayname); + g_free(old_displayname); + member->current_displayname = new_displayname; +} + + +static void _on_member_left(PurpleConversation *conv, + const gchar *member_user_id, MatrixRoomMember *member) +{ + PurpleConvChat *chat = PURPLE_CONV_CHAT(conv); + gchar *old_displayname; + + old_displayname = member->current_displayname; + g_assert(old_displayname != NULL); + purple_conv_chat_remove_user(chat, old_displayname, NULL); + g_free(old_displayname); + member->current_displayname = NULL; +} +#endif + + +const gchar *matrix_roommembers_get_displayname_for_member( + MatrixRoomMemberTable *table, const gchar *user_id) +{ + MatrixRoomMember *member = _lookup_member(table, user_id); + gchar *displayname; + + if(member == NULL) + return user_id; + + displayname = member -> current_displayname; + + if (displayname != NULL) + return displayname; + + member -> current_displayname = displayname; + return displayname; +} + + +void matrix_roommembers_get_new_members(MatrixRoomMemberTable *table, + GList **display_names, GList **flags) +{ + while(table->new_members != NULL) { + MatrixRoomMember *member = table->new_members->data; + gchar *displayname; + GSList *tmp; + + g_assert(member->current_displayname == NULL); + displayname = _calculate_displayname_for_member(member); + member->current_displayname = displayname; + *display_names = g_list_prepend(*display_names, displayname); + *flags = g_list_prepend(*flags, GINT_TO_POINTER(0)); + + tmp = table->new_members; + table->new_members = tmp->next; + g_slist_free_1(tmp); + } +} + +void matrix_roommembers_get_renamed_members(MatrixRoomMemberTable *table, + GList **old_names, GList **new_names) +{ + while(table->renamed_members != NULL) { + MatrixRoomMember *member = table->renamed_members->data; + gchar *displayname; + GSList *tmp; + + g_assert(member->current_displayname != NULL); + displayname = _calculate_displayname_for_member(member); + *old_names = g_list_prepend(*old_names, member->current_displayname); + *new_names = g_list_prepend(*new_names, displayname); + member->current_displayname = displayname; + + tmp = table->renamed_members; + table->renamed_members = tmp->next; + g_slist_free_1(tmp); + } +} + +void matrix_roommembers_get_left_members(MatrixRoomMemberTable *table, + GList **names) +{ + while(table->left_members != NULL) { + MatrixRoomMember *member = table->left_members->data; + GSList *tmp; + + g_assert(member->current_displayname != NULL); + *names = g_list_prepend(*names, member->current_displayname); + member->current_displayname = NULL; + + tmp = table->left_members; + table->left_members = tmp->next; + g_slist_free_1(tmp); + } +} + + +void matrix_roommembers_update_member(MatrixRoomMemberTable *table, + const gchar *member_user_id, JsonObject *new_state) +{ + const gchar *old_displayname = NULL; + MatrixRoomMember *member; + int old_membership_val = MATRIX_ROOM_MEMBERSHIP_NONE, + new_membership_val; + const gchar *new_displayname, *new_membership; + + new_displayname = matrix_json_object_get_string_member( + new_state, "displayname"); + new_membership = matrix_json_object_get_string_member( + new_state, "membership"); + + new_membership_val = _parse_membership(new_membership); + + member = _lookup_member(table, member_user_id); + + if(member != NULL) { + old_displayname = member -> state_displayname; + old_membership_val = member -> membership; + } + + if(!member) { + member = _new_member(member_user_id); + g_hash_table_insert(table->hash_table, g_strdup(member_user_id), + member); + } + member->membership = new_membership_val; + member->state_displayname = new_displayname; + + purple_debug_info("matrixprpl", "member %s change %i->%i, " + "%s->%s\n", member_user_id, + old_membership_val, new_membership_val, + old_displayname, new_displayname); + + if(new_membership_val == MATRIX_ROOM_MEMBERSHIP_JOIN) { + if(old_membership_val != MATRIX_ROOM_MEMBERSHIP_JOIN) { + table->new_members = g_slist_append( + table->new_members, member); + } else if(g_strcmp0(old_displayname, new_displayname) != 0) { + table->renamed_members = g_slist_append( + table->renamed_members, member); + } + } else { + if(old_membership_val == MATRIX_ROOM_MEMBERSHIP_JOIN) { + table->left_members = g_slist_append( + table->left_members, member); + } + } +} + + +/** + * Returns a list of user ids. Free the list, but not the string pointers. + */ +GList *matrix_roommembers_get_active_members( + MatrixRoomMemberTable *member_table) +{ + GHashTableIter iter; + gpointer key, value; + GList *members = NULL; + + g_hash_table_iter_init (&iter, member_table->hash_table); + while (g_hash_table_iter_next (&iter, &key, &value)) { + const gchar *user_id = key; + MatrixRoomMember *member = value; + + if(member->membership == MATRIX_ROOM_MEMBERSHIP_JOIN) + members = g_list_prepend(members, (gpointer)user_id); + } + return members; +} diff --git a/matrix-roommembers.h b/matrix-roommembers.h new file mode 100644 index 0000000..e00cc70 --- /dev/null +++ b/matrix-roommembers.h @@ -0,0 +1,110 @@ +/* + * matrix-roommembers.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_ROOMMEMBERS_H_ +#define MATRIX_ROOMMEMBERS_H_ + +#include <glib.h> + +/* the potential states of a user's membership of a room */ +#define MATRIX_ROOM_MEMBERSHIP_NONE 0 +#define MATRIX_ROOM_MEMBERSHIP_JOIN 1 +#define MATRIX_ROOM_MEMBERSHIP_INVITE 2 +#define MATRIX_ROOM_MEMBERSHIP_LEAVE 3 + +typedef struct _MatrixRoomMemberTable MatrixRoomMemberTable; +struct _JsonObject; + +/** + * Allocate a new MatrixRoomMemberTable + */ +MatrixRoomMemberTable *matrix_roommembers_new_table(); + +/** + * Free a MatrixRoomMemberTable + */ +void matrix_roommembers_free_table(MatrixRoomMemberTable *table); + + +/** + * Handle the update of a room member. + * + * For efficiency, we do not immediately notify purple of the changes. Instead, + * you should call matrix_roommembers_get_(new,renamed,left)_members once + * the whole state table has been handled. + */ +void matrix_roommembers_update_member(MatrixRoomMemberTable *table, + const gchar *member_user_id, struct _JsonObject *new_state); + + +/** + * Get the displayname for the given userid + * + * @returns a string, which should *not* be freed + */ +const gchar *matrix_roommembers_get_displayname_for_member( + MatrixRoomMemberTable *table, const gchar *user_id); + + +/** + * Get a list of the members who have joined this room. + * + * Returns a list of user ids. Free the list, but not the string pointers. + */ +GList *matrix_roommembers_get_active_members( + MatrixRoomMemberTable *member_table); + + +/** + * Get a list of the new members since the last time this function was called. + * + * @param display_names returns the list of display names. Do not free the + * pointers. + * @param flags returns a corresponding list of zeros + */ +void matrix_roommembers_get_new_members(MatrixRoomMemberTable *table, + GList **display_names, GList **flags); + + +/** + * Get a list of the members who have been renamed since the last time this + * function was called. + * + * @param old_names returns the list of old display names. These are no + * longer required, so should be freed + * @param new_names returns the list of new display names. Do *not* free these + * pointers. + */ +void matrix_roommembers_get_renamed_members(MatrixRoomMemberTable *table, + GList **old_names, GList **new_names); + + +/** + * Get a list of the members who have left the channel since the last time this + * function was called. + * + * @param new_names returns the list of display names. These are no + * longer required, so should be freed + */ +void matrix_roommembers_get_left_members(MatrixRoomMemberTable *table, + GList **names); + +#endif /* MATRIX_ROOMMEMBERS_H_ */ diff --git a/matrix-sync.c b/matrix-sync.c index 0137ae1..2f3b887 100644 --- a/matrix-sync.c +++ b/matrix-sync.c @@ -35,7 +35,6 @@ typedef struct _RoomEventParserData { PurpleConversation *conv; JsonObject *event_map; gboolean state_events; - gboolean initial_sync; } RoomEventParserData; @@ -70,8 +69,7 @@ static void _parse_room_event(JsonArray *event_array, guint event_idx, } if(data->state_events) - matrix_room_handle_state_event(conv, event_id, json_event_obj, - data->initial_sync); + matrix_room_handle_state_event(conv, event_id, json_event_obj); else matrix_room_handle_timeline_event(conv, event_id, json_event_obj); } @@ -80,9 +78,9 @@ static void _parse_room_event(JsonArray *event_array, guint event_idx, * parse the list of events in a sync response */ static void _parse_room_event_array(PurpleConversation *conv, JsonArray *events, - JsonObject* event_map, gboolean state_events, gboolean initial_sync) + JsonObject* event_map, gboolean state_events) { - RoomEventParserData data = {conv, event_map, state_events, initial_sync}; + RoomEventParserData data = {conv, event_map, state_events}; json_array_foreach_element(events, _parse_room_event, &data); } @@ -150,11 +148,9 @@ static void matrix_sync_room(const gchar *room_id, state_object = matrix_json_object_get_object_member(room_data, "state"); state_array = matrix_json_object_get_array_member(state_object, "events"); if(state_array != NULL) - _parse_room_event_array(conv, state_array, event_map, TRUE, - initial_sync); - if(initial_sync) - matrix_room_handle_initial_state(conv); + _parse_room_event_array(conv, state_array, event_map, TRUE); + matrix_room_complete_state_update(conv, !initial_sync); /* parse the timeline events */ timeline_object = matrix_json_object_get_object_member( @@ -162,8 +158,7 @@ static void matrix_sync_room(const gchar *room_id, timeline_array = matrix_json_object_get_array_member( timeline_object, "events"); if(timeline_array != NULL) - _parse_room_event_array(conv, timeline_array, event_map, FALSE, - initial_sync); + _parse_room_event_array(conv, timeline_array, event_map, FALSE); } |