aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--matrix-room.c386
-rw-r--r--matrix-room.h35
-rw-r--r--matrix-roommembers.c316
-rw-r--r--matrix-roommembers.h110
-rw-r--r--matrix-sync.c17
6 files changed, 549 insertions, 316 deletions
diff --git a/Makefile b/Makefile
index 5f41767..8d1de63 100644
--- a/Makefile
+++ b/Makefile
@@ -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);
}