/**
* Implementation of the matrix login process
*
* 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-login.h"
#include <string.h>
/* libpurple */
#include "connection.h"
#include "conversation.h"
#include "debug.h"
/* json-glib */
#include <json-glib/json-glib.h>
/* libmatrix */
#include "matrix-api.h"
#include "matrix-json.h"
#include "matrix-room.h"
/* TODO: make this configurable */
#define MATRIX_HOMESERVER "www.sw1v.org"
/**
* handle a room within the initial sync response
*/
static void matrixprpl_handle_initial_sync_room(
const gchar *room_id, JsonObject *room_data, MatrixAccount *ma)
{
JsonObject *state_object, *event_map;
JsonArray *state_array;
const gchar *room_name;
PurpleConversation *conv;
PurpleChat *chat;
PurpleGroup *group;
MatrixRoomStateEventTable *state_table;
event_map = matrix_json_object_get_object_member(room_data, "event_map");
state_object = matrix_json_object_get_object_member(room_data, "state");
state_array = matrix_json_object_get_array_member(state_object, "events");
/* parse the room state */
state_table = g_hash_table_new(g_str_hash, g_str_equal); /* TODO: free */
if(state_array != NULL)
matrix_room_parse_state_events(state_table, state_array, event_map);
/* todo: distinguish between 1-1 chats and group chats? */
/* add the room to the buddy list */
chat = purple_blist_find_chat(ma->pa, room_id);
if (!chat)
{
GHashTable *comp;
group = purple_find_group("Matrix");
if (!group)
{
group = purple_group_new("Matrix");
purple_blist_add_group(group, NULL);
}
comp = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free); /* TODO:
* free? */
g_hash_table_insert(comp, "room_id", g_strdup(room_id)); /* TODO: free? */
chat = purple_chat_new(ma->pa, room_id, comp);
purple_blist_add_chat(chat, group, NULL);
}
/* ensure the alias in the buddy list is up-to-date */
room_name = matrix_room_get_name(state_table);
purple_blist_alias_chat(chat, room_name);
/* tell purple we have joined this chat */
conv = serv_got_joined_chat(ma->pc, g_str_hash(room_id), room_id);
purple_conversation_set_data(conv, "room_id", g_strdup(room_id)); /* TODO: free */
purple_conversation_set_data(conv, "state", state_table);
}
/**
* handle the results of the intialSync request
*/
static void matrixprpl_handle_initial_sync(MatrixAccount *ma, JsonNode *body)
{
JsonObject *rootObj;
JsonObject *rooms;
JsonObject *joined_rooms;
GList *room_ids, *elem;
rootObj = matrix_json_node_get_object(body);
rooms = matrix_json_object_get_object_member(rootObj, "rooms");
joined_rooms = matrix_json_object_get_object_member(rooms, "joined");
if(joined_rooms == NULL) {
purple_debug_warning("matrixprpl", "didn't find joined rooms list\n");
return;
}
room_ids = json_object_get_members(joined_rooms);
for(elem = room_ids; elem; elem = elem->next) {
const gchar *room_id = elem->data;
JsonObject *room_data = matrix_json_object_get_object_member(
joined_rooms, room_id);
purple_debug_info("matrixprpl", "got room %s\n", room_id);
matrixprpl_handle_initial_sync_room(room_id, room_data, ma);
}
g_list_free(room_ids);
#if 0
/* tell purple about everyone on our buddy list who's connected */
foreach_matrixprpl_gc(discover_status, gc, NULL);
/* notify other matrixprpl accounts */
foreach_matrixprpl_gc(report_status_change, gc, NULL);
/* fetch stored offline messages */
purple_debug_info("matrixprpl", "checking for offline messages for %s\n",
acct->username);
offline_messages = g_hash_table_lookup(goffline_messages, acct->username);
while (offline_messages) {
GOfflineMessage *message = (GOfflineMessage *)offline_messages->data;
purple_debug_info("matrixprpl", "delivering offline message to %s: %s\n",
acct->username, message->message);
serv_got_im(gc, message->from, message->message, message->flags,
message->mtime);
offline_messages = g_list_next(offline_messages);
g_free(message->from);
g_free(message->message);
g_free(message);
}
g_list_free(offline_messages);
g_hash_table_remove(goffline_messages, &acct->username);
#endif
}
static void matrixprpl_sync_complete(MatrixAccount *ma,
gpointer user_data,
int http_response_code,
const gchar *http_response_msg,
const gchar *body_start,
JsonNode *body,
const gchar *error_message)
{
if (error_message) {
purple_debug_info("matrixprpl", "initial sync gave error %s\n",
error_message);
purple_connection_error_reason(ma->pc,
PURPLE_CONNECTION_ERROR_NETWORK_ERROR, error_message);
return;
}
if (http_response_code >= 400) {
purple_debug_info("matrixprpl", "initial sync gave response %s: %s\n",
http_response_msg, body_start);
purple_connection_error_reason(ma->pc,
PURPLE_CONNECTION_ERROR_OTHER_ERROR, http_response_msg);
return;
}
purple_connection_update_progress(ma->pc, _("Connected"), 2, 3);
purple_connection_set_state(ma->pc, PURPLE_CONNECTED);
matrixprpl_handle_initial_sync(ma, body);
}
/*
* do the initial sync after login, to get room state
*/
static void matrixprpl_start_sync(MatrixAccount *ma)
{
purple_connection_set_state(ma->pc, PURPLE_CONNECTING);
purple_connection_update_progress(ma->pc, _("Initial Sync"), 1, 3);
matrix_sync(ma, matrixprpl_sync_complete, ma);
}
void matrixprpl_login(PurpleAccount *acct)
{
PurpleConnection *pc = purple_account_get_connection(acct);
MatrixAccount *ma = g_new0(MatrixAccount, 1); /* TODO: free */
purple_connection_set_protocol_data(pc, ma);
ma->pa = acct;
ma->pc = pc;
purple_debug_info("matrixprpl", "logging in %s\n", acct->username);
/* TODO: make this configurable */
ma->homeserver = g_strdup(MATRIX_HOMESERVER);
/* TODO: use the login API to get our access token.
*
* For now, assume that the username *is* the access token.
*/
ma->access_token = g_strdup(acct->username);
matrixprpl_start_sync(ma);
}