about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--.github/FUNDING.yml12
-rw-r--r--.travis.yml45
-rw-r--r--Brewfile.travis20
-rw-r--r--Dockerfile.arch58
-rw-r--r--Dockerfile.debian49
-rw-r--r--Dockerfile.tumbleweed (renamed from Dockerfile)28
-rwxr-xr-xbootstrap.sh2
-rw-r--r--src/command/cmd_ac.c53
-rw-r--r--src/command/cmd_funcs.c91
-rw-r--r--src/config/theme.c8
-rw-r--r--src/event/server_events.c8
-rw-r--r--src/log.c15
-rw-r--r--src/omemo/omemo.c129
-rw-r--r--src/omemo/store.c9
-rw-r--r--src/plugins/plugins.c31
-rw-r--r--src/plugins/plugins.h4
-rw-r--r--theme_template2
-rw-r--r--themes/aqua1
-rw-r--r--themes/batman1
-rw-r--r--themes/bios2
-rw-r--r--themes/boothj52
-rw-r--r--themes/boothj5_laptop2
-rw-r--r--themes/boothj5_slack2
-rw-r--r--themes/forest1
-rw-r--r--themes/hacker1
-rw-r--r--themes/headache1
-rw-r--r--themes/joker1
-rw-r--r--themes/mono1
-rw-r--r--themes/orange1
-rw-r--r--themes/original1
-rw-r--r--themes/original_bright1
-rw-r--r--themes/shade1
-rwxr-xr-xtravis-build.sh180
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