diff options
-rw-r--r-- | .github/FUNDING.yml | 12 | ||||
-rw-r--r-- | .travis.yml | 45 | ||||
-rw-r--r-- | Brewfile.travis | 20 | ||||
-rw-r--r-- | Dockerfile.arch | 58 | ||||
-rw-r--r-- | Dockerfile.debian | 49 | ||||
-rw-r--r-- | Dockerfile.tumbleweed (renamed from Dockerfile) | 28 | ||||
-rwxr-xr-x | bootstrap.sh | 2 | ||||
-rw-r--r-- | src/command/cmd_ac.c | 53 | ||||
-rw-r--r-- | src/command/cmd_funcs.c | 91 | ||||
-rw-r--r-- | src/config/theme.c | 8 | ||||
-rw-r--r-- | src/event/server_events.c | 8 | ||||
-rw-r--r-- | src/log.c | 15 | ||||
-rw-r--r-- | src/omemo/omemo.c | 129 | ||||
-rw-r--r-- | src/omemo/store.c | 9 | ||||
-rw-r--r-- | src/plugins/plugins.c | 31 | ||||
-rw-r--r-- | src/plugins/plugins.h | 4 | ||||
-rw-r--r-- | theme_template | 2 | ||||
-rw-r--r-- | themes/aqua | 1 | ||||
-rw-r--r-- | themes/batman | 1 | ||||
-rw-r--r-- | themes/bios | 2 | ||||
-rw-r--r-- | themes/boothj5 | 2 | ||||
-rw-r--r-- | themes/boothj5_laptop | 2 | ||||
-rw-r--r-- | themes/boothj5_slack | 2 | ||||
-rw-r--r-- | themes/forest | 1 | ||||
-rw-r--r-- | themes/hacker | 1 | ||||
-rw-r--r-- | themes/headache | 1 | ||||
-rw-r--r-- | themes/joker | 1 | ||||
-rw-r--r-- | themes/mono | 1 | ||||
-rw-r--r-- | themes/orange | 1 | ||||
-rw-r--r-- | themes/original | 1 | ||||
-rw-r--r-- | themes/original_bright | 1 | ||||
-rw-r--r-- | themes/shade | 1 | ||||
-rwxr-xr-x | travis-build.sh | 180 |
33 files changed, 512 insertions, 251 deletions
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..416b4ddb --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: jubalh +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: # Replace with a single custom sponsorship URL diff --git a/.travis.yml b/.travis.yml index abdeaa98..ed87ab56 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,10 +1,47 @@ sudo: required -language: c +language: bash + services: - - docker + - docker # Linux tests are run in Docker containers. + +addons: + homebrew: + brewfile: Brewfile.travis # mac OS dependencies. + # libsignal-protocol-c is still not in the Travis CI Homebrew snapshot, the + # line below could be removed when the snapshot has been updated to speed up + # the OSX job. + update: true + +matrix: + include: + - os: linux + env: BUILD_FLAVOR=tumbleweed + - os: linux + env: BUILD_FLAVOR=debian + - os: linux + env: BUILD_FLAVOR=arch + - os: osx + env: + # Ensure that "keg-only" Homebrew versions are used. + - PKG_CONFIG_PATH="/usr/local/opt/ncurses/lib/pkgconfig:$PKG_CONFIG_PATH" + - PKG_CONFIG_PATH="/usr/local/opt/expat/lib/pkgconfig:$PKG_CONFIG_PATH" + - PKG_CONFIG_PATH="/usr/local/opt/curl/lib/pkgconfig:$PKG_CONFIG_PATH" + - PKG_CONFIG_PATH="/usr/local/opt/openssl/lib/pkgconfig:$PKG_CONFIG_PATH" + - PKG_CONFIG_PATH="/usr/local/opt/libffi/lib/pkgconfig:$PKG_CONFIG_PATH" before_install: - - docker build -f Dockerfile -t profanity . + - if [ "$TRAVIS_OS_NAME" = "linux" ]; then + docker build -f Dockerfile."$BUILD_FLAVOR" -t profanity .; + fi script: -- docker run -it profanity ./travis-build.sh + - if [ "$TRAVIS_OS_NAME" = "linux" ]; then + docker run -it profanity ./travis-build.sh; + fi + - if [ "$TRAVIS_OS_NAME" = "osx" ]; then + ./travis-build.sh; + fi + +after_failure: + - cat ./config.log + - env diff --git a/Brewfile.travis b/Brewfile.travis new file mode 100644 index 00000000..e6743e03 --- /dev/null +++ b/Brewfile.travis @@ -0,0 +1,20 @@ +brew 'autoconf' +brew 'autoconf-archive' +brew 'automake' +brew 'check' +brew 'curl' +brew 'expat' +brew 'glib' +brew 'gnutls' +brew 'gpgme' +brew 'gtk+' +brew 'libffi' +brew 'libotr' +brew 'libsignal-protocol-c' +brew 'libstrophe' +brew 'libtool' +brew 'ncurses' +brew 'openssl' +brew 'ossp-uuid' +brew 'pkg-config' +brew 'readline' diff --git a/Dockerfile.arch b/Dockerfile.arch new file mode 100644 index 00000000..4e0eee6d --- /dev/null +++ b/Dockerfile.arch @@ -0,0 +1,58 @@ +FROM archlinux/base + +RUN pacman -Syu --noconfirm && pacman -S --needed --noconfirm \ + autoconf \ + autoconf-archive \ + automake \ + base-devel \ + check \ + cmake \ + cmocka \ + curl \ + doxygen \ + expat \ + gcc \ + git \ + gpgme \ + gtk2 \ + libgcrypt \ + libmicrohttpd \ + libnotify \ + libotr \ + libtool \ + libxss \ + make \ + openssl \ + pkg-config \ + python \ + wget + +RUN mkdir -p /usr/src/{stabber,profanity} + +RUN useradd -mb /usr/src --shell=/bin/false aur && usermod -L aur +USER aur + +WORKDIR /usr/src/aur +RUN wget https://aur.archlinux.org/cgit/aur.git/snapshot/libstrophe-git.tar.gz +RUN wget https://aur.archlinux.org/cgit/aur.git/snapshot/libsignal-protocol-c.tar.gz +RUN tar -zxvf libstrophe-git.tar.gz +RUN tar -zxvf libsignal-protocol-c.tar.gz +RUN pushd libstrophe-git && makepkg && popd +RUN pushd libsignal-protocol-c && makepkg && popd + +USER root + +RUN pacman -U --noconfirm libstrophe-git/libstrophe-git-*.pkg.tar.xz +RUN pacman -U --noconfirm libsignal-protocol-c/libsignal-protocol-c-*.pkg.tar.xz + +WORKDIR /usr/src +RUN git clone git://github.com/boothj5/stabber.git + +WORKDIR /usr/src/stabber +RUN ./bootstrap.sh +RUN ./configure --prefix=/usr --disable-dependency-tracking +RUN make +RUN make install + +WORKDIR /usr/src/profanity +COPY . /usr/src/profanity diff --git a/Dockerfile.debian b/Dockerfile.debian new file mode 100644 index 00000000..29ceae80 --- /dev/null +++ b/Dockerfile.debian @@ -0,0 +1,49 @@ +# Build the latest Debian testing image +FROM debian:testing + +RUN apt-get update && apt-get install -y --no-install-recommends \ + autoconf \ + autoconf-archive \ + automake \ + expect \ + gcc \ + git \ + libcmocka-dev \ + libcurl3-dev \ + libgcrypt-dev \ + libglib2.0-dev \ + libgpgme11-dev \ + libgtk2.0-dev \ + libmicrohttpd-dev \ + libncursesw5-dev \ + libnotify-dev \ + libotr5-dev \ + libreadline-dev \ + libsignal-protocol-c-dev \ + libssl-dev \ + libtool \ + libxss-dev \ + make \ + pkg-config \ + python-dev + +RUN mkdir -p /usr/src/{stabber,libmesode,profanity} +WORKDIR /usr/src + +RUN git clone git://github.com/boothj5/stabber.git +RUN git clone git://github.com/profanity-im/libmesode.git + +WORKDIR /usr/src/stabber +RUN ./bootstrap.sh +RUN ./configure --prefix=/usr --disable-dependency-tracking +RUN make +RUN make install + +WORKDIR /usr/src/libmesode +RUN ./bootstrap.sh +RUN ./configure --prefix=/usr +RUN make +RUN make install + +WORKDIR /usr/src/profanity +COPY . /usr/src/profanity diff --git a/Dockerfile b/Dockerfile.tumbleweed index f0106b7f..9acf4cce 100644 --- a/Dockerfile +++ b/Dockerfile.tumbleweed @@ -5,35 +5,35 @@ FROM opensuse/tumbleweed # libmicrohttpd - for stabber # glibc-locale - to have en_US locale RUN zypper --non-interactive in --no-recommends \ - git \ - gcc \ autoconf \ autoconf-archive \ - make \ automake \ - libtool \ + expect-devel \ + gcc \ + git \ glib2-devel \ + glibc-locale \ gtk2-devel \ - expect-devel \ libXss-devel \ + libcmocka-devel \ libcurl-devel \ libexpat-devel \ + libgcrypt-devel \ libgpgme-devel \ libmesode-devel \ + libmicrohttpd-devel \ libnotify-devel \ libotr-devel \ + libsignal-protocol-c-devel \ + libtool \ libuuid-devel \ - libcmocka-devel \ + make \ ncurses-devel \ - python3-devel \ - python3 \ - python-devel \ python \ - readline-devel \ - libsignal-protocol-c-devel \ - libgcrypt-devel \ - libmicrohttpd-devel \ - glibc-locale + python-devel \ + python3 \ + python3-devel \ + readline-devel # https://github.com/openSUSE/docker-containers-build/issues/26 ENV LANG en_US.UTF-8 diff --git a/bootstrap.sh b/bootstrap.sh index abae7294..80624f20 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -1,4 +1,4 @@ #!/bin/sh mkdir -p m4 -autoreconf -i $@ +autoreconf -i "$@" diff --git a/src/command/cmd_ac.c b/src/command/cmd_ac.c index 5d1dff12..7e3837bc 100644 --- a/src/command/cmd_ac.c +++ b/src/command/cmd_ac.c @@ -2183,47 +2183,48 @@ _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_ac(input, "/omemo log", omemo_log_ac, TRUE, previous); + if (found) { + return found; } - found = autocomplete_param_with_func(input, "/omemo fingerprint", roster_contact_autocomplete, previous); + found = autocomplete_param_with_ac(input, "/omemo policy", omemo_policy_ac, TRUE, 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); + 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_no_with_func(input, "/omemo trust", 4, omemo_fingerprint_autocomplete, previous); + found = autocomplete_param_with_func(input, "/omemo fingerprint", roster_contact_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 policy", omemo_policy_ac, TRUE, 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", omemo_ac, TRUE, previous); diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c index 999afd10..b5e996e9 100644 --- a/src/command/cmd_funcs.c +++ b/src/command/cmd_funcs.c @@ -6555,12 +6555,20 @@ cmd_autoconnect(ProfWin *window, const char *const command, gchar **args) prefs_set_string(PREF_CONNECT_ACCOUNT, NULL); cons_show("Autoconnect account disabled."); } else if (strcmp(args[0], "set") == 0) { - prefs_set_string(PREF_CONNECT_ACCOUNT, args[1]); - cons_show("Autoconnect account set to: %s.", args[1]); + if (args[1] == NULL || strlen(args[1]) == 0) { + cons_bad_cmd_usage(command); + } else { + if (accounts_account_exists(args[1])) { + prefs_set_string(PREF_CONNECT_ACCOUNT, args[1]); + cons_show("Autoconnect account set to: %s.", args[1]); + } else { + cons_show_error("Account '%s' does not exist.", args[1]); + } + } } else { cons_bad_cmd_usage(command); } - return true; + return TRUE; } gboolean @@ -6904,12 +6912,14 @@ cmd_plugins_load(ProfWin *window, const char *const command, gchar **args) return TRUE; } - gboolean res = plugins_load(args[1]); + GString* error_message = g_string_new(NULL); + gboolean res = plugins_load(args[1], error_message); if (res) { cons_show("Loaded plugin: %s", args[1]); } else { - cons_show("Failed to load plugin: %s", args[1]); + cons_show("Failed to load plugin: %s. %s", args[1], error_message->str); } + g_string_free(error_message, TRUE); return TRUE; } @@ -6946,12 +6956,14 @@ cmd_plugins_reload(ProfWin *window, const char *const command, gchar **args) return TRUE; } - gboolean res = plugins_reload(args[1]); + GString* error_message = g_string_new(NULL); + gboolean res = plugins_reload(args[1], error_message); if (res) { cons_show("Reloaded plugin: %s", args[1]); } else { - cons_show("Failed to reload plugin: %s", args[1]); + cons_show("Failed to reload plugin: %s, %s", args[1], error_message); } + g_string_free(error_message, TRUE); return TRUE; } @@ -8048,6 +8060,8 @@ cmd_omemo_start(ProfWin *window, const char *const command, gchar **args) return TRUE; } + ProfChatWin *chatwin = NULL; + // recipient supplied if (args[1]) { char *contact = args[1]; @@ -8056,67 +8070,50 @@ cmd_omemo_start(ProfWin *window, const char *const command, gchar **args) barejid = contact; } - ProfChatWin *chatwin = wins_get_chat(barejid); + chatwin = wins_get_chat(barejid); if (!chatwin) { chatwin = chatwin_new(barejid); } ui_focus_win((ProfWin*)chatwin); + } else { + if (window->type == WIN_CHAT) { + chatwin = (ProfChatWin*)window; + assert(chatwin->memcheck == PROFCHATWIN_MEMCHECK); + } + } + if (chatwin) { if (chatwin->pgp_send) { - win_println(window, THEME_DEFAULT, '!', "You must disable PGP encryption before starting an OMEMO session."); + win_println((ProfWin*)chatwin, 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."); + win_println((ProfWin*)chatwin, 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."); + win_println((ProfWin*)chatwin, THEME_DEFAULT, '!', "You are already in an OMEMO session."); return TRUE; } - accounts_add_omemo_state(session_get_account_name(), barejid, TRUE); - omemo_start_session(barejid); + accounts_add_omemo_state(session_get_account_name(), chatwin->barejid, TRUE); + omemo_start_session(chatwin->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; - } - - accounts_add_omemo_state(session_get_account_name(), chatwin->barejid, 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); + } else if (window->type == WIN_MUC) { + ProfMucWin *mucwin = (ProfMucWin*)window; + assert(mucwin->memcheck == PROFMUCWIN_MEMCHECK); - if (muc_anonymity_type(mucwin->roomjid) == MUC_ANONYMITY_TYPE_NONANONYMOUS) { - accounts_add_omemo_state(session_get_account_name(), mucwin->roomjid, TRUE); - omemo_start_muc_sessions(mucwin->roomjid); - mucwin->is_omemo = TRUE; - } else { - win_println(window, THEME_DEFAULT, '!', "MUC must be non-anonymous (i.e. be configured to present real jid to anyone) in order to support OMEMO."); - } + if (muc_anonymity_type(mucwin->roomjid) == MUC_ANONYMITY_TYPE_NONANONYMOUS) { + accounts_add_omemo_state(session_get_account_name(), mucwin->roomjid, TRUE); + 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."); + win_println(window, THEME_DEFAULT, '!', "MUC must be non-anonymous (i.e. be configured to present real jid to anyone) in order to support OMEMO."); } - + } else { + win_println(window, THEME_DEFAULT, '-', "You must be in a regular chat window to start an OMEMO session."); } return TRUE; diff --git a/src/config/theme.c b/src/config/theme.c index a0ca8356..53f55401 100644 --- a/src/config/theme.c +++ b/src/config/theme.c @@ -574,6 +574,14 @@ _load_preferences(void) } } + if (g_key_file_has_key(theme, "ui", "omemo.char", NULL)) { + gchar *ch = g_key_file_get_string(theme, "ui", "omemo.char", NULL); + if (ch && strlen(ch) > 0) { + prefs_set_omemo_char(ch[0]); + g_free(ch); + } + } + if (g_key_file_has_key(theme, "ui", "titlebar.position", NULL) && g_key_file_has_key(theme, "ui", "mainwin.position", NULL) && g_key_file_has_key(theme, "ui", "statusbar.position", NULL) && diff --git a/src/event/server_events.c b/src/event/server_events.c index bb31f9c6..269de51b 100644 --- a/src/event/server_events.c +++ b/src/event/server_events.c @@ -89,15 +89,13 @@ sv_ev_login_account_success(char *account_name, gboolean secured) ui_handle_login_account_success(account, secured); - // attempt to rejoin rooms with passwords + // attempt to rejoin all rooms GList *rooms = muc_rooms(); GList *curr = rooms; while (curr) { char *password = muc_password(curr->data); - if (password) { - char *nick = muc_nick(curr->data); - presence_join_room(curr->data, nick, password); - } + char *nick = muc_nick(curr->data); + presence_join_room(curr->data, nick, password); curr = g_list_next(curr); } g_list_free(rooms); diff --git a/src/log.c b/src/log.c index b679eb1a..e8b2bcbd 100644 --- a/src/log.c +++ b/src/log.c @@ -227,14 +227,17 @@ log_level_from_string(char *log_level) static void _rotate_log_file(void) { - char *log_file = files_get_log_file(); + gchar *log_file = files_get_log_file(); size_t len = strlen(log_file); - char *log_file_new = malloc(len + 3); + gchar *log_file_new = malloc(len + 4); + int i = 1; - memcpy(log_file_new, log_file, len); - log_file_new[len] = '.'; - log_file_new[len+1] = '1'; - log_file_new[len+2] = 0; + // find an empty name. from .log -> log.01 -> log.99 + for(; i<100; i++) { + g_sprintf(log_file_new, "%s.%02d", log_file, i); + if (!g_file_test(log_file_new, G_FILE_TEST_EXISTS)) + break; + } log_close(); rename(log_file, log_file_new); diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c index 15dd162d..5eebf790 100644 --- a/src/omemo/omemo.c +++ b/src/omemo/omemo.c @@ -34,6 +34,7 @@ static void _generate_signed_pre_key(void); static gboolean _load_identity(void); static void _load_trust(void); static void _load_sessions(void); +static void _load_known_devices(void); static void _lock(void *user_data); static void _unlock(void *user_data); static void _omemo_log(int level, const char *message, size_t len, void *user_data); @@ -41,6 +42,7 @@ static gboolean _handle_own_device_list(const char *const jid, GList *device_lis static gboolean _handle_device_list_start_session(const char *const jid, GList *device_list); static char * _omemo_fingerprint(ec_public_key *identity, gboolean formatted); static unsigned char *_omemo_fingerprint_decode(const char *const fingerprint, size_t *len); +static char * _omemo_unformat_fingerprint(const char *const fingerprint_formatted); static void _cache_device_identity(const char *const jid, uint32_t device_id, ec_public_key *identity); static void _g_hash_table_free(GHashTable *hash_table); @@ -69,6 +71,8 @@ struct omemo_context_t { GString *sessions_filename; GKeyFile *sessions_keyfile; GHashTable *known_devices; + GString *known_devices_filename; + GKeyFile *known_devices_keyfile; Autocomplete fingerprint_ac; }; @@ -196,6 +200,8 @@ omemo_on_connect(ProfAccount *account) g_string_append(omemo_ctx.trust_filename, "trust.txt"); omemo_ctx.sessions_filename = g_string_new(basedir->str); g_string_append(omemo_ctx.sessions_filename, "sessions.txt"); + omemo_ctx.known_devices_filename = g_string_new(basedir->str); + g_string_append(omemo_ctx.known_devices_filename, "known_devices.txt"); errno = 0; @@ -216,6 +222,7 @@ omemo_on_connect(ProfAccount *account) omemo_ctx.identity_keyfile = g_key_file_new(); omemo_ctx.trust_keyfile = g_key_file_new(); omemo_ctx.sessions_keyfile = g_key_file_new(); + omemo_ctx.known_devices_keyfile = g_key_file_new(); if (g_key_file_load_from_file(omemo_ctx.identity_keyfile, omemo_ctx.identity_filename->str, G_KEY_FILE_KEEP_COMMENTS, &error)) { if (!_load_identity()) { @@ -230,7 +237,7 @@ omemo_on_connect(ProfAccount *account) if (g_key_file_load_from_file(omemo_ctx.trust_keyfile, omemo_ctx.trust_filename->str, G_KEY_FILE_KEEP_COMMENTS, &error)) { _load_trust(); } else if (error->code != G_FILE_ERROR_NOENT) { - log_warning("OMEMO: error loading trust from: %s, %s", omemo_ctx.sessions_filename->str, error->message); + log_warning("OMEMO: error loading trust from: %s, %s", omemo_ctx.trust_filename->str, error->message); } error = NULL; @@ -239,6 +246,13 @@ omemo_on_connect(ProfAccount *account) } else if (error->code != G_FILE_ERROR_NOENT) { log_warning("OMEMO: error loading sessions from: %s, %s", omemo_ctx.sessions_filename->str, error->message); } + + error = NULL; + if (g_key_file_load_from_file(omemo_ctx.known_devices_keyfile, omemo_ctx.known_devices_filename->str, G_KEY_FILE_KEEP_COMMENTS, &error)) { + _load_known_devices(); + } else if (error->code != G_FILE_ERROR_NOENT) { + log_warning("OMEMO: error loading known devices from: %s, %s", omemo_ctx.known_devices_filename->str, error->message); + } } void @@ -257,6 +271,8 @@ omemo_on_disconnect(void) g_key_file_free(omemo_ctx.trust_keyfile); g_string_free(omemo_ctx.sessions_filename, TRUE); g_key_file_free(omemo_ctx.sessions_keyfile); + g_string_free(omemo_ctx.known_devices_filename, TRUE); + g_key_file_free(omemo_ctx.known_devices_keyfile); } void @@ -536,6 +552,16 @@ omemo_sessions_keyfile_save(void) } void +omemo_known_devices_keyfile_save(void) +{ + GError *error = NULL; + + if (!g_key_file_save_to_file(omemo_ctx.known_devices_keyfile, omemo_ctx.known_devices_filename->str, &error)) { + log_error("OMEMO: error saving known devices to: %s, %s", omemo_ctx.known_devices_filename->str, error->message); + } +} + +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_raw, size_t signed_prekey_len, @@ -912,6 +938,25 @@ omemo_format_fingerprint(const char *const fingerprint) return output; } +static char * +_omemo_unformat_fingerprint(const char *const fingerprint_formatted) +{ + /* Unformat fingerprint */ + char *fingerprint = malloc(strlen(fingerprint_formatted)); + int i; + int j; + for (i = 0, j = 0; fingerprint_formatted[i] != '\0'; i++) { + if (!g_ascii_isxdigit(fingerprint_formatted[i])) { + continue; + } + fingerprint[j++] = fingerprint_formatted[i]; + } + + fingerprint[j] = '\0'; + + return fingerprint; +} + char * omemo_own_fingerprint(gboolean formatted) { @@ -1040,18 +1085,7 @@ omemo_trust(const char *const jid, const char *const fingerprint_formatted) return; } - /* Unformat fingerprint */ - char *fingerprint = malloc(strlen(fingerprint_formatted)); - int i; - int j; - for (i = 0, j = 0; fingerprint_formatted[i] != '\0'; i++) { - if (!g_ascii_isxdigit(fingerprint_formatted[i])) { - continue; - } - fingerprint[j++] = fingerprint_formatted[i]; - } - - fingerprint[j] = '\0'; + char *fingerprint = _omemo_unformat_fingerprint(fingerprint_formatted); uint32_t device_id = GPOINTER_TO_INT(g_hash_table_lookup(known_identities, fingerprint)); free(fingerprint); @@ -1085,7 +1119,7 @@ void omemo_untrust(const char *const jid, const char *const fingerprint_formatted) { size_t len; - unsigned char *fingerprint = _omemo_fingerprint_decode(fingerprint_formatted, &len); + unsigned char *identity = _omemo_fingerprint_decode(fingerprint_formatted, &len); GHashTableIter iter; gpointer key, value; @@ -1101,10 +1135,41 @@ omemo_untrust(const char *const jid, const char *const fingerprint_formatted) unsigned char *original = signal_buffer_data(buffer); /* Skip DJB_TYPE byte */ original++; - if ((signal_buffer_len(buffer) - 1) == len && memcmp(original, fingerprint, len) == 0) { + if ((signal_buffer_len(buffer) - 1) == len && memcmp(original, identity, len) == 0) { g_hash_table_remove(trusted, key); } } + free(identity); + + char *fingerprint = _omemo_unformat_fingerprint(fingerprint_formatted); + + /* Remove existing session */ + GHashTable *known_identities = g_hash_table_lookup(omemo_ctx.known_devices, jid); + if (!known_identities) { + log_error("OMEMO: cannot find known device while untrusting a fingerprint"); + goto out; + } + + uint32_t device_id = GPOINTER_TO_INT(g_hash_table_lookup(known_identities, fingerprint)); + if (!device_id) { + log_error("OMEMO: cannot find device id while untrusting a fingerprint"); + goto out; + } + signal_protocol_address address = { + .name = jid, + .name_len = strlen(jid), + .device_id = device_id + }; + + delete_session(&address, omemo_ctx.session_store); + + /* Remove from keyfile */ + char *device_id_str = g_strdup_printf("%d", device_id); + g_key_file_remove_key(omemo_ctx.trust_keyfile, jid, device_id_str, NULL); + g_free(device_id_str); + omemo_trust_keyfile_save(); + +out: free(fingerprint); } @@ -1430,6 +1495,35 @@ _load_sessions(void) } static void +_load_known_devices(void) +{ + int i; + char **groups = g_key_file_get_groups(omemo_ctx.known_devices_keyfile, NULL); + if (groups) { + for (i = 0; groups[i] != NULL; i++) { + int j; + GHashTable *known_identities = NULL; + + known_identities = g_hash_table_lookup(omemo_ctx.known_devices, groups[i]); + if (!known_identities) { + known_identities = g_hash_table_new_full(g_str_hash, g_str_equal, free, NULL); + g_hash_table_insert(omemo_ctx.known_devices, strdup(groups[i]), known_identities); + } + + char **keys = g_key_file_get_keys(omemo_ctx.known_devices_keyfile, groups[i], NULL, NULL); + for (j = 0; keys[j] != NULL; j++) { + uint32_t device_id = strtoul(keys[j], NULL, 10); + char *fingerprint = g_key_file_get_string(omemo_ctx.known_devices_keyfile, groups[i], keys[j], NULL); + g_hash_table_insert(known_identities, strdup(fingerprint), GINT_TO_POINTER(device_id)); + g_free(fingerprint); + } + g_strfreev(keys); + } + g_strfreev(groups); + } +} + +static void _cache_device_identity(const char *const jid, uint32_t device_id, ec_public_key *identity) { GHashTable *known_identities = g_hash_table_lookup(omemo_ctx.known_devices, jid); @@ -1442,6 +1536,11 @@ _cache_device_identity(const char *const jid, uint32_t device_id, ec_public_key log_info("OMEMO: cache identity for %s:%d: %s", jid, device_id, fingerprint); g_hash_table_insert(known_identities, strdup(fingerprint), GINT_TO_POINTER(device_id)); + char *device_id_str = g_strdup_printf("%d", device_id); + g_key_file_set_string(omemo_ctx.known_devices_keyfile, jid, device_id_str, fingerprint); + g_free(device_id_str); + omemo_known_devices_keyfile_save(); + char *formatted_fingerprint = omemo_format_fingerprint(fingerprint); autocomplete_add(omemo_ctx.fingerprint_ac, formatted_fingerprint); free(formatted_fingerprint); diff --git a/src/omemo/store.c b/src/omemo/store.c index 76b7449c..4fa77d63 100644 --- a/src/omemo/store.c +++ b/src/omemo/store.c @@ -148,7 +148,14 @@ delete_session(const signal_protocol_address *address, void *user_data) return SG_SUCCESS; } - return g_hash_table_remove(device_store, GINT_TO_POINTER(address->device_id)); + g_hash_table_remove(device_store, GINT_TO_POINTER(address->device_id)); + + char *device_id_str = g_strdup_printf("%d", address->device_id); + g_key_file_remove_key(omemo_sessions_keyfile(), address->name, device_id_str, NULL); + g_free(device_id_str); + omemo_sessions_keyfile_save(); + + return SG_SUCCESS; } int diff --git a/src/plugins/plugins.c b/src/plugins/plugins.c index 4d109886..19d57c44 100644 --- a/src/plugins/plugins.c +++ b/src/plugins/plugins.c @@ -208,7 +208,7 @@ plugins_install(const char *const plugin_name, const char *const filename, GStri g_string_free(target_path, TRUE); if (result) { - result = plugins_load(plugin_name); + result = plugins_load(plugin_name, error_message); } return result; } @@ -219,11 +219,14 @@ plugins_load_all(void) GSList *plugins = plugins_unloaded_list(); GSList *loaded = NULL; GSList *curr = plugins; + GString *error_message = NULL; while (curr) { - if (plugins_load(curr->data)) { + error_message = g_string_new(NULL); + if (plugins_load(curr->data, error_message)) { loaded = g_slist_append(loaded, strdup(curr->data)); } curr = g_slist_next(curr); + g_string_free(error_message, TRUE); } g_slist_free_full(plugins, g_free); @@ -231,7 +234,7 @@ plugins_load_all(void) } gboolean -plugins_load(const char *const name) +plugins_load(const char *const name, GString *error_message) { ProfPlugin *plugin = g_hash_table_lookup(plugins, name); if (plugin) { @@ -239,16 +242,21 @@ plugins_load(const char *const name) return FALSE; } -#ifdef HAVE_PYTHON if (g_str_has_suffix(name, ".py")) { +#ifdef HAVE_PYTHON plugin = python_plugin_create(name); - } +#else + g_string_assign(error_message, "Python plugins support is disabled."); #endif -#ifdef HAVE_C + } + if (g_str_has_suffix(name, ".so")) { +#ifdef HAVE_C plugin = c_plugin_create(name); - } +#else + g_string_assign(error_message, "C plugins support is disabled."); #endif + } if (plugin) { g_hash_table_insert(plugins, strdup(name), plugin); if (connection_get_status() == JABBER_CONNECTED) { @@ -336,9 +344,12 @@ plugins_reload_all(void) } g_list_free(plugin_names); + GString *error_message = NULL; curr = plugin_names_dup; while (curr) { - plugins_reload(curr->data); + error_message = g_string_new(NULL); + plugins_reload(curr->data, error_message); + g_string_free(error_message, TRUE); curr = g_list_next(curr); } @@ -346,11 +357,11 @@ plugins_reload_all(void) } gboolean -plugins_reload(const char *const name) +plugins_reload(const char *const name, GString *error_message) { gboolean res = plugins_unload(name); if (res) { - res = plugins_load(name); + res = plugins_load(name, error_message); } return res; diff --git a/src/plugins/plugins.h b/src/plugins/plugins.h index 4267cb22..8b373cfe 100644 --- a/src/plugins/plugins.h +++ b/src/plugins/plugins.h @@ -118,11 +118,11 @@ gboolean plugins_install(const char *const plugin_name, const char *const filena gboolean plugins_uninstall(const char *const plugin_name); gboolean plugins_update(const char *const plugin_name, const char *const filename, GString * error_message); PluginsInstallResult* plugins_install_all(const char *const path); -gboolean plugins_load(const char *const name); +gboolean plugins_load(const char *const name, GString *error_message); GSList* plugins_load_all(void); gboolean plugins_unload(const char *const name); gboolean plugins_unload_all(void); -gboolean plugins_reload(const char *const name); +gboolean plugins_reload(const char *const name, GString *error_message); void plugins_reload_all(void); void plugins_on_start(void); diff --git a/theme_template b/theme_template index feab8bfc..d59425de 100644 --- a/theme_template +++ b/theme_template @@ -15,6 +15,7 @@ titlebar.dnd= titlebar.xa= statusbar= statusbar.text= +statusbar.time= statusbar.brackets= statusbar.active= statusbar.new= @@ -139,6 +140,7 @@ occupants.header.char= wins.autotidy= otr.char= pgp.char= +omemo.char= console.muc= console.chat= console.private= diff --git a/themes/aqua b/themes/aqua index 8c86da3a..48b3d4e7 100644 --- a/themes/aqua +++ b/themes/aqua @@ -15,6 +15,7 @@ titlebar.dnd=cyan titlebar.xa=bold_cyan statusbar=blue statusbar.text=bold_white +statusbar.time=bold_white statusbar.brackets=white statusbar.active=cyan statusbar.new=white diff --git a/themes/batman b/themes/batman index f9186525..fa7e7f18 100644 --- a/themes/batman +++ b/themes/batman @@ -5,6 +5,7 @@ statusbar=yellow titlebar.text=black titlebar.brackets=black statusbar.text=magenta +statusbar.time=magenta statusbar.brackets=black statusbar.active=magenta statusbar.new=magenta diff --git a/themes/bios b/themes/bios index aded8473..570641bd 100644 --- a/themes/bios +++ b/themes/bios @@ -15,6 +15,7 @@ titlebar.dnd=black titlebar.chat=black statusbar=white statusbar.text=black +statusbar.time=black statusbar.brackets=black statusbar.active=black statusbar.new=black @@ -129,6 +130,7 @@ occupants.jid=false wins.autotidy=true otr.char=@ pgp.char=% +omemo.char=* tls.show=true console.muc=first titlebar.position=1 diff --git a/themes/boothj5 b/themes/boothj5 index bbac89a6..ae8ff914 100644 --- a/themes/boothj5 +++ b/themes/boothj5 @@ -15,6 +15,7 @@ titlebar.dnd=bold_red titlebar.chat=bold_green statusbar=blue statusbar.text=bold_white +statusbar.time=bold_white statusbar.brackets=bold_white statusbar.active=bold_cyan statusbar.new=bold_white @@ -134,6 +135,7 @@ occupants.jid=false wins.autotidy=true otr.char=@ pgp.char=% +omemo.char=* tls.show=true console.muc=first console.chat=all diff --git a/themes/boothj5_laptop b/themes/boothj5_laptop index 4ca185fc..af3958bd 100644 --- a/themes/boothj5_laptop +++ b/themes/boothj5_laptop @@ -15,6 +15,7 @@ titlebar.dnd=bold_red titlebar.chat=bold_green statusbar=blue statusbar.text=bold_white +statusbar.time=bold_white statusbar.brackets=bold_white statusbar.active=bold_cyan statusbar.new=bold_white @@ -134,6 +135,7 @@ occupants.jid=false wins.autotidy=true otr.char=@ pgp.char=% +omemo.char=* tls.show=true console.muc=first console.chat=all diff --git a/themes/boothj5_slack b/themes/boothj5_slack index b8efb132..d1546d05 100644 --- a/themes/boothj5_slack +++ b/themes/boothj5_slack @@ -15,6 +15,7 @@ titlebar.dnd=bold_red titlebar.chat=bold_green statusbar=blue statusbar.text=bold_white +statusbar.time=bold_white statusbar.brackets=bold_white statusbar.active=bold_cyan statusbar.new=bold_white @@ -130,6 +131,7 @@ occupants.jid=false wins.autotidy=true otr.char=@ pgp.char=% +omemo.char=* tls.show=true console.muc=first console.chat=all diff --git a/themes/forest b/themes/forest index d216a2f1..5675e496 100644 --- a/themes/forest +++ b/themes/forest @@ -15,6 +15,7 @@ titlebar.dnd=white titlebar.xa=white statusbar=green statusbar.text=black +statusbar.time=black statusbar.brackets=black statusbar.active=bold_green statusbar.new=white diff --git a/themes/hacker b/themes/hacker index 95785bf9..8639dbe6 100644 --- a/themes/hacker +++ b/themes/hacker @@ -15,6 +15,7 @@ titlebar.dnd=black titlebar.xa=black statusbar=green statusbar.text=black +statusbar.time=black statusbar.brackets=black statusbar.active=black statusbar.new=black diff --git a/themes/headache b/themes/headache index fd8ccca5..5b2f5cba 100644 --- a/themes/headache +++ b/themes/headache @@ -15,6 +15,7 @@ titlebar.dnd=black titlebar.xa=white statusbar=default statusbar.text=white +statusbar.time=white statusbar.brackets=red statusbar.active=cyan statusbar.new=white diff --git a/themes/joker b/themes/joker index db5e6d54..39324896 100644 --- a/themes/joker +++ b/themes/joker @@ -15,6 +15,7 @@ titlebar.dnd=black titlebar.xa=white statusbar=magenta statusbar.text=green +statusbar.time=green statusbar.brackets=cyan statusbar.active=green statusbar.new=white diff --git a/themes/mono b/themes/mono index fcb16230..e40a3278 100644 --- a/themes/mono +++ b/themes/mono @@ -15,6 +15,7 @@ titlebar.dnd=black titlebar.xa=black statusbar=white statusbar.text=black +statusbar.time=black statusbar.brackets=black statusbar.active=black statusbar.new=black diff --git a/themes/orange b/themes/orange index 353dc4d3..b3a92ead 100644 --- a/themes/orange +++ b/themes/orange @@ -15,6 +15,7 @@ titlebar.dnd=black titlebar.xa=white statusbar=green statusbar.text=black +statusbar.time=black statusbar.brackets=blue statusbar.active=white statusbar.new=white diff --git a/themes/original b/themes/original index 38eba694..ef325bd0 100644 --- a/themes/original +++ b/themes/original @@ -15,6 +15,7 @@ titlebar.dnd=white titlebar.xa=white statusbar=blue statusbar.text=white +statusbar.time=white statusbar.brackets=cyan statusbar.active=cyan statusbar.new=white diff --git a/themes/original_bright b/themes/original_bright index fea535ca..8f073610 100644 --- a/themes/original_bright +++ b/themes/original_bright @@ -15,6 +15,7 @@ titlebar.dnd=bold_white titlebar.xa=bold_white statusbar=blue statusbar.text=bold_white +statusbar.time=bold_white statusbar.brackets=bold_cyan statusbar.active=bold_cyan statusbar.new=bold_white diff --git a/themes/shade b/themes/shade index 7672ea32..980b7e7f 100644 --- a/themes/shade +++ b/themes/shade @@ -15,6 +15,7 @@ titlebar.dnd=red titlebar.xa=green statusbar=default statusbar.text=magenta +statusbar.time=magenta statusbar.brackets=magenta statusbar.active=white statusbar.new=green diff --git a/travis-build.sh b/travis-build.sh index aa88105a..10ba684f 100755 --- a/travis-build.sh +++ b/travis-build.sh @@ -13,123 +13,63 @@ trap error_handler ERR ./bootstrap.sh -echo -echo "--> Building with ./configure --enable-notifications --enable-icons --enable-otr --enable-pgp --enable-omemo --enable-plugins --enable-c-plugins --enable-python-plugins --with-xscreensaver" -echo -./configure --enable-notifications --enable-icons --enable-otr --enable-pgp --enable-omemo --enable-plugins --enable-c-plugins --enable-python-plugins --with-xscreensaver -make -./profanity -v -make clean - -echo -echo "--> Building with ./configure --disable-notifications --disable-icons --disable-otr --disable-pgp --disable-omemo --disable-plugins --disable-c-plugins --disable-python-plugins --without-xscreensaver" -echo -./configure --disable-notifications --disable-icons --disable-otr --disable-pgp --disable-omemo --disable-plugins --disable-c-plugins --disable-python-plugins --without-xscreensaver -make -./profanity -v -make clean - -echo -echo "--> Building with ./configure --disable-notifications" -echo -./configure --disable-notifications -make -./profanity -v -make clean - -echo -echo "--> Building with ./configure --disable-icons" -echo -./configure --disable-icons -make -./profanity -v -make clean - -echo -echo "--> Building with ./configure --disable-otr" -echo -./configure --disable-otr -make -./profanity -v -make clean - -echo -echo "--> Building with ./configure --disable-pgp" -echo -./configure --disable-pgp -make -./profanity -v -make clean - -echo -echo "--> Building with ./configure --disable-omemo" -echo -./configure --disable-omemo -make -./profanity -v -make clean - -echo -echo "--> Building with ./configure --disable-pgp --disable-otr" -echo -./configure --disable-pgp --disable-otr -make -./profanity -v -make clean - -echo -echo "--> Building with ./configure --disable-pgp --disable-otr --disable-omemo" -echo -./configure --disable-pgp --disable-otr --disable-omemo -make -./profanity -v -make clean - -echo -echo "--> Building with ./configure --disable-plugins" -echo -./configure --disable-plugins -make -./profanity -v -make clean - -echo -echo "--> Building with ./configure --disable-python-plugins" -echo -./configure --disable-python-plugins -make -./profanity -v -make clean - -echo -echo "--> Building with ./configure --disable-c-plugins" -echo -./configure --disable-c-plugins -make -./profanity -v -make clean - -echo -echo "--> Building with ./configure --disable-c-plugins --disable-python-plugins" -echo -./configure --disable-c-plugins --disable-python-plugins -make -./profanity -v -make clean - -echo -echo "--> Building with ./configure --without-xscreensaver" -echo -./configure --without-xscreensaver -make -./profanity -v -make clean - -echo -echo "--> Building with ./configure" -echo -./configure -make -make check -./profanity -v -make clean +tests=() +case $(uname | tr '[:upper:]' '[:lower:]') in + linux*) + tests=( + "--enable-notifications --enable-icons --enable-otr --enable-pgp + --enable-omemo --enable-plugins --enable-c-plugins + --enable-python-plugins --with-xscreensaver" + "--disable-notifications --disable-icons --disable-otr --disable-pgp + --disable-omemo --disable-plugins --disable-c-plugins + --disable-python-plugins --without-xscreensaver" + "--disable-notifications" + "--disable-icons" + "--disable-otr" + "--disable-pgp" + "--disable-omemo" + "--disable-pgp --disable-otr" + "--disable-pgp --disable-otr --disable-omemo" + "--disable-plugins" + "--disable-python-plugins" + "--disable-c-plugins" + "--disable-c-plugins --disable-python-plugins" + "--without-xscreensaver" + "") + ;; + darwin*) + tests=( + "--enable-notifications --enable-icons --enable-otr --enable-pgp + --enable-omemo --enable-plugins --enable-c-plugins + --enable-python-plugins" + "--disable-notifications --disable-icons --disable-otr --disable-pgp + --disable-omemo --disable-plugins --disable-c-plugins + --disable-python-plugins" + "--disable-notifications" + "--disable-icons" + "--disable-otr" + "--disable-pgp" + "--disable-omemo" + "--disable-pgp --disable-otr" + "--disable-pgp --disable-otr --disable-omemo" + "--disable-plugins" + "--disable-python-plugins" + "--disable-c-plugins" + "--disable-c-plugins --disable-python-plugins" + "") + ;; +esac + +for flags in "${tests[@]}" +do + echo + echo "--> Building with ./configure $flags" + echo + # shellcheck disable=SC2086 + ./configure $flags + make + ./profanity -v + make clean + + echo "$flags" +done |