aboutsummaryrefslogtreecommitdiffstats
path: root/matrix-room.c
diff options
context:
space:
mode:
Diffstat (limited to 'matrix-room.c')
-rw-r--r--matrix-room.c126
1 files changed, 112 insertions, 14 deletions
diff --git a/matrix-room.c b/matrix-room.c
index 2fbb6a1..6350997 100644
--- a/matrix-room.c
+++ b/matrix-room.c
@@ -31,11 +31,14 @@
#include "libmatrix.h"
#include "matrix-api.h"
+#include "matrix-e2e.h"
#include "matrix-event.h"
#include "matrix-json.h"
#include "matrix-roommembers.h"
#include "matrix-statetable.h"
+#include <gcrypt.h>
+
static gchar *_get_room_name(MatrixConnectionData *conn,
PurpleConversation *conv);
@@ -290,6 +293,9 @@ static void _on_state_update(const gchar *event_type,
strcmp(event_type, "m.room.canonical_alias") == 0 ||
strcmp(event_type, "m.room.name") == 0) {
_schedule_name_update(conv);
+ } else if (strcmp(event_type, "m.room.encryption") == 0) {
+ purple_debug_info("matrixprpl",
+ "Got m.room.encryption on_state_update\n");
}
else if(strcmp(event_type, "m.typing") == 0) {
_on_typing(conv, old_state, new_state);
@@ -621,22 +627,49 @@ static void _send_image_hook(MatrixRoomEvent *event, gboolean just_free)
}
}
-/* Passed through matrix_api_download_file all the way
- * downto _image_download_complete
- */
struct ReceiveImageData {
PurpleConversation *conv;
gint64 timestamp;
const gchar *room_id;
const gchar *sender_display_name;
gchar *original_body;
+ MatrixMediaCryptInfo *crypt;
};
+/* Deal with encrypted image data */
+static void _image_download_complete_crypt(struct ReceiveImageData *rid,
+ const char *raw_body, size_t raw_body_len)
+{
+ void *decrypted = NULL;
+
+ const char *fail_str = matrix_e2e_decrypt_media(rid->crypt,
+ raw_body_len, raw_body, &decrypted);
+
+ if (fail_str) {
+ serv_got_chat_in(rid->conv->account->gc, g_str_hash(rid->room_id),
+ rid->sender_display_name, PURPLE_MESSAGE_RECV,
+ g_strdup_printf("%s (%s)",
+ rid->original_body, fail_str), rid->timestamp / 1000);
+ } else {
+ int img_id = purple_imgstore_add_with_id(decrypted, raw_body_len, NULL);
+ serv_got_chat_in(rid->conv->account->gc, g_str_hash(rid->room_id), rid->sender_display_name,
+ PURPLE_MESSAGE_RECV | PURPLE_MESSAGE_IMAGES,
+ g_strdup_printf("<IMG ID=\"%d\">", img_id), rid->timestamp / 1000);
+ }
+
+ g_free(rid->crypt);
+ g_free(rid->original_body);
+ g_free(rid);
+}
+
static void _image_download_complete(MatrixConnectionData *ma,
gpointer user_data, JsonNode *json_root,
const char *raw_body, size_t raw_body_len, const char *content_type)
{
struct ReceiveImageData *rid = user_data;
+ if (rid->crypt) {
+ return _image_download_complete_crypt(rid, raw_body, raw_body_len);
+ }
if (is_known_image_type(content_type)) {
/* Excellent - something to work with */
int img_id = purple_imgstore_add_with_id(g_memdup(raw_body, raw_body_len),
@@ -669,6 +702,7 @@ static void _image_download_bad_response(MatrixConnectionData *ma, gpointer user
purple_conversation_set_data(rid->conv, PURPLE_CONV_DATA_ACTIVE_SEND,
NULL);
g_free(escaped_body);
+ g_free(rid->crypt);
g_free(rid->original_body);
g_free(rid);
}
@@ -685,11 +719,11 @@ static void _image_download_error(MatrixConnectionData *ma, gpointer user_data,
purple_conversation_set_data(rid->conv, PURPLE_CONV_DATA_ACTIVE_SEND,
NULL);
g_free(escaped_body);
+ g_free(rid->crypt);
g_free(rid->original_body);
g_free(rid);
}
-
/*
* Called from matrix_room_handle_timeline_event when it finds an m.video
* or m.audio or m.file or m.image; msg_body has the fallback text,
@@ -701,24 +735,31 @@ static gboolean _handle_incoming_media(PurpleConversation *conv,
JsonObject *json_content_object, const gchar *msg_type) {
MatrixConnectionData *conn = _get_connection_data_from_conversation(conv);
MatrixApiRequestData *fetch_data = NULL;
+ int is_image = !strcmp("m.image", msg_type);
const gchar *url;
GString *download_url;
guint64 size = 0;
const gchar *mime_type = "unknown";
+ JsonObject *json_file_obj = NULL;
JsonObject *json_info_object;
url = matrix_json_object_get_string_member(json_content_object, "url");
if (!url) {
- /* That seems odd, oh well, no point in getting upset */
- purple_debug_info("matrixprpl", "failed to get url for media\n");
- return FALSE;
+ /* Could be a new style format used by the e2e world */
+ json_file_obj = matrix_json_object_get_object_member(
+ json_content_object, "file");
+ if (json_file_obj) {
+ url = matrix_json_object_get_string_member(json_file_obj,
+ "url");
+ }
+ if (!url) {
+ /* That seems odd, oh well, no point in getting upset */
+ purple_debug_info("matrixprpl", "failed to get url for media\n");
+ return FALSE;
+ }
}
download_url = get_download_url(conn->homeserver, url);
- if (!download_url) {
- purple_debug_error("matrixprpl", "failed to get download_url for media\n");
- return FALSE;
- }
/* the 'info' member is optional */
json_info_object = matrix_json_object_get_object_member(json_content_object,
@@ -748,7 +789,6 @@ static gboolean _handle_incoming_media(PurpleConversation *conv,
* download that. Otherwise, only for m.image, ask for a server generated
* thumbnail.
*/
- int is_image = !strcmp("m.image", msg_type);
const gchar *thumb_url = "";
JsonObject *json_thumb_info;
guint64 thumb_size = 0;
@@ -773,6 +813,20 @@ static gboolean _handle_incoming_media(PurpleConversation *conv,
/* if an m.image is small, get that instead of the thumbnail */
thumb_url = url;
thumb_size = size;
+ } else if (json_file_obj) {
+ /* In the world with file members, we've also got a thumbnail_file
+ * member with potentially quite different data.
+ */
+ JsonObject *tmp_file_obj = matrix_json_object_get_object_member(
+ json_info_object, "thumbnail_file");
+ if (tmp_file_obj) {
+ const char *tmp_url;
+ tmp_url = matrix_json_object_get_string_member(tmp_file_obj, "url");
+ if (tmp_url) {
+ thumb_url = tmp_url;
+ json_file_obj = tmp_file_obj;
+ }
+ }
}
if (thumb_url || is_image) {
struct ReceiveImageData *rid;
@@ -783,6 +837,17 @@ static gboolean _handle_incoming_media(PurpleConversation *conv,
rid->room_id = room_id;
rid->original_body = g_strdup(msg_body);
+ if (json_file_obj) {
+ /* It's almost certainly encrypted data, extract the info.
+ * Note if it is then we must fetch the raw, we can't ask for the server
+ * to generate a thumb.
+ */
+ if (!matrix_e2e_parse_media_decrypt_info(&rid->crypt, json_file_obj)) {
+ g_free(rid);
+ return FALSE;
+ }
+ }
+
if (thumb_url && (thumb_size > 0) && (thumb_size < purple_max_media_size)) {
fetch_data = matrix_api_download_file(conn, thumb_url,
purple_max_media_size,
@@ -790,7 +855,7 @@ static gboolean _handle_incoming_media(PurpleConversation *conv,
_image_download_error,
_image_download_bad_response,
rid);
- } else if (thumb_url) {
+ } else if (thumb_url && !rid->crypt) {
/* Ask the server to generate a thumbnail of the thumbnail.
* Useful to improve the chance of showing something when the
* original thumbnail is too big.
@@ -802,7 +867,7 @@ static gboolean _handle_incoming_media(PurpleConversation *conv,
_image_download_error,
_image_download_bad_response,
rid);
- } else {
+ } else if (!rid->crypt) {
/* Ask the server to generate a thumbnail. Only for m.image.
* TODO: Configure the size of thumbnails.
* 640x480 is a good a width as any and reasonably likely to
@@ -819,6 +884,9 @@ static gboolean _handle_incoming_media(PurpleConversation *conv,
}
purple_conversation_set_data(conv, PURPLE_CONV_DATA_ACTIVE_SEND,
fetch_data);
+ if (!fetch_data) {
+ g_free(rid->crypt);
+ }
return fetch_data != NULL;
}
return TRUE;
@@ -928,6 +996,7 @@ void matrix_room_handle_timeline_event(PurpleConversation *conv,
gchar *tmp_body = NULL;
gchar *escaped_body = NULL;
PurpleMessageFlags flags;
+ JsonParser *decrypted_parser = NULL;
const gchar *sender_display_name;
MatrixRoomMember *sender = NULL;
@@ -947,6 +1016,31 @@ void matrix_room_handle_timeline_event(PurpleConversation *conv,
return;
}
+ if(!strcmp(event_type, "m.room.encrypted")) {
+ purple_debug_info("matrixprpl", "Got an m.room.encrypted!\n");
+ decrypted_parser = matrix_e2e_decrypt_room(conv, json_event_obj);
+ if (!decrypted_parser) {
+ purple_debug_warning("matrixprpl",
+ "Failed to decrypt m.room.encrypted");
+ return;
+ }
+ JsonNode *decrypted_node = json_parser_get_root(decrypted_parser);
+ JsonObject *decrypted_body;
+ decrypted_body = matrix_json_node_get_object(decrypted_node);
+ event_type = matrix_json_object_get_string_member(decrypted_body,
+ "type");
+ json_content_obj = matrix_json_object_get_object_member(decrypted_body,
+ "content");
+ // TODO: Check room_id matches
+ // TODO: Add some info about device trust etc
+ if (!event_type || !json_content_obj) {
+ purple_debug_warning("matrixprpl",
+ "Failed to find members of decrypted json");
+ g_object_unref(decrypted_parser);
+ return;
+ }
+ }
+
if(strcmp(event_type, "m.room.message") != 0) {
purple_debug_info("matrixprpl", "ignoring unknown room event %s\n",
event_type);
@@ -1013,6 +1107,9 @@ void matrix_room_handle_timeline_event(PurpleConversation *conv,
serv_got_chat_in(conv->account->gc, g_str_hash(room_id),
sender_display_name, flags, escaped_body, timestamp / 1000);
g_free(escaped_body);
+ if (decrypted_parser) {
+ g_object_unref(decrypted_parser);
+ }
}
@@ -1081,6 +1178,7 @@ void matrix_room_leave_chat(PurpleConversation *conv)
g_list_free_full(event_queue, (GDestroyNotify)matrix_event_free);
purple_conversation_set_data(conv, PURPLE_CONV_DATA_EVENT_QUEUE, NULL);
}
+ matrix_e2e_cleanup_conversation(conv);
}