1
1
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:
Michael Vetter 2019-04-11 10:58:22 +02:00 committed by GitHub
commit 61df0c8e85
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 5091 additions and 110 deletions

View File

@ -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

View File

@ -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"])

View File

@ -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)
{

View File

@ -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;

View File

@ -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
}

View File

@ -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

View File

@ -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);

View File

@ -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;
}

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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
View 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
View 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

File diff suppressed because it is too large Load Diff

55
src/omemo/omemo.h Normal file
View 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
View 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
View 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);

View File

@ -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);

View File

@ -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();

View File

@ -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;

View File

@ -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();
}

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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, ...);

View File

@ -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;

View File

@ -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;

View File

@ -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:

View File

@ -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)
{

View File

@ -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);

View File

@ -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;
}

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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
View 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
View 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);

View File

@ -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);

View File

@ -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)){

View File

@ -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)
{

View File

@ -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);

View File

@ -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);

View File

@ -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) {}

View 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) {}

View File

@ -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)
{

View File

@ -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) {}