Commit 664709cf authored by Richard Bayerle's avatar Richard Bayerle
Browse files

Add 'status' for chats to API

parent ed3af540
......@@ -171,7 +171,9 @@ $(BDIR)/test_lurch_api: $(OBJECTS_W_COVERAGE) $(VENDOR_LIBS) $(BDIR)/test_lurch_
-Wl,--wrap=purple_signal_unregister \
-Wl,--wrap=purple_signal_connect \
-Wl,--wrap=purple_signal_disconnect \
-Wl,--wrap=purple_find_conversation_with_account \
-Wl,--wrap=jabber_pep_publish \
-Wl,--wrap=jabber_chat_find_by_conv \
-Wl,--wrap=axc_get_device_id \
-Wl,--wrap=axc_key_load_public_own \
-Wl,--wrap=axc_key_load_public_addr \
......
......@@ -2,6 +2,7 @@
#include <glib.h>
#include <purple.h>
#include "chat.h"
#include "pep.h"
#include "axc.h"
......@@ -460,6 +461,89 @@ cleanup:
axc_context_destroy_all(axc_ctx_p);
}
void lurch_api_status_chat_handler(PurpleAccount * acc_p, const char * full_conversation_name, void (*cb)(int32_t err, lurch_status_chat_t status, void * user_data_p), void * user_data_p) {
int32_t ret_val = 0;
lurch_status_chat_t status = LURCH_STATUS_CHAT_DISABLED;
char * uname = (void *) 0;
char * db_fn_omemo = (void *) 0;
PurpleConversation * conv_p = (void *) 0;
JabberChat * muc_p = (void *) 0;
GList * curr_item_p = (void *) 0;
JabberChatMember * curr_muc_member_p = (void *) 0;
char * curr_muc_member_bare_jid = (void *) 0;
omemo_devicelist * curr_dl_p = (void *) 0;
uname = lurch_util_uname_strip(purple_account_get_username(acc_p));
db_fn_omemo = lurch_util_uname_get_db_fn(uname, LURCH_DB_NAME_OMEMO);
conv_p = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, full_conversation_name, acc_p);
if (!conv_p) {
purple_debug_error(MODULE_NAME, "Could not find groupchat %s.\n", full_conversation_name);
ret_val = EXIT_FAILURE;
goto cleanup;
}
muc_p = jabber_chat_find_by_conv(conv_p);
if (!muc_p) {
purple_debug_error(MODULE_NAME, "Could not find the data for groupchat %s.\n", full_conversation_name);
ret_val = EXIT_FAILURE;
goto cleanup;
}
for (curr_item_p = g_hash_table_get_values(muc_p->members); curr_item_p; curr_item_p = curr_item_p->next) {
curr_muc_member_p = (JabberChatMember *) curr_item_p->data;
curr_muc_member_bare_jid = jabber_get_bare_jid(curr_muc_member_p->jid);
if (!curr_muc_member_bare_jid) {
purple_debug_warning(
MODULE_NAME,
"Could not get the JID of chat member with handle %s, which probably means the chat is anonymous.\n",
curr_muc_member_p->handle
);
status = LURCH_STATUS_CHAT_ANONYMOUS;
goto cleanup;
}
ret_val = omemo_storage_user_devicelist_retrieve(curr_muc_member_bare_jid, db_fn_omemo, &curr_dl_p);
if (ret_val) {
purple_debug_error(MODULE_NAME, "Could not retrieve the devicelist for %s from %s.\n", curr_muc_member_bare_jid, db_fn_omemo);
goto cleanup;
}
if (omemo_devicelist_is_empty(curr_dl_p)) {
purple_debug_warning(
MODULE_NAME,
"Could not find %s's devicelist in chat %s. The user is probably not in %s's contact list.\n",
curr_muc_member_bare_jid, full_conversation_name, uname
);
status = LURCH_STATUS_CHAT_NO_DEVICELIST;
goto cleanup;
}
g_free(curr_muc_member_bare_jid);
curr_muc_member_bare_jid = (void *) 0;
omemo_devicelist_destroy(curr_dl_p);
curr_dl_p = (void *) 0;
}
status = LURCH_STATUS_CHAT_OK;
cleanup:
cb(ret_val, status, user_data_p);
g_free(uname);
g_free(db_fn_omemo);
// if loop was exited early
g_free(curr_muc_member_bare_jid);
omemo_devicelist_destroy(curr_dl_p);
}
static void lurch_api_marshal_VOID__POINTER_INT_POINTER_POINTER(PurpleCallback cb, va_list args, void * data, void ** return_val) {
void * arg1 = va_arg(args, void *);
gint32 arg2 = va_arg(args, guint);
......@@ -480,7 +564,7 @@ typedef enum {
* When adding a new signal: increase this number and add the name, handler function, and handler function type
* to the respective array.
*/
#define NUM_OF_SIGNALS 10
#define NUM_OF_SIGNALS 11
const char * signal_names[NUM_OF_SIGNALS] = {
"lurch-id-list",
......
......@@ -12,6 +12,13 @@ typedef enum {
LURCH_STATUS_OK // OMEMO is supported and session exists
} lurch_status_t;
typedef enum {
LURCH_STATUS_CHAT_DISABLED = 0, // OMEMO was not manually enabled
LURCH_STATUS_CHAT_ANONYMOUS, // chat is anonymous, i.e. a member's JID could not be accessed
LURCH_STATUS_CHAT_NO_DEVICELIST, // a member's devicelist could not be accessed, probably because s/he is not a contact
LURCH_STATUS_CHAT_OK // in theory, OMEMO should work
} lurch_status_chat_t;
/**
* Initializes the API by registering the signals and signal handlers.
*/
......@@ -118,3 +125,12 @@ void lurch_api_fp_other_handler(PurpleAccount * acc_p, const char * contact_bare
* Checks the OMEMO status for the given contact. Result is one of lurch_status_t.
*/
void lurch_api_status_im_handler(PurpleAccount * acc_p, const char * contact_bare_jid, void (*cb)(int32_t err, lurch_status_t status, void * user_data_p), void * user_data_p);
/**
* SIGNAL: lurch-status-chat
*
* Checks the OMEMO status for a MUC.
* It not only looks up whether OMEMO was enabled, but also checks the preconditions for this feature, i.e.
* whether all chat members' JIDs are visible (non-anonymous) and their devicelists accessible (in contact list).
*/
void lurch_api_status_chat_handler(PurpleAccount * acc_p, const char * full_conversation_name, void (*cb)(int32_t err, lurch_status_chat_t status, void * user_data_p), void * user_data_p);
......@@ -175,6 +175,35 @@ void lurch_status_im_print(int32_t err, lurch_status_t status, void * user_data_
lurch_cmd_print(conv_p, msg);
}
void lurch_status_chat_print(int32_t err, lurch_status_chat_t status, void * user_data_p) {
PurpleConversation * conv_p = (PurpleConversation *) user_data_p;
const char * msg = (void *) 0;
if (err) {
lurch_cmd_print_err(conv_p, "Failed to get the conversation status. Check the debug log for details.");
return;
}
switch (status) {
case LURCH_STATUS_CHAT_DISABLED:
msg = "OMEMO was not enabled for this conversation. Type '/lurch enable' to switch it on.";
break;
case LURCH_STATUS_CHAT_ANONYMOUS:
msg = "Could not access the JID of at least one of the chat members. The MUC is probably set to anonymous.";
break;
case LURCH_STATUS_CHAT_NO_DEVICELIST:
msg = "Could not access the OMEMO devicelist of at least one of the chat members. Make sure every member is in every other member's contact list.";
break;
case LURCH_STATUS_CHAT_OK:
msg = "OMEMO is enabled for this conversation and everything should work. You can turn it off by typing '/lurch disable'.";
break;
default:
msg = "Received unknown status code.";
}
lurch_cmd_print(conv_p, msg);
}
static void lurch_cmd_id(PurpleConversation * conv_p, const char * arg, const char * param) {
PurpleAccount * acc_p = purple_conversation_get_account(conv_p);
void * plugins_handle = purple_plugins_get_handle();
......@@ -257,14 +286,25 @@ static void lurch_cmd_fp(PurpleConversation * conv_p, const char * arg) {
}
static void lurch_cmd_status(PurpleConversation * conv_p) {
char * bare_jid = (void *) 0;
const char * conv_name = purple_conversation_get_name(conv_p);
PurpleConversationType conv_type = purple_conversation_get_type(conv_p);
char * conv_bare_jid = jabber_get_bare_jid(purple_conversation_get_name(conv_p));
void * plugins_handle = purple_plugins_get_handle();
PurpleAccount * acc_p = purple_conversation_get_account(conv_p);
if (conv_type == PURPLE_CONV_TYPE_IM) {
purple_signal_emit(purple_plugins_get_handle(), "lurch-status-im", purple_conversation_get_account(conv_p), conv_bare_jid, lurch_status_im_print ,conv_p);
switch (conv_type) {
case PURPLE_CONV_TYPE_IM:
bare_jid = jabber_get_bare_jid(conv_name);
purple_signal_emit(plugins_handle, "lurch-status-im", acc_p, bare_jid, lurch_status_im_print, conv_p);
break;
case PURPLE_CONV_TYPE_CHAT:
purple_signal_emit(plugins_handle, "lurch-status-chat", acc_p, conv_name, lurch_status_chat_print, conv_p);
break;
default:
lurch_cmd_print_err(conv_p, "Conversation type not supported.");
}
g_free(conv_bare_jid);
g_free(bare_jid);
}
PurpleCmdRet lurch_cmd_func_v2(PurpleConversation * conv_p,
......
......@@ -5,6 +5,7 @@
#include <purple.h>
#include "jabber.h"
#include "chat.h"
#include "axc.h"
#include "libomemo.h"
......@@ -62,6 +63,16 @@ int __wrap_axc_get_device_id(axc_context * ctx_p, uint32_t * id_p) {
return ret_val;
}
PurpleConversation * __wrap_purple_find_conversation_with_account(PurpleConversationType type, const char *name,
const PurpleAccount *account) {
check_expected(type);
check_expected(name);
PurpleConversation * ret_val;
ret_val = mock_ptr_type(PurpleConversation *);
return ret_val;
}
void __wrap_jabber_pep_publish(JabberStream * js_p, xmlnode * publish_node_p) {
xmlnode * item_node_p = xmlnode_get_child(publish_node_p, "item");
xmlnode * list_node_p = xmlnode_get_child(item_node_p, "list");
......@@ -73,6 +84,12 @@ void __wrap_jabber_pep_publish(JabberStream * js_p, xmlnode * publish_node_p) {
check_expected_ptr(device_node_p->next);
}
JabberChat * __wrap_jabber_chat_find_by_conv(PurpleConversation * conv_p) {
JabberChat * ret_val;
ret_val = mock_ptr_type(JabberChat *);
return ret_val;
}
int __wrap_omemo_storage_chatlist_delete(const char * chat, const char * db_fn) {
check_expected(chat);
......@@ -921,14 +938,189 @@ static void test_lurch_api_status_im_handler_err(void ** state) {
lurch_api_status_im_handler(NULL, other_bare_jid, lurch_api_status_im_handler_cb_mock, mock_user_data);
}
static void lurch_api_status_chat_handler_cb_mock(int32_t err, lurch_status_chat_t status, void * user_data_p) {
check_expected(err);
check_expected(status);
check_expected(user_data_p);
}
/**
* Returns the "anonymous" status when a chat members' JID cannot be accessed, i.e. the chat is anonymous.
*/
static void test_lurch_api_status_chat_handler_anonymous(void ** state) {
(void) state;
const char * own_jid = "me-testing@test.org/resource";
const char * test_conversation_name = "test-room@conference.test.org";
will_return(__wrap_purple_account_get_username, own_jid);
PurpleConversation conversation_mock = {
.name = test_conversation_name
};
expect_value(__wrap_purple_find_conversation_with_account, type, PURPLE_CONV_TYPE_CHAT);
expect_value(__wrap_purple_find_conversation_with_account, name, test_conversation_name);
will_return(__wrap_purple_find_conversation_with_account, &conversation_mock);
JabberChatMember member = {
.handle = "anonymous member without jid"
};
// keys do not really matter as the tests function loops over the values
GHashTable * member_table_mock = g_hash_table_new(g_str_hash, g_str_equal);
g_hash_table_insert(member_table_mock, "anon member", &member);
JabberChat muc_mock = {
.members = member_table_mock
};
will_return(__wrap_jabber_chat_find_by_conv, &muc_mock);
expect_value(lurch_api_status_chat_handler_cb_mock, err, EXIT_SUCCESS);
expect_value(lurch_api_status_chat_handler_cb_mock, status, LURCH_STATUS_CHAT_ANONYMOUS);
const char * mock_user_data = "MOCK_USER_DATA";
expect_value(lurch_api_status_chat_handler_cb_mock, user_data_p, mock_user_data);
lurch_api_status_chat_handler(NULL, test_conversation_name, lurch_api_status_chat_handler_cb_mock, mock_user_data);
}
/**
* Returns the "no devicelist" status when a devicelist for a chat member can not be found.
* This usually means the member is not in the user's contact list.
*/
static void test_lurch_api_status_chat_handler_no_devicelist(void ** state) {
(void) state;
const char * own_jid = "me-testing@test.org/resource";
const char * test_conversation_name = "test-room@conference.test.org";
will_return(__wrap_purple_account_get_username, own_jid);
PurpleConversation conversation_mock = {
.name = test_conversation_name
};
expect_value(__wrap_purple_find_conversation_with_account, type, PURPLE_CONV_TYPE_CHAT);
expect_value(__wrap_purple_find_conversation_with_account, name, test_conversation_name);
will_return(__wrap_purple_find_conversation_with_account, &conversation_mock);
JabberChatMember member = {
.handle = "n0t4fr13nd",
.jid = "not-a-contact@test.org/phone"
};
// keys do not really matter as the tests function loops over the values
GHashTable * member_table_mock = g_hash_table_new(g_str_hash, g_str_equal);
g_hash_table_insert(member_table_mock, "non-contact member", &member);
JabberChat muc_mock = {
.members = member_table_mock
};
will_return(__wrap_jabber_chat_find_by_conv, &muc_mock);
char * devicelist = "<items node='urn:xmpp:omemo:0:devicelist'>"
"<item>"
"<list xmlns='urn:xmpp:omemo:0'>"
"</list>"
"</item>"
"</items>";
omemo_devicelist * dl_p;
omemo_devicelist_import(devicelist, member.jid, &dl_p);
will_return(__wrap_omemo_storage_user_devicelist_retrieve, dl_p);
will_return(__wrap_omemo_storage_user_devicelist_retrieve, EXIT_SUCCESS);
expect_value(lurch_api_status_chat_handler_cb_mock, err, EXIT_SUCCESS);
expect_value(lurch_api_status_chat_handler_cb_mock, status, LURCH_STATUS_CHAT_NO_DEVICELIST);
const char * mock_user_data = "MOCK_USER_DATA";
expect_value(lurch_api_status_chat_handler_cb_mock, user_data_p, mock_user_data);
lurch_api_status_chat_handler(NULL, test_conversation_name, lurch_api_status_chat_handler_cb_mock, mock_user_data);
}
/**
* Returns the "OK" status when for all members all necessary information could be found.
*/
static void test_lurch_api_status_chat_handler_ok(void ** state) {
(void) state;
const char * own_jid = "me-testing@test.org/resource";
const char * test_conversation_name = "test-room@conference.test.org";
will_return(__wrap_purple_account_get_username, own_jid);
PurpleConversation conversation_mock = {
.name = test_conversation_name
};
expect_value(__wrap_purple_find_conversation_with_account, type, PURPLE_CONV_TYPE_CHAT);
expect_value(__wrap_purple_find_conversation_with_account, name, test_conversation_name);
will_return(__wrap_purple_find_conversation_with_account, &conversation_mock);
JabberChatMember member = {
.handle = "perfect-contact",
.jid = "perfect-contact@test.org/lurch"
};
// keys do not really matter as the tests function loops over the values
GHashTable * member_table_mock = g_hash_table_new(g_str_hash, g_str_equal);
g_hash_table_insert(member_table_mock, "contact member", &member);
JabberChat muc_mock = {
.members = member_table_mock
};
will_return(__wrap_jabber_chat_find_by_conv, &muc_mock);
char * devicelist = "<items node='urn:xmpp:omemo:0:devicelist'>"
"<item>"
"<list xmlns='urn:xmpp:omemo:0'>"
"<device id='4223' />"
"</list>"
"</item>"
"</items>";
omemo_devicelist * dl_p;
omemo_devicelist_import(devicelist, member.jid, &dl_p);
will_return(__wrap_omemo_storage_user_devicelist_retrieve, dl_p);
will_return(__wrap_omemo_storage_user_devicelist_retrieve, EXIT_SUCCESS);
expect_value(lurch_api_status_chat_handler_cb_mock, err, EXIT_SUCCESS);
expect_value(lurch_api_status_chat_handler_cb_mock, status, LURCH_STATUS_CHAT_OK);
const char * mock_user_data = "MOCK_USER_DATA";
expect_value(lurch_api_status_chat_handler_cb_mock, user_data_p, mock_user_data);
lurch_api_status_chat_handler(NULL, test_conversation_name, lurch_api_status_chat_handler_cb_mock, mock_user_data);
}
/**
* Returns "disabled" and an error return value when the conversation could not be found, i.e. an error.
*/
static void test_lurch_api_status_chat_handler_conv_not_found(void ** state) {
(void) state;
const char * own_jid = "me-testing@test.org/resource";
const char * test_conversation_name = "nonexistent-test-room@conference.test.org";
will_return(__wrap_purple_account_get_username, own_jid);
expect_value(__wrap_purple_find_conversation_with_account, type, PURPLE_CONV_TYPE_CHAT);
expect_value(__wrap_purple_find_conversation_with_account, name, test_conversation_name);
will_return(__wrap_purple_find_conversation_with_account, NULL);
expect_value(lurch_api_status_chat_handler_cb_mock, err, EXIT_FAILURE);
expect_value(lurch_api_status_chat_handler_cb_mock, status, LURCH_STATUS_CHAT_DISABLED);
const char * mock_user_data = "MOCK_USER_DATA";
expect_value(lurch_api_status_chat_handler_cb_mock, user_data_p, mock_user_data);
lurch_api_status_chat_handler(NULL, test_conversation_name, lurch_api_status_chat_handler_cb_mock, mock_user_data);
}
/**
* Registers and connects all signals.
*/
static void test_lurch_api_init(void ** state) {
(void) state;
// currently, there are 9 signals
size_t signals = 10;
size_t signals = 11;
for (int i = 0; i < signals; i++) {
expect_function_call(__wrap_purple_signal_register);
......@@ -944,7 +1136,7 @@ static void test_lurch_api_init(void ** state) {
static void test_lurch_api_unload(void ** state) {
(void) state;
size_t signals = 10;
size_t signals = 11;
for (int i = 0; i < signals; i++) {
expect_function_call(__wrap_purple_signal_disconnect);
......@@ -981,6 +1173,10 @@ int main(void) {
cmocka_unit_test(test_lurch_api_status_im_handler_no_session),
cmocka_unit_test(test_lurch_api_status_im_handler_ok),
cmocka_unit_test(test_lurch_api_status_im_handler_err),
cmocka_unit_test(test_lurch_api_status_chat_handler_anonymous),
cmocka_unit_test(test_lurch_api_status_chat_handler_no_devicelist),
cmocka_unit_test(test_lurch_api_status_chat_handler_ok),
cmocka_unit_test(test_lurch_api_status_chat_handler_conv_not_found),
cmocka_unit_test(test_lurch_api_init),
cmocka_unit_test(test_lurch_api_unload)
};
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment