mirror of
https://github.com/profanity-im/profanity.git
synced 2025-02-02 15:08:15 -05:00
Merge pull request #1039 from paulfariello/feature/omemo
Add basic OMEMO support.
This commit is contained in:
commit
61df0c8e85
15
Makefile.am
15
Makefile.am
@ -69,6 +69,9 @@ unittest_sources = \
|
||||
src/ui/ui.h \
|
||||
src/otr/otr.h \
|
||||
src/pgp/gpg.h \
|
||||
src/omemo/omemo.h \
|
||||
src/omemo/crypto.h \
|
||||
src/omemo/store.h \
|
||||
src/command/cmd_defs.h src/command/cmd_defs.c \
|
||||
src/command/cmd_funcs.h src/command/cmd_funcs.c \
|
||||
src/command/cmd_ac.h src/command/cmd_ac.c \
|
||||
@ -168,6 +171,13 @@ otr3_sources = \
|
||||
otr4_sources = \
|
||||
src/otr/otrlib.h src/otr/otrlibv4.c src/otr/otr.h src/otr/otr.c
|
||||
|
||||
omemo_sources = \
|
||||
src/omemo/omemo.h src/omemo/omemo.c src/omemo/crypto.h src/omemo/crypto.c \
|
||||
src/omemo/store.h src/omemo/store.c src/xmpp/omemo.h src/xmpp/omemo.c
|
||||
|
||||
omemo_unittest_sources = \
|
||||
tests/unittests/omemo/stub_omemo.c
|
||||
|
||||
if BUILD_PYTHON_API
|
||||
core_sources += $(python_sources)
|
||||
unittest_sources += $(python_sources)
|
||||
@ -204,6 +214,11 @@ core_sources += $(otr4_sources)
|
||||
endif
|
||||
endif
|
||||
|
||||
if BUILD_OMEMO
|
||||
core_sources += $(omemo_sources)
|
||||
unittest_sources += $(omemo_unittest_sources)
|
||||
endif
|
||||
|
||||
AM_CFLAGS = @AM_CFLAGS@ -I$(srcdir)/src
|
||||
|
||||
bin_PROGRAMS = profanity
|
||||
|
26
configure.ac
26
configure.ac
@ -60,6 +60,8 @@ AC_ARG_ENABLE([otr],
|
||||
[AS_HELP_STRING([--enable-otr], [enable otr encryption])])
|
||||
AC_ARG_ENABLE([pgp],
|
||||
[AS_HELP_STRING([--enable-pgp], [enable pgp])])
|
||||
AC_ARG_ENABLE([omemo],
|
||||
[AS_HELP_STRING([--enable-omemo], [enable OMEMO encryption])])
|
||||
AC_ARG_WITH([xscreensaver],
|
||||
[AS_HELP_STRING([--with-xscreensaver], [use libXScrnSaver to determine idle time])])
|
||||
AC_ARG_WITH([themes],
|
||||
@ -264,6 +266,30 @@ if test "x$enable_otr" != xno; then
|
||||
[AC_MSG_NOTICE([libotr not found, otr encryption support not enabled])])])
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL([BUILD_OMEMO], [false])
|
||||
if test "x$enable_omemo" != xno; then
|
||||
AM_CONDITIONAL([BUILD_OMEMO], [true])
|
||||
PKG_CHECK_MODULES([libsignal], [libsignal-protocol-c >= 2.3.2],
|
||||
[LIBS="-lsignal-protocol-c $LIBS"],
|
||||
[AC_MSG_NOTICE([libsignal >= 2.3.2 not found, checking for libsignal 2.3.x...])
|
||||
PKG_CHECK_MODULES([libsignal], [libsignal-protocol-c >= 2.3.0],
|
||||
[LIBS="-lsignal-protocol-c $LIBS"
|
||||
AC_DEFINE([HAVE_LIBSIGNAL_LT_2_3_2], [1], [Have libsignal-protocol-c < 2.3.2])],
|
||||
[AM_CONDITIONAL([BUILD_OMEMO], [false])
|
||||
AS_IF([test "x$enable_omemo" = xyes],
|
||||
[AC_MSG_ERROR([libsignal-protocol-c is required for OMEMO support])],
|
||||
[AC_MSG_NOTICE([libsignal-protocol-c not found, OMEMO support not enabled])])])])
|
||||
|
||||
AC_CHECK_LIB([gcrypt], [gcry_check_version],
|
||||
[LIBS="-lgcrypt $LIBS"],
|
||||
[AM_CONDITIONAL([BUILD_OMEMO], [false])
|
||||
AS_IF([test "x$enable_omemo" = xyes],
|
||||
[AC_MSG_ERROR([gcrypt is required for OMEMO support])],
|
||||
[AC_MSG_NOTICE([gcrypt not found, OMEMO support not enabled])])])
|
||||
|
||||
AM_COND_IF([BUILD_OMEMO], [AC_DEFINE([HAVE_OMEMO], [1], [Have OMEMO])])
|
||||
fi
|
||||
|
||||
AS_IF([test "x$with_themes" = xno],
|
||||
[THEMES_INSTALL="false"],
|
||||
[THEMES_INSTALL="true"])
|
||||
|
@ -57,6 +57,10 @@
|
||||
#include "pgp/gpg.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OMEMO
|
||||
#include "omemo/omemo.h"
|
||||
#endif
|
||||
|
||||
static char* _sub_autocomplete(ProfWin *window, const char *const input, gboolean previous);
|
||||
static char* _notify_autocomplete(ProfWin *window, const char *const input, gboolean previous);
|
||||
static char* _theme_autocomplete(ProfWin *window, const char *const input, gboolean previous);
|
||||
@ -69,6 +73,7 @@ static char* _group_autocomplete(ProfWin *window, const char *const input, gbool
|
||||
static char* _bookmark_autocomplete(ProfWin *window, const char *const input, gboolean previous);
|
||||
static char* _otr_autocomplete(ProfWin *window, const char *const input, gboolean previous);
|
||||
static char* _pgp_autocomplete(ProfWin *window, const char *const input, gboolean previous);
|
||||
static char* _omemo_autocomplete(ProfWin *window, const char *const input, gboolean previous);
|
||||
static char* _connect_autocomplete(ProfWin *window, const char *const input, gboolean previous);
|
||||
static char* _alias_autocomplete(ProfWin *window, const char *const input, gboolean previous);
|
||||
static char* _join_autocomplete(ProfWin *window, const char *const input, gboolean previous);
|
||||
@ -157,6 +162,8 @@ static Autocomplete bookmark_property_ac;
|
||||
static Autocomplete otr_ac;
|
||||
static Autocomplete otr_log_ac;
|
||||
static Autocomplete otr_policy_ac;
|
||||
static Autocomplete omemo_ac;
|
||||
static Autocomplete omemo_log_ac;
|
||||
static Autocomplete connect_property_ac;
|
||||
static Autocomplete tls_property_ac;
|
||||
static Autocomplete alias_ac;
|
||||
@ -237,6 +244,7 @@ cmd_ac_init(void)
|
||||
autocomplete_add(prefs_ac, "presence");
|
||||
autocomplete_add(prefs_ac, "otr");
|
||||
autocomplete_add(prefs_ac, "pgp");
|
||||
autocomplete_add(prefs_ac, "omemo");
|
||||
|
||||
notify_ac = autocomplete_new();
|
||||
autocomplete_add(notify_ac, "chat");
|
||||
@ -574,6 +582,21 @@ cmd_ac_init(void)
|
||||
autocomplete_add(otr_policy_ac, "opportunistic");
|
||||
autocomplete_add(otr_policy_ac, "always");
|
||||
|
||||
omemo_ac = autocomplete_new();
|
||||
autocomplete_add(omemo_ac, "gen");
|
||||
autocomplete_add(omemo_ac, "log");
|
||||
autocomplete_add(omemo_ac, "start");
|
||||
autocomplete_add(omemo_ac, "end");
|
||||
autocomplete_add(omemo_ac, "trust");
|
||||
autocomplete_add(omemo_ac, "untrust");
|
||||
autocomplete_add(omemo_ac, "fingerprint");
|
||||
autocomplete_add(omemo_ac, "clear_device_list");
|
||||
|
||||
omemo_log_ac = autocomplete_new();
|
||||
autocomplete_add(omemo_log_ac, "on");
|
||||
autocomplete_add(omemo_log_ac, "off");
|
||||
autocomplete_add(omemo_log_ac, "redact");
|
||||
|
||||
connect_property_ac = autocomplete_new();
|
||||
autocomplete_add(connect_property_ac, "server");
|
||||
autocomplete_add(connect_property_ac, "port");
|
||||
@ -982,6 +1005,9 @@ cmd_ac_reset(ProfWin *window)
|
||||
presence_reset_sub_request_search();
|
||||
#ifdef HAVE_LIBGPGME
|
||||
p_gpg_autocomplete_key_reset();
|
||||
#endif
|
||||
#ifdef HAVE_OMEMO
|
||||
omemo_fingerprint_autocomplete_reset();
|
||||
#endif
|
||||
autocomplete_reset(help_ac);
|
||||
autocomplete_reset(help_commands_ac);
|
||||
@ -1052,6 +1078,8 @@ cmd_ac_reset(ProfWin *window)
|
||||
autocomplete_reset(otr_ac);
|
||||
autocomplete_reset(otr_log_ac);
|
||||
autocomplete_reset(otr_policy_ac);
|
||||
autocomplete_reset(omemo_ac);
|
||||
autocomplete_reset(omemo_log_ac);
|
||||
autocomplete_reset(connect_property_ac);
|
||||
autocomplete_reset(tls_property_ac);
|
||||
autocomplete_reset(alias_ac);
|
||||
@ -1179,6 +1207,8 @@ cmd_ac_uninit(void)
|
||||
autocomplete_free(otr_ac);
|
||||
autocomplete_free(otr_log_ac);
|
||||
autocomplete_free(otr_policy_ac);
|
||||
autocomplete_free(omemo_ac);
|
||||
autocomplete_free(omemo_log_ac);
|
||||
autocomplete_free(connect_property_ac);
|
||||
autocomplete_free(tls_property_ac);
|
||||
autocomplete_free(alias_ac);
|
||||
@ -1438,6 +1468,7 @@ _cmd_ac_complete_params(ProfWin *window, const char *const input, gboolean previ
|
||||
g_hash_table_insert(ac_funcs, "/autoconnect", _autoconnect_autocomplete);
|
||||
g_hash_table_insert(ac_funcs, "/otr", _otr_autocomplete);
|
||||
g_hash_table_insert(ac_funcs, "/pgp", _pgp_autocomplete);
|
||||
g_hash_table_insert(ac_funcs, "/omemo", _omemo_autocomplete);
|
||||
g_hash_table_insert(ac_funcs, "/connect", _connect_autocomplete);
|
||||
g_hash_table_insert(ac_funcs, "/alias", _alias_autocomplete);
|
||||
g_hash_table_insert(ac_funcs, "/join", _join_autocomplete);
|
||||
@ -2117,6 +2148,57 @@ _pgp_autocomplete(ProfWin *window, const char *const input, gboolean previous)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char*
|
||||
_omemo_autocomplete(ProfWin *window, const char *const input, gboolean previous)
|
||||
{
|
||||
char *found = NULL;
|
||||
|
||||
jabber_conn_status_t conn_status = connection_get_status();
|
||||
|
||||
if (conn_status == JABBER_CONNECTED) {
|
||||
found = autocomplete_param_with_func(input, "/omemo start", roster_contact_autocomplete, previous);
|
||||
if (found) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
|
||||
found = autocomplete_param_with_func(input, "/omemo fingerprint", roster_contact_autocomplete, previous);
|
||||
if (found) {
|
||||
return found;
|
||||
}
|
||||
|
||||
#ifdef HAVE_OMEMO
|
||||
if (window->type == WIN_CHAT) {
|
||||
found = autocomplete_param_with_func(input, "/omemo trust", omemo_fingerprint_autocomplete, previous);
|
||||
if (found) {
|
||||
return found;
|
||||
}
|
||||
} else {
|
||||
found = autocomplete_param_with_func(input, "/omemo trust", roster_contact_autocomplete, previous);
|
||||
if (found) {
|
||||
return found;
|
||||
}
|
||||
|
||||
found = autocomplete_param_no_with_func(input, "/omemo trust", 4, omemo_fingerprint_autocomplete, previous);
|
||||
if (found) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
found = autocomplete_param_with_ac(input, "/omemo log", omemo_log_ac, TRUE, previous);
|
||||
if (found) {
|
||||
return found;
|
||||
}
|
||||
|
||||
found = autocomplete_param_with_ac(input, "/omemo", omemo_ac, TRUE, previous);
|
||||
if (found) {
|
||||
return found;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char*
|
||||
_plugins_autocomplete(ProfWin *window, const char *const input, gboolean previous)
|
||||
{
|
||||
|
@ -2134,7 +2134,7 @@ static struct cmd_t command_defs[] =
|
||||
CMD_MAINFUNC(cmd_prefs)
|
||||
CMD_NOTAGS
|
||||
CMD_SYN(
|
||||
"/prefs [ui|desktop|chat|log|conn|presence|otr|pgp]")
|
||||
"/prefs [ui|desktop|chat|log|conn|presence|otr|pgp|omemo]")
|
||||
CMD_DESC(
|
||||
"Show preferences for different areas of functionality. "
|
||||
"Passing no arguments shows all preferences.")
|
||||
@ -2146,7 +2146,8 @@ static struct cmd_t command_defs[] =
|
||||
{ "conn", "Connection handling preferences." },
|
||||
{ "presence", "Chat presence preferences." },
|
||||
{ "otr", "Off The Record preferences." },
|
||||
{ "pgp", "OpenPGP preferences." })
|
||||
{ "pgp", "OpenPGP preferences." },
|
||||
{ "omemo", "OMEMO preferences." })
|
||||
CMD_NOEXAMPLES
|
||||
},
|
||||
|
||||
@ -2328,7 +2329,51 @@ static struct cmd_t command_defs[] =
|
||||
CMD_EXAMPLES(
|
||||
"/cmd list",
|
||||
"/cmd exec ping")
|
||||
}
|
||||
},
|
||||
|
||||
{ "/omemo",
|
||||
parse_args, 1, 3, NULL,
|
||||
CMD_SUBFUNCS(
|
||||
{ "gen", cmd_omemo_gen },
|
||||
{ "log", cmd_omemo_log },
|
||||
{ "start", cmd_omemo_start },
|
||||
{ "end", cmd_omemo_end },
|
||||
{ "trust", cmd_omemo_trust },
|
||||
{ "untrust", cmd_omemo_untrust },
|
||||
{ "fingerprint", cmd_omemo_fingerprint },
|
||||
{ "char", cmd_omemo_char },
|
||||
{ "clear_device_list", cmd_omemo_clear_device_list })
|
||||
CMD_NOMAINFUNC
|
||||
CMD_TAGS(
|
||||
CMD_TAG_CHAT,
|
||||
CMD_TAG_UI)
|
||||
CMD_SYN(
|
||||
"/omemo gen",
|
||||
"/omemo log on|off|redact",
|
||||
"/omemo start [<contact>]",
|
||||
"/omemo trust [<contact>] <fingerprint>",
|
||||
"/omemo end",
|
||||
"/omemo fingerprint [<contact>]",
|
||||
"/omemo char <char>",
|
||||
"/omemo clear_device_list")
|
||||
CMD_DESC(
|
||||
"OMEMO commands to manage keys, and perform encryption during chat sessions.")
|
||||
CMD_ARGS(
|
||||
{ "gen", "Generate OMEMO crytographic materials for current account." },
|
||||
{ "start [<contact>]", "Start an OMEMO session with contact, or current recipient if omitted." },
|
||||
{ "end", "End the current OMEMO session." },
|
||||
{ "log on|off", "Enable or disable plaintext logging of OMEMO encrypted messages." },
|
||||
{ "log redact", "Log OMEMO encrypted messages, but replace the contents with [redacted]. This is the default." },
|
||||
{ "fingerprint [<contact>]", "Show contact fingerprints, or current recipient if omitted." },
|
||||
{ "char <char>", "Set the character to be displayed next to OMEMO encrypted messages." },
|
||||
{ "clear_device_list", "Clear your own device list on server side. Each client will reannounce itself when connected back."})
|
||||
CMD_EXAMPLES(
|
||||
"/omemo gen",
|
||||
"/omemo start buddy@buddychat.org",
|
||||
"/omemo trust c4f9c875-144d7a3b-0c4a05b6-ca3be51a-a037f329-0bd3ae62-07f99719-55559d2a",
|
||||
"/omemo untrust buddy@buddychat.org c4f9c875-144d7a3b-0c4a05b6-ca3be51a-a037f329-0bd3ae62-07f99719-55559d2a",
|
||||
"/omemo char *")
|
||||
},
|
||||
};
|
||||
|
||||
static GHashTable *search_index;
|
||||
|
@ -85,6 +85,11 @@
|
||||
#include "pgp/gpg.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OMEMO
|
||||
#include "omemo/omemo.h"
|
||||
#include "xmpp/omemo.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_GTK
|
||||
#include "ui/tray.h"
|
||||
#endif
|
||||
@ -1674,6 +1679,10 @@ cmd_prefs(ProfWin *window, const char *const command, gchar **args)
|
||||
cons_show("");
|
||||
cons_show_pgp_prefs();
|
||||
cons_show("");
|
||||
} else if (strcmp(args[0], "omemo") == 0) {
|
||||
cons_show("");
|
||||
cons_show_omemo_prefs();
|
||||
cons_show("");
|
||||
} else {
|
||||
cons_bad_cmd_usage(command);
|
||||
}
|
||||
@ -2141,17 +2150,67 @@ cmd_msg(ProfWin *window, const char *const command, gchar **args)
|
||||
}
|
||||
ui_focus_win((ProfWin*)chatwin);
|
||||
|
||||
#ifdef HAVE_OMEMO
|
||||
#ifndef HAVE_LIBOTR
|
||||
if (omemo_is_trusted_jid(barejid)) {
|
||||
omemo_start_session(barejid);
|
||||
chatwin->is_omemo = TRUE;
|
||||
}
|
||||
|
||||
if (msg) {
|
||||
cl_ev_send_msg(chatwin, msg, NULL);
|
||||
} else {
|
||||
#ifdef HAVE_LIBOTR
|
||||
if (otr_is_secure(barejid)) {
|
||||
chatwin_otr_secured(chatwin, otr_is_trusted(barejid));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OMEMO
|
||||
#ifdef HAVE_LIBOTR
|
||||
if (omemo_is_trusted_jid(barejid) && otr_is_secure(barejid)) {
|
||||
win_println(window, THEME_DEFAULT, '!', "Chat could be either OMEMO or OTR encrypted. Use '/omemo start %s' or '/otr start %s' to start a session.", usr, usr);
|
||||
return TRUE;
|
||||
} else if (omemo_is_trusted_jid(barejid)) {
|
||||
omemo_start_session(barejid);
|
||||
chatwin->is_omemo = TRUE;
|
||||
}
|
||||
|
||||
if (msg) {
|
||||
cl_ev_send_msg(chatwin, msg, NULL);
|
||||
} else {
|
||||
if (otr_is_secure(barejid)) {
|
||||
chatwin_otr_secured(chatwin, otr_is_trusted(barejid));
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_OMEMO
|
||||
#ifdef HAVE_LIBOTR
|
||||
if (msg) {
|
||||
cl_ev_send_msg(chatwin, msg, NULL);
|
||||
} else {
|
||||
if (otr_is_secure(barejid)) {
|
||||
chatwin_otr_secured(chatwin, otr_is_trusted(barejid));
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_OMEMO
|
||||
#ifndef HAVE_LIBOTR
|
||||
if (msg) {
|
||||
cl_ev_send_msg(chatwin, msg, NULL);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -7304,6 +7363,11 @@ cmd_otr_start(ProfWin *window, const char *const command, gchar **args)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (chatwin->is_omemo) {
|
||||
win_println(window, THEME_DEFAULT, '!', "You must disable OMEMO before starting an OTR session.");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (chatwin->is_otr) {
|
||||
win_println(window, THEME_DEFAULT, '!', "You are already in an OTR session.");
|
||||
return TRUE;
|
||||
@ -7872,3 +7936,432 @@ _cmd_set_boolean_preference(gchar *arg, const char *const command,
|
||||
g_string_free(enabled, TRUE);
|
||||
g_string_free(disabled, TRUE);
|
||||
}
|
||||
|
||||
gboolean
|
||||
cmd_omemo_gen(ProfWin *window, const char *const command, gchar **args)
|
||||
{
|
||||
#ifdef HAVE_OMEMO
|
||||
if (connection_get_status() != JABBER_CONNECTED) {
|
||||
cons_show("You must be connected with an account to initialize OMEMO.");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (omemo_loaded()) {
|
||||
cons_show("OMEMO crytographic materials have already been generated.");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
ProfAccount *account = accounts_get_account(session_get_account_name());
|
||||
omemo_generate_crypto_materials(account);
|
||||
cons_show("OMEMO crytographic materials generated.");
|
||||
return TRUE;
|
||||
#else
|
||||
cons_show("This version of Profanity has not been built with OMEMO support enabled");
|
||||
return TRUE;
|
||||
#endif
|
||||
}
|
||||
|
||||
gboolean
|
||||
cmd_omemo_start(ProfWin *window, const char *const command, gchar **args)
|
||||
{
|
||||
#ifdef HAVE_OMEMO
|
||||
if (connection_get_status() != JABBER_CONNECTED) {
|
||||
cons_show("You must be connected with an account to load OMEMO information.");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (!omemo_loaded()) {
|
||||
win_println(window, THEME_DEFAULT, '!', "You have not generated or loaded a cryptographic materials, use '/omemo gen'");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// recipient supplied
|
||||
if (args[1]) {
|
||||
char *contact = args[1];
|
||||
char *barejid = roster_barejid_from_name(contact);
|
||||
if (barejid == NULL) {
|
||||
barejid = contact;
|
||||
}
|
||||
|
||||
ProfChatWin *chatwin = wins_get_chat(barejid);
|
||||
if (!chatwin) {
|
||||
chatwin = chatwin_new(barejid);
|
||||
}
|
||||
ui_focus_win((ProfWin*)chatwin);
|
||||
|
||||
if (chatwin->pgp_send) {
|
||||
win_println(window, THEME_DEFAULT, '!', "You must disable PGP encryption before starting an OMEMO session.");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (chatwin->is_otr) {
|
||||
win_println(window, THEME_DEFAULT, '!', "You must disable OTR encryption before starting an OMEMO session.");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (chatwin->is_omemo) {
|
||||
win_println(window, THEME_DEFAULT, '!', "You are already in an OMEMO session.");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
omemo_start_session(barejid);
|
||||
chatwin->is_omemo = TRUE;
|
||||
} else {
|
||||
if (window->type == WIN_CHAT) {
|
||||
ProfChatWin *chatwin = (ProfChatWin*)window;
|
||||
assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
|
||||
if (chatwin->pgp_send) {
|
||||
win_println(window, THEME_DEFAULT, '!', "You must disable PGP encryption before starting an OMEMO session.");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (chatwin->is_otr) {
|
||||
win_println(window, THEME_DEFAULT, '!', "You must disable OTR encryption before starting an OMEMO session.");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (chatwin->is_omemo) {
|
||||
win_println(window, THEME_DEFAULT, '!', "You are already in an OMEMO session.");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
omemo_start_session(chatwin->barejid);
|
||||
chatwin->is_omemo = TRUE;
|
||||
} else if (window->type == WIN_MUC) {
|
||||
ProfMucWin *mucwin = (ProfMucWin*)window;
|
||||
assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
|
||||
|
||||
/* TODO: Check room is configured correctly, no anonymous and access to
|
||||
* full jid */
|
||||
omemo_start_muc_sessions(mucwin->roomjid);
|
||||
|
||||
mucwin->is_omemo = TRUE;
|
||||
} else {
|
||||
win_println(window, THEME_DEFAULT, '-', "You must be in a regular chat window to start an OMEMO session.");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
#else
|
||||
cons_show("This version of Profanity has not been built with OMEMO support enabled");
|
||||
return TRUE;
|
||||
#endif
|
||||
}
|
||||
|
||||
gboolean
|
||||
cmd_omemo_char(ProfWin *window, const char *const command, gchar **args)
|
||||
{
|
||||
#ifdef HAVE_OMEMO
|
||||
if (args[1] == NULL) {
|
||||
cons_bad_cmd_usage(command);
|
||||
} else if (strlen(args[1]) != 1) {
|
||||
cons_bad_cmd_usage(command);
|
||||
} else {
|
||||
prefs_set_omemo_char(args[1][0]);
|
||||
cons_show("OMEMO char set to %c.", args[1][0]);
|
||||
}
|
||||
return TRUE;
|
||||
#else
|
||||
cons_show("This version of Profanity has not been built with OMEMO support enabled");
|
||||
return TRUE;
|
||||
#endif
|
||||
}
|
||||
|
||||
gboolean
|
||||
cmd_omemo_log(ProfWin *window, const char *const command, gchar **args)
|
||||
{
|
||||
#ifdef HAVE_OMEMO
|
||||
char *choice = args[1];
|
||||
if (g_strcmp0(choice, "on") == 0) {
|
||||
prefs_set_string(PREF_OMEMO_LOG, "on");
|
||||
cons_show("OMEMO messages will be logged as plaintext.");
|
||||
if (!prefs_get_boolean(PREF_CHLOG)) {
|
||||
cons_show("Chat logging is currently disabled, use '/chlog on' to enable.");
|
||||
}
|
||||
} else if (g_strcmp0(choice, "off") == 0) {
|
||||
prefs_set_string(PREF_OMEMO_LOG, "off");
|
||||
cons_show("OMEMO message logging disabled.");
|
||||
} else if (g_strcmp0(choice, "redact") == 0) {
|
||||
prefs_set_string(PREF_OMEMO_LOG, "redact");
|
||||
cons_show("OMEMO messages will be logged as '[redacted]'.");
|
||||
if (!prefs_get_boolean(PREF_CHLOG)) {
|
||||
cons_show("Chat logging is currently disabled, use '/chlog on' to enable.");
|
||||
}
|
||||
} else {
|
||||
cons_bad_cmd_usage(command);
|
||||
}
|
||||
return TRUE;
|
||||
#else
|
||||
cons_show("This version of Profanity has not been built with OMEMO support enabled");
|
||||
return TRUE;
|
||||
#endif
|
||||
}
|
||||
|
||||
gboolean
|
||||
cmd_omemo_end(ProfWin *window, const char *const command, gchar **args)
|
||||
{
|
||||
#ifdef HAVE_OMEMO
|
||||
if (connection_get_status() != JABBER_CONNECTED) {
|
||||
cons_show("You must be connected with an account to load OMEMO information.");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (window->type == WIN_CHAT) {
|
||||
ProfChatWin *chatwin = (ProfChatWin*)window;
|
||||
assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
|
||||
|
||||
if (!chatwin->is_omemo) {
|
||||
win_println(window, THEME_DEFAULT, '!', "You are not currently in an OMEMO session.");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
chatwin->is_omemo = FALSE;
|
||||
} else if (window->type == WIN_MUC) {
|
||||
ProfMucWin *mucwin = (ProfMucWin*)window;
|
||||
assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK);
|
||||
|
||||
if (!mucwin->is_omemo) {
|
||||
win_println(window, THEME_DEFAULT, '!', "You are not currently in an OMEMO session.");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
mucwin->is_omemo = FALSE;
|
||||
} else {
|
||||
win_println(window, THEME_DEFAULT, '-', "You must be in a regular chat window to start an OMEMO session.");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
#else
|
||||
cons_show("This version of Profanity has not been built with OMEMO support enabled");
|
||||
return TRUE;
|
||||
#endif
|
||||
}
|
||||
|
||||
gboolean
|
||||
cmd_omemo_fingerprint(ProfWin *window, const char *const command, gchar **args)
|
||||
{
|
||||
#ifdef HAVE_OMEMO
|
||||
if (connection_get_status() != JABBER_CONNECTED) {
|
||||
cons_show("You must be connected with an account to load OMEMO information.");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (!omemo_loaded()) {
|
||||
win_println(window, THEME_DEFAULT, '!', "You have not generated or loaded a cryptographic materials, use '/omemo gen'");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
Jid *jid;
|
||||
if (!args[1]) {
|
||||
if (window->type == WIN_CONSOLE) {
|
||||
char *fingerprint = omemo_own_fingerprint(TRUE);
|
||||
cons_show("Your OMEMO fingerprint: %s", fingerprint);
|
||||
free(fingerprint);
|
||||
jid = jid_create(connection_get_fulljid());
|
||||
} else if (window->type == WIN_CHAT) {
|
||||
ProfChatWin *chatwin = (ProfChatWin*)window;
|
||||
jid = jid_create(chatwin->barejid);
|
||||
} else {
|
||||
win_println(window, THEME_DEFAULT, '-', "You must be in a regular chat window to print fingerprint without providing the contact.");
|
||||
return TRUE;
|
||||
}
|
||||
} else {
|
||||
char *barejid = roster_barejid_from_name(args[1]);
|
||||
if (barejid) {
|
||||
jid = jid_create(barejid);
|
||||
} else {
|
||||
jid = jid_create(args[1]);
|
||||
if (!jid) {
|
||||
cons_show("%s is not a valid jid", args[1]);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GList *fingerprints = omemo_known_device_identities(jid->barejid);
|
||||
GList *fingerprint;
|
||||
|
||||
if (!fingerprints) {
|
||||
win_println(window, THEME_DEFAULT, '-', "There is no known fingerprints for %s", jid->barejid);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
for (fingerprint = fingerprints; fingerprint != NULL; fingerprint = fingerprint->next) {
|
||||
char *formatted_fingerprint = omemo_format_fingerprint(fingerprint->data);
|
||||
gboolean trusted = omemo_is_trusted_identity(jid->barejid, fingerprint->data);
|
||||
|
||||
win_println(window, THEME_DEFAULT, '-', "%s's OMEMO fingerprint: %s%s", jid->barejid, formatted_fingerprint, trusted ? " (trusted)" : "");
|
||||
|
||||
free(formatted_fingerprint);
|
||||
}
|
||||
|
||||
g_list_free(fingerprints);
|
||||
|
||||
win_println(window, THEME_DEFAULT, '-', "You can trust it with '/omemo trust <fingerprint>'");
|
||||
win_println(window, THEME_DEFAULT, '-', "You can untrust it with '/omemo untrust <fingerprint>'");
|
||||
|
||||
return TRUE;
|
||||
#else
|
||||
cons_show("This version of Profanity has not been built with OMEMO support enabled");
|
||||
return TRUE;
|
||||
#endif
|
||||
}
|
||||
|
||||
gboolean
|
||||
cmd_omemo_trust(ProfWin *window, const char *const command, gchar **args)
|
||||
{
|
||||
#ifdef HAVE_OMEMO
|
||||
if (connection_get_status() != JABBER_CONNECTED) {
|
||||
cons_show("You must be connected with an account to load OMEMO information.");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (!args[1]) {
|
||||
cons_bad_cmd_usage(command);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (!omemo_loaded()) {
|
||||
win_println(window, THEME_DEFAULT, '!', "You have not generated or loaded a cryptographic materials, use '/omemo gen'");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
char *fingerprint;
|
||||
char *barejid;
|
||||
|
||||
/* Contact not provided */
|
||||
if (!args[2]) {
|
||||
fingerprint = args[1];
|
||||
|
||||
if (window->type != WIN_CHAT) {
|
||||
win_println(window, THEME_DEFAULT, '-', "You must be in a regular chat window to trust a device without providing the contact.");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
ProfChatWin *chatwin = (ProfChatWin*)window;
|
||||
assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
|
||||
barejid = chatwin->barejid;
|
||||
} else {
|
||||
fingerprint = args[2];
|
||||
char *contact = args[1];
|
||||
barejid = roster_barejid_from_name(contact);
|
||||
if (barejid == NULL) {
|
||||
barejid = contact;
|
||||
}
|
||||
}
|
||||
|
||||
omemo_trust(barejid, fingerprint);
|
||||
|
||||
char *unformatted_fingerprint = malloc(strlen(fingerprint));
|
||||
int i;
|
||||
int j;
|
||||
for (i = 0, j = 0; fingerprint[i] != '\0'; i++) {
|
||||
if (!g_ascii_isxdigit(fingerprint[i])) {
|
||||
continue;
|
||||
}
|
||||
unformatted_fingerprint[j++] = fingerprint[i];
|
||||
}
|
||||
|
||||
unformatted_fingerprint[j] = '\0';
|
||||
gboolean trusted = omemo_is_trusted_identity(barejid, unformatted_fingerprint);
|
||||
|
||||
win_println(window, THEME_DEFAULT, '-', "%s's OMEMO fingerprint: %s%s", barejid, fingerprint, trusted ? " (trusted)" : "");
|
||||
|
||||
free(unformatted_fingerprint);
|
||||
|
||||
return TRUE;
|
||||
#else
|
||||
cons_show("This version of Profanity has not been built with OMEMO support enabled");
|
||||
return TRUE;
|
||||
#endif
|
||||
}
|
||||
|
||||
gboolean
|
||||
cmd_omemo_untrust(ProfWin *window, const char *const command, gchar **args)
|
||||
{
|
||||
#ifdef HAVE_OMEMO
|
||||
if (connection_get_status() != JABBER_CONNECTED) {
|
||||
cons_show("You must be connected with an account to load OMEMO information.");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (!args[1]) {
|
||||
cons_bad_cmd_usage(command);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (!omemo_loaded()) {
|
||||
win_println(window, THEME_DEFAULT, '!', "You have not generated or loaded a cryptographic materials, use '/omemo gen'");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
char *fingerprint;
|
||||
char *barejid;
|
||||
|
||||
/* Contact not provided */
|
||||
if (!args[2]) {
|
||||
fingerprint = args[1];
|
||||
|
||||
if (window->type != WIN_CHAT) {
|
||||
win_println(window, THEME_DEFAULT, '-', "You must be in a regular chat window to trust a device without providing the contact.");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
ProfChatWin *chatwin = (ProfChatWin*)window;
|
||||
assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK);
|
||||
barejid = chatwin->barejid;
|
||||
} else {
|
||||
fingerprint = args[2];
|
||||
char *contact = args[1];
|
||||
barejid = roster_barejid_from_name(contact);
|
||||
if (barejid == NULL) {
|
||||
barejid = contact;
|
||||
}
|
||||
}
|
||||
|
||||
omemo_untrust(barejid, fingerprint);
|
||||
|
||||
char *unformatted_fingerprint = malloc(strlen(fingerprint));
|
||||
int i;
|
||||
int j;
|
||||
for (i = 0, j = 0; fingerprint[i] != '\0'; i++) {
|
||||
if (!g_ascii_isxdigit(fingerprint[i])) {
|
||||
continue;
|
||||
}
|
||||
unformatted_fingerprint[j++] = fingerprint[i];
|
||||
}
|
||||
|
||||
unformatted_fingerprint[j] = '\0';
|
||||
gboolean trusted = omemo_is_trusted_identity(barejid, unformatted_fingerprint);
|
||||
|
||||
win_println(window, THEME_DEFAULT, '-', "%s's OMEMO fingerprint: %s%s", barejid, fingerprint, trusted ? " (trusted)" : "");
|
||||
|
||||
free(unformatted_fingerprint);
|
||||
|
||||
return TRUE;
|
||||
#else
|
||||
cons_show("This version of Profanity has not been built with OMEMO support enabled");
|
||||
return TRUE;
|
||||
#endif
|
||||
}
|
||||
|
||||
gboolean
|
||||
cmd_omemo_clear_device_list(ProfWin *window, const char *const command, gchar **args)
|
||||
{
|
||||
#ifdef HAVE_OMEMO
|
||||
if (connection_get_status() != JABBER_CONNECTED) {
|
||||
cons_show("You must be connected with an account to initialize OMEMO.");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
omemo_devicelist_publish(NULL);
|
||||
cons_show("Cleared OMEMO device list");
|
||||
return TRUE;
|
||||
#else
|
||||
cons_show("This version of Profanity has not been built with OMEMO support enabled");
|
||||
return TRUE;
|
||||
#endif
|
||||
}
|
||||
|
@ -214,4 +214,14 @@ gboolean cmd_wins_swap(ProfWin *window, const char *const command, gchar **args)
|
||||
|
||||
gboolean cmd_form_field(ProfWin *window, char *tag, gchar **args);
|
||||
|
||||
gboolean cmd_omemo_gen(ProfWin *window, const char *const command, gchar **args);
|
||||
gboolean cmd_omemo_char(ProfWin *window, const char *const command, gchar **args);
|
||||
gboolean cmd_omemo_log(ProfWin *window, const char *const command, gchar **args);
|
||||
gboolean cmd_omemo_start(ProfWin *window, const char *const command, gchar **args);
|
||||
gboolean cmd_omemo_end(ProfWin *window, const char *const command, gchar **args);
|
||||
gboolean cmd_omemo_fingerprint(ProfWin *window, const char *const command, gchar **args);
|
||||
gboolean cmd_omemo_trust(ProfWin *window, const char *const command, gchar **args);
|
||||
gboolean cmd_omemo_untrust(ProfWin *window, const char *const command, gchar **args);
|
||||
gboolean cmd_omemo_clear_device_list(ProfWin *window, const char *const command, gchar **args);
|
||||
|
||||
#endif
|
||||
|
@ -50,6 +50,7 @@
|
||||
#define DIR_CHATLOGS "chatlogs"
|
||||
#define DIR_OTR "otr"
|
||||
#define DIR_PGP "pgp"
|
||||
#define DIR_OMEMO "omemo"
|
||||
#define DIR_PLUGINS "plugins"
|
||||
|
||||
void files_create_directories(void);
|
||||
|
@ -58,6 +58,7 @@
|
||||
#define PREF_GROUP_ALIAS "alias"
|
||||
#define PREF_GROUP_OTR "otr"
|
||||
#define PREF_GROUP_PGP "pgp"
|
||||
#define PREF_GROUP_OMEMO "omemo"
|
||||
#define PREF_GROUP_MUC "muc"
|
||||
#define PREF_GROUP_PLUGINS "plugins"
|
||||
|
||||
@ -822,6 +823,33 @@ prefs_set_pgp_char(char ch)
|
||||
_save_prefs();
|
||||
}
|
||||
|
||||
char
|
||||
prefs_get_omemo_char(void)
|
||||
{
|
||||
char result = '~';
|
||||
|
||||
char *resultstr = g_key_file_get_string(prefs, PREF_GROUP_OMEMO, "omemo.char", NULL);
|
||||
if (!resultstr) {
|
||||
result = '~';
|
||||
} else {
|
||||
result = resultstr[0];
|
||||
}
|
||||
free(resultstr);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
prefs_set_omemo_char(char ch)
|
||||
{
|
||||
char str[2];
|
||||
str[0] = ch;
|
||||
str[1] = '\0';
|
||||
|
||||
g_key_file_set_string(prefs, PREF_GROUP_OMEMO, "omemo.char", str);
|
||||
_save_prefs();
|
||||
}
|
||||
|
||||
char
|
||||
prefs_get_roster_header_char(void)
|
||||
{
|
||||
@ -1657,6 +1685,8 @@ _get_group(preference_t pref)
|
||||
return PREF_GROUP_MUC;
|
||||
case PREF_PLUGINS_SOURCEPATH:
|
||||
return PREF_GROUP_PLUGINS;
|
||||
case PREF_OMEMO_LOG:
|
||||
return PREF_GROUP_OMEMO;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
@ -1871,6 +1901,8 @@ _get_key(preference_t pref)
|
||||
return "statusbar.chat";
|
||||
case PREF_STATUSBAR_ROOM:
|
||||
return "statusbar.room";
|
||||
case PREF_OMEMO_LOG:
|
||||
return "log";
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
@ -1989,6 +2021,8 @@ _get_default_string(preference_t pref)
|
||||
return "user";
|
||||
case PREF_STATUSBAR_ROOM:
|
||||
return "room";
|
||||
case PREF_OMEMO_LOG:
|
||||
return "redact";
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
@ -148,6 +148,7 @@ typedef enum {
|
||||
PREF_STATUSBAR_SELF,
|
||||
PREF_STATUSBAR_CHAT,
|
||||
PREF_STATUSBAR_ROOM,
|
||||
PREF_OMEMO_LOG,
|
||||
} preference_t;
|
||||
|
||||
typedef struct prof_alias_t {
|
||||
@ -216,6 +217,8 @@ char prefs_get_otr_char(void);
|
||||
void prefs_set_otr_char(char ch);
|
||||
char prefs_get_pgp_char(void);
|
||||
void prefs_set_pgp_char(char ch);
|
||||
char prefs_get_omemo_char(void);
|
||||
void prefs_set_omemo_char(char ch);
|
||||
|
||||
char prefs_get_roster_header_char(void);
|
||||
void prefs_set_roster_header_char(char ch);
|
||||
|
@ -54,6 +54,10 @@
|
||||
#include "pgp/gpg.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OMEMO
|
||||
#include "omemo/omemo.h"
|
||||
#endif
|
||||
|
||||
jabber_conn_status_t
|
||||
cl_ev_connect_jid(const char *const jid, const char *const passwd, const char *const altdomain, const int port, const char *const tls_policy)
|
||||
{
|
||||
@ -94,6 +98,9 @@ cl_ev_disconnect(void)
|
||||
#ifdef HAVE_LIBGPGME
|
||||
p_gpg_on_disconnect();
|
||||
#endif
|
||||
#ifdef HAVE_OMEMO
|
||||
omemo_on_disconnect();
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
@ -141,9 +148,10 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oo
|
||||
return;
|
||||
}
|
||||
|
||||
// OTR suported, PGP supported
|
||||
// OTR suported, PGP supported, OMEMO unsupported
|
||||
#ifdef HAVE_LIBOTR
|
||||
#ifdef HAVE_LIBGPGME
|
||||
#ifndef HAVE_OMEMO
|
||||
if (chatwin->pgp_send) {
|
||||
char *id = message_send_chat_pgp(chatwin->barejid, plugin_msg, request_receipt);
|
||||
chat_log_pgp_msg_out(chatwin->barejid, plugin_msg);
|
||||
@ -164,10 +172,12 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oo
|
||||
return;
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// OTR supported, PGP unsupported
|
||||
// OTR supported, PGP unsupported, OMEMO unsupported
|
||||
#ifdef HAVE_LIBOTR
|
||||
#ifndef HAVE_LIBGPGME
|
||||
#ifndef HAVE_OMEMO
|
||||
gboolean handled = otr_on_message_send(chatwin, plugin_msg, request_receipt);
|
||||
if (!handled) {
|
||||
char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt);
|
||||
@ -181,10 +191,12 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oo
|
||||
return;
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// OTR unsupported, PGP supported
|
||||
// OTR unsupported, PGP supported, OMEMO unsupported
|
||||
#ifndef HAVE_LIBOTR
|
||||
#ifdef HAVE_LIBGPGME
|
||||
#ifndef HAVE_OMEMO
|
||||
if (chatwin->pgp_send) {
|
||||
char *id = message_send_chat_pgp(chatwin->barejid, plugin_msg, request_receipt);
|
||||
chat_log_pgp_msg_out(chatwin->barejid, plugin_msg);
|
||||
@ -202,10 +214,120 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oo
|
||||
return;
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// OTR unsupported, PGP unsupported
|
||||
// OTR unsupported, PGP unsupported, OMEMO supported
|
||||
#ifndef HAVE_LIBOTR
|
||||
#ifndef HAVE_LIBGPGME
|
||||
#ifdef HAVE_OMEMO
|
||||
if (chatwin->is_omemo) {
|
||||
char *id = omemo_on_message_send((ProfWin *)chatwin, plugin_msg, request_receipt, FALSE);
|
||||
chat_log_omemo_msg_out(chatwin->barejid, plugin_msg);
|
||||
chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_OMEMO, request_receipt);
|
||||
free(id);
|
||||
} else {
|
||||
char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt);
|
||||
chat_log_msg_out(chatwin->barejid, plugin_msg);
|
||||
chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_PLAIN, request_receipt);
|
||||
free(id);
|
||||
}
|
||||
|
||||
plugins_post_chat_message_send(chatwin->barejid, plugin_msg);
|
||||
free(plugin_msg);
|
||||
return;
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// OTR supported, PGP unsupported, OMEMO supported
|
||||
#ifdef HAVE_LIBOTR
|
||||
#ifndef HAVE_LIBGPGME
|
||||
#ifdef HAVE_OMEMO
|
||||
if (chatwin->is_omemo) {
|
||||
char *id = omemo_on_message_send((ProfWin *)chatwin, plugin_msg, request_receipt, FALSE);
|
||||
chat_log_omemo_msg_out(chatwin->barejid, plugin_msg);
|
||||
chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_OMEMO, request_receipt);
|
||||
free(id);
|
||||
} else {
|
||||
gboolean handled = otr_on_message_send(chatwin, plugin_msg, request_receipt);
|
||||
if (!handled) {
|
||||
char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt);
|
||||
chat_log_msg_out(chatwin->barejid, plugin_msg);
|
||||
chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_PLAIN, request_receipt);
|
||||
free(id);
|
||||
}
|
||||
}
|
||||
|
||||
plugins_post_chat_message_send(chatwin->barejid, plugin_msg);
|
||||
free(plugin_msg);
|
||||
return;
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// OTR unsupported, PGP supported, OMEMO supported
|
||||
#ifndef HAVE_LIBOTR
|
||||
#ifdef HAVE_LIBGPGME
|
||||
#ifdef HAVE_OMEMO
|
||||
if (chatwin->is_omemo) {
|
||||
char *id = omemo_on_message_send((ProfWin *)chatwin, plugin_msg, request_receipt, FALSE);
|
||||
chat_log_omemo_msg_out(chatwin->barejid, plugin_msg);
|
||||
chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_OMEMO, request_receipt);
|
||||
free(id);
|
||||
} else if (chatwin->pgp_send) {
|
||||
char *id = message_send_chat_pgp(chatwin->barejid, plugin_msg, request_receipt);
|
||||
chat_log_pgp_msg_out(chatwin->barejid, plugin_msg);
|
||||
chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_PGP, request_receipt);
|
||||
free(id);
|
||||
} else {
|
||||
char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt);
|
||||
chat_log_msg_out(chatwin->barejid, plugin_msg);
|
||||
chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_PLAIN, request_receipt);
|
||||
free(id);
|
||||
}
|
||||
|
||||
plugins_post_chat_message_send(chatwin->barejid, plugin_msg);
|
||||
free(plugin_msg);
|
||||
return;
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// OTR supported, PGP supported, OMEMO supported
|
||||
#ifdef HAVE_LIBOTR
|
||||
#ifdef HAVE_LIBGPGME
|
||||
#ifdef HAVE_OMEMO
|
||||
if (chatwin->is_omemo) {
|
||||
char *id = omemo_on_message_send((ProfWin *)chatwin, plugin_msg, request_receipt, FALSE);
|
||||
chat_log_omemo_msg_out(chatwin->barejid, plugin_msg);
|
||||
chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_OMEMO, request_receipt);
|
||||
free(id);
|
||||
} else if (chatwin->pgp_send) {
|
||||
char *id = message_send_chat_pgp(chatwin->barejid, plugin_msg, request_receipt);
|
||||
chat_log_pgp_msg_out(chatwin->barejid, plugin_msg);
|
||||
chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_PGP, request_receipt);
|
||||
free(id);
|
||||
} else {
|
||||
gboolean handled = otr_on_message_send(chatwin, plugin_msg, request_receipt);
|
||||
if (!handled) {
|
||||
char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt);
|
||||
chat_log_msg_out(chatwin->barejid, plugin_msg);
|
||||
chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_PLAIN, request_receipt);
|
||||
free(id);
|
||||
}
|
||||
}
|
||||
|
||||
plugins_post_chat_message_send(chatwin->barejid, plugin_msg);
|
||||
free(plugin_msg);
|
||||
return;
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// OTR unsupported, PGP unsupported, OMEMO unsupported
|
||||
#ifndef HAVE_LIBOTR
|
||||
#ifndef HAVE_LIBGPGME
|
||||
#ifndef HAVE_OMEMO
|
||||
char *id = message_send_chat(chatwin->barejid, plugin_msg, oob_url, request_receipt);
|
||||
chat_log_msg_out(chatwin->barejid, plugin_msg);
|
||||
chatwin_outgoing_msg(chatwin, plugin_msg, id, PROF_MSG_PLAIN, request_receipt);
|
||||
@ -216,6 +338,7 @@ cl_ev_send_msg(ProfChatWin *chatwin, const char *const msg, const char *const oo
|
||||
return;
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
@ -226,10 +349,34 @@ cl_ev_send_muc_msg(ProfMucWin *mucwin, const char *const msg, const char *const
|
||||
return;
|
||||
}
|
||||
|
||||
message_send_groupchat(mucwin->roomjid, plugin_msg, oob_url);
|
||||
#ifdef HAVE_OMEMO
|
||||
if (mucwin->is_omemo) {
|
||||
char *id = omemo_on_message_send((ProfWin *)mucwin, plugin_msg, FALSE, TRUE);
|
||||
groupchat_log_omemo_msg_out(mucwin->roomjid, plugin_msg);
|
||||
mucwin_outgoing_msg(mucwin, plugin_msg, id, PROF_MSG_OMEMO);
|
||||
free(id);
|
||||
} else {
|
||||
char *id = message_send_groupchat(mucwin->roomjid, plugin_msg, oob_url);
|
||||
groupchat_log_msg_out(mucwin->roomjid, plugin_msg);
|
||||
mucwin_outgoing_msg(mucwin, plugin_msg, id, PROF_MSG_PLAIN);
|
||||
free(id);
|
||||
}
|
||||
|
||||
plugins_post_room_message_send(mucwin->roomjid, plugin_msg);
|
||||
free(plugin_msg);
|
||||
return;
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_OMEMO
|
||||
char *id = message_send_groupchat(mucwin->roomjid, plugin_msg, oob_url);
|
||||
groupchat_log_msg_out(mucwin->roomjid, plugin_msg);
|
||||
mucwin_outgoing_msg(mucwin, plugin_msg, id, PROF_MSG_PLAIN);
|
||||
free(id);
|
||||
|
||||
plugins_post_room_message_send(mucwin->roomjid, plugin_msg);
|
||||
free(plugin_msg);
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -59,6 +59,10 @@
|
||||
#include "pgp/gpg.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OMEMO
|
||||
#include "omemo/omemo.h"
|
||||
#endif
|
||||
|
||||
#include "ui/ui.h"
|
||||
|
||||
void
|
||||
@ -76,6 +80,10 @@ sv_ev_login_account_success(char *account_name, gboolean secured)
|
||||
p_gpg_on_connect(account->jid);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OMEMO
|
||||
omemo_on_connect(account);
|
||||
#endif
|
||||
|
||||
ui_handle_login_account_success(account, secured);
|
||||
|
||||
// attempt to rejoin rooms with passwords
|
||||
@ -166,6 +174,18 @@ sv_ev_roster_received(void)
|
||||
|
||||
const char *fulljid = connection_get_fulljid();
|
||||
plugins_on_connect(account_name, fulljid);
|
||||
|
||||
#ifdef HAVE_OMEMO
|
||||
omemo_start_sessions();
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
sv_ev_connection_features_received(void)
|
||||
{
|
||||
#ifdef HAVE_OMEMO
|
||||
omemo_publish_crypto_materials();
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
@ -252,22 +272,23 @@ sv_ev_room_history(const char *const room_jid, const char *const nick,
|
||||
}
|
||||
|
||||
void
|
||||
sv_ev_room_message(const char *const room_jid, const char *const nick, const char *const message)
|
||||
sv_ev_room_message(const char *const room_jid, const char *const nick, const char *const message, const char *const id, gboolean omemo)
|
||||
{
|
||||
if (prefs_get_boolean(PREF_GRLOG)) {
|
||||
Jid *jid = jid_create(connection_get_fulljid());
|
||||
groupchat_log_chat(jid->barejid, room_jid, nick, message);
|
||||
jid_destroy(jid);
|
||||
}
|
||||
|
||||
ProfMucWin *mucwin = wins_get_muc(room_jid);
|
||||
if (!mucwin) {
|
||||
return;
|
||||
}
|
||||
|
||||
char *new_message = plugins_pre_room_message_display(room_jid, nick, message);
|
||||
char *mynick = muc_nick(mucwin->roomjid);
|
||||
|
||||
if (omemo) {
|
||||
groupchat_log_omemo_msg_in(room_jid, nick, message);
|
||||
} else {
|
||||
groupchat_log_msg_in(room_jid, nick, message);
|
||||
}
|
||||
|
||||
char *new_message = plugins_pre_room_message_display(room_jid, nick, message);
|
||||
|
||||
gboolean whole_word = prefs_get_boolean(PREF_NOTIFY_MENTION_WHOLE_WORD);
|
||||
gboolean case_sensitive = prefs_get_boolean(PREF_NOTIFY_MENTION_CASE_SENSITIVE);
|
||||
char *message_search = case_sensitive ? strdup(new_message) : g_utf8_strdown(new_message, -1);
|
||||
@ -281,7 +302,11 @@ sv_ev_room_message(const char *const room_jid, const char *const nick, const cha
|
||||
|
||||
GList *triggers = prefs_message_get_triggers(new_message);
|
||||
|
||||
mucwin_message(mucwin, nick, new_message, mentions, triggers);
|
||||
if (omemo) {
|
||||
mucwin_incoming_msg(mucwin, nick, new_message, id, mentions, triggers, PROF_MSG_OMEMO);
|
||||
} else {
|
||||
mucwin_incoming_msg(mucwin, nick, new_message, id, mentions, triggers, PROF_MSG_PLAIN);
|
||||
}
|
||||
|
||||
g_slist_free(mentions);
|
||||
|
||||
@ -370,7 +395,7 @@ sv_ev_delayed_private_message(const char *const fulljid, char *message, GDateTim
|
||||
}
|
||||
|
||||
void
|
||||
sv_ev_outgoing_carbon(char *barejid, char *message, char *pgp_message)
|
||||
sv_ev_outgoing_carbon(char *barejid, char *message, char *pgp_message, gboolean omemo)
|
||||
{
|
||||
ProfChatWin *chatwin = wins_get_chat(barejid);
|
||||
if (!chatwin) {
|
||||
@ -380,6 +405,7 @@ sv_ev_outgoing_carbon(char *barejid, char *message, char *pgp_message)
|
||||
chat_state_active(chatwin->state);
|
||||
|
||||
#ifdef HAVE_LIBGPGME
|
||||
#ifndef HAVE_OMEMO
|
||||
if (pgp_message) {
|
||||
char *decrypted = p_gpg_decrypt(pgp_message);
|
||||
if (decrypted) {
|
||||
@ -390,9 +416,44 @@ sv_ev_outgoing_carbon(char *barejid, char *message, char *pgp_message)
|
||||
} else {
|
||||
chatwin_outgoing_carbon(chatwin, message, PROF_MSG_PLAIN);
|
||||
}
|
||||
#else
|
||||
return;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_LIBGPGME
|
||||
#ifdef HAVE_OMEMO
|
||||
if (omemo) {
|
||||
chatwin_outgoing_carbon(chatwin, message, PROF_MSG_OMEMO);
|
||||
} else {
|
||||
chatwin_outgoing_carbon(chatwin, message, PROF_MSG_PLAIN);
|
||||
}
|
||||
return;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LIBGPGME
|
||||
#ifdef HAVE_OMEMO
|
||||
if (omemo) {
|
||||
chatwin_outgoing_carbon(chatwin, message, PROF_MSG_OMEMO);
|
||||
} else if (pgp_message) {
|
||||
char *decrypted = p_gpg_decrypt(pgp_message);
|
||||
if (decrypted) {
|
||||
chatwin_outgoing_carbon(chatwin, decrypted, PROF_MSG_PGP);
|
||||
} else {
|
||||
chatwin_outgoing_carbon(chatwin, message, PROF_MSG_PLAIN);
|
||||
}
|
||||
} else {
|
||||
chatwin_outgoing_carbon(chatwin, message, PROF_MSG_PLAIN);
|
||||
}
|
||||
return;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_LIBGPGME
|
||||
#ifndef HAVE_OMEMO
|
||||
chatwin_outgoing_carbon(chatwin, message, PROF_MSG_PLAIN);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBGPGME
|
||||
@ -433,6 +494,16 @@ _sv_ev_incoming_otr(ProfChatWin *chatwin, gboolean new_win, char *barejid, char
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OMEMO
|
||||
static void
|
||||
_sv_ev_incoming_omemo(ProfChatWin *chatwin, gboolean new_win, char *barejid, char *resource, char *message, GDateTime *timestamp)
|
||||
{
|
||||
chatwin_incoming_msg(chatwin, resource, message, timestamp, new_win, PROF_MSG_OMEMO);
|
||||
chat_log_omemo_msg_in(barejid, message, timestamp);
|
||||
chatwin->pgp_recv = FALSE;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
_sv_ev_incoming_plain(ProfChatWin *chatwin, gboolean new_win, char *barejid, char *resource, char *message, GDateTime *timestamp)
|
||||
{
|
||||
@ -442,7 +513,7 @@ _sv_ev_incoming_plain(ProfChatWin *chatwin, gboolean new_win, char *barejid, cha
|
||||
}
|
||||
|
||||
void
|
||||
sv_ev_incoming_message(char *barejid, char *resource, char *message, char *pgp_message, GDateTime *timestamp)
|
||||
sv_ev_incoming_message(char *barejid, char *resource, char *message, char *pgp_message, GDateTime *timestamp, gboolean omemo)
|
||||
{
|
||||
gboolean new_win = FALSE;
|
||||
ProfChatWin *chatwin = wins_get_chat(barejid);
|
||||
@ -452,9 +523,10 @@ sv_ev_incoming_message(char *barejid, char *resource, char *message, char *pgp_m
|
||||
new_win = TRUE;
|
||||
}
|
||||
|
||||
// OTR suported, PGP supported
|
||||
// OTR suported, PGP supported, OMEMO unsupported
|
||||
#ifdef HAVE_LIBOTR
|
||||
#ifdef HAVE_LIBGPGME
|
||||
#ifndef HAVE_OMEMO
|
||||
if (pgp_message) {
|
||||
if (chatwin->is_otr) {
|
||||
win_println((ProfWin*)chatwin, THEME_DEFAULT, '-', "PGP encrypted message received whilst in OTR session.");
|
||||
@ -468,19 +540,23 @@ sv_ev_incoming_message(char *barejid, char *resource, char *message, char *pgp_m
|
||||
return;
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// OTR supported, PGP unsupported
|
||||
// OTR supported, PGP unsupported, OMEMO unsupported
|
||||
#ifdef HAVE_LIBOTR
|
||||
#ifndef HAVE_LIBGPGME
|
||||
#ifndef HAVE_OMEMO
|
||||
_sv_ev_incoming_otr(chatwin, new_win, barejid, resource, message, timestamp);
|
||||
rosterwin_roster();
|
||||
return;
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// OTR unsupported, PGP supported
|
||||
// OTR unsupported, PGP supported, OMEMO unsupported
|
||||
#ifndef HAVE_LIBOTR
|
||||
#ifdef HAVE_LIBGPGME
|
||||
#ifndef HAVE_OMEMO
|
||||
if (pgp_message) {
|
||||
_sv_ev_incoming_pgp(chatwin, new_win, barejid, resource, message, pgp_message, timestamp);
|
||||
} else {
|
||||
@ -490,19 +566,90 @@ sv_ev_incoming_message(char *barejid, char *resource, char *message, char *pgp_m
|
||||
return;
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// OTR unsupported, PGP unsupported
|
||||
// OTR suported, PGP supported, OMEMO supported
|
||||
#ifdef HAVE_LIBOTR
|
||||
#ifdef HAVE_LIBGPGME
|
||||
#ifdef HAVE_OMEMO
|
||||
if (pgp_message) {
|
||||
if (chatwin->is_otr) {
|
||||
win_println((ProfWin*)chatwin, THEME_DEFAULT, '-', "PGP encrypted message received whilst in OTR session.");
|
||||
} else { // PROF_ENC_NONE, PROF_ENC_PGP
|
||||
_sv_ev_incoming_pgp(chatwin, new_win, barejid, resource, message, pgp_message, timestamp);
|
||||
}
|
||||
} else if (omemo) {
|
||||
_sv_ev_incoming_omemo(chatwin, new_win, barejid, resource, message, timestamp);
|
||||
} else {
|
||||
_sv_ev_incoming_otr(chatwin, new_win, barejid, resource, message, timestamp);
|
||||
}
|
||||
rosterwin_roster();
|
||||
return;
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// OTR supported, PGP unsupported, OMEMO supported
|
||||
#ifdef HAVE_LIBOTR
|
||||
#ifndef HAVE_LIBGPGME
|
||||
#ifdef HAVE_OMEMO
|
||||
if (omemo) {
|
||||
_sv_ev_incoming_omemo(chatwin, new_win, barejid, resource, message, timestamp);
|
||||
} else {
|
||||
_sv_ev_incoming_otr(chatwin, new_win, barejid, resource, message, timestamp);
|
||||
}
|
||||
rosterwin_roster();
|
||||
return;
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// OTR unsupported, PGP supported, OMEMO supported
|
||||
#ifndef HAVE_LIBOTR
|
||||
#ifdef HAVE_LIBGPGME
|
||||
#ifdef HAVE_OMEMO
|
||||
if (pgp_message) {
|
||||
_sv_ev_incoming_pgp(chatwin, new_win, barejid, resource, message, pgp_message, timestamp);
|
||||
} else if (omemo) {
|
||||
_sv_ev_incoming_omemo(chatwin, new_win, barejid, resource, message, timestamp);
|
||||
} else {
|
||||
_sv_ev_incoming_plain(chatwin, new_win, barejid, resource, message, timestamp);
|
||||
}
|
||||
rosterwin_roster();
|
||||
return;
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// OTR unsupported, PGP unsupported, OMEMO supported
|
||||
#ifndef HAVE_LIBOTR
|
||||
#ifndef HAVE_LIBGPGME
|
||||
#ifdef HAVE_OMEMO
|
||||
if (omemo) {
|
||||
_sv_ev_incoming_omemo(chatwin, new_win, barejid, resource, message, timestamp);
|
||||
} else {
|
||||
_sv_ev_incoming_plain(chatwin, new_win, barejid, resource, message, timestamp);
|
||||
}
|
||||
rosterwin_roster();
|
||||
return;
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// OTR unsupported, PGP unsupported, OMEMO unsupported
|
||||
#ifndef HAVE_LIBOTR
|
||||
#ifndef HAVE_LIBGPGME
|
||||
#ifndef HAVE_OMEMO
|
||||
_sv_ev_incoming_plain(chatwin, new_win, barejid, resource, message, timestamp);
|
||||
rosterwin_roster();
|
||||
return;
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
sv_ev_incoming_carbon(char *barejid, char *resource, char *message, char *pgp_message)
|
||||
sv_ev_incoming_carbon(char *barejid, char *resource, char *message, char *pgp_message, gboolean omemo)
|
||||
{
|
||||
gboolean new_win = FALSE;
|
||||
ProfChatWin *chatwin = wins_get_chat(barejid);
|
||||
@ -513,15 +660,50 @@ sv_ev_incoming_carbon(char *barejid, char *resource, char *message, char *pgp_me
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBGPGME
|
||||
#ifndef HAVE_OMEMO
|
||||
if (pgp_message) {
|
||||
_sv_ev_incoming_pgp(chatwin, new_win, barejid, resource, message, pgp_message, NULL);
|
||||
} else {
|
||||
_sv_ev_incoming_plain(chatwin, new_win, barejid, resource, message, NULL);
|
||||
}
|
||||
#else
|
||||
_sv_ev_incoming_plain(chatwin, new_win, barejid, resource, message, NULL);
|
||||
#endif
|
||||
rosterwin_roster();
|
||||
return;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LIBGPGME
|
||||
#ifdef HAVE_OMEMO
|
||||
if (pgp_message) {
|
||||
_sv_ev_incoming_pgp(chatwin, new_win, barejid, resource, message, pgp_message, NULL);
|
||||
} else if (omemo) {
|
||||
_sv_ev_incoming_omemo(chatwin, new_win, barejid, resource, message, NULL);
|
||||
} else {
|
||||
_sv_ev_incoming_plain(chatwin, new_win, barejid, resource, message, NULL);
|
||||
}
|
||||
rosterwin_roster();
|
||||
return;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_LIBGPGME
|
||||
#ifdef HAVE_OMEMO
|
||||
if (omemo) {
|
||||
_sv_ev_incoming_omemo(chatwin, new_win, barejid, resource, message, NULL);
|
||||
} else {
|
||||
_sv_ev_incoming_plain(chatwin, new_win, barejid, resource, message, NULL);
|
||||
}
|
||||
rosterwin_roster();
|
||||
return;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_LIBGPGME
|
||||
#ifndef HAVE_OMEMO
|
||||
_sv_ev_incoming_plain(chatwin, new_win, barejid, resource, message, NULL);
|
||||
rosterwin_roster();
|
||||
return;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -46,10 +46,10 @@ void sv_ev_room_invite(jabber_invite_t invite_type,
|
||||
void sv_ev_room_broadcast(const char *const room_jid, const char *const message);
|
||||
void sv_ev_room_subject(const char *const room, const char *const nick, const char *const subject);
|
||||
void sv_ev_room_history(const char *const room_jid, const char *const nick,
|
||||
GDateTime *timestamp, const char *const message);
|
||||
GDateTime *timestamp, const char *const message, gboolean omemo);
|
||||
void sv_ev_room_message(const char *const room_jid, const char *const nick,
|
||||
const char *const message);
|
||||
void sv_ev_incoming_message(char *barejid, char *resource, char *message, char *pgp_message, GDateTime *timestamp);
|
||||
const char *const message, const char *const id, gboolean omemo);
|
||||
void sv_ev_incoming_message(char *barejid, char *resource, char *message, char *pgp_message, GDateTime *timestamp, gboolean omemo);
|
||||
void sv_ev_incoming_private_message(const char *const fulljid, char *message);
|
||||
void sv_ev_delayed_private_message(const char *const fulljid, char *message, GDateTime *timestamp);
|
||||
void sv_ev_typing(char *barejid, char *resource);
|
||||
@ -73,8 +73,8 @@ void sv_ev_room_occupent_kicked(const char *const room, const char *const nick,
|
||||
void sv_ev_room_banned(const char *const room, const char *const actor, const char *const reason);
|
||||
void sv_ev_room_occupent_banned(const char *const room, const char *const nick, const char *const actor,
|
||||
const char *const reason);
|
||||
void sv_ev_outgoing_carbon(char *barejid, char *message, char *pgp_message);
|
||||
void sv_ev_incoming_carbon(char *barejid, char *resource, char *message, char *pgp_message);
|
||||
void sv_ev_outgoing_carbon(char *barejid, char *message, char *pgp_message, gboolean omemo);
|
||||
void sv_ev_incoming_carbon(char *barejid, char *resource, char *message, char *pgp_message, gboolean omemo);
|
||||
void sv_ev_xmpp_stanza(const char *const msg);
|
||||
void sv_ev_muc_self_online(const char *const room, const char *const nick, gboolean config_required,
|
||||
const char *const role, const char *const affiliation, const char *const actor, const char *const reason,
|
||||
@ -85,6 +85,7 @@ void sv_ev_muc_occupant_online(const char *const room, const char *const nick, c
|
||||
void sv_ev_roster_update(const char *const barejid, const char *const name,
|
||||
GSList *groups, const char *const subscription, gboolean pending_out);
|
||||
void sv_ev_roster_received(void);
|
||||
void sv_ev_connection_features_received(void);
|
||||
int sv_ev_certfail(const char *const errormsg, TLSCertificate *cert);
|
||||
void sv_ev_lastactivity_response(const char *const from, const int seconds, const char *const msg);
|
||||
void sv_ev_bookmark_autojoin(Bookmark *bookmark);
|
||||
|
98
src/log.c
98
src/log.c
@ -48,6 +48,7 @@
|
||||
#include "config/files.h"
|
||||
#include "config/preferences.h"
|
||||
#include "xmpp/xmpp.h"
|
||||
#include "xmpp/muc.h"
|
||||
|
||||
#define PROF "prof"
|
||||
|
||||
@ -89,6 +90,8 @@ static void _rotate_log_file(void);
|
||||
static char* _log_string_from_level(log_level_t level);
|
||||
static void _chat_log_chat(const char *const login, const char *const other, const gchar *const msg,
|
||||
chat_log_direction_t direction, GDateTime *timestamp);
|
||||
static void _groupchat_log_chat(const gchar *const login, const gchar *const room, const gchar *const nick,
|
||||
const gchar *const msg);
|
||||
|
||||
void
|
||||
log_debug(const char *const msg, ...)
|
||||
@ -304,6 +307,23 @@ chat_log_pgp_msg_out(const char *const barejid, const char *const msg)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
chat_log_omemo_msg_out(const char *const barejid, const char *const msg)
|
||||
{
|
||||
if (prefs_get_boolean(PREF_CHLOG)) {
|
||||
const char *jid = connection_get_fulljid();
|
||||
Jid *jidp = jid_create(jid);
|
||||
char *pref_omemo_log = prefs_get_string(PREF_OMEMO_LOG);
|
||||
if (strcmp(pref_omemo_log, "on") == 0) {
|
||||
_chat_log_chat(jidp->barejid, barejid, msg, PROF_OUT_LOG, NULL);
|
||||
} else if (strcmp(pref_omemo_log, "redact") == 0) {
|
||||
_chat_log_chat(jidp->barejid, barejid, "[redacted]", PROF_OUT_LOG, NULL);
|
||||
}
|
||||
prefs_free_string(pref_omemo_log);
|
||||
jid_destroy(jidp);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
chat_log_otr_msg_in(const char *const barejid, const char *const msg, gboolean was_decrypted, GDateTime *timestamp)
|
||||
{
|
||||
@ -338,6 +358,23 @@ chat_log_pgp_msg_in(const char *const barejid, const char *const msg, GDateTime
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
chat_log_omemo_msg_in(const char *const barejid, const char *const msg, GDateTime *timestamp)
|
||||
{
|
||||
if (prefs_get_boolean(PREF_CHLOG)) {
|
||||
const char *jid = connection_get_fulljid();
|
||||
Jid *jidp = jid_create(jid);
|
||||
char *pref_omemo_log = prefs_get_string(PREF_OMEMO_LOG);
|
||||
if (strcmp(pref_omemo_log, "on") == 0) {
|
||||
_chat_log_chat(jidp->barejid, barejid, msg, PROF_IN_LOG, timestamp);
|
||||
} else if (strcmp(pref_omemo_log, "redact") == 0) {
|
||||
_chat_log_chat(jidp->barejid, barejid, "[redacted]", PROF_IN_LOG, timestamp);
|
||||
}
|
||||
prefs_free_string(pref_omemo_log);
|
||||
jid_destroy(jidp);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
chat_log_msg_in(const char *const barejid, const char *const msg, GDateTime *timestamp)
|
||||
{
|
||||
@ -406,7 +443,66 @@ _chat_log_chat(const char *const login, const char *const other, const char *con
|
||||
}
|
||||
|
||||
void
|
||||
groupchat_log_chat(const gchar *const login, const gchar *const room, const gchar *const nick, const gchar *const msg)
|
||||
groupchat_log_msg_out(const gchar *const room, const gchar *const msg)
|
||||
{
|
||||
if (prefs_get_boolean(PREF_GRLOG)) {
|
||||
const char *jid = connection_get_fulljid();
|
||||
Jid *jidp = jid_create(jid);
|
||||
char *mynick = muc_nick(room);
|
||||
_groupchat_log_chat(jidp->barejid, room, mynick, msg);
|
||||
jid_destroy(jidp);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
groupchat_log_msg_in(const gchar *const room, const gchar *const nick, const gchar *const msg)
|
||||
{
|
||||
if (prefs_get_boolean(PREF_GRLOG)) {
|
||||
const char *jid = connection_get_fulljid();
|
||||
Jid *jidp = jid_create(jid);
|
||||
_groupchat_log_chat(jidp->barejid, room, nick, msg);
|
||||
jid_destroy(jidp);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
groupchat_log_omemo_msg_out(const gchar *const room, const gchar *const msg)
|
||||
{
|
||||
if (prefs_get_boolean(PREF_CHLOG)) {
|
||||
const char *jid = connection_get_fulljid();
|
||||
Jid *jidp = jid_create(jid);
|
||||
char *pref_omemo_log = prefs_get_string(PREF_OMEMO_LOG);
|
||||
char *mynick = muc_nick(room);
|
||||
if (strcmp(pref_omemo_log, "on") == 0) {
|
||||
_groupchat_log_chat(jidp->barejid, room, mynick, msg);
|
||||
} else if (strcmp(pref_omemo_log, "redact") == 0) {
|
||||
_groupchat_log_chat(jidp->barejid, room, mynick, "[redacted]");
|
||||
}
|
||||
prefs_free_string(pref_omemo_log);
|
||||
jid_destroy(jidp);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
groupchat_log_omemo_msg_in(const gchar *const room, const gchar *const nick, const gchar *const msg)
|
||||
{
|
||||
if (prefs_get_boolean(PREF_CHLOG)) {
|
||||
const char *jid = connection_get_fulljid();
|
||||
Jid *jidp = jid_create(jid);
|
||||
char *pref_omemo_log = prefs_get_string(PREF_OMEMO_LOG);
|
||||
if (strcmp(pref_omemo_log, "on") == 0) {
|
||||
_groupchat_log_chat(jidp->barejid, room, nick, msg);
|
||||
} else if (strcmp(pref_omemo_log, "redact") == 0) {
|
||||
_groupchat_log_chat(jidp->barejid, room, nick, "[redacted]");
|
||||
}
|
||||
prefs_free_string(pref_omemo_log);
|
||||
jid_destroy(jidp);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
_groupchat_log_chat(const gchar *const login, const gchar *const room, const gchar *const nick,
|
||||
const gchar *const msg)
|
||||
{
|
||||
struct dated_chat_log *dated_log = g_hash_table_lookup(groupchat_logs, room);
|
||||
|
||||
|
@ -71,16 +71,21 @@ void chat_log_init(void);
|
||||
void chat_log_msg_out(const char *const barejid, const char *const msg);
|
||||
void chat_log_otr_msg_out(const char *const barejid, const char *const msg);
|
||||
void chat_log_pgp_msg_out(const char *const barejid, const char *const msg);
|
||||
void chat_log_omemo_msg_out(const char *const barejid, const char *const msg);
|
||||
|
||||
void chat_log_msg_in(const char *const barejid, const char *const msg, GDateTime *timestamp);
|
||||
void chat_log_otr_msg_in(const char *const barejid, const char *const msg, gboolean was_decrypted, GDateTime *timestamp);
|
||||
void chat_log_pgp_msg_in(const char *const barejid, const char *const msg, GDateTime *timestamp);
|
||||
void chat_log_omemo_msg_in(const char *const barejid, const char *const msg, GDateTime *timestamp);
|
||||
|
||||
void chat_log_close(void);
|
||||
GSList* chat_log_get_previous(const gchar *const login, const gchar *const recipient);
|
||||
|
||||
void groupchat_log_init(void);
|
||||
void groupchat_log_chat(const gchar *const login, const gchar *const room, const gchar *const nick,
|
||||
const gchar *const msg);
|
||||
|
||||
void groupchat_log_msg_out(const gchar *const room, const gchar *const msg);
|
||||
void groupchat_log_msg_in(const gchar *const room, const gchar *const nick, const gchar *const msg);
|
||||
void groupchat_log_omemo_msg_out(const gchar *const room, const gchar *const msg);
|
||||
void groupchat_log_omemo_msg_in(const gchar *const room, const gchar *const nick, const gchar *const msg);
|
||||
|
||||
#endif
|
||||
|
@ -138,6 +138,12 @@ main(int argc, char **argv)
|
||||
g_print("PGP support: Disabled\n");
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OMEMO
|
||||
g_print("OMEMO support: Enabled\n");
|
||||
#else
|
||||
g_print("OMEMO support: Disabled\n");
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_C
|
||||
g_print("C plugins: Enabled\n");
|
||||
#else
|
||||
|
331
src/omemo/crypto.c
Normal file
331
src/omemo/crypto.c
Normal file
@ -0,0 +1,331 @@
|
||||
#include <assert.h>
|
||||
#include <signal/signal_protocol.h>
|
||||
#include <signal/signal_protocol_types.h>
|
||||
#include <gcrypt.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "omemo/omemo.h"
|
||||
#include "omemo/crypto.h"
|
||||
|
||||
int
|
||||
omemo_crypto_init(void)
|
||||
{
|
||||
if (!gcry_check_version(GCRYPT_VERSION)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
omemo_random_func(uint8_t *data, size_t len, void *user_data)
|
||||
{
|
||||
gcry_randomize(data, len, GCRY_VERY_STRONG_RANDOM);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
omemo_hmac_sha256_init_func(void **hmac_context, const uint8_t *key, size_t key_len, void *user_data)
|
||||
{
|
||||
gcry_error_t res;
|
||||
gcry_mac_hd_t hd;
|
||||
|
||||
res = gcry_mac_open(&hd, GCRY_MAC_HMAC_SHA256, 0, NULL);
|
||||
if (res != GPG_ERR_NO_ERROR) {
|
||||
log_error("OMEMO: %s", gcry_strerror(res));
|
||||
return OMEMO_ERR_GCRYPT;
|
||||
}
|
||||
|
||||
*hmac_context = hd;
|
||||
res = gcry_mac_setkey(hd, key, key_len);
|
||||
if (res != GPG_ERR_NO_ERROR) {
|
||||
log_error("OMEMO: %s", gcry_strerror(res));
|
||||
return OMEMO_ERR_GCRYPT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
omemo_hmac_sha256_update_func(void *hmac_context, const uint8_t *data, size_t data_len, void *user_data)
|
||||
{
|
||||
gcry_error_t res;
|
||||
|
||||
res = gcry_mac_write(hmac_context, data, data_len);
|
||||
if (res != GPG_ERR_NO_ERROR) {
|
||||
log_error("OMEMO: %s", gcry_strerror(res));
|
||||
return OMEMO_ERR_GCRYPT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
omemo_hmac_sha256_final_func(void *hmac_context, signal_buffer **output, void *user_data)
|
||||
{
|
||||
gcry_error_t res;
|
||||
size_t mac_len = 32;
|
||||
unsigned char out[mac_len];
|
||||
|
||||
res = gcry_mac_read(hmac_context, out, &mac_len);
|
||||
if (res != GPG_ERR_NO_ERROR) {
|
||||
log_error("OMEMO: %s", gcry_strerror(res));
|
||||
return OMEMO_ERR_GCRYPT;
|
||||
}
|
||||
|
||||
*output = signal_buffer_create(out, mac_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
omemo_hmac_sha256_cleanup_func(void *hmac_context, void *user_data)
|
||||
{
|
||||
gcry_mac_close(hmac_context);
|
||||
}
|
||||
|
||||
int
|
||||
omemo_sha512_digest_init_func(void **digest_context, void *user_data)
|
||||
{
|
||||
gcry_error_t res;
|
||||
gcry_md_hd_t hd;
|
||||
|
||||
res = gcry_md_open(&hd, GCRY_MD_SHA512, 0);
|
||||
if (res != GPG_ERR_NO_ERROR) {
|
||||
log_error("OMEMO: %s", gcry_strerror(res));
|
||||
return OMEMO_ERR_GCRYPT;
|
||||
}
|
||||
|
||||
*digest_context = hd;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
omemo_sha512_digest_update_func(void *digest_context, const uint8_t *data, size_t data_len, void *user_data)
|
||||
{
|
||||
gcry_md_write(digest_context, data, data_len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
omemo_sha512_digest_final_func(void *digest_context, signal_buffer **output, void *user_data)
|
||||
{
|
||||
gcry_error_t res;
|
||||
unsigned char out[64];
|
||||
|
||||
res = gcry_md_extract(digest_context, GCRY_MD_SHA512, out, 64);
|
||||
if (res != GPG_ERR_NO_ERROR) {
|
||||
log_error("OMEMO: %s", gcry_strerror(res));
|
||||
return OMEMO_ERR_GCRYPT;
|
||||
}
|
||||
|
||||
*output = signal_buffer_create(out, 64);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
omemo_sha512_digest_cleanup_func(void *digest_context, void *user_data)
|
||||
{
|
||||
gcry_md_close(digest_context);
|
||||
}
|
||||
|
||||
int
|
||||
omemo_encrypt_func(signal_buffer **output, int cipher, const uint8_t *key, size_t key_len, const uint8_t *iv, size_t iv_len,
|
||||
const uint8_t *plaintext, size_t plaintext_len, void *user_data)
|
||||
{
|
||||
gcry_cipher_hd_t hd;
|
||||
unsigned char *padded_plaintext;
|
||||
unsigned char *ciphertext;
|
||||
size_t ciphertext_len;
|
||||
int mode;
|
||||
int algo;
|
||||
uint8_t padding = 0;
|
||||
|
||||
switch (key_len) {
|
||||
case 32:
|
||||
algo = GCRY_CIPHER_AES256;
|
||||
break;
|
||||
default:
|
||||
return OMEMO_ERR_UNSUPPORTED_CRYPTO;
|
||||
}
|
||||
|
||||
switch (cipher) {
|
||||
case SG_CIPHER_AES_CBC_PKCS5:
|
||||
mode = GCRY_CIPHER_MODE_CBC;
|
||||
break;
|
||||
default:
|
||||
return OMEMO_ERR_UNSUPPORTED_CRYPTO;
|
||||
}
|
||||
|
||||
gcry_cipher_open(&hd, algo, mode, GCRY_CIPHER_SECURE);
|
||||
|
||||
gcry_cipher_setkey(hd, key, key_len);
|
||||
|
||||
switch (cipher) {
|
||||
case SG_CIPHER_AES_CBC_PKCS5:
|
||||
gcry_cipher_setiv(hd, iv, iv_len);
|
||||
padding = 16 - (plaintext_len % 16);
|
||||
break;
|
||||
default:
|
||||
assert(FALSE);
|
||||
}
|
||||
|
||||
padded_plaintext = malloc(plaintext_len + padding);
|
||||
memcpy(padded_plaintext, plaintext, plaintext_len);
|
||||
memset(padded_plaintext + plaintext_len, padding, padding);
|
||||
|
||||
ciphertext_len = plaintext_len + padding;
|
||||
ciphertext = malloc(ciphertext_len);
|
||||
gcry_cipher_encrypt(hd, ciphertext, ciphertext_len, padded_plaintext, plaintext_len + padding);
|
||||
|
||||
*output = signal_buffer_create(ciphertext, ciphertext_len);
|
||||
free(padded_plaintext);
|
||||
free(ciphertext);
|
||||
|
||||
gcry_cipher_close(hd);
|
||||
|
||||
return SG_SUCCESS;
|
||||
}
|
||||
|
||||
int
|
||||
omemo_decrypt_func(signal_buffer **output, int cipher, const uint8_t *key, size_t key_len, const uint8_t *iv, size_t iv_len,
|
||||
const uint8_t *ciphertext, size_t ciphertext_len, void *user_data)
|
||||
{
|
||||
int ret = SG_SUCCESS;
|
||||
gcry_cipher_hd_t hd;
|
||||
unsigned char *plaintext;
|
||||
size_t plaintext_len;
|
||||
int mode;
|
||||
int algo;
|
||||
uint8_t padding = 0;
|
||||
|
||||
switch (key_len) {
|
||||
case 32:
|
||||
algo = GCRY_CIPHER_AES256;
|
||||
break;
|
||||
default:
|
||||
return OMEMO_ERR_UNSUPPORTED_CRYPTO;
|
||||
}
|
||||
|
||||
switch (cipher) {
|
||||
case SG_CIPHER_AES_CBC_PKCS5:
|
||||
mode = GCRY_CIPHER_MODE_CBC;
|
||||
break;
|
||||
default:
|
||||
return OMEMO_ERR_UNSUPPORTED_CRYPTO;
|
||||
}
|
||||
|
||||
gcry_cipher_open(&hd, algo, mode, GCRY_CIPHER_SECURE);
|
||||
|
||||
gcry_cipher_setkey(hd, key, key_len);
|
||||
|
||||
switch (cipher) {
|
||||
case SG_CIPHER_AES_CBC_PKCS5:
|
||||
gcry_cipher_setiv(hd, iv, iv_len);
|
||||
break;
|
||||
default:
|
||||
assert(FALSE);
|
||||
}
|
||||
|
||||
plaintext_len = ciphertext_len;
|
||||
plaintext = malloc(plaintext_len);
|
||||
gcry_cipher_decrypt(hd, plaintext, plaintext_len, ciphertext, ciphertext_len);
|
||||
|
||||
switch (cipher) {
|
||||
case SG_CIPHER_AES_CBC_PKCS5:
|
||||
padding = plaintext[plaintext_len - 1];
|
||||
break;
|
||||
default:
|
||||
assert(FALSE);
|
||||
}
|
||||
|
||||
int i;
|
||||
for (i = 0; i < padding; i++) {
|
||||
if (plaintext[plaintext_len - 1 - i] != padding) {
|
||||
ret = SG_ERR_UNKNOWN;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
*output = signal_buffer_create(plaintext, plaintext_len - padding);
|
||||
|
||||
out:
|
||||
free(plaintext);
|
||||
|
||||
gcry_cipher_close(hd);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
aes128gcm_encrypt(unsigned char *ciphertext, size_t *ciphertext_len, unsigned char *tag, size_t *tag_len, const unsigned char *const plaintext, size_t plaintext_len, const unsigned char *const iv, const unsigned char *const key)
|
||||
{
|
||||
gcry_error_t res;
|
||||
gcry_cipher_hd_t hd;
|
||||
|
||||
res = gcry_cipher_open(&hd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_GCM, GCRY_CIPHER_SECURE);
|
||||
if (res != GPG_ERR_NO_ERROR) {
|
||||
goto out;
|
||||
}
|
||||
res = gcry_cipher_setkey(hd, key, AES128_GCM_KEY_LENGTH);
|
||||
if (res != GPG_ERR_NO_ERROR) {
|
||||
goto out;
|
||||
}
|
||||
res = gcry_cipher_setiv(hd, iv, AES128_GCM_IV_LENGTH);
|
||||
if (res != GPG_ERR_NO_ERROR) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
res = gcry_cipher_encrypt(hd, ciphertext, *ciphertext_len, plaintext, plaintext_len);
|
||||
if (res != GPG_ERR_NO_ERROR) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
res = gcry_cipher_gettag(hd, tag, *tag_len);
|
||||
if (res != GPG_ERR_NO_ERROR) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
gcry_cipher_close(hd);
|
||||
return res;
|
||||
}
|
||||
|
||||
int
|
||||
aes128gcm_decrypt(unsigned char *plaintext, size_t *plaintext_len, const unsigned char *const ciphertext, size_t ciphertext_len, const unsigned char *const iv, const unsigned char *const key, const unsigned char *const tag)
|
||||
{
|
||||
gcry_error_t res;
|
||||
gcry_cipher_hd_t hd;
|
||||
|
||||
res = gcry_cipher_open(&hd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_GCM, GCRY_CIPHER_SECURE);
|
||||
if (res != GPG_ERR_NO_ERROR) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
res = gcry_cipher_setkey(hd, key, AES128_GCM_KEY_LENGTH);
|
||||
if (res != GPG_ERR_NO_ERROR) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
res = gcry_cipher_setiv(hd, iv, AES128_GCM_IV_LENGTH);
|
||||
if (res != GPG_ERR_NO_ERROR) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
res = gcry_cipher_decrypt(hd, plaintext, *plaintext_len, ciphertext, ciphertext_len);
|
||||
if (res != GPG_ERR_NO_ERROR) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
res = gcry_cipher_checktag(hd, tag, AES128_GCM_TAG_LENGTH);
|
||||
if (res != GPG_ERR_NO_ERROR) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
gcry_cipher_close(hd);
|
||||
return res;
|
||||
}
|
148
src/omemo/crypto.h
Normal file
148
src/omemo/crypto.h
Normal file
@ -0,0 +1,148 @@
|
||||
#include <signal/signal_protocol_types.h>
|
||||
|
||||
#define AES128_GCM_KEY_LENGTH 16
|
||||
#define AES128_GCM_IV_LENGTH 16
|
||||
#define AES128_GCM_TAG_LENGTH 16
|
||||
|
||||
int omemo_crypto_init(void);
|
||||
/**
|
||||
* Callback for a secure random number generator.
|
||||
* This function shall fill the provided buffer with random bytes.
|
||||
*
|
||||
* @param data pointer to the output buffer
|
||||
* @param len size of the output buffer
|
||||
* @return 0 on success, negative on failure
|
||||
*/
|
||||
int omemo_random_func(uint8_t *data, size_t len, void *user_data);
|
||||
|
||||
/**
|
||||
* Callback for an HMAC-SHA256 implementation.
|
||||
* This function shall initialize an HMAC context with the provided key.
|
||||
*
|
||||
* @param hmac_context private HMAC context pointer
|
||||
* @param key pointer to the key
|
||||
* @param key_len length of the key
|
||||
* @return 0 on success, negative on failure
|
||||
*/
|
||||
int omemo_hmac_sha256_init_func(void **hmac_context, const uint8_t *key, size_t key_len, void *user_data);
|
||||
|
||||
/**
|
||||
* Callback for an HMAC-SHA256 implementation.
|
||||
* This function shall update the HMAC context with the provided data
|
||||
*
|
||||
* @param hmac_context private HMAC context pointer
|
||||
* @param data pointer to the data
|
||||
* @param data_len length of the data
|
||||
* @return 0 on success, negative on failure
|
||||
*/
|
||||
int omemo_hmac_sha256_update_func(void *hmac_context, const uint8_t *data, size_t data_len, void *user_data);
|
||||
|
||||
/**
|
||||
* Callback for an HMAC-SHA256 implementation.
|
||||
* This function shall finalize an HMAC calculation and populate the output
|
||||
* buffer with the result.
|
||||
*
|
||||
* @param hmac_context private HMAC context pointer
|
||||
* @param output buffer to be allocated and populated with the result
|
||||
* @return 0 on success, negative on failure
|
||||
*/
|
||||
int omemo_hmac_sha256_final_func(void *hmac_context, signal_buffer **output, void *user_data);
|
||||
|
||||
/**
|
||||
* Callback for an HMAC-SHA256 implementation.
|
||||
* This function shall free the private context allocated in
|
||||
* hmac_sha256_init_func.
|
||||
*
|
||||
* @param hmac_context private HMAC context pointer
|
||||
*/
|
||||
void omemo_hmac_sha256_cleanup_func(void *hmac_context, void *user_data);
|
||||
|
||||
/**
|
||||
* Callback for a SHA512 message digest implementation.
|
||||
* This function shall initialize a digest context.
|
||||
*
|
||||
* @param digest_context private digest context pointer
|
||||
* @return 0 on success, negative on failure
|
||||
*/
|
||||
int omemo_sha512_digest_init_func(void **digest_context, void *user_data);
|
||||
|
||||
/**
|
||||
* Callback for a SHA512 message digest implementation.
|
||||
* This function shall update the digest context with the provided data.
|
||||
*
|
||||
* @param digest_context private digest context pointer
|
||||
* @param data pointer to the data
|
||||
* @param data_len length of the data
|
||||
* @return 0 on success, negative on failure
|
||||
*/
|
||||
int omemo_sha512_digest_update_func(void *digest_context, const uint8_t *data, size_t data_len, void *user_data);
|
||||
|
||||
/**
|
||||
* Callback for a SHA512 message digest implementation.
|
||||
* This function shall finalize the digest calculation, populate the
|
||||
* output buffer with the result, and prepare the context for reuse.
|
||||
*
|
||||
* @param digest_context private digest context pointer
|
||||
* @param output buffer to be allocated and populated with the result
|
||||
* @return 0 on success, negative on failure
|
||||
*/
|
||||
int omemo_sha512_digest_final_func(void *digest_context, signal_buffer **output, void *user_data);
|
||||
|
||||
/**
|
||||
* Callback for a SHA512 message digest implementation.
|
||||
* This function shall free the private context allocated in
|
||||
* sha512_digest_init_func.
|
||||
*
|
||||
* @param digest_context private digest context pointer
|
||||
*/
|
||||
void omemo_sha512_digest_cleanup_func(void *digest_context, void *user_data);
|
||||
|
||||
/**
|
||||
* Callback for an AES encryption implementation.
|
||||
*
|
||||
* @param output buffer to be allocated and populated with the ciphertext
|
||||
* @param cipher specific cipher variant to use, either SG_CIPHER_AES_CTR_NOPADDING or SG_CIPHER_AES_CBC_PKCS5
|
||||
* @param key the encryption key
|
||||
* @param key_len length of the encryption key
|
||||
* @param iv the initialization vector
|
||||
* @param iv_len length of the initialization vector
|
||||
* @param plaintext the plaintext to encrypt
|
||||
* @param plaintext_len length of the plaintext
|
||||
* @return 0 on success, negative on failure
|
||||
*/
|
||||
int omemo_encrypt_func(signal_buffer **output,
|
||||
int cipher,
|
||||
const uint8_t *key, size_t key_len,
|
||||
const uint8_t *iv, size_t iv_len,
|
||||
const uint8_t *plaintext, size_t plaintext_len,
|
||||
void *user_data);
|
||||
|
||||
/**
|
||||
* Callback for an AES decryption implementation.
|
||||
*
|
||||
* @param output buffer to be allocated and populated with the plaintext
|
||||
* @param cipher specific cipher variant to use, either SG_CIPHER_AES_CTR_NOPADDING or SG_CIPHER_AES_CBC_PKCS5
|
||||
* @param key the encryption key
|
||||
* @param key_len length of the encryption key
|
||||
* @param iv the initialization vector
|
||||
* @param iv_len length of the initialization vector
|
||||
* @param ciphertext the ciphertext to decrypt
|
||||
* @param ciphertext_len length of the ciphertext
|
||||
* @return 0 on success, negative on failure
|
||||
*/
|
||||
int omemo_decrypt_func(signal_buffer **output,
|
||||
int cipher,
|
||||
const uint8_t *key, size_t key_len,
|
||||
const uint8_t *iv, size_t iv_len,
|
||||
const uint8_t *ciphertext, size_t ciphertext_len,
|
||||
void *user_data);
|
||||
|
||||
int aes128gcm_encrypt(unsigned char *ciphertext, size_t *ciphertext_len,
|
||||
unsigned char *tag, size_t *tag_len,
|
||||
const unsigned char *const plaintext, size_t plaintext_len,
|
||||
const unsigned char *const iv, const unsigned char *const key);
|
||||
|
||||
int aes128gcm_decrypt(unsigned char *plaintext,
|
||||
size_t *plaintext_len, const unsigned char *const ciphertext,
|
||||
size_t ciphertext_len, const unsigned char *const iv,
|
||||
const unsigned char *const key, const unsigned char *const tag);
|
1410
src/omemo/omemo.c
Normal file
1410
src/omemo/omemo.c
Normal file
File diff suppressed because it is too large
Load Diff
55
src/omemo/omemo.h
Normal file
55
src/omemo/omemo.h
Normal file
@ -0,0 +1,55 @@
|
||||
#include <glib.h>
|
||||
|
||||
#include "ui/ui.h"
|
||||
#include "config/account.h"
|
||||
|
||||
#define OMEMO_ERR_UNSUPPORTED_CRYPTO -10000
|
||||
#define OMEMO_ERR_GCRYPT -20000
|
||||
|
||||
typedef struct omemo_context_t omemo_context;
|
||||
|
||||
typedef struct omemo_key {
|
||||
unsigned char *data;
|
||||
size_t length;
|
||||
gboolean prekey;
|
||||
uint32_t device_id;
|
||||
uint32_t id;
|
||||
} omemo_key_t;
|
||||
|
||||
void omemo_init(void);
|
||||
void omemo_on_connect(ProfAccount *account);
|
||||
void omemo_on_disconnect(void);
|
||||
void omemo_generate_crypto_materials(ProfAccount *account);
|
||||
void omemo_key_free(omemo_key_t *key);
|
||||
void omemo_publish_crypto_materials(void);
|
||||
|
||||
uint32_t omemo_device_id(void);
|
||||
void omemo_identity_key(unsigned char **output, size_t *length);
|
||||
void omemo_signed_prekey(unsigned char **output, size_t *length);
|
||||
void omemo_signed_prekey_signature(unsigned char **output, size_t *length);
|
||||
void omemo_prekeys(GList **prekeys, GList **ids, GList **lengths);
|
||||
void omemo_set_device_list(const char *const jid, GList * device_list);
|
||||
GKeyFile *omemo_identity_keyfile(void);
|
||||
void omemo_identity_keyfile_save(void);
|
||||
GKeyFile *omemo_trust_keyfile(void);
|
||||
void omemo_trust_keyfile_save(void);
|
||||
GKeyFile *omemo_sessions_keyfile(void);
|
||||
void omemo_sessions_keyfile_save(void);
|
||||
char *omemo_format_fingerprint(const char *const fingerprint);
|
||||
char *omemo_own_fingerprint(gboolean formatted);
|
||||
void omemo_trust(const char *const jid, const char *const fingerprint);
|
||||
void omemo_untrust(const char *const jid, const char *const fingerprint);
|
||||
GList *omemo_known_device_identities(const char *const jid);
|
||||
gboolean omemo_is_trusted_jid(const char *const jid);
|
||||
gboolean omemo_is_trusted_identity(const char *const jid, const char *const fingerprint);
|
||||
char *omemo_fingerprint_autocomplete(const char *const search_str, gboolean previous);
|
||||
void omemo_fingerprint_autocomplete_reset(void);
|
||||
|
||||
void omemo_start_sessions(void);
|
||||
void omemo_start_session(const char *const barejid);
|
||||
void omemo_start_muc_sessions(const char *const roomjid);
|
||||
void omemo_start_device_session(const char *const jid, uint32_t device_id, GList *prekeys, uint32_t signed_prekey_id, const unsigned char *const signed_prekey, size_t signed_prekey_len, const unsigned char *const signature, size_t signature_len, const unsigned char *const identity_key, size_t identity_key_len);
|
||||
|
||||
gboolean omemo_loaded(void);
|
||||
char * omemo_on_message_send(ProfWin *win, const char *const message, gboolean request_receipt, gboolean muc);
|
||||
char * omemo_on_message_recv(const char *const from, uint32_t sid, const unsigned char *const iv, size_t iv_len, GList *keys, const unsigned char *const payload, size_t payload_len, gboolean muc);
|
382
src/omemo/store.c
Normal file
382
src/omemo/store.c
Normal file
@ -0,0 +1,382 @@
|
||||
#include <glib.h>
|
||||
#include <signal/signal_protocol.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "omemo/omemo.h"
|
||||
#include "omemo/store.h"
|
||||
|
||||
GHashTable *
|
||||
session_store_new(void)
|
||||
{
|
||||
return g_hash_table_new_full(g_str_hash, g_str_equal, free, NULL);
|
||||
}
|
||||
|
||||
GHashTable *
|
||||
pre_key_store_new(void)
|
||||
{
|
||||
return g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)signal_buffer_free);
|
||||
}
|
||||
|
||||
GHashTable *
|
||||
signed_pre_key_store_new(void)
|
||||
{
|
||||
return g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)signal_buffer_free);
|
||||
}
|
||||
|
||||
void
|
||||
identity_key_store_new(identity_key_store_t *identity_key_store)
|
||||
{
|
||||
identity_key_store->trusted = g_hash_table_new_full(g_str_hash, g_str_equal, free, (GDestroyNotify)signal_buffer_free);
|
||||
identity_key_store->private = NULL;
|
||||
identity_key_store->public = NULL;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBSIGNAL_LT_2_3_2
|
||||
int
|
||||
load_session(signal_buffer **record, const signal_protocol_address *address,
|
||||
void *user_data)
|
||||
#else
|
||||
int
|
||||
load_session(signal_buffer **record, signal_buffer **user_record,
|
||||
const signal_protocol_address *address, void *user_data)
|
||||
#endif
|
||||
{
|
||||
GHashTable *session_store = (GHashTable *)user_data;
|
||||
GHashTable *device_store = NULL;
|
||||
|
||||
device_store = g_hash_table_lookup(session_store, address->name);
|
||||
if (!device_store) {
|
||||
*record = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
signal_buffer *original = g_hash_table_lookup(device_store, GINT_TO_POINTER(address->device_id));
|
||||
if (!original) {
|
||||
*record = NULL;
|
||||
return 0;
|
||||
}
|
||||
*record = signal_buffer_copy(original);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
get_sub_device_sessions(signal_int_list **sessions, const char *name,
|
||||
size_t name_len, void *user_data)
|
||||
{
|
||||
GHashTable *session_store = (GHashTable *)user_data;
|
||||
GHashTable *device_store = NULL;
|
||||
GHashTableIter iter;
|
||||
gpointer key, value;
|
||||
|
||||
device_store = g_hash_table_lookup(session_store, name);
|
||||
if (!device_store) {
|
||||
return SG_SUCCESS;
|
||||
}
|
||||
|
||||
*sessions = signal_int_list_alloc();
|
||||
g_hash_table_iter_init(&iter, device_store);
|
||||
while (g_hash_table_iter_next(&iter, &key, &value)) {
|
||||
signal_int_list_push_back(*sessions, GPOINTER_TO_INT(key));
|
||||
}
|
||||
|
||||
|
||||
return SG_SUCCESS;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBSIGNAL_LT_2_3_2
|
||||
int
|
||||
store_session(const signal_protocol_address *address, uint8_t *record,
|
||||
size_t record_len, void *user_data)
|
||||
#else
|
||||
int
|
||||
store_session(const signal_protocol_address *address,
|
||||
uint8_t *record, size_t record_len,
|
||||
uint8_t *user_record, size_t user_record_len,
|
||||
void *user_data)
|
||||
#endif
|
||||
{
|
||||
GHashTable *session_store = (GHashTable *)user_data;
|
||||
GHashTable *device_store = NULL;
|
||||
|
||||
device_store = g_hash_table_lookup(session_store, (void *)address->name);
|
||||
if (!device_store) {
|
||||
device_store = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)signal_buffer_free);
|
||||
g_hash_table_insert(session_store, strdup(address->name), device_store);
|
||||
}
|
||||
|
||||
signal_buffer *buffer = signal_buffer_create(record, record_len);
|
||||
g_hash_table_insert(device_store, GINT_TO_POINTER(address->device_id), buffer);
|
||||
|
||||
|
||||
char *record_b64 = g_base64_encode(record, record_len);
|
||||
char *device_id = g_strdup_printf("%d", address->device_id);
|
||||
g_key_file_set_string(omemo_sessions_keyfile(), address->name, device_id, record_b64);
|
||||
free(device_id);
|
||||
g_free(record_b64);
|
||||
|
||||
omemo_sessions_keyfile_save();
|
||||
|
||||
return SG_SUCCESS;
|
||||
}
|
||||
|
||||
int
|
||||
contains_session(const signal_protocol_address *address, void *user_data)
|
||||
{
|
||||
GHashTable *session_store = (GHashTable *)user_data;
|
||||
GHashTable *device_store = NULL;
|
||||
|
||||
device_store = g_hash_table_lookup(session_store, address->name);
|
||||
if (!device_store) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!g_hash_table_lookup(device_store, GINT_TO_POINTER(address->device_id))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
delete_session(const signal_protocol_address *address, void *user_data)
|
||||
{
|
||||
GHashTable *session_store = (GHashTable *)user_data;
|
||||
GHashTable *device_store = NULL;
|
||||
|
||||
device_store = g_hash_table_lookup(session_store, address->name);
|
||||
if (!device_store) {
|
||||
return SG_SUCCESS;
|
||||
}
|
||||
|
||||
return g_hash_table_remove(device_store, GINT_TO_POINTER(address->device_id));
|
||||
}
|
||||
|
||||
int
|
||||
delete_all_sessions(const char *name, size_t name_len, void *user_data)
|
||||
{
|
||||
GHashTable *session_store = (GHashTable *)user_data;
|
||||
GHashTable *device_store = NULL;
|
||||
|
||||
device_store = g_hash_table_lookup(session_store, name);
|
||||
if (!device_store) {
|
||||
return SG_SUCCESS;
|
||||
}
|
||||
|
||||
guint len = g_hash_table_size(device_store);
|
||||
g_hash_table_remove_all(device_store);
|
||||
return len;
|
||||
}
|
||||
|
||||
int
|
||||
load_pre_key(signal_buffer **record, uint32_t pre_key_id, void *user_data)
|
||||
{
|
||||
signal_buffer *original;
|
||||
GHashTable *pre_key_store = (GHashTable *)user_data;
|
||||
|
||||
original = g_hash_table_lookup(pre_key_store, GINT_TO_POINTER(pre_key_id));
|
||||
if (original == NULL) {
|
||||
return SG_ERR_INVALID_KEY_ID;
|
||||
}
|
||||
|
||||
*record = signal_buffer_copy(original);
|
||||
return SG_SUCCESS;
|
||||
}
|
||||
|
||||
int
|
||||
store_pre_key(uint32_t pre_key_id, uint8_t *record, size_t record_len,
|
||||
void *user_data)
|
||||
{
|
||||
GHashTable *pre_key_store = (GHashTable *)user_data;
|
||||
|
||||
signal_buffer *buffer = signal_buffer_create(record, record_len);
|
||||
g_hash_table_insert(pre_key_store, GINT_TO_POINTER(pre_key_id), buffer);
|
||||
|
||||
/* Long term storage */
|
||||
char *pre_key_id_str = g_strdup_printf("%d", pre_key_id);
|
||||
char *record_b64 = g_base64_encode(record, record_len);
|
||||
g_key_file_set_string(omemo_identity_keyfile(), OMEMO_STORE_GROUP_PREKEYS, pre_key_id_str, record_b64);
|
||||
g_free(pre_key_id_str);
|
||||
g_free(record_b64);
|
||||
|
||||
omemo_identity_keyfile_save();
|
||||
|
||||
return SG_SUCCESS;
|
||||
}
|
||||
|
||||
int
|
||||
contains_pre_key(uint32_t pre_key_id, void *user_data)
|
||||
{
|
||||
GHashTable *pre_key_store = (GHashTable *)user_data;
|
||||
|
||||
return g_hash_table_lookup(pre_key_store, GINT_TO_POINTER(pre_key_id)) != NULL;
|
||||
}
|
||||
|
||||
int
|
||||
remove_pre_key(uint32_t pre_key_id, void *user_data)
|
||||
{
|
||||
GHashTable *pre_key_store = (GHashTable *)user_data;
|
||||
|
||||
int ret = g_hash_table_remove(pre_key_store, GINT_TO_POINTER(pre_key_id));
|
||||
|
||||
/* Long term storage */
|
||||
char *pre_key_id_str = g_strdup_printf("%d", pre_key_id);
|
||||
g_key_file_remove_key(omemo_identity_keyfile(), OMEMO_STORE_GROUP_PREKEYS, pre_key_id_str, NULL);
|
||||
g_free(pre_key_id_str);
|
||||
|
||||
omemo_identity_keyfile_save();
|
||||
|
||||
if (ret > 0) {
|
||||
return SG_SUCCESS;
|
||||
} else {
|
||||
return SG_ERR_INVALID_KEY_ID;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
load_signed_pre_key(signal_buffer **record, uint32_t signed_pre_key_id,
|
||||
void *user_data)
|
||||
{
|
||||
signal_buffer *original;
|
||||
GHashTable *signed_pre_key_store = (GHashTable *)user_data;
|
||||
|
||||
original = g_hash_table_lookup(signed_pre_key_store, GINT_TO_POINTER(signed_pre_key_id));
|
||||
if (!original) {
|
||||
return SG_ERR_INVALID_KEY_ID;
|
||||
}
|
||||
|
||||
*record = signal_buffer_copy(original);
|
||||
return SG_SUCCESS;
|
||||
}
|
||||
|
||||
int
|
||||
store_signed_pre_key(uint32_t signed_pre_key_id, uint8_t *record,
|
||||
size_t record_len, void *user_data)
|
||||
{
|
||||
GHashTable *signed_pre_key_store = (GHashTable *)user_data;
|
||||
|
||||
signal_buffer *buffer = signal_buffer_create(record, record_len);
|
||||
g_hash_table_insert(signed_pre_key_store, GINT_TO_POINTER(signed_pre_key_id), buffer);
|
||||
|
||||
/* Long term storage */
|
||||
char *signed_pre_key_id_str = g_strdup_printf("%d", signed_pre_key_id);
|
||||
char *record_b64 = g_base64_encode(record, record_len);
|
||||
g_key_file_set_string(omemo_identity_keyfile(), OMEMO_STORE_GROUP_SIGNED_PREKEYS, signed_pre_key_id_str, record_b64);
|
||||
g_free(signed_pre_key_id_str);
|
||||
g_free(record_b64);
|
||||
|
||||
omemo_identity_keyfile_save();
|
||||
|
||||
return SG_SUCCESS;
|
||||
}
|
||||
|
||||
int
|
||||
contains_signed_pre_key(uint32_t signed_pre_key_id, void *user_data)
|
||||
{
|
||||
GHashTable *signed_pre_key_store = (GHashTable *)user_data;
|
||||
|
||||
return g_hash_table_lookup(signed_pre_key_store, GINT_TO_POINTER(signed_pre_key_id)) != NULL;
|
||||
}
|
||||
|
||||
int
|
||||
remove_signed_pre_key(uint32_t signed_pre_key_id, void *user_data)
|
||||
{
|
||||
GHashTable *signed_pre_key_store = (GHashTable *)user_data;
|
||||
|
||||
int ret = g_hash_table_remove(signed_pre_key_store, GINT_TO_POINTER(signed_pre_key_id));
|
||||
|
||||
/* Long term storage */
|
||||
char *signed_pre_key_id_str = g_strdup_printf("%d", signed_pre_key_id);
|
||||
g_key_file_remove_key(omemo_identity_keyfile(), OMEMO_STORE_GROUP_PREKEYS, signed_pre_key_id_str, NULL);
|
||||
g_free(signed_pre_key_id_str);
|
||||
|
||||
omemo_identity_keyfile_save();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
get_identity_key_pair(signal_buffer **public_data, signal_buffer **private_data,
|
||||
void *user_data)
|
||||
{
|
||||
identity_key_store_t *identity_key_store = (identity_key_store_t *)user_data;
|
||||
|
||||
*public_data = signal_buffer_copy(identity_key_store->public);
|
||||
*private_data = signal_buffer_copy(identity_key_store->private);
|
||||
|
||||
return SG_SUCCESS;
|
||||
}
|
||||
|
||||
int
|
||||
get_local_registration_id(void *user_data, uint32_t *registration_id)
|
||||
{
|
||||
identity_key_store_t *identity_key_store = (identity_key_store_t *)user_data;
|
||||
|
||||
*registration_id = identity_key_store->registration_id;
|
||||
|
||||
return SG_SUCCESS;
|
||||
}
|
||||
|
||||
int
|
||||
save_identity(const signal_protocol_address *address, uint8_t *key_data,
|
||||
size_t key_len, void *user_data)
|
||||
{
|
||||
identity_key_store_t *identity_key_store = (identity_key_store_t *)user_data;
|
||||
|
||||
signal_buffer *buffer = signal_buffer_create(key_data, key_len);
|
||||
|
||||
GHashTable *trusted = g_hash_table_lookup(identity_key_store->trusted, strdup(address->name));
|
||||
if (!trusted) {
|
||||
trusted = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, (GDestroyNotify)signal_buffer_free);
|
||||
g_hash_table_insert(identity_key_store->trusted, strdup(address->name), trusted);
|
||||
}
|
||||
g_hash_table_insert(trusted, GINT_TO_POINTER(address->device_id), buffer);
|
||||
|
||||
/* Long term storage */
|
||||
char *key_b64 = g_base64_encode(key_data, key_len);
|
||||
char *device_id = g_strdup_printf("%d", address->device_id);
|
||||
g_key_file_set_string(omemo_trust_keyfile(), address->name, strdup(device_id), key_b64);
|
||||
g_free(device_id);
|
||||
g_free(key_b64);
|
||||
|
||||
omemo_trust_keyfile_save();
|
||||
|
||||
return SG_SUCCESS;
|
||||
}
|
||||
|
||||
int
|
||||
is_trusted_identity(const signal_protocol_address *address, uint8_t *key_data,
|
||||
size_t key_len, void *user_data)
|
||||
{
|
||||
int ret;
|
||||
identity_key_store_t *identity_key_store = (identity_key_store_t *)user_data;
|
||||
|
||||
GHashTable *trusted = g_hash_table_lookup(identity_key_store->trusted, address->name);
|
||||
if (!trusted) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
signal_buffer *buffer = signal_buffer_create(key_data, key_len);
|
||||
signal_buffer *original = g_hash_table_lookup(trusted, GINT_TO_POINTER(address->device_id));
|
||||
|
||||
ret = original != NULL && signal_buffer_compare(buffer, original) == 0;
|
||||
|
||||
signal_buffer_free(buffer);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
store_sender_key(const signal_protocol_sender_key_name *sender_key_name,
|
||||
uint8_t *record, size_t record_len, uint8_t *user_record,
|
||||
size_t user_record_len, void *user_data)
|
||||
{
|
||||
return SG_SUCCESS;
|
||||
}
|
||||
|
||||
int
|
||||
load_sender_key(signal_buffer **record, signal_buffer **user_record,
|
||||
const signal_protocol_sender_key_name *sender_key_name,
|
||||
void *user_data)
|
||||
{
|
||||
return SG_SUCCESS;
|
||||
}
|
250
src/omemo/store.h
Normal file
250
src/omemo/store.h
Normal file
@ -0,0 +1,250 @@
|
||||
#include <signal/signal_protocol.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#define OMEMO_STORE_GROUP_IDENTITY "identity"
|
||||
#define OMEMO_STORE_GROUP_PREKEYS "prekeys"
|
||||
#define OMEMO_STORE_GROUP_SIGNED_PREKEYS "signed_prekeys"
|
||||
#define OMEMO_STORE_KEY_DEVICE_ID "device_id"
|
||||
#define OMEMO_STORE_KEY_REGISTRATION_ID "registration_id"
|
||||
#define OMEMO_STORE_KEY_IDENTITY_KEY_PUBLIC "identity_key_public"
|
||||
#define OMEMO_STORE_KEY_IDENTITY_KEY_PRIVATE "identity_key_private"
|
||||
|
||||
typedef struct {
|
||||
signal_buffer *public;
|
||||
signal_buffer *private;
|
||||
uint32_t registration_id;
|
||||
GHashTable *trusted;
|
||||
} identity_key_store_t;
|
||||
|
||||
GHashTable * session_store_new(void);
|
||||
GHashTable * pre_key_store_new(void);
|
||||
GHashTable * signed_pre_key_store_new(void);
|
||||
void identity_key_store_new(identity_key_store_t *identity_key_store);
|
||||
|
||||
/**
|
||||
* Returns a copy of the serialized session record corresponding to the
|
||||
* provided recipient ID + device ID tuple.
|
||||
*
|
||||
* @param record pointer to a freshly allocated buffer containing the
|
||||
* serialized session record. Unset if no record was found.
|
||||
* The Signal Protocol library is responsible for freeing this buffer.
|
||||
* @param address the address of the remote client
|
||||
* @return 1 if the session was loaded, 0 if the session was not found, negative on failure
|
||||
*/
|
||||
#ifdef HAVE_LIBSIGNAL_LT_2_3_2
|
||||
int load_session(signal_buffer **record, const signal_protocol_address *address, void *user_data);
|
||||
#else
|
||||
int load_session(signal_buffer **record, signal_buffer **user_record, const signal_protocol_address *address, void *user_data);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Returns all known devices with active sessions for a recipient
|
||||
*
|
||||
* @param pointer to an array that will be allocated and populated with the result
|
||||
* @param name the name of the remote client
|
||||
* @param name_len the length of the name
|
||||
* @return size of the sessions array, or negative on failure
|
||||
*/
|
||||
int get_sub_device_sessions(signal_int_list **sessions, const char *name, size_t name_len, void *user_data);
|
||||
|
||||
/**
|
||||
* Commit to storage the session record for a given
|
||||
* recipient ID + device ID tuple.
|
||||
*
|
||||
* @param address the address of the remote client
|
||||
* @param record pointer to a buffer containing the serialized session
|
||||
* record for the remote client
|
||||
* @param record_len length of the serialized session record
|
||||
* @return 0 on success, negative on failure
|
||||
*/
|
||||
#ifdef HAVE_LIBSIGNAL_LT_2_3_2
|
||||
int store_session(const signal_protocol_address *address, uint8_t *record, size_t record_len, void *user_data);
|
||||
#else
|
||||
int store_session(const signal_protocol_address *address, uint8_t *record, size_t record_len, uint8_t *user_record, size_t user_record_len, void *user_data);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Determine whether there is a committed session record for a
|
||||
* recipient ID + device ID tuple.
|
||||
*
|
||||
* @param address the address of the remote client
|
||||
* @return 1 if a session record exists, 0 otherwise.
|
||||
*/
|
||||
int contains_session(const signal_protocol_address *address, void *user_data);
|
||||
|
||||
/**
|
||||
* Remove a session record for a recipient ID + device ID tuple.
|
||||
*
|
||||
* @param address the address of the remote client
|
||||
* @return 1 if a session was deleted, 0 if a session was not deleted, negative on error
|
||||
*/
|
||||
int delete_session(const signal_protocol_address *address, void *user_data);
|
||||
|
||||
/**
|
||||
* Remove the session records corresponding to all devices of a recipient ID.
|
||||
*
|
||||
* @param name the name of the remote client
|
||||
* @param name_len the length of the name
|
||||
* @return the number of deleted sessions on success, negative on failure
|
||||
*/
|
||||
int delete_all_sessions(const char *name, size_t name_len, void *user_data);
|
||||
|
||||
/**
|
||||
* Load a local serialized PreKey record.
|
||||
*
|
||||
* @param record pointer to a newly allocated buffer containing the record,
|
||||
* if found. Unset if no record was found.
|
||||
* The Signal Protocol library is responsible for freeing this buffer.
|
||||
* @param pre_key_id the ID of the local serialized PreKey record
|
||||
* @retval SG_SUCCESS if the key was found
|
||||
* @retval SG_ERR_INVALID_KEY_ID if the key could not be found
|
||||
*/
|
||||
int load_pre_key(signal_buffer **record, uint32_t pre_key_id, void *user_data);
|
||||
|
||||
/**
|
||||
* Store a local serialized PreKey record.
|
||||
*
|
||||
* @param pre_key_id the ID of the PreKey record to store.
|
||||
* @param record pointer to a buffer containing the serialized record
|
||||
* @param record_len length of the serialized record
|
||||
* @return 0 on success, negative on failure
|
||||
*/
|
||||
int store_pre_key(uint32_t pre_key_id, uint8_t *record, size_t record_len, void *user_data);
|
||||
|
||||
/**
|
||||
* Determine whether there is a committed PreKey record matching the
|
||||
* provided ID.
|
||||
*
|
||||
* @param pre_key_id A PreKey record ID.
|
||||
* @return 1 if the store has a record for the PreKey ID, 0 otherwise
|
||||
*/
|
||||
int contains_pre_key(uint32_t pre_key_id, void *user_data);
|
||||
|
||||
/**
|
||||
* Delete a PreKey record from local storage.
|
||||
*
|
||||
* @param pre_key_id The ID of the PreKey record to remove.
|
||||
* @return 0 on success, negative on failure
|
||||
*/
|
||||
int remove_pre_key(uint32_t pre_key_id, void *user_data);
|
||||
|
||||
/**
|
||||
* Load a local serialized signed PreKey record.
|
||||
*
|
||||
* @param record pointer to a newly allocated buffer containing the record,
|
||||
* if found. Unset if no record was found.
|
||||
* The Signal Protocol library is responsible for freeing this buffer.
|
||||
* @param signed_pre_key_id the ID of the local signed PreKey record
|
||||
* @retval SG_SUCCESS if the key was found
|
||||
* @retval SG_ERR_INVALID_KEY_ID if the key could not be found
|
||||
*/
|
||||
int load_signed_pre_key(signal_buffer **record, uint32_t signed_pre_key_id, void *user_data);
|
||||
|
||||
/**
|
||||
* Store a local serialized signed PreKey record.
|
||||
*
|
||||
* @param signed_pre_key_id the ID of the signed PreKey record to store
|
||||
* @param record pointer to a buffer containing the serialized record
|
||||
* @param record_len length of the serialized record
|
||||
* @return 0 on success, negative on failure
|
||||
*/
|
||||
int store_signed_pre_key(uint32_t signed_pre_key_id, uint8_t *record, size_t record_len, void *user_data);
|
||||
|
||||
/**
|
||||
* Determine whether there is a committed signed PreKey record matching
|
||||
* the provided ID.
|
||||
*
|
||||
* @param signed_pre_key_id A signed PreKey record ID.
|
||||
* @return 1 if the store has a record for the signed PreKey ID, 0 otherwise
|
||||
*/
|
||||
int contains_signed_pre_key(uint32_t signed_pre_key_id, void *user_data);
|
||||
|
||||
/**
|
||||
* Delete a SignedPreKeyRecord from local storage.
|
||||
*
|
||||
* @param signed_pre_key_id The ID of the signed PreKey record to remove.
|
||||
* @return 0 on success, negative on failure
|
||||
*/
|
||||
int remove_signed_pre_key(uint32_t signed_pre_key_id, void *user_data);
|
||||
|
||||
/**
|
||||
* Get the local client's identity key pair.
|
||||
*
|
||||
* @param public_data pointer to a newly allocated buffer containing the
|
||||
* public key, if found. Unset if no record was found.
|
||||
* The Signal Protocol library is responsible for freeing this buffer.
|
||||
* @param private_data pointer to a newly allocated buffer containing the
|
||||
* private key, if found. Unset if no record was found.
|
||||
* The Signal Protocol library is responsible for freeing this buffer.
|
||||
* @return 0 on success, negative on failure
|
||||
*/
|
||||
int get_identity_key_pair(signal_buffer **public_data, signal_buffer **private_data, void *user_data);
|
||||
|
||||
/**
|
||||
* Return the local client's registration ID.
|
||||
*
|
||||
* Clients should maintain a registration ID, a random number
|
||||
* between 1 and 16380 that's generated once at install time.
|
||||
*
|
||||
* @param registration_id pointer to be set to the local client's
|
||||
* registration ID, if it was successfully retrieved.
|
||||
* @return 0 on success, negative on failure
|
||||
*/
|
||||
int get_local_registration_id(void *user_data, uint32_t *registration_id);
|
||||
|
||||
/**
|
||||
* Save a remote client's identity key
|
||||
* <p>
|
||||
* Store a remote client's identity key as trusted.
|
||||
* The value of key_data may be null. In this case remove the key data
|
||||
* from the identity store, but retain any metadata that may be kept
|
||||
* alongside it.
|
||||
*
|
||||
* @param address the address of the remote client
|
||||
* @param key_data Pointer to the remote client's identity key, may be null
|
||||
* @param key_len Length of the remote client's identity key
|
||||
* @return 0 on success, negative on failure
|
||||
*/
|
||||
int save_identity(const signal_protocol_address *address, uint8_t *key_data, size_t key_len, void *user_data);
|
||||
|
||||
/**
|
||||
* Verify a remote client's identity key.
|
||||
*
|
||||
* Determine whether a remote client's identity is trusted. Convention is
|
||||
* that the TextSecure protocol is 'trust on first use.' This means that
|
||||
* an identity key is considered 'trusted' if there is no entry for the recipient
|
||||
* in the local store, or if it matches the saved key for a recipient in the local
|
||||
* store. Only if it mismatches an entry in the local store is it considered
|
||||
* 'untrusted.'
|
||||
*
|
||||
* @param address the address of the remote client
|
||||
* @param identityKey The identity key to verify.
|
||||
* @param key_data Pointer to the identity key to verify
|
||||
* @param key_len Length of the identity key to verify
|
||||
* @return 1 if trusted, 0 if untrusted, negative on failure
|
||||
*/
|
||||
int is_trusted_identity(const signal_protocol_address *address, uint8_t *key_data, size_t key_len, void *user_data);
|
||||
|
||||
/**
|
||||
* Store a serialized sender key record for a given
|
||||
* (groupId + senderId + deviceId) tuple.
|
||||
*
|
||||
* @param sender_key_name the (groupId + senderId + deviceId) tuple
|
||||
* @param record pointer to a buffer containing the serialized record
|
||||
* @param record_len length of the serialized record
|
||||
* @return 0 on success, negative on failure
|
||||
*/
|
||||
int store_sender_key(const signal_protocol_sender_key_name *sender_key_name, uint8_t *record, size_t record_len, uint8_t *user_record, size_t user_record_len, void *user_data);
|
||||
|
||||
/**
|
||||
* Returns a copy of the sender key record corresponding to the
|
||||
* (groupId + senderId + deviceId) tuple.
|
||||
*
|
||||
* @param record pointer to a newly allocated buffer containing the record,
|
||||
* if found. Unset if no record was found.
|
||||
* The Signal Protocol library is responsible for freeing this buffer.
|
||||
* @param sender_key_name the (groupId + senderId + deviceId) tuple
|
||||
* @return 1 if the record was loaded, 0 if the record was not found, negative on failure
|
||||
*/
|
||||
int load_sender_key(signal_buffer **record, signal_buffer **user_record, const signal_protocol_sender_key_name *sender_key_name, void *user_data);
|
@ -473,7 +473,7 @@ api_settings_int_set(const char *const group, const char *const key, int value)
|
||||
void
|
||||
api_incoming_message(const char *const barejid, const char *const resource, const char *const message)
|
||||
{
|
||||
sv_ev_incoming_message((char*)barejid, (char*)resource, (char*)message, NULL, NULL);
|
||||
sv_ev_incoming_message((char*)barejid, (char*)resource, (char*)message, NULL, NULL, FALSE);
|
||||
|
||||
// TODO handle all states
|
||||
sv_ev_activity((char*)barejid, (char*)resource, FALSE);
|
||||
|
@ -80,6 +80,10 @@
|
||||
#include "pgp/gpg.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OMEMO
|
||||
#include "omemo/omemo.h"
|
||||
#endif
|
||||
|
||||
static void _init(char *log_level);
|
||||
static void _shutdown(void);
|
||||
static void _connect_default(const char * const account);
|
||||
@ -196,6 +200,9 @@ _init(char *log_level)
|
||||
#endif
|
||||
#ifdef HAVE_LIBGPGME
|
||||
p_gpg_init();
|
||||
#endif
|
||||
#ifdef HAVE_OMEMO
|
||||
omemo_init();
|
||||
#endif
|
||||
atexit(_shutdown);
|
||||
plugins_init();
|
||||
|
@ -305,6 +305,8 @@ chatwin_outgoing_msg(ProfChatWin *chatwin, const char *const message, char *id,
|
||||
enc_char = prefs_get_otr_char();
|
||||
} else if (enc_mode == PROF_MSG_PGP) {
|
||||
enc_char = prefs_get_pgp_char();
|
||||
} else if (enc_mode == PROF_MSG_OMEMO) {
|
||||
enc_char = prefs_get_omemo_char();
|
||||
}
|
||||
|
||||
if (request_receipt && id) {
|
||||
@ -322,6 +324,8 @@ chatwin_outgoing_carbon(ProfChatWin *chatwin, const char *const message, prof_en
|
||||
char enc_char = '-';
|
||||
if (enc_mode == PROF_MSG_PGP) {
|
||||
enc_char = prefs_get_pgp_char();
|
||||
} else if (enc_mode == PROF_MSG_OMEMO) {
|
||||
enc_char = prefs_get_omemo_char();
|
||||
}
|
||||
|
||||
ProfWin *window = (ProfWin*)chatwin;
|
||||
|
@ -1998,6 +1998,28 @@ cons_show_pgp_prefs(void)
|
||||
cons_alert();
|
||||
}
|
||||
|
||||
void
|
||||
cons_show_omemo_prefs(void)
|
||||
{
|
||||
cons_show("OMEMO preferences:");
|
||||
cons_show("");
|
||||
|
||||
char *log_value = prefs_get_string(PREF_OMEMO_LOG);
|
||||
if (strcmp(log_value, "on") == 0) {
|
||||
cons_show("OMEMO logging (/omemo log) : ON");
|
||||
} else if (strcmp(log_value, "off") == 0) {
|
||||
cons_show("OMEMO logging (/omemo log) : OFF");
|
||||
} else {
|
||||
cons_show("OMEMO logging (/omemo log) : Redacted");
|
||||
}
|
||||
prefs_free_string(log_value);
|
||||
|
||||
char ch = prefs_get_omemo_char();
|
||||
cons_show("OMEMO char (/omemo char) : %c", ch);
|
||||
|
||||
cons_alert();
|
||||
}
|
||||
|
||||
void
|
||||
cons_show_themes(GSList *themes)
|
||||
{
|
||||
@ -2072,6 +2094,8 @@ cons_prefs(void)
|
||||
cons_show("");
|
||||
cons_show_pgp_prefs();
|
||||
cons_show("");
|
||||
cons_show_omemo_prefs();
|
||||
cons_show("");
|
||||
|
||||
cons_alert();
|
||||
}
|
||||
|
@ -478,29 +478,60 @@ _mucwin_print_triggers(ProfWin *window, const char *const message, GList *trigge
|
||||
}
|
||||
|
||||
void
|
||||
mucwin_message(ProfMucWin *mucwin, const char *const nick, const char *const message, GSList *mentions, GList *triggers)
|
||||
mucwin_outgoing_msg(ProfMucWin *mucwin, const char *const message, const char *const id, prof_enc_t enc_mode)
|
||||
{
|
||||
assert(mucwin != NULL);
|
||||
|
||||
g_hash_table_insert(mucwin->sent_messages, strdup(id), NULL);
|
||||
|
||||
ProfWin *window = (ProfWin*)mucwin;
|
||||
char *mynick = muc_nick(mucwin->roomjid);
|
||||
|
||||
char ch = '-';
|
||||
if (mucwin->message_char) {
|
||||
ch = mucwin->message_char[0];
|
||||
} else if (enc_mode == PROF_MSG_OTR) {
|
||||
ch = prefs_get_otr_char();
|
||||
} else if (enc_mode == PROF_MSG_PGP) {
|
||||
ch = prefs_get_pgp_char();
|
||||
} else if (enc_mode == PROF_MSG_OMEMO) {
|
||||
ch = prefs_get_omemo_char();
|
||||
}
|
||||
|
||||
if (g_strcmp0(nick, mynick) != 0) {
|
||||
if (g_slist_length(mentions) > 0) {
|
||||
_mucwin_print_mention(window, message, nick, mynick, mentions, &ch);
|
||||
} else if (triggers) {
|
||||
win_print_them(window, THEME_ROOMTRIGGER, ch, nick);
|
||||
_mucwin_print_triggers(window, message, triggers);
|
||||
} else {
|
||||
win_println_them_message(window, ch, nick, "%s", message);
|
||||
}
|
||||
win_println_me_message(window, ch, mynick, "%s", message);
|
||||
}
|
||||
|
||||
void
|
||||
mucwin_incoming_msg(ProfMucWin *mucwin, const char *const nick, const char *const message, const char *const id, GSList *mentions, GList *triggers, prof_enc_t enc_mode)
|
||||
{
|
||||
assert(mucwin != NULL);
|
||||
|
||||
if (g_hash_table_remove(mucwin->sent_messages, id)) {
|
||||
/* Ignore reflection messages */
|
||||
return;
|
||||
}
|
||||
|
||||
ProfWin *window = (ProfWin*)mucwin;
|
||||
char *mynick = muc_nick(mucwin->roomjid);
|
||||
|
||||
char ch = '-';
|
||||
if (mucwin->message_char) {
|
||||
ch = mucwin->message_char[0];
|
||||
} else if (enc_mode == PROF_MSG_OTR) {
|
||||
ch = prefs_get_otr_char();
|
||||
} else if (enc_mode == PROF_MSG_PGP) {
|
||||
ch = prefs_get_pgp_char();
|
||||
} else if (enc_mode == PROF_MSG_OMEMO) {
|
||||
ch = prefs_get_omemo_char();
|
||||
}
|
||||
|
||||
if (g_slist_length(mentions) > 0) {
|
||||
_mucwin_print_mention(window, message, nick, mynick, mentions, &ch);
|
||||
} else if (triggers) {
|
||||
win_print_them(window, THEME_ROOMTRIGGER, ch, nick);
|
||||
_mucwin_print_triggers(window, message, triggers);
|
||||
} else {
|
||||
win_println_me_message(window, ch, mynick, "%s", message);
|
||||
win_println_them_message(window, ch, nick, "%s", message);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -321,6 +321,21 @@ _show_muc_privacy(ProfMucWin *mucwin)
|
||||
int bracket_attrs = theme_attrs(THEME_TITLE_BRACKET);
|
||||
int encrypted_attrs = theme_attrs(THEME_TITLE_ENCRYPTED);
|
||||
|
||||
if (mucwin->is_omemo) {
|
||||
wprintw(win, " ");
|
||||
wattron(win, bracket_attrs);
|
||||
wprintw(win, "[");
|
||||
wattroff(win, bracket_attrs);
|
||||
wattron(win, encrypted_attrs);
|
||||
wprintw(win, "OMEMO");
|
||||
wattroff(win, encrypted_attrs);
|
||||
wattron(win, bracket_attrs);
|
||||
wprintw(win, "]");
|
||||
wattroff(win, bracket_attrs);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (mucwin->enctext) {
|
||||
wprintw(win, " ");
|
||||
wattron(win, bracket_attrs);
|
||||
@ -421,6 +436,21 @@ _show_privacy(ProfChatWin *chatwin)
|
||||
return;
|
||||
}
|
||||
|
||||
if (chatwin->is_omemo) {
|
||||
wprintw(win, " ");
|
||||
wattron(win, bracket_attrs);
|
||||
wprintw(win, "[");
|
||||
wattroff(win, bracket_attrs);
|
||||
wattron(win, encrypted_attrs);
|
||||
wprintw(win, "OMEMO");
|
||||
wattroff(win, encrypted_attrs);
|
||||
wattron(win, bracket_attrs);
|
||||
wprintw(win, "]");
|
||||
wattroff(win, bracket_attrs);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (prefs_get_boolean(PREF_ENC_WARN)) {
|
||||
wprintw(win, " ");
|
||||
wattron(win, bracket_attrs);
|
||||
|
@ -56,7 +56,8 @@
|
||||
typedef enum {
|
||||
PROF_MSG_PLAIN,
|
||||
PROF_MSG_OTR,
|
||||
PROF_MSG_PGP
|
||||
PROF_MSG_PGP,
|
||||
PROF_MSG_OMEMO
|
||||
} prof_enc_t;
|
||||
|
||||
// core UI
|
||||
@ -161,7 +162,8 @@ void mucwin_occupant_role_and_affiliation_change(ProfMucWin *mucwin, const char
|
||||
const char *const role, const char *const affiliation, const char *const actor, const char *const reason);
|
||||
void mucwin_roster(ProfMucWin *mucwin, GList *occupants, const char *const presence);
|
||||
void mucwin_history(ProfMucWin *mucwin, const char *const nick, GDateTime *timestamp, const char *const message);
|
||||
void mucwin_message(ProfMucWin *mucwin, const char *const nick, const char *const message, GSList *mentions, GList *triggers);
|
||||
void mucwin_outgoing_msg(ProfMucWin *mucwin, const char *const message, const char *const id, prof_enc_t enc_mode);
|
||||
void mucwin_incoming_msg(ProfMucWin *mucwin, const char *const nick, const char *const message, const char *const id, GSList *mentions, GList *triggers, prof_enc_t enc_mode);
|
||||
void mucwin_subject(ProfMucWin *mucwin, const char *const nick, const char *const subject);
|
||||
void mucwin_requires_config(ProfMucWin *mucwin);
|
||||
void mucwin_info(ProfMucWin *mucwin);
|
||||
@ -250,6 +252,7 @@ void cons_show_presence_prefs(void);
|
||||
void cons_show_connection_prefs(void);
|
||||
void cons_show_otr_prefs(void);
|
||||
void cons_show_pgp_prefs(void);
|
||||
void cons_show_omemo_prefs(void);
|
||||
void cons_show_account(ProfAccount *account);
|
||||
void cons_debug(const char *const msg, ...);
|
||||
void cons_show_error(const char *const cmd, ...);
|
||||
|
@ -152,6 +152,7 @@ typedef struct prof_chat_win_t {
|
||||
gboolean otr_is_trusted;
|
||||
gboolean pgp_send;
|
||||
gboolean pgp_recv;
|
||||
gboolean is_omemo;
|
||||
char *resource_override;
|
||||
gboolean history_shown;
|
||||
unsigned long memcheck;
|
||||
@ -167,9 +168,11 @@ typedef struct prof_muc_win_t {
|
||||
gboolean unread_mentions;
|
||||
gboolean unread_triggers;
|
||||
gboolean showjid;
|
||||
gboolean is_omemo;
|
||||
unsigned long memcheck;
|
||||
char *enctext;
|
||||
char *message_char;
|
||||
GHashTable *sent_messages;
|
||||
} ProfMucWin;
|
||||
|
||||
typedef struct prof_conf_win_t ProfConfWin;
|
||||
|
@ -143,6 +143,7 @@ win_create_chat(const char *const barejid)
|
||||
new_win->otr_is_trusted = FALSE;
|
||||
new_win->pgp_recv = FALSE;
|
||||
new_win->pgp_send = FALSE;
|
||||
new_win->is_omemo = FALSE;
|
||||
new_win->history_shown = FALSE;
|
||||
new_win->unread = 0;
|
||||
new_win->state = chat_state_new();
|
||||
@ -196,6 +197,8 @@ win_create_muc(const char *const roomjid)
|
||||
}
|
||||
new_win->enctext = NULL;
|
||||
new_win->message_char = NULL;
|
||||
new_win->is_omemo = FALSE;
|
||||
new_win->sent_messages = g_hash_table_new_full(g_str_hash, g_str_equal, free, NULL);
|
||||
|
||||
new_win->memcheck = PROFMUCWIN_MEMCHECK;
|
||||
|
||||
@ -1057,6 +1060,8 @@ win_print_incoming(ProfWin *window, GDateTime *timestamp,
|
||||
enc_char = prefs_get_otr_char();
|
||||
} else if (enc_mode == PROF_MSG_PGP) {
|
||||
enc_char = prefs_get_pgp_char();
|
||||
} else if (enc_mode == PROF_MSG_OMEMO) {
|
||||
enc_char = prefs_get_omemo_char();
|
||||
}
|
||||
_win_printf(window, enc_char, 0, timestamp, NO_ME, THEME_TEXT_THEM, from, "%s", message);
|
||||
break;
|
||||
|
@ -561,6 +561,7 @@ wins_close_by_num(int i)
|
||||
ProfMucWin *mucwin = (ProfMucWin*)window;
|
||||
autocomplete_remove(wins_ac, mucwin->roomjid);
|
||||
autocomplete_remove(wins_close_ac, mucwin->roomjid);
|
||||
g_hash_table_remove_all(mucwin->sent_messages);
|
||||
break;
|
||||
}
|
||||
case WIN_PRIVATE:
|
||||
|
@ -63,6 +63,7 @@ typedef struct prof_conn_t {
|
||||
char *domain;
|
||||
GHashTable *available_resources;
|
||||
GHashTable *features_by_jid;
|
||||
GHashTable *requested_features;
|
||||
} ProfConnection;
|
||||
|
||||
static ProfConnection conn;
|
||||
@ -89,6 +90,7 @@ connection_init(void)
|
||||
conn.domain = NULL;
|
||||
conn.features_by_jid = NULL;
|
||||
conn.available_resources = g_hash_table_new_full(g_str_hash, g_str_equal, free, (GDestroyNotify)resource_destroy);
|
||||
conn.requested_features = g_hash_table_new_full(g_str_hash, g_str_equal, free, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
@ -231,6 +233,10 @@ connection_clear_data(void)
|
||||
if (conn.available_resources) {
|
||||
g_hash_table_remove_all(conn.available_resources);
|
||||
}
|
||||
|
||||
if (conn.requested_features) {
|
||||
g_hash_table_remove_all(conn.requested_features);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBMESODE
|
||||
@ -313,12 +319,21 @@ connection_jid_for_feature(const char *const feature)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
connection_request_features(void)
|
||||
{
|
||||
/* We don't record it as a requested feature to avoid triggering th
|
||||
* sv_ev_connection_features_received too soon */
|
||||
iq_disco_info_request_onconnect(conn.domain);
|
||||
}
|
||||
|
||||
void
|
||||
connection_set_disco_items(GSList *items)
|
||||
{
|
||||
GSList *curr = items;
|
||||
while (curr) {
|
||||
DiscoItem *item = curr->data;
|
||||
g_hash_table_insert(conn.requested_features, strdup(item->jid), NULL);
|
||||
g_hash_table_insert(conn.features_by_jid, strdup(item->jid),
|
||||
g_hash_table_new_full(g_str_hash, g_str_equal, free, NULL));
|
||||
|
||||
@ -357,6 +372,14 @@ connection_get_fulljid(void)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
connection_features_received(const char *const jid)
|
||||
{
|
||||
if (g_hash_table_remove(conn.requested_features, jid) && g_hash_table_size(conn.requested_features) == 0) {
|
||||
sv_ev_connection_features_received();
|
||||
}
|
||||
}
|
||||
|
||||
GHashTable*
|
||||
connection_get_features(const char *const jid)
|
||||
{
|
||||
|
@ -53,6 +53,8 @@ void connection_set_disco_items(GSList *items);
|
||||
xmpp_conn_t* connection_get_conn(void);
|
||||
xmpp_ctx_t* connection_get_ctx(void);
|
||||
char *connection_get_domain(void);
|
||||
void connection_request_features(void);
|
||||
void connection_features_received(const char *const jid);
|
||||
GHashTable* connection_get_features(const char *const jid);
|
||||
|
||||
void connection_clear_data(void);
|
||||
|
@ -77,11 +77,11 @@ typedef struct p_room_info_data_t {
|
||||
gboolean display;
|
||||
} ProfRoomInfoData;
|
||||
|
||||
typedef struct p_id_handle_t {
|
||||
ProfIdCallback func;
|
||||
ProfIdFreeCallback free_func;
|
||||
typedef struct p_iq_handle_t {
|
||||
ProfIqCallback func;
|
||||
ProfIqFreeCallback free_func;
|
||||
void *userdata;
|
||||
} ProfIdHandler;
|
||||
} ProfIqHandler;
|
||||
|
||||
typedef struct privilege_set_t {
|
||||
char *item;
|
||||
@ -205,7 +205,7 @@ _iq_handler(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza, void *const us
|
||||
|
||||
const char *id = xmpp_stanza_get_id(stanza);
|
||||
if (id) {
|
||||
ProfIdHandler *handler = g_hash_table_lookup(id_handlers, id);
|
||||
ProfIqHandler *handler = g_hash_table_lookup(id_handlers, id);
|
||||
if (handler) {
|
||||
int keep = handler->func(stanza, handler->userdata);
|
||||
if (!keep) {
|
||||
@ -234,7 +234,7 @@ iq_handlers_init(void)
|
||||
GList *keys = g_hash_table_get_keys(id_handlers);
|
||||
GList *curr = keys;
|
||||
while (curr) {
|
||||
ProfIdHandler *handler = g_hash_table_lookup(id_handlers, curr->data);
|
||||
ProfIqHandler *handler = g_hash_table_lookup(id_handlers, curr->data);
|
||||
if (handler->free_func && handler->userdata) {
|
||||
handler->free_func(handler->userdata);
|
||||
}
|
||||
@ -248,9 +248,9 @@ iq_handlers_init(void)
|
||||
}
|
||||
|
||||
void
|
||||
iq_id_handler_add(const char *const id, ProfIdCallback func, ProfIdFreeCallback free_func, void *userdata)
|
||||
iq_id_handler_add(const char *const id, ProfIqCallback func, ProfIqFreeCallback free_func, void *userdata)
|
||||
{
|
||||
ProfIdHandler *handler = malloc(sizeof(ProfIdHandler));
|
||||
ProfIqHandler *handler = malloc(sizeof(ProfIqHandler));
|
||||
handler->func = func;
|
||||
handler->free_func = free_func;
|
||||
handler->userdata = userdata;
|
||||
@ -438,7 +438,7 @@ iq_room_info_request(const char *const room, gboolean display_result)
|
||||
cb_data->room = strdup(room);
|
||||
cb_data->display = display_result;
|
||||
|
||||
iq_id_handler_add(id, _room_info_response_id_handler, (ProfIdFreeCallback)_iq_free_room_data, cb_data);
|
||||
iq_id_handler_add(id, _room_info_response_id_handler, (ProfIqFreeCallback)_iq_free_room_data, cb_data);
|
||||
|
||||
free(id);
|
||||
|
||||
@ -651,7 +651,7 @@ iq_room_affiliation_set(const char *const room, const char *const jid, char *aff
|
||||
affiliation_set->item = strdup(jid);
|
||||
affiliation_set->privilege = strdup(affiliation);
|
||||
|
||||
iq_id_handler_add(id, _room_affiliation_set_result_id_handler, (ProfIdFreeCallback)_iq_free_affiliation_set, affiliation_set);
|
||||
iq_id_handler_add(id, _room_affiliation_set_result_id_handler, (ProfIqFreeCallback)_iq_free_affiliation_set, affiliation_set);
|
||||
|
||||
iq_send_stanza(iq);
|
||||
xmpp_stanza_release(iq);
|
||||
@ -670,7 +670,7 @@ iq_room_role_set(const char *const room, const char *const nick, char *role,
|
||||
role_set->item = strdup(nick);
|
||||
role_set->privilege = strdup(role);
|
||||
|
||||
iq_id_handler_add(id, _room_role_set_result_id_handler, (ProfIdFreeCallback)_iq_free_affiliation_set, role_set);
|
||||
iq_id_handler_add(id, _room_role_set_result_id_handler, (ProfIqFreeCallback)_iq_free_affiliation_set, role_set);
|
||||
|
||||
iq_send_stanza(iq);
|
||||
xmpp_stanza_release(iq);
|
||||
@ -697,7 +697,7 @@ iq_send_ping(const char *const target)
|
||||
const char *id = xmpp_stanza_get_id(iq);
|
||||
|
||||
GDateTime *now = g_date_time_new_now_local();
|
||||
iq_id_handler_add(id, _manual_pong_id_handler, (ProfIdFreeCallback)g_date_time_unref, now);
|
||||
iq_id_handler_add(id, _manual_pong_id_handler, (ProfIqFreeCallback)g_date_time_unref, now);
|
||||
|
||||
iq_send_stanza(iq);
|
||||
xmpp_stanza_release(iq);
|
||||
@ -2291,6 +2291,8 @@ _disco_info_response_id_handler_onconnect(xmpp_stanza_t *const stanza, void *con
|
||||
}
|
||||
}
|
||||
|
||||
connection_features_received(from);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -35,12 +35,12 @@
|
||||
#ifndef XMPP_IQ_H
|
||||
#define XMPP_IQ_H
|
||||
|
||||
typedef int(*ProfIdCallback)(xmpp_stanza_t *const stanza, void *const userdata);
|
||||
typedef void(*ProfIdFreeCallback)(void *userdata);
|
||||
typedef int(*ProfIqCallback)(xmpp_stanza_t *const stanza, void *const userdata);
|
||||
typedef void(*ProfIqFreeCallback)(void *userdata);
|
||||
|
||||
void iq_handlers_init(void);
|
||||
void iq_send_stanza(xmpp_stanza_t *const stanza);
|
||||
void iq_id_handler_add(const char *const id, ProfIdCallback func, ProfIdFreeCallback free_func, void *userdata);
|
||||
void iq_id_handler_add(const char *const id, ProfIqCallback func, ProfIqFreeCallback free_func, void *userdata);
|
||||
void iq_disco_info_request_onconnect(gchar *jid);
|
||||
void iq_disco_items_request_onconnect(gchar *jid);
|
||||
void iq_send_caps_request(const char *const to, const char *const id, const char *const node, const char *const ver);
|
||||
|
@ -52,6 +52,7 @@
|
||||
#include "pgp/gpg.h"
|
||||
#include "plugins/plugins.h"
|
||||
#include "ui/ui.h"
|
||||
#include "ui/window_list.h"
|
||||
#include "xmpp/chat_session.h"
|
||||
#include "xmpp/muc.h"
|
||||
#include "xmpp/session.h"
|
||||
@ -62,6 +63,17 @@
|
||||
#include "xmpp/connection.h"
|
||||
#include "xmpp/xmpp.h"
|
||||
|
||||
#ifdef HAVE_OMEMO
|
||||
#include "xmpp/omemo.h"
|
||||
#include "omemo/omemo.h"
|
||||
#endif
|
||||
|
||||
typedef struct p_message_handle_t {
|
||||
ProfMessageCallback func;
|
||||
ProfMessageFreeCallback free_func;
|
||||
void *userdata;
|
||||
} ProfMessageHandler;
|
||||
|
||||
static int _message_handler(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza, void *const userdata);
|
||||
|
||||
static void _handle_error(xmpp_stanza_t *const stanza);
|
||||
@ -74,6 +86,8 @@ static void _handle_chat(xmpp_stanza_t *const stanza);
|
||||
|
||||
static void _send_message_stanza(xmpp_stanza_t *const stanza);
|
||||
|
||||
static GHashTable *pubsub_event_handlers;
|
||||
|
||||
static int
|
||||
_message_handler(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza, void *const userdata)
|
||||
{
|
||||
@ -118,6 +132,23 @@ _message_handler(xmpp_conn_t *const conn, xmpp_stanza_t *const stanza, void *con
|
||||
_handle_receipt_received(stanza);
|
||||
}
|
||||
|
||||
xmpp_stanza_t *event = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_PUBSUB_EVENT);
|
||||
if (event) {
|
||||
xmpp_stanza_t *child = xmpp_stanza_get_children(event);
|
||||
if (child) {
|
||||
const char *node = xmpp_stanza_get_attribute(child, STANZA_ATTR_NODE);
|
||||
if (node) {
|
||||
ProfMessageHandler *handler = g_hash_table_lookup(pubsub_event_handlers, node);
|
||||
if (handler) {
|
||||
int keep = handler->func(stanza, handler->userdata);
|
||||
if (!keep) {
|
||||
g_hash_table_remove(pubsub_event_handlers, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_handle_chat(stanza);
|
||||
|
||||
return 1;
|
||||
@ -129,6 +160,33 @@ message_handlers_init(void)
|
||||
xmpp_conn_t * const conn = connection_get_conn();
|
||||
xmpp_ctx_t * const ctx = connection_get_ctx();
|
||||
xmpp_handler_add(conn, _message_handler, NULL, STANZA_NAME_MESSAGE, NULL, ctx);
|
||||
|
||||
if (pubsub_event_handlers) {
|
||||
GList *keys = g_hash_table_get_keys(pubsub_event_handlers);
|
||||
GList *curr = keys;
|
||||
while (curr) {
|
||||
ProfMessageHandler *handler = g_hash_table_lookup(pubsub_event_handlers, curr->data);
|
||||
if (handler->free_func && handler->userdata) {
|
||||
handler->free_func(handler->userdata);
|
||||
}
|
||||
curr = g_list_next(curr);
|
||||
}
|
||||
g_list_free(keys);
|
||||
g_hash_table_destroy(pubsub_event_handlers);
|
||||
}
|
||||
|
||||
pubsub_event_handlers = g_hash_table_new_full(g_str_hash, g_str_equal, free, free);
|
||||
}
|
||||
|
||||
void
|
||||
message_pubsub_event_handler_add(const char *const node, ProfMessageCallback func, ProfMessageFreeCallback free_func, void *userdata)
|
||||
{
|
||||
ProfMessageHandler *handler = malloc(sizeof(ProfMessageHandler));
|
||||
handler->func = func;
|
||||
handler->free_func = free_func;
|
||||
handler->userdata = userdata;
|
||||
|
||||
g_hash_table_insert(pubsub_event_handlers, strdup(node), handler);
|
||||
}
|
||||
|
||||
char*
|
||||
@ -254,6 +312,118 @@ message_send_chat_otr(const char *const barejid, const char *const msg, gboolean
|
||||
return id;
|
||||
}
|
||||
|
||||
#ifdef HAVE_OMEMO
|
||||
char*
|
||||
message_send_chat_omemo(const char *const jid, uint32_t sid, GList *keys,
|
||||
const unsigned char *const iv, size_t iv_len,
|
||||
const unsigned char *const ciphertext, size_t ciphertext_len,
|
||||
gboolean request_receipt, gboolean muc)
|
||||
{
|
||||
char *state = chat_session_get_state(jid);
|
||||
xmpp_ctx_t * const ctx = connection_get_ctx();
|
||||
char *id;
|
||||
xmpp_stanza_t *message;
|
||||
if (muc) {
|
||||
id = connection_create_stanza_id("muc");
|
||||
message = xmpp_message_new(ctx, STANZA_TYPE_GROUPCHAT, jid, id);
|
||||
stanza_attach_origin_id(ctx, message, id);
|
||||
} else {
|
||||
id = connection_create_stanza_id("msg");
|
||||
message = xmpp_message_new(ctx, STANZA_TYPE_CHAT, jid, id);
|
||||
}
|
||||
|
||||
xmpp_stanza_t *encrypted = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_name(encrypted, "encrypted");
|
||||
xmpp_stanza_set_ns(encrypted, STANZA_NS_OMEMO);
|
||||
|
||||
xmpp_stanza_t *header = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_name(header, "header");
|
||||
char *sid_text = g_strdup_printf("%d", sid);
|
||||
xmpp_stanza_set_attribute(header, "sid", sid_text);
|
||||
g_free(sid_text);
|
||||
|
||||
GList *key_iter;
|
||||
for (key_iter = keys; key_iter != NULL; key_iter = key_iter->next) {
|
||||
omemo_key_t *key = (omemo_key_t *)key_iter->data;
|
||||
|
||||
xmpp_stanza_t *key_stanza = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_name(key_stanza, "key");
|
||||
char *rid = g_strdup_printf("%d", key->device_id);
|
||||
xmpp_stanza_set_attribute(key_stanza, "rid", rid);
|
||||
g_free(rid);
|
||||
if (key->prekey) {
|
||||
xmpp_stanza_set_attribute(key_stanza, "prekey", "true");
|
||||
}
|
||||
|
||||
gchar *key_raw = g_base64_encode(key->data, key->length);
|
||||
xmpp_stanza_t *key_text = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_text(key_text, key_raw);
|
||||
g_free(key_raw);
|
||||
|
||||
xmpp_stanza_add_child(key_stanza, key_text);
|
||||
xmpp_stanza_add_child(header, key_stanza);
|
||||
xmpp_stanza_release(key_text);
|
||||
xmpp_stanza_release(key_stanza);
|
||||
}
|
||||
|
||||
xmpp_stanza_t *iv_stanza = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_name(iv_stanza, "iv");
|
||||
|
||||
gchar *iv_raw = g_base64_encode(iv, iv_len);
|
||||
xmpp_stanza_t *iv_text = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_text(iv_text, iv_raw);
|
||||
g_free(iv_raw);
|
||||
|
||||
xmpp_stanza_add_child(iv_stanza, iv_text);
|
||||
xmpp_stanza_add_child(header, iv_stanza);
|
||||
xmpp_stanza_release(iv_text);
|
||||
xmpp_stanza_release(iv_stanza);
|
||||
|
||||
xmpp_stanza_add_child(encrypted, header);
|
||||
xmpp_stanza_release(header);
|
||||
|
||||
xmpp_stanza_t *payload = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_name(payload, "payload");
|
||||
|
||||
gchar *ciphertext_raw = g_base64_encode(ciphertext, ciphertext_len);
|
||||
xmpp_stanza_t *payload_text = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_text(payload_text, ciphertext_raw);
|
||||
g_free(ciphertext_raw);
|
||||
|
||||
xmpp_stanza_add_child(payload, payload_text);
|
||||
xmpp_stanza_add_child(encrypted, payload);
|
||||
xmpp_stanza_release(payload_text);
|
||||
xmpp_stanza_release(payload);
|
||||
|
||||
xmpp_stanza_add_child(message, encrypted);
|
||||
xmpp_stanza_release(encrypted);
|
||||
|
||||
xmpp_stanza_t *body = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_name(body, "body");
|
||||
xmpp_stanza_t *body_text = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_text(body_text, "You received a message encrypted with OMEMO but your client doesn't support OMEMO.");
|
||||
xmpp_stanza_add_child(body, body_text);
|
||||
xmpp_stanza_release(body_text);
|
||||
xmpp_stanza_add_child(message, body);
|
||||
xmpp_stanza_release(body);
|
||||
|
||||
if (state) {
|
||||
stanza_attach_state(ctx, message, state);
|
||||
}
|
||||
|
||||
stanza_attach_hints_store(ctx, message);
|
||||
|
||||
if (request_receipt) {
|
||||
stanza_attach_receipt_request(ctx, message);
|
||||
}
|
||||
|
||||
_send_message_stanza(message);
|
||||
xmpp_stanza_release(message);
|
||||
|
||||
return id;
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
message_send_private(const char *const fulljid, const char *const msg, const char *const oob_url)
|
||||
{
|
||||
@ -273,23 +443,24 @@ message_send_private(const char *const fulljid, const char *const msg, const cha
|
||||
xmpp_stanza_release(message);
|
||||
}
|
||||
|
||||
void
|
||||
char*
|
||||
message_send_groupchat(const char *const roomjid, const char *const msg, const char *const oob_url)
|
||||
{
|
||||
xmpp_ctx_t * const ctx = connection_get_ctx();
|
||||
char *id = connection_create_stanza_id("muc");
|
||||
|
||||
xmpp_stanza_t *message = xmpp_message_new(ctx, STANZA_TYPE_GROUPCHAT, roomjid, id);
|
||||
stanza_attach_origin_id(ctx, message, id);
|
||||
xmpp_message_set_body(message, msg);
|
||||
|
||||
free(id);
|
||||
|
||||
if (oob_url) {
|
||||
stanza_attach_x_oob_url(ctx, message, oob_url);
|
||||
}
|
||||
|
||||
_send_message_stanza(message);
|
||||
xmpp_stanza_release(message);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void
|
||||
@ -518,6 +689,14 @@ _handle_groupchat(xmpp_stanza_t *const stanza)
|
||||
{
|
||||
xmpp_ctx_t *ctx = connection_get_ctx();
|
||||
char *message = NULL;
|
||||
|
||||
const char *id = xmpp_stanza_get_id(stanza);
|
||||
|
||||
xmpp_stanza_t *origin = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_STABLE_ID);
|
||||
if (origin && g_strcmp0(xmpp_stanza_get_name(origin), STANZA_NAME_ORIGIN_ID) == 0) {
|
||||
id = xmpp_stanza_get_attribute(origin, STANZA_ATTR_ID);
|
||||
}
|
||||
|
||||
const char *room_jid = xmpp_stanza_get_from(stanza);
|
||||
Jid *jid = jid_create(room_jid);
|
||||
|
||||
@ -560,19 +739,28 @@ _handle_groupchat(xmpp_stanza_t *const stanza)
|
||||
return;
|
||||
}
|
||||
|
||||
message = xmpp_message_get_body(stanza);
|
||||
// check omemo encryption
|
||||
gboolean omemo = FALSE;
|
||||
#ifdef HAVE_OMEMO
|
||||
message = omemo_receive_message(stanza);
|
||||
omemo = message != NULL;
|
||||
#endif
|
||||
|
||||
if (!message) {
|
||||
jid_destroy(jid);
|
||||
return;
|
||||
message = xmpp_message_get_body(stanza);
|
||||
if (!message) {
|
||||
jid_destroy(jid);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// determine if the notifications happened whilst offline
|
||||
GDateTime *timestamp = stanza_get_delay(stanza);
|
||||
if (timestamp) {
|
||||
sv_ev_room_history(jid->barejid, jid->resourcepart, timestamp, message);
|
||||
sv_ev_room_history(jid->barejid, jid->resourcepart, timestamp, message, omemo);
|
||||
g_date_time_unref(timestamp);
|
||||
} else {
|
||||
sv_ev_room_message(jid->barejid, jid->resourcepart, message);
|
||||
sv_ev_room_message(jid->barejid, jid->resourcepart, message, id, omemo);
|
||||
}
|
||||
|
||||
xmpp_free(ctx, message);
|
||||
@ -675,6 +863,7 @@ _private_chat_handler(xmpp_stanza_t *const stanza, const char *const fulljid)
|
||||
static gboolean
|
||||
_handle_carbons(xmpp_stanza_t *const stanza)
|
||||
{
|
||||
char *message_txt = NULL;
|
||||
xmpp_stanza_t *carbons = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_CARBONS);
|
||||
if (!carbons) {
|
||||
return FALSE;
|
||||
@ -708,10 +897,19 @@ _handle_carbons(xmpp_stanza_t *const stanza)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
char *message_txt = xmpp_message_get_body(message);
|
||||
// check omemo encryption
|
||||
gboolean omemo = FALSE;
|
||||
#ifdef HAVE_OMEMO
|
||||
message_txt = omemo_receive_message(message);
|
||||
omemo = message_txt != NULL;
|
||||
#endif
|
||||
|
||||
if (!message_txt) {
|
||||
log_warning("Carbon received with no message.");
|
||||
return TRUE;
|
||||
message_txt = xmpp_message_get_body(message);
|
||||
if (!message_txt) {
|
||||
log_warning("Carbon received with no message.");
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
Jid *my_jid = jid_create(connection_get_fulljid());
|
||||
@ -739,11 +937,11 @@ _handle_carbons(xmpp_stanza_t *const stanza)
|
||||
|
||||
// if we are the recipient, treat as standard incoming message
|
||||
if (g_strcmp0(my_jid->barejid, jid_to->barejid) == 0) {
|
||||
sv_ev_incoming_carbon(jid_from->barejid, jid_from->resourcepart, message_txt, enc_message);
|
||||
sv_ev_incoming_carbon(jid_from->barejid, jid_from->resourcepart, message_txt, enc_message, omemo);
|
||||
|
||||
// else treat as a sent message
|
||||
} else {
|
||||
sv_ev_outgoing_carbon(jid_to->barejid, message_txt, enc_message);
|
||||
sv_ev_outgoing_carbon(jid_to->barejid, message_txt, enc_message, omemo);
|
||||
}
|
||||
|
||||
xmpp_ctx_t *ctx = connection_get_ctx();
|
||||
@ -760,6 +958,7 @@ _handle_carbons(xmpp_stanza_t *const stanza)
|
||||
static void
|
||||
_handle_chat(xmpp_stanza_t *const stanza)
|
||||
{
|
||||
char *message = NULL;
|
||||
// ignore if type not chat or absent
|
||||
const char *type = xmpp_stanza_get_type(stanza);
|
||||
if (!(g_strcmp0(type, "chat") == 0 || type == NULL)) {
|
||||
@ -772,6 +971,13 @@ _handle_chat(xmpp_stanza_t *const stanza)
|
||||
return;
|
||||
}
|
||||
|
||||
// check omemo encryption
|
||||
gboolean omemo = FALSE;
|
||||
#ifdef HAVE_OMEMO
|
||||
message = omemo_receive_message(stanza);
|
||||
omemo = message != NULL;
|
||||
#endif
|
||||
|
||||
// ignore handled namespaces
|
||||
xmpp_stanza_t *conf = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_CONFERENCE);
|
||||
xmpp_stanza_t *captcha = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_CAPTCHA);
|
||||
@ -801,19 +1007,24 @@ _handle_chat(xmpp_stanza_t *const stanza)
|
||||
// standard chat message, use jid without resource
|
||||
xmpp_ctx_t *ctx = connection_get_ctx();
|
||||
GDateTime *timestamp = stanza_get_delay(stanza);
|
||||
if (body) {
|
||||
char *message = xmpp_stanza_get_text(body);
|
||||
if (message) {
|
||||
char *enc_message = NULL;
|
||||
xmpp_stanza_t *x = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_ENCRYPTED);
|
||||
if (x) {
|
||||
enc_message = xmpp_stanza_get_text(x);
|
||||
}
|
||||
sv_ev_incoming_message(jid->barejid, jid->resourcepart, message, enc_message, timestamp);
|
||||
xmpp_free(ctx, enc_message);
|
||||
if (!message && body) {
|
||||
message = xmpp_stanza_get_text(body);
|
||||
}
|
||||
|
||||
_receipt_request_handler(stanza);
|
||||
if (message) {
|
||||
char *enc_message = NULL;
|
||||
xmpp_stanza_t *x = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_ENCRYPTED);
|
||||
if (x) {
|
||||
enc_message = xmpp_stanza_get_text(x);
|
||||
}
|
||||
sv_ev_incoming_message(jid->barejid, jid->resourcepart, message, enc_message, timestamp, omemo);
|
||||
xmpp_free(ctx, enc_message);
|
||||
|
||||
_receipt_request_handler(stanza);
|
||||
|
||||
if (omemo) {
|
||||
free(message);
|
||||
} else {
|
||||
xmpp_free(ctx, message);
|
||||
}
|
||||
}
|
||||
|
@ -35,6 +35,10 @@
|
||||
#ifndef XMPP_MESSAGE_H
|
||||
#define XMPP_MESSAGE_H
|
||||
|
||||
typedef int(*ProfMessageCallback)(xmpp_stanza_t *const stanza, void *const userdata);
|
||||
typedef void(*ProfMessageFreeCallback)(void *userdata);
|
||||
|
||||
void message_handlers_init(void);
|
||||
void message_pubsub_event_handler_add(const char *const node, ProfMessageCallback func, ProfMessageFreeCallback free_func, void *userdata);
|
||||
|
||||
#endif
|
||||
|
448
src/xmpp/omemo.c
Normal file
448
src/xmpp/omemo.c
Normal file
@ -0,0 +1,448 @@
|
||||
#include <glib.h>
|
||||
|
||||
#include "log.h"
|
||||
#include "xmpp/connection.h"
|
||||
#include "xmpp/form.h"
|
||||
#include "xmpp/iq.h"
|
||||
#include "xmpp/message.h"
|
||||
#include "xmpp/stanza.h"
|
||||
|
||||
#include "omemo/omemo.h"
|
||||
|
||||
static int _omemo_receive_devicelist(xmpp_stanza_t *const stanza, void *const userdata);
|
||||
static int _omemo_bundle_publish_result(xmpp_stanza_t *const stanza, void *const userdata);
|
||||
static int _omemo_bundle_publish_configure(xmpp_stanza_t *const stanza, void *const userdata);
|
||||
static int _omemo_bundle_publish_configure_result(xmpp_stanza_t *const stanza, void *const userdata);
|
||||
|
||||
void
|
||||
omemo_devicelist_subscribe(void)
|
||||
{
|
||||
message_pubsub_event_handler_add(STANZA_NS_OMEMO_DEVICELIST, _omemo_receive_devicelist, NULL, NULL);
|
||||
|
||||
caps_add_feature(XMPP_FEATURE_OMEMO_DEVICELIST_NOTIFY);
|
||||
}
|
||||
|
||||
void
|
||||
omemo_devicelist_publish(GList *device_list)
|
||||
{
|
||||
xmpp_ctx_t * const ctx = connection_get_ctx();
|
||||
xmpp_stanza_t *iq = stanza_create_omemo_devicelist_publish(ctx, device_list);
|
||||
|
||||
if (connection_supports(XMPP_FEATURE_PUBSUB_PUBLISH_OPTIONS)) {
|
||||
stanza_attach_publish_options(ctx, iq, "pubsub#access_model", "open");
|
||||
}
|
||||
|
||||
iq_send_stanza(iq);
|
||||
xmpp_stanza_release(iq);
|
||||
}
|
||||
|
||||
void
|
||||
omemo_devicelist_request(const char * const jid)
|
||||
{
|
||||
xmpp_ctx_t * const ctx = connection_get_ctx();
|
||||
char *id = connection_create_stanza_id("devicelist_request");
|
||||
|
||||
xmpp_stanza_t *iq = stanza_create_omemo_devicelist_request(ctx, id, jid);
|
||||
iq_id_handler_add(id, _omemo_receive_devicelist, NULL, NULL);
|
||||
|
||||
iq_send_stanza(iq);
|
||||
|
||||
free(id);
|
||||
xmpp_stanza_release(iq);
|
||||
}
|
||||
|
||||
void
|
||||
omemo_bundle_publish(gboolean first)
|
||||
{
|
||||
xmpp_ctx_t * const ctx = connection_get_ctx();
|
||||
unsigned char *identity_key = NULL;
|
||||
size_t identity_key_length;
|
||||
unsigned char *signed_prekey = NULL;
|
||||
size_t signed_prekey_length;
|
||||
unsigned char *signed_prekey_signature = NULL;
|
||||
size_t signed_prekey_signature_length;
|
||||
GList *prekeys = NULL, *ids = NULL, *lengths = NULL;
|
||||
|
||||
omemo_identity_key(&identity_key, &identity_key_length);
|
||||
omemo_signed_prekey(&signed_prekey, &signed_prekey_length);
|
||||
omemo_signed_prekey_signature(&signed_prekey_signature, &signed_prekey_signature_length);
|
||||
omemo_prekeys(&prekeys, &ids, &lengths);
|
||||
|
||||
char *id = connection_create_stanza_id("omemo_bundle_publish");
|
||||
xmpp_stanza_t *iq = stanza_create_omemo_bundle_publish(ctx, id,
|
||||
omemo_device_id(), identity_key, identity_key_length, signed_prekey,
|
||||
signed_prekey_length, signed_prekey_signature,
|
||||
signed_prekey_signature_length, prekeys, ids, lengths);
|
||||
|
||||
g_list_free_full(prekeys, free);
|
||||
g_list_free(lengths);
|
||||
g_list_free(ids);
|
||||
|
||||
if (connection_supports(XMPP_FEATURE_PUBSUB_PUBLISH_OPTIONS)) {
|
||||
stanza_attach_publish_options(ctx, iq, "pubsub#access_model", "open");
|
||||
}
|
||||
|
||||
iq_id_handler_add(id, _omemo_bundle_publish_result, NULL, GINT_TO_POINTER(first));
|
||||
|
||||
iq_send_stanza(iq);
|
||||
|
||||
xmpp_stanza_release(iq);
|
||||
free(identity_key);
|
||||
free(signed_prekey);
|
||||
free(signed_prekey_signature);
|
||||
free(id);
|
||||
}
|
||||
|
||||
void
|
||||
omemo_bundle_request(const char * const jid, uint32_t device_id, ProfIqCallback func, ProfIqFreeCallback free_func, void *userdata)
|
||||
{
|
||||
xmpp_ctx_t * const ctx = connection_get_ctx();
|
||||
char *id = connection_create_stanza_id("bundle_request");
|
||||
|
||||
xmpp_stanza_t *iq = stanza_create_omemo_bundle_request(ctx, id, jid, device_id);
|
||||
iq_id_handler_add(id, func, free_func, userdata);
|
||||
|
||||
iq_send_stanza(iq);
|
||||
|
||||
free(id);
|
||||
xmpp_stanza_release(iq);
|
||||
}
|
||||
|
||||
int
|
||||
omemo_start_device_session_handle_bundle(xmpp_stanza_t *const stanza, void *const userdata)
|
||||
{
|
||||
char *from = NULL;
|
||||
const char *from_attr = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM);
|
||||
if (!from_attr) {
|
||||
Jid *jid = jid_create(connection_get_fulljid());
|
||||
from = strdup(jid->barejid);
|
||||
jid_destroy(jid);
|
||||
} else {
|
||||
from = strdup(from_attr);
|
||||
}
|
||||
|
||||
if (g_strcmp0(from, userdata) != 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
xmpp_stanza_t *pubsub = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_PUBSUB);
|
||||
if (!pubsub) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
xmpp_stanza_t *items = xmpp_stanza_get_child_by_name(pubsub, "items");
|
||||
if (!items) {
|
||||
return 1;
|
||||
}
|
||||
const char *node = xmpp_stanza_get_attribute(items, "node");
|
||||
char *device_id_str = strstr(node, ":");
|
||||
if (!device_id_str) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint32_t device_id = strtoul(++device_id_str, NULL, 10);
|
||||
|
||||
xmpp_stanza_t *item = xmpp_stanza_get_child_by_name(items, "item");
|
||||
if (!item) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
xmpp_stanza_t *bundle = xmpp_stanza_get_child_by_ns(item, STANZA_NS_OMEMO);
|
||||
if (!bundle) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
xmpp_stanza_t *prekeys = xmpp_stanza_get_child_by_name(bundle, "prekeys");
|
||||
if (!prekeys) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
GList *prekeys_list = NULL;
|
||||
xmpp_stanza_t *prekey;
|
||||
for (prekey = xmpp_stanza_get_children(prekeys); prekey != NULL; prekey = xmpp_stanza_get_next(prekey)) {
|
||||
omemo_key_t *key = malloc(sizeof(omemo_key_t));
|
||||
|
||||
const char *prekey_id_text = xmpp_stanza_get_attribute(prekey, "preKeyId");
|
||||
if (!prekey_id_text) {
|
||||
return 1;
|
||||
}
|
||||
key->id = strtoul(prekey_id_text, NULL, 10);
|
||||
xmpp_stanza_t *prekey_text = xmpp_stanza_get_children(prekey);
|
||||
if (!prekey_text) {
|
||||
return 1;
|
||||
}
|
||||
char *prekey_b64 = xmpp_stanza_get_text(prekey_text);
|
||||
key->data = g_base64_decode(prekey_b64, &key->length);
|
||||
free(prekey_b64);
|
||||
key->prekey = TRUE;
|
||||
key->device_id = device_id;
|
||||
|
||||
prekeys_list = g_list_append(prekeys_list, key);
|
||||
}
|
||||
|
||||
xmpp_stanza_t *signed_prekey = xmpp_stanza_get_child_by_name(bundle, "signedPreKeyPublic");
|
||||
if (!signed_prekey) {
|
||||
return 1;
|
||||
}
|
||||
const char *signed_prekey_id_text = xmpp_stanza_get_attribute(signed_prekey, "signedPreKeyId");
|
||||
if (!signed_prekey_id_text) {
|
||||
return 1;
|
||||
}
|
||||
uint32_t signed_prekey_id = strtoul(signed_prekey_id_text, NULL, 10);
|
||||
xmpp_stanza_t *signed_prekey_text = xmpp_stanza_get_children(signed_prekey);
|
||||
if (!signed_prekey_text) {
|
||||
return 1;
|
||||
}
|
||||
size_t signed_prekey_len;
|
||||
char *signed_prekey_b64 = xmpp_stanza_get_text(signed_prekey_text);
|
||||
unsigned char *signed_prekey_raw = g_base64_decode(signed_prekey_b64, &signed_prekey_len);
|
||||
free(signed_prekey_b64);
|
||||
|
||||
xmpp_stanza_t *signed_prekey_signature = xmpp_stanza_get_child_by_name(bundle, "signedPreKeySignature");
|
||||
if (!signed_prekey_signature) {
|
||||
return 1;
|
||||
}
|
||||
xmpp_stanza_t *signed_prekey_signature_text = xmpp_stanza_get_children(signed_prekey_signature);
|
||||
if (!signed_prekey_signature_text) {
|
||||
return 1;
|
||||
}
|
||||
size_t signed_prekey_signature_len;
|
||||
char *signed_prekey_signature_b64 = xmpp_stanza_get_text(signed_prekey_signature_text);
|
||||
unsigned char *signed_prekey_signature_raw = g_base64_decode(signed_prekey_signature_b64, &signed_prekey_signature_len);
|
||||
free(signed_prekey_signature_b64);
|
||||
|
||||
xmpp_stanza_t *identity_key = xmpp_stanza_get_child_by_name(bundle, "identityKey");
|
||||
if (!identity_key) {
|
||||
return 1;
|
||||
}
|
||||
xmpp_stanza_t *identity_key_text = xmpp_stanza_get_children(identity_key);
|
||||
if (!identity_key_text) {
|
||||
return 1;
|
||||
}
|
||||
size_t identity_key_len;
|
||||
char *identity_key_b64 = xmpp_stanza_get_text(identity_key_text);
|
||||
unsigned char *identity_key_raw = g_base64_decode(identity_key_b64, &identity_key_len);
|
||||
free(identity_key_b64);
|
||||
|
||||
omemo_start_device_session(from, device_id, prekeys_list, signed_prekey_id,
|
||||
signed_prekey_raw, signed_prekey_len, signed_prekey_signature_raw,
|
||||
signed_prekey_signature_len, identity_key_raw, identity_key_len);
|
||||
|
||||
free(from);
|
||||
g_list_free_full(prekeys_list, (GDestroyNotify)omemo_key_free);
|
||||
g_free(signed_prekey_raw);
|
||||
g_free(identity_key_raw);
|
||||
g_free(signed_prekey_signature_raw);
|
||||
return 1;
|
||||
}
|
||||
|
||||
char *
|
||||
omemo_receive_message(xmpp_stanza_t *const stanza)
|
||||
{
|
||||
const char *type = xmpp_stanza_get_type(stanza);
|
||||
|
||||
xmpp_stanza_t *encrypted = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_OMEMO);
|
||||
if (!encrypted) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
xmpp_stanza_t *header = xmpp_stanza_get_child_by_name(encrypted, "header");
|
||||
if (!header) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *sid_text = xmpp_stanza_get_attribute(header, "sid");
|
||||
if (!sid_text) {
|
||||
return NULL;
|
||||
}
|
||||
uint32_t sid = strtoul(sid_text, NULL, 10);
|
||||
|
||||
xmpp_stanza_t *iv = xmpp_stanza_get_child_by_name(header, "iv");
|
||||
if (!iv) {
|
||||
return NULL;
|
||||
}
|
||||
char *iv_text = xmpp_stanza_get_text(iv);
|
||||
if (!iv_text) {
|
||||
return NULL;
|
||||
}
|
||||
size_t iv_len;
|
||||
unsigned char *iv_raw = g_base64_decode(iv_text, &iv_len);
|
||||
|
||||
xmpp_stanza_t *payload = xmpp_stanza_get_child_by_name(encrypted, "payload");
|
||||
if (!payload) {
|
||||
return NULL;
|
||||
}
|
||||
char *payload_text = xmpp_stanza_get_text(payload);
|
||||
if (!payload_text) {
|
||||
return NULL;
|
||||
}
|
||||
size_t payload_len;
|
||||
unsigned char *payload_raw = g_base64_decode(payload_text, &payload_len);
|
||||
|
||||
GList *keys = NULL;
|
||||
xmpp_stanza_t *key_stanza;
|
||||
for (key_stanza = xmpp_stanza_get_children(header); key_stanza != NULL; key_stanza = xmpp_stanza_get_next(key_stanza)) {
|
||||
if (g_strcmp0(xmpp_stanza_get_name(key_stanza), "key") != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
omemo_key_t *key = malloc(sizeof(omemo_key_t));
|
||||
char *key_text = xmpp_stanza_get_text(key_stanza);
|
||||
if (!key_text) {
|
||||
goto skip;
|
||||
}
|
||||
|
||||
|
||||
const char *rid_text = xmpp_stanza_get_attribute(key_stanza, "rid");
|
||||
key->device_id = strtoul(rid_text, NULL, 10);
|
||||
if (!key->device_id) {
|
||||
goto skip;
|
||||
}
|
||||
key->data = g_base64_decode(key_text, &key->length);
|
||||
free(key_text);
|
||||
key->prekey = g_strcmp0(xmpp_stanza_get_attribute(key_stanza, "prekey"), "true") == 0;
|
||||
keys = g_list_append(keys, key);
|
||||
continue;
|
||||
|
||||
skip:
|
||||
free(key);
|
||||
}
|
||||
|
||||
const char *from = xmpp_stanza_get_from(stanza);
|
||||
|
||||
char *plaintext = omemo_on_message_recv(from, sid, iv_raw, iv_len,
|
||||
keys, payload_raw, payload_len,
|
||||
g_strcmp0(type, STANZA_TYPE_GROUPCHAT) == 0);
|
||||
|
||||
g_list_free_full(keys, (GDestroyNotify)omemo_key_free);
|
||||
g_free(iv_raw);
|
||||
g_free(payload_raw);
|
||||
g_free(iv_text);
|
||||
g_free(payload_text);
|
||||
|
||||
return plaintext;
|
||||
}
|
||||
|
||||
static int
|
||||
_omemo_receive_devicelist(xmpp_stanza_t *const stanza, void *const userdata)
|
||||
{
|
||||
GList *device_list = NULL;
|
||||
const char *from = xmpp_stanza_get_attribute(stanza, STANZA_ATTR_FROM);
|
||||
|
||||
xmpp_stanza_t *root = NULL;
|
||||
xmpp_stanza_t *event = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_PUBSUB_EVENT);
|
||||
if (event) {
|
||||
root = event;
|
||||
}
|
||||
|
||||
xmpp_stanza_t *pubsub = xmpp_stanza_get_child_by_ns(stanza, STANZA_NS_PUBSUB);
|
||||
if (pubsub) {
|
||||
root = pubsub;
|
||||
}
|
||||
|
||||
if (!root) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
xmpp_stanza_t *items = xmpp_stanza_get_child_by_name(root, "items");
|
||||
if (!items) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
xmpp_stanza_t *item = xmpp_stanza_get_child_by_name(items, "item");
|
||||
if (item) {
|
||||
xmpp_stanza_t *list = xmpp_stanza_get_child_by_ns(item, STANZA_NS_OMEMO);
|
||||
if (!list) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
xmpp_stanza_t *device;
|
||||
for (device = xmpp_stanza_get_children(list); device != NULL; device = xmpp_stanza_get_next(device)) {
|
||||
const char *id = xmpp_stanza_get_id(device);
|
||||
device_list = g_list_append(device_list, GINT_TO_POINTER(strtoul(id, NULL, 10)));
|
||||
}
|
||||
}
|
||||
omemo_set_device_list(from, device_list);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int
|
||||
_omemo_bundle_publish_result(xmpp_stanza_t *const stanza, void *const userdata)
|
||||
{
|
||||
const char *type = xmpp_stanza_get_type(stanza);
|
||||
|
||||
if (g_strcmp0(type, STANZA_TYPE_ERROR) != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!GPOINTER_TO_INT(userdata)) {
|
||||
log_error("OMEMO: definitely cannot publish bundle with an open access model");
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_info("OMEMO: cannot publish bundle with open access model, trying to configure node");
|
||||
xmpp_ctx_t * const ctx = connection_get_ctx();
|
||||
Jid *jid = jid_create(connection_get_fulljid());
|
||||
char *id = connection_create_stanza_id("omemo_bundle_node_configure_request");
|
||||
char *node = g_strdup_printf("%s:%d", STANZA_NS_OMEMO_BUNDLES, omemo_device_id());
|
||||
xmpp_stanza_t *iq = stanza_create_pubsub_configure_request(ctx, id, jid->barejid, node);
|
||||
g_free(node);
|
||||
|
||||
iq_id_handler_add(id, _omemo_bundle_publish_configure, NULL, userdata);
|
||||
|
||||
iq_send_stanza(iq);
|
||||
|
||||
xmpp_stanza_release(iq);
|
||||
free(id);
|
||||
jid_destroy(jid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
_omemo_bundle_publish_configure(xmpp_stanza_t *const stanza, void *const userdata)
|
||||
{
|
||||
/* TODO handle error */
|
||||
xmpp_stanza_t *pubsub = xmpp_stanza_get_child_by_name(stanza, "pubsub");
|
||||
xmpp_stanza_t *configure = xmpp_stanza_get_child_by_name(pubsub, STANZA_NAME_CONFIGURE);
|
||||
xmpp_stanza_t *x = xmpp_stanza_get_child_by_name(configure, "x");
|
||||
|
||||
DataForm* form = form_create(x);
|
||||
char *tag = g_hash_table_lookup(form->var_to_tag, "pubsub#access_model");
|
||||
if (!tag) {
|
||||
log_info("OMEMO: cannot configure bundle to an open access model");
|
||||
return 0;
|
||||
}
|
||||
form_set_value(form, tag, "open");
|
||||
|
||||
xmpp_ctx_t * const ctx = connection_get_ctx();
|
||||
Jid *jid = jid_create(connection_get_fulljid());
|
||||
char *id = connection_create_stanza_id("omemo_bundle_node_configure_submit");
|
||||
char *node = g_strdup_printf("%s:%d", STANZA_NS_OMEMO_BUNDLES, omemo_device_id());
|
||||
xmpp_stanza_t *iq = stanza_create_pubsub_configure_submit(ctx, id, jid->barejid, node, form);
|
||||
g_free(node);
|
||||
|
||||
iq_id_handler_add(id, _omemo_bundle_publish_configure_result, NULL, userdata);
|
||||
|
||||
iq_send_stanza(iq);
|
||||
|
||||
xmpp_stanza_release(iq);
|
||||
free(id);
|
||||
jid_destroy(jid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
_omemo_bundle_publish_configure_result(xmpp_stanza_t *const stanza, void *const userdata)
|
||||
{
|
||||
const char *type = xmpp_stanza_get_type(stanza);
|
||||
|
||||
if (g_strcmp0(type, STANZA_TYPE_ERROR) == 0) {
|
||||
log_error("OMEMO: cannot configure bundle to an open access model");
|
||||
return 0;
|
||||
}
|
||||
|
||||
omemo_bundle_publish(TRUE);
|
||||
|
||||
return 0;
|
||||
}
|
11
src/xmpp/omemo.h
Normal file
11
src/xmpp/omemo.h
Normal file
@ -0,0 +1,11 @@
|
||||
#include <glib.h>
|
||||
|
||||
#include "xmpp/iq.h"
|
||||
|
||||
void omemo_devicelist_subscribe(void);
|
||||
void omemo_devicelist_publish(GList *device_list);
|
||||
void omemo_devicelist_request(const char * const jid);
|
||||
void omemo_bundle_publish(gboolean first);
|
||||
void omemo_bundle_request(const char * const jid, uint32_t device_id, ProfIqCallback func, ProfIqFreeCallback free_func, void *userdata);
|
||||
int omemo_start_device_session_handle_bundle(xmpp_stanza_t *const stanza, void *const userdata);
|
||||
char * omemo_receive_message(xmpp_stanza_t *const stanza);
|
@ -137,7 +137,7 @@ roster_send_add_to_group(const char *const group, PContact contact)
|
||||
}
|
||||
|
||||
xmpp_ctx_t * const ctx = connection_get_ctx();
|
||||
iq_id_handler_add(unique_id, _group_add_id_handler, (ProfIdFreeCallback)_free_group_data, data);
|
||||
iq_id_handler_add(unique_id, _group_add_id_handler, (ProfIqFreeCallback)_free_group_data, data);
|
||||
xmpp_stanza_t *iq = stanza_create_roster_set(ctx, unique_id, p_contact_barejid(contact),
|
||||
p_contact_name(contact), new_groups);
|
||||
iq_send_stanza(iq);
|
||||
@ -180,7 +180,7 @@ roster_send_remove_from_group(const char *const group, PContact contact)
|
||||
data->name = strdup(p_contact_barejid(contact));
|
||||
}
|
||||
|
||||
iq_id_handler_add(unique_id, _group_remove_id_handler, (ProfIdFreeCallback)_free_group_data, data);
|
||||
iq_id_handler_add(unique_id, _group_remove_id_handler, (ProfIqFreeCallback)_free_group_data, data);
|
||||
xmpp_stanza_t *iq = stanza_create_roster_set(ctx, unique_id, p_contact_barejid(contact),
|
||||
p_contact_name(contact), new_groups);
|
||||
iq_send_stanza(iq);
|
||||
|
@ -60,6 +60,11 @@
|
||||
#include "xmpp/chat_session.h"
|
||||
#include "xmpp/jid.h"
|
||||
|
||||
#ifdef HAVE_OMEMO
|
||||
#include "omemo/omemo.h"
|
||||
#include "xmpp/omemo.h"
|
||||
#endif
|
||||
|
||||
// for auto reconnect
|
||||
static struct {
|
||||
char *name;
|
||||
@ -286,6 +291,12 @@ session_get_account_name(void)
|
||||
void
|
||||
session_login_success(gboolean secured)
|
||||
{
|
||||
chat_sessions_init();
|
||||
|
||||
message_handlers_init();
|
||||
presence_handlers_init();
|
||||
iq_handlers_init();
|
||||
|
||||
// logged in with account
|
||||
if (saved_account.name) {
|
||||
log_debug("Connection handler: logged in with account name: %s", saved_account.name);
|
||||
@ -297,26 +308,20 @@ session_login_success(gboolean secured)
|
||||
accounts_add(saved_details.name, saved_details.altdomain, saved_details.port, saved_details.tls_policy);
|
||||
accounts_set_jid(saved_details.name, saved_details.jid);
|
||||
|
||||
sv_ev_login_account_success(saved_details.name, secured);
|
||||
saved_account.name = strdup(saved_details.name);
|
||||
saved_account.passwd = strdup(saved_details.passwd);
|
||||
|
||||
_session_free_saved_details();
|
||||
sv_ev_login_account_success(saved_account.name, secured);
|
||||
}
|
||||
|
||||
chat_sessions_init();
|
||||
|
||||
message_handlers_init();
|
||||
presence_handlers_init();
|
||||
iq_handlers_init();
|
||||
|
||||
roster_request();
|
||||
bookmark_request();
|
||||
blocking_request();
|
||||
|
||||
// items discovery
|
||||
connection_request_features();
|
||||
char *domain = connection_get_domain();
|
||||
iq_disco_info_request_onconnect(domain);
|
||||
iq_disco_items_request_onconnect(domain);
|
||||
|
||||
if (prefs_get_boolean(PREF_CARBONS)){
|
||||
|
@ -395,6 +395,18 @@ stanza_attach_hints_no_store(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza)
|
||||
return stanza;
|
||||
}
|
||||
|
||||
xmpp_stanza_t*
|
||||
stanza_attach_hints_store(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza)
|
||||
{
|
||||
xmpp_stanza_t *store = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_name(store, "store");
|
||||
xmpp_stanza_set_ns(store, STANZA_NS_HINTS);
|
||||
xmpp_stanza_add_child(stanza, store);
|
||||
xmpp_stanza_release(store);
|
||||
|
||||
return stanza;
|
||||
}
|
||||
|
||||
xmpp_stanza_t*
|
||||
stanza_attach_receipt_request(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza)
|
||||
{
|
||||
@ -1820,6 +1832,45 @@ stanza_get_error_message(xmpp_stanza_t *stanza)
|
||||
return strdup("unknown");
|
||||
}
|
||||
|
||||
void
|
||||
stanza_attach_publish_options(xmpp_ctx_t *const ctx, xmpp_stanza_t *const iq, const char *const option, const char *const value)
|
||||
{
|
||||
xmpp_stanza_t *publish_options = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_name(publish_options, STANZA_NAME_PUBLISH_OPTIONS);
|
||||
|
||||
xmpp_stanza_t *x = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_name(x, STANZA_NAME_X);
|
||||
xmpp_stanza_set_ns(x, STANZA_NS_DATA);
|
||||
xmpp_stanza_set_type(x, "submit");
|
||||
xmpp_stanza_add_child(publish_options, x);
|
||||
|
||||
xmpp_stanza_t *form_type = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_name(form_type, STANZA_NAME_FIELD);
|
||||
xmpp_stanza_set_attribute(form_type, STANZA_ATTR_VAR, "FORM_TYPE");
|
||||
xmpp_stanza_set_type(form_type, "hidden");
|
||||
xmpp_stanza_t *form_type_value = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_name(form_type_value, STANZA_NAME_VALUE);
|
||||
xmpp_stanza_t *form_type_value_text = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_text(form_type_value_text, XMPP_FEATURE_PUBSUB_PUBLISH_OPTIONS);
|
||||
xmpp_stanza_add_child(form_type_value, form_type_value_text);
|
||||
xmpp_stanza_add_child(form_type, form_type_value);
|
||||
xmpp_stanza_add_child(x, form_type);
|
||||
|
||||
xmpp_stanza_t *access_model = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_name(access_model, STANZA_NAME_FIELD);
|
||||
xmpp_stanza_set_attribute(access_model, STANZA_ATTR_VAR, option);
|
||||
xmpp_stanza_t *access_model_value = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_name(access_model_value, STANZA_NAME_VALUE);
|
||||
xmpp_stanza_t *access_model_value_text = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_text(access_model_value_text, value);
|
||||
xmpp_stanza_add_child(access_model_value, access_model_value_text);
|
||||
xmpp_stanza_add_child(access_model, access_model_value);
|
||||
xmpp_stanza_add_child(x, access_model);
|
||||
|
||||
xmpp_stanza_t *pubsub = xmpp_stanza_get_child_by_ns(iq, STANZA_NS_PUBSUB);
|
||||
xmpp_stanza_add_child(pubsub, publish_options);
|
||||
}
|
||||
|
||||
void
|
||||
stanza_attach_priority(xmpp_ctx_t *const ctx, xmpp_stanza_t *const presence, const int pri)
|
||||
{
|
||||
@ -2092,6 +2143,295 @@ stanza_create_command_config_submit_iq(xmpp_ctx_t *ctx, const char *const room,
|
||||
return iq;
|
||||
}
|
||||
|
||||
xmpp_stanza_t*
|
||||
stanza_create_omemo_devicelist_request(xmpp_ctx_t *ctx, const char *const id,
|
||||
const char *const jid)
|
||||
{
|
||||
xmpp_stanza_t *iq = xmpp_iq_new(ctx, STANZA_TYPE_GET, id);
|
||||
xmpp_stanza_set_to(iq, jid);
|
||||
|
||||
xmpp_stanza_t *pubsub = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_name(pubsub, STANZA_NAME_PUBSUB);
|
||||
xmpp_stanza_set_ns(pubsub, STANZA_NS_PUBSUB);
|
||||
|
||||
xmpp_stanza_t *items = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_name(items, "items");
|
||||
xmpp_stanza_set_attribute(items, STANZA_ATTR_NODE, STANZA_NS_OMEMO_DEVICELIST);
|
||||
|
||||
xmpp_stanza_add_child(pubsub, items);
|
||||
xmpp_stanza_add_child(iq, pubsub);
|
||||
|
||||
xmpp_stanza_release(items);
|
||||
xmpp_stanza_release(pubsub);
|
||||
|
||||
return iq;
|
||||
}
|
||||
|
||||
xmpp_stanza_t*
|
||||
stanza_create_omemo_devicelist_subscribe(xmpp_ctx_t *ctx, const char *const jid)
|
||||
{
|
||||
char *id = connection_create_stanza_id("omemo_devicelist_subscribe");
|
||||
xmpp_stanza_t *iq = xmpp_iq_new(ctx, STANZA_TYPE_SET, id);
|
||||
free(id);
|
||||
|
||||
xmpp_stanza_t *pubsub = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_name(pubsub, STANZA_NAME_PUBSUB);
|
||||
xmpp_stanza_set_ns(pubsub, STANZA_NS_PUBSUB);
|
||||
|
||||
xmpp_stanza_t *subscribe = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_name(subscribe, STANZA_NAME_SUBSCRIBE);
|
||||
xmpp_stanza_set_attribute(subscribe, STANZA_ATTR_NODE, STANZA_NS_OMEMO_DEVICELIST);
|
||||
xmpp_stanza_set_attribute(subscribe, "jid", jid);
|
||||
|
||||
xmpp_stanza_add_child(pubsub, subscribe);
|
||||
xmpp_stanza_add_child(iq, pubsub);
|
||||
|
||||
xmpp_stanza_release(subscribe);
|
||||
xmpp_stanza_release(pubsub);
|
||||
|
||||
return iq;
|
||||
}
|
||||
|
||||
xmpp_stanza_t*
|
||||
stanza_create_omemo_devicelist_publish(xmpp_ctx_t *ctx, GList *const ids)
|
||||
{
|
||||
char *id = connection_create_stanza_id("omemo_devicelist_publish");
|
||||
xmpp_stanza_t *iq = xmpp_iq_new(ctx, STANZA_TYPE_SET, id);
|
||||
free(id);
|
||||
|
||||
xmpp_stanza_t *pubsub = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_name(pubsub, STANZA_NAME_PUBSUB);
|
||||
xmpp_stanza_set_ns(pubsub, STANZA_NS_PUBSUB);
|
||||
|
||||
xmpp_stanza_t *publish = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_name(publish, STANZA_NAME_PUBLISH);
|
||||
xmpp_stanza_set_attribute(publish, STANZA_ATTR_NODE, STANZA_NS_OMEMO_DEVICELIST);
|
||||
|
||||
xmpp_stanza_t *item = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_name(item, STANZA_NAME_ITEM);
|
||||
xmpp_stanza_set_attribute(item, "id", "current");
|
||||
|
||||
xmpp_stanza_t *list = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_name(list, "list");
|
||||
xmpp_stanza_set_ns(list, "eu.siacs.conversations.axolotl");
|
||||
|
||||
GList *i;
|
||||
for (i = ids; i != NULL; i = i->next) {
|
||||
xmpp_stanza_t *device = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_name(device, "device");
|
||||
char *id = g_strdup_printf("%d", GPOINTER_TO_INT(i->data));
|
||||
xmpp_stanza_set_attribute(device, "id", id);
|
||||
g_free(id);
|
||||
|
||||
xmpp_stanza_add_child(list, device);
|
||||
xmpp_stanza_release(device);
|
||||
}
|
||||
|
||||
xmpp_stanza_add_child(item, list);
|
||||
xmpp_stanza_add_child(publish, item);
|
||||
xmpp_stanza_add_child(pubsub, publish);
|
||||
xmpp_stanza_add_child(iq, pubsub);
|
||||
|
||||
xmpp_stanza_release(list);
|
||||
xmpp_stanza_release(item);
|
||||
xmpp_stanza_release(publish);
|
||||
xmpp_stanza_release(pubsub);
|
||||
|
||||
return iq;
|
||||
}
|
||||
|
||||
xmpp_stanza_t*
|
||||
stanza_create_omemo_bundle_publish(xmpp_ctx_t *ctx, const char *const id,
|
||||
uint32_t device_id,
|
||||
const unsigned char * const identity_key, size_t identity_key_length,
|
||||
const unsigned char * const signed_prekey, size_t signed_prekey_length,
|
||||
const unsigned char * const signed_prekey_signature, size_t signed_prekey_signature_length,
|
||||
GList *const prekeys, GList *const prekeys_id, GList *const prekeys_length)
|
||||
{
|
||||
xmpp_stanza_t *iq = xmpp_iq_new(ctx, STANZA_TYPE_SET, id);
|
||||
|
||||
xmpp_stanza_t *pubsub = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_name(pubsub, STANZA_NAME_PUBSUB);
|
||||
xmpp_stanza_set_ns(pubsub, STANZA_NS_PUBSUB);
|
||||
|
||||
xmpp_stanza_t *publish = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_name(publish, STANZA_NAME_PUBLISH);
|
||||
char *node = g_strdup_printf("%s:%d", "eu.siacs.conversations.axolotl.bundles", device_id);
|
||||
xmpp_stanza_set_attribute(publish, STANZA_ATTR_NODE, node);
|
||||
g_free(node);
|
||||
|
||||
xmpp_stanza_t *item = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_name(item, STANZA_NAME_ITEM);
|
||||
xmpp_stanza_set_attribute(item, "id", "current");
|
||||
|
||||
xmpp_stanza_t *bundle = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_name(bundle, "bundle");
|
||||
xmpp_stanza_set_ns(bundle, "eu.siacs.conversations.axolotl");
|
||||
|
||||
xmpp_stanza_t *signed_prekey_public_stanza = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_name(signed_prekey_public_stanza , "signedPreKeyPublic");
|
||||
xmpp_stanza_set_attribute(signed_prekey_public_stanza, "signedPreKeyId", "1");
|
||||
|
||||
xmpp_stanza_t *signed_prekey_public_stanza_text= xmpp_stanza_new(ctx);
|
||||
char *signed_prekey_b64 = g_base64_encode(signed_prekey, signed_prekey_length);
|
||||
xmpp_stanza_set_text(signed_prekey_public_stanza_text, signed_prekey_b64);
|
||||
g_free(signed_prekey_b64);
|
||||
xmpp_stanza_add_child(signed_prekey_public_stanza, signed_prekey_public_stanza_text);
|
||||
xmpp_stanza_release(signed_prekey_public_stanza_text);
|
||||
|
||||
xmpp_stanza_t *signed_prekey_signature_stanza = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_name(signed_prekey_signature_stanza , "signedPreKeySignature");
|
||||
|
||||
xmpp_stanza_t *signed_prekey_signature_stanza_text= xmpp_stanza_new(ctx);
|
||||
char *signed_prekey_signature_b64 = g_base64_encode(signed_prekey_signature, signed_prekey_signature_length);
|
||||
xmpp_stanza_set_text(signed_prekey_signature_stanza_text, signed_prekey_signature_b64);
|
||||
g_free(signed_prekey_signature_b64);
|
||||
xmpp_stanza_add_child(signed_prekey_signature_stanza, signed_prekey_signature_stanza_text);
|
||||
xmpp_stanza_release(signed_prekey_signature_stanza_text);
|
||||
|
||||
xmpp_stanza_t *identity_key_stanza = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_name(identity_key_stanza , "identityKey");
|
||||
|
||||
xmpp_stanza_t *identity_key_stanza_text= xmpp_stanza_new(ctx);
|
||||
char *identity_key_b64 = g_base64_encode(identity_key, identity_key_length);
|
||||
xmpp_stanza_set_text(identity_key_stanza_text, identity_key_b64);
|
||||
g_free(identity_key_b64);
|
||||
xmpp_stanza_add_child(identity_key_stanza, identity_key_stanza_text);
|
||||
xmpp_stanza_release(identity_key_stanza_text);
|
||||
|
||||
xmpp_stanza_t *prekeys_stanza = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_name(prekeys_stanza, "prekeys");
|
||||
|
||||
GList *p, *i, *l;
|
||||
for (p = prekeys, i = prekeys_id, l = prekeys_length; p != NULL; p = p->next, i = i->next, l = l->next) {
|
||||
xmpp_stanza_t *prekey = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_name(prekey, "preKeyPublic");
|
||||
char *id = g_strdup_printf("%d", GPOINTER_TO_INT(i->data));
|
||||
xmpp_stanza_set_attribute(prekey, "preKeyId", id);
|
||||
g_free(id);
|
||||
|
||||
xmpp_stanza_t *prekey_text = xmpp_stanza_new(ctx);
|
||||
char *prekey_b64 = g_base64_encode(p->data, GPOINTER_TO_INT(l->data));
|
||||
xmpp_stanza_set_text(prekey_text, prekey_b64);
|
||||
g_free(prekey_b64);
|
||||
|
||||
xmpp_stanza_add_child(prekey, prekey_text);
|
||||
xmpp_stanza_add_child(prekeys_stanza, prekey);
|
||||
xmpp_stanza_release(prekey_text);
|
||||
xmpp_stanza_release(prekey);
|
||||
}
|
||||
|
||||
xmpp_stanza_add_child(bundle, signed_prekey_public_stanza);
|
||||
xmpp_stanza_add_child(bundle, signed_prekey_signature_stanza);
|
||||
xmpp_stanza_add_child(bundle, identity_key_stanza);
|
||||
xmpp_stanza_add_child(bundle, prekeys_stanza);
|
||||
xmpp_stanza_add_child(item, bundle);
|
||||
xmpp_stanza_add_child(publish, item);
|
||||
xmpp_stanza_add_child(pubsub, publish);
|
||||
xmpp_stanza_add_child(iq, pubsub);
|
||||
|
||||
xmpp_stanza_release(signed_prekey_public_stanza);
|
||||
xmpp_stanza_release(signed_prekey_signature_stanza);
|
||||
xmpp_stanza_release(identity_key_stanza);
|
||||
xmpp_stanza_release(prekeys_stanza);
|
||||
xmpp_stanza_release(bundle);
|
||||
xmpp_stanza_release(item);
|
||||
xmpp_stanza_release(publish);
|
||||
xmpp_stanza_release(pubsub);
|
||||
|
||||
return iq;
|
||||
}
|
||||
|
||||
xmpp_stanza_t*
|
||||
stanza_create_omemo_bundle_request(xmpp_ctx_t *ctx, const char *const id, const char *const jid, uint32_t device_id)
|
||||
{
|
||||
xmpp_stanza_t *iq = xmpp_iq_new(ctx, STANZA_TYPE_GET, id);
|
||||
xmpp_stanza_set_to(iq, jid);
|
||||
|
||||
xmpp_stanza_t *pubsub = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_name(pubsub, STANZA_NAME_PUBSUB);
|
||||
xmpp_stanza_set_ns(pubsub, STANZA_NS_PUBSUB);
|
||||
|
||||
xmpp_stanza_t *items = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_name(items, "items");
|
||||
char *node = g_strdup_printf("%s:%d", STANZA_NS_OMEMO_BUNDLES, device_id);
|
||||
xmpp_stanza_set_attribute(items, STANZA_ATTR_NODE, node);
|
||||
g_free(node);
|
||||
|
||||
xmpp_stanza_add_child(pubsub, items);
|
||||
xmpp_stanza_add_child(iq, pubsub);
|
||||
|
||||
xmpp_stanza_release(items);
|
||||
xmpp_stanza_release(pubsub);
|
||||
|
||||
return iq;
|
||||
}
|
||||
|
||||
xmpp_stanza_t*
|
||||
stanza_create_pubsub_configure_request(xmpp_ctx_t *ctx, const char *const id, const char *const jid, const char *const node)
|
||||
{
|
||||
xmpp_stanza_t *iq = xmpp_iq_new(ctx, STANZA_TYPE_GET, id);
|
||||
xmpp_stanza_set_to(iq, jid);
|
||||
|
||||
xmpp_stanza_t *pubsub = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_name(pubsub, STANZA_NAME_PUBSUB);
|
||||
xmpp_stanza_set_ns(pubsub, STANZA_NS_PUBSUB_OWNER);
|
||||
|
||||
xmpp_stanza_t *configure = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_name(configure, STANZA_NAME_CONFIGURE);
|
||||
xmpp_stanza_set_attribute(configure, STANZA_ATTR_NODE, node);
|
||||
|
||||
xmpp_stanza_add_child(pubsub, configure);
|
||||
xmpp_stanza_add_child(iq, pubsub);
|
||||
|
||||
xmpp_stanza_release(configure);
|
||||
xmpp_stanza_release(pubsub);
|
||||
|
||||
return iq;
|
||||
}
|
||||
|
||||
xmpp_stanza_t*
|
||||
stanza_create_pubsub_configure_submit(xmpp_ctx_t *ctx, const char *const id, const char *const jid, const char *const node, DataForm *form)
|
||||
{
|
||||
xmpp_stanza_t *iq = xmpp_iq_new(ctx, STANZA_TYPE_SET, id);
|
||||
xmpp_stanza_set_to(iq, jid);
|
||||
|
||||
xmpp_stanza_t *pubsub = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_name(pubsub, STANZA_NAME_PUBSUB);
|
||||
xmpp_stanza_set_ns(pubsub, STANZA_NS_PUBSUB_OWNER);
|
||||
|
||||
xmpp_stanza_t *configure = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_name(configure, STANZA_NAME_CONFIGURE);
|
||||
xmpp_stanza_set_attribute(configure, STANZA_ATTR_NODE, node);
|
||||
|
||||
xmpp_stanza_t *x = form_create_submission(form);
|
||||
|
||||
xmpp_stanza_add_child(configure, x);
|
||||
xmpp_stanza_add_child(pubsub, configure);
|
||||
xmpp_stanza_add_child(iq, pubsub);
|
||||
|
||||
xmpp_stanza_release(x);
|
||||
xmpp_stanza_release(configure);
|
||||
xmpp_stanza_release(pubsub);
|
||||
|
||||
return iq;
|
||||
}
|
||||
|
||||
xmpp_stanza_t*
|
||||
stanza_attach_origin_id(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza, const char *const id)
|
||||
{
|
||||
xmpp_stanza_t *origin_id = xmpp_stanza_new(ctx);
|
||||
xmpp_stanza_set_name(origin_id, STANZA_NAME_ORIGIN_ID);
|
||||
xmpp_stanza_set_ns(origin_id, STANZA_NS_STABLE_ID);
|
||||
xmpp_stanza_set_attribute(origin_id, STANZA_ATTR_ID, id);
|
||||
|
||||
xmpp_stanza_add_child(stanza, origin_id);
|
||||
|
||||
xmpp_stanza_release(origin_id);
|
||||
|
||||
return stanza;
|
||||
}
|
||||
|
||||
static void
|
||||
_stanza_add_unique_id(xmpp_stanza_t *stanza, char *prefix)
|
||||
{
|
||||
|
@ -82,6 +82,7 @@
|
||||
#define STANZA_NAME_PUBSUB "pubsub"
|
||||
#define STANZA_NAME_PUBLISH "publish"
|
||||
#define STANZA_NAME_PUBLISH_OPTIONS "publish-options"
|
||||
#define STANZA_NAME_SUBSCRIBE "subscribe"
|
||||
#define STANZA_NAME_FIELD "field"
|
||||
#define STANZA_NAME_STORAGE "storage"
|
||||
#define STANZA_NAME_NICK "nick"
|
||||
@ -100,6 +101,8 @@
|
||||
#define STANZA_NAME_GET "get"
|
||||
#define STANZA_NAME_URL "url"
|
||||
#define STANZA_NAME_COMMAND "command"
|
||||
#define STANZA_NAME_CONFIGURE "configure"
|
||||
#define STANZA_NAME_ORIGIN_ID "origin-id"
|
||||
|
||||
// error conditions
|
||||
#define STANZA_NAME_BAD_REQUEST "bad-request"
|
||||
@ -179,6 +182,8 @@
|
||||
#define STANZA_NS_CONFERENCE "jabber:x:conference"
|
||||
#define STANZA_NS_CAPTCHA "urn:xmpp:captcha"
|
||||
#define STANZA_NS_PUBSUB "http://jabber.org/protocol/pubsub"
|
||||
#define STANZA_NS_PUBSUB_OWNER "http://jabber.org/protocol/pubsub#owner"
|
||||
#define STANZA_NS_PUBSUB_EVENT "http://jabber.org/protocol/pubsub#event"
|
||||
#define STANZA_NS_CARBONS "urn:xmpp:carbons:2"
|
||||
#define STANZA_NS_HINTS "urn:xmpp:hints"
|
||||
#define STANZA_NS_FORWARD "urn:xmpp:forward:0"
|
||||
@ -189,6 +194,10 @@
|
||||
#define STANZA_NS_X_OOB "jabber:x:oob"
|
||||
#define STANZA_NS_BLOCKING "urn:xmpp:blocking"
|
||||
#define STANZA_NS_COMMAND "http://jabber.org/protocol/commands"
|
||||
#define STANZA_NS_OMEMO "eu.siacs.conversations.axolotl"
|
||||
#define STANZA_NS_OMEMO_DEVICELIST "eu.siacs.conversations.axolotl.devicelist"
|
||||
#define STANZA_NS_OMEMO_BUNDLES "eu.siacs.conversations.axolotl.bundles"
|
||||
#define STANZA_NS_STABLE_ID "urn:xmpp:sid:0"
|
||||
|
||||
#define STANZA_DATAFORM_SOFTWARE "urn:xmpp:dataforms:softwareinfo"
|
||||
|
||||
@ -228,8 +237,10 @@ xmpp_stanza_t* stanza_attach_state(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza, const
|
||||
xmpp_stanza_t* stanza_attach_carbons_private(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza);
|
||||
xmpp_stanza_t* stanza_attach_hints_no_copy(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza);
|
||||
xmpp_stanza_t* stanza_attach_hints_no_store(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza);
|
||||
xmpp_stanza_t* stanza_attach_hints_store(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza);
|
||||
xmpp_stanza_t* stanza_attach_receipt_request(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza);
|
||||
xmpp_stanza_t* stanza_attach_x_oob_url(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza, const char *const url);
|
||||
xmpp_stanza_t* stanza_attach_origin_id(xmpp_ctx_t *ctx, xmpp_stanza_t *stanza, const char *const id);
|
||||
|
||||
xmpp_stanza_t* stanza_create_room_join_presence(xmpp_ctx_t *const ctx,
|
||||
const char *const full_room_jid, const char *const passwd);
|
||||
@ -284,6 +295,17 @@ xmpp_stanza_t* stanza_create_room_kick_iq(xmpp_ctx_t *const ctx, const char *con
|
||||
xmpp_stanza_t* stanza_create_command_exec_iq(xmpp_ctx_t *ctx, const char *const target, const char *const node);
|
||||
xmpp_stanza_t* stanza_create_command_config_submit_iq(xmpp_ctx_t *ctx, const char *const room, const char *const node, const char *const sessionid, DataForm *form);
|
||||
|
||||
void stanza_attach_publish_options(xmpp_ctx_t *const ctx, xmpp_stanza_t *const publish, const char *const option, const char *const value);
|
||||
|
||||
xmpp_stanza_t* stanza_create_omemo_devicelist_request(xmpp_ctx_t *ctx, const char *const id, const char *const jid);
|
||||
xmpp_stanza_t* stanza_create_omemo_devicelist_subscribe(xmpp_ctx_t *ctx, const char *const jid);
|
||||
xmpp_stanza_t* stanza_create_omemo_devicelist_publish(xmpp_ctx_t *ctx, GList *const ids);
|
||||
xmpp_stanza_t* stanza_create_omemo_bundle_publish(xmpp_ctx_t *ctx, const char *const id, uint32_t device_id, const unsigned char * const identity_key, size_t identity_key_length, const unsigned char * const signed_prekey, size_t signed_prekey_length, const unsigned char * const signed_prekey_signature, size_t signed_prekey_signature_length, GList *const prekeys, GList *const prekeys_id, GList *const prekeys_length);
|
||||
xmpp_stanza_t* stanza_create_omemo_bundle_request(xmpp_ctx_t *ctx, const char *const id, const char *const jid, uint32_t device_id);
|
||||
|
||||
xmpp_stanza_t* stanza_create_pubsub_configure_request(xmpp_ctx_t *ctx, const char *const id, const char *const jid, const char *const node);
|
||||
xmpp_stanza_t* stanza_create_pubsub_configure_submit(xmpp_ctx_t *ctx, const char *const id, const char *const jid, const char *const node, DataForm *form);
|
||||
|
||||
int stanza_get_idle_time(xmpp_stanza_t *const stanza);
|
||||
|
||||
void stanza_attach_priority(xmpp_ctx_t *const ctx, xmpp_stanza_t *const presence, const int pri);
|
||||
|
@ -35,6 +35,8 @@
|
||||
#ifndef XMPP_XMPP_H
|
||||
#define XMPP_XMPP_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef HAVE_LIBMESODE
|
||||
@ -61,6 +63,9 @@
|
||||
#define XMPP_FEATURE_LASTACTIVITY "jabber:iq:last"
|
||||
#define XMPP_FEATURE_MUC "http://jabber.org/protocol/muc"
|
||||
#define XMPP_FEATURE_COMMANDS "http://jabber.org/protocol/commands"
|
||||
#define XMPP_FEATURE_OMEMO_DEVICELIST_NOTIFY "eu.siacs.conversations.axolotl.devicelist+notify"
|
||||
#define XMPP_FEATURE_PUBSUB "http://jabber.org/protocol/pubsub"
|
||||
#define XMPP_FEATURE_PUBSUB_PUBLISH_OPTIONS "http://jabber.org/protocol/pubsub#publish-options"
|
||||
|
||||
typedef enum {
|
||||
JABBER_CONNECTING,
|
||||
@ -139,8 +144,9 @@ char* message_send_chat(const char *const barejid, const char *const msg, const
|
||||
gboolean request_receipt);
|
||||
char* message_send_chat_otr(const char *const barejid, const char *const msg, gboolean request_receipt);
|
||||
char* message_send_chat_pgp(const char *const barejid, const char *const msg, gboolean request_receipt);
|
||||
char* message_send_chat_omemo(const char *const jid, uint32_t sid, GList *keys, const unsigned char *const iv, size_t iv_len, const unsigned char *const ciphertext, size_t ciphertext_len, gboolean request_receipt, gboolean muc);
|
||||
void message_send_private(const char *const fulljid, const char *const msg, const char *const oob_url);
|
||||
void message_send_groupchat(const char *const roomjid, const char *const msg, const char *const oob_url);
|
||||
char* message_send_groupchat(const char *const roomjid, const char *const msg, const char *const oob_url);
|
||||
void message_send_groupchat_subject(const char *const roomjid, const char *const subject);
|
||||
void message_send_inactive(const char *const jid);
|
||||
void message_send_composing(const char *const jid);
|
||||
|
@ -58,10 +58,12 @@ void chat_log_init(void) {}
|
||||
void chat_log_msg_out(const char * const barejid, const char * const msg) {}
|
||||
void chat_log_otr_msg_out(const char * const barejid, const char * const msg) {}
|
||||
void chat_log_pgp_msg_out(const char * const barejid, const char * const msg) {}
|
||||
void chat_log_omemo_msg_out(const char *const barejid, const char *const msg) {}
|
||||
|
||||
void chat_log_msg_in(const char * const barejid, const char * const msg, GDateTime *timestamp) {}
|
||||
void chat_log_otr_msg_in(const char * const barejid, const char * const msg, gboolean was_decrypted, GDateTime *timestamp) {}
|
||||
void chat_log_pgp_msg_in(const char * const barejid, const char * const msg, GDateTime *timestamp) {}
|
||||
void chat_log_omemo_msg_in(const char *const barejid, const char *const msg, GDateTime *timestamp) {}
|
||||
|
||||
void chat_log_close(void) {}
|
||||
GSList * chat_log_get_previous(const gchar * const login,
|
||||
@ -71,5 +73,7 @@ GSList * chat_log_get_previous(const gchar * const login,
|
||||
}
|
||||
|
||||
void groupchat_log_init(void) {}
|
||||
void groupchat_log_chat(const gchar * const login, const gchar * const room,
|
||||
const gchar * const nick, const gchar * const msg) {}
|
||||
void groupchat_log_msg_in(const gchar *const room, const gchar *const nick, const gchar *const msg) {}
|
||||
void groupchat_log_msg_out(const gchar *const room, const gchar *const msg) {}
|
||||
void groupchat_log_omemo_msg_in(const gchar *const room, const gchar *const nick, const gchar *const msg) {}
|
||||
void groupchat_log_omemo_msg_out(const gchar *const room, const gchar *const msg) {}
|
||||
|
68
tests/unittests/omemo/stub_omemo.c
Normal file
68
tests/unittests/omemo/stub_omemo.c
Normal file
@ -0,0 +1,68 @@
|
||||
#include <glib.h>
|
||||
|
||||
#include "config/account.h"
|
||||
#include "ui/ui.h"
|
||||
|
||||
void omemo_init(void) {}
|
||||
|
||||
char*
|
||||
omemo_fingerprint_autocomplete(const char *const search_str, gboolean previous)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void omemo_fingerprint_autocomplete_reset(void) {}
|
||||
|
||||
char *
|
||||
omemo_format_fingerprint(const char *const fingerprint)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void omemo_generate_crypto_materials(ProfAccount *account) {}
|
||||
|
||||
gboolean
|
||||
omemo_is_trusted_jid(const char *const jid)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
omemo_is_trusted_identity(const char *const jid, const char *const fingerprint)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
GList *
|
||||
omemo_known_device_identities(const char *const jid)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
gboolean
|
||||
omemo_loaded(void)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void omemo_on_connect(ProfAccount *account) {}
|
||||
void omemo_on_disconnect(void) {}
|
||||
|
||||
char *
|
||||
omemo_on_message_send(ProfWin *win, const char *const message, gboolean request_receipt, gboolean muc)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *
|
||||
omemo_own_fingerprint(gboolean formatted)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void omemo_start_muc_sessions(const char *const roomjid) {}
|
||||
void omemo_start_session(const char *const barejid) {}
|
||||
void omemo_trust(const char *const jid, const char *const fingerprint_formatted) {}
|
||||
void omemo_untrust(const char *const jid, const char *const fingerprint_formatted) {}
|
||||
void omemo_devicelist_publish(GList *device_list) {}
|
||||
void omemo_publish_crypto_materials(void) {}
|
@ -190,7 +190,8 @@ void mucwin_occupant_role_and_affiliation_change(ProfMucWin *mucwin, const char
|
||||
const char * const affiliation, const char * const actor, const char * const reason) {}
|
||||
void mucwin_roster(ProfMucWin *mucwin, GList *occupants, const char * const presence) {}
|
||||
void mucwin_history(ProfMucWin *mucwin, const char * const nick, GDateTime *timestamp, const char * const message) {}
|
||||
void mucwin_message(ProfMucWin *mucwin, const char *const nick, const char *const message, GSList *mentions, GList *triggers) {}
|
||||
void mucwin_incoming_msg(ProfMucWin *mucwin, const char *const nick, const char *const message, const char *const id, GSList *mentions, GList *triggers, prof_enc_t enc_mode) {}
|
||||
void mucwin_outgoing_msg(ProfMucWin *mucwin, const char *const message, const char *const id, prof_enc_t enc_mode) {}
|
||||
void mucwin_subject(ProfMucWin *mucwin, const char * const nick, const char * const subject) {}
|
||||
void mucwin_requires_config(ProfMucWin *mucwin) {}
|
||||
void ui_room_destroy(const char * const roomjid) {}
|
||||
@ -337,6 +338,7 @@ void cons_show_presence_prefs(void) {}
|
||||
void cons_show_connection_prefs(void) {}
|
||||
void cons_show_otr_prefs(void) {}
|
||||
void cons_show_pgp_prefs(void) {}
|
||||
void cons_show_omemo_prefs(void) {}
|
||||
|
||||
void cons_show_account(ProfAccount *account)
|
||||
{
|
||||
|
@ -114,7 +114,10 @@ char* message_send_chat_pgp(const char * const barejid, const char * const msg,
|
||||
}
|
||||
|
||||
void message_send_private(const char * const fulljid, const char * const msg, const char *const oob_url) {}
|
||||
void message_send_groupchat(const char * const roomjid, const char * const msg, const char *const oob_url) {}
|
||||
char* message_send_groupchat(const char * const roomjid, const char * const msg, const char *const oob_url)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
void message_send_groupchat_subject(const char * const roomjid, const char * const subject) {}
|
||||
|
||||
void message_send_inactive(const char * const barejid) {}
|
||||
|
Loading…
Reference in New Issue
Block a user