about summary refs log tree commit diff stats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/chatlog.c47
-rw-r--r--src/command/cmd_ac.c43
-rw-r--r--src/command/cmd_defs.c14
-rw-r--r--src/command/cmd_funcs.c87
-rw-r--r--src/common.c18
-rw-r--r--src/common.h6
-rw-r--r--src/config/files.c4
-rw-r--r--src/config/preferences.c47
-rw-r--r--src/config/theme.c2
-rw-r--r--src/config/tlscerts.c46
-rw-r--r--src/database.c2
-rw-r--r--src/database.h2
-rw-r--r--src/event/client_events.c16
-rw-r--r--src/event/client_events.h1
-rw-r--r--src/event/server_events.c4
-rw-r--r--src/main.c3
-rw-r--r--src/omemo/omemo.c56
-rw-r--r--src/omemo/store.c33
-rw-r--r--src/plugins/python_plugins.c6
-rw-r--r--src/profanity.c6
-rw-r--r--src/tools/autocomplete.c2
-rw-r--r--src/tools/editor.c20
-rw-r--r--src/tools/http_upload.c6
-rw-r--r--src/ui/chatwin.c2
-rw-r--r--src/ui/confwin.c2
-rw-r--r--src/ui/inputwin.c8
-rw-r--r--src/ui/mucwin.c16
-rw-r--r--src/ui/ui.h2
-rw-r--r--src/xmpp/capabilities.c21
-rw-r--r--src/xmpp/connection.c2
-rw-r--r--src/xmpp/connection.h1
-rw-r--r--src/xmpp/iq.c54
-rw-r--r--src/xmpp/jid.c7
-rw-r--r--src/xmpp/message.c17
-rw-r--r--src/xmpp/omemo.c6
-rw-r--r--src/xmpp/session.h1
-rw-r--r--src/xmpp/stanza.c178
-rw-r--r--src/xmpp/xmpp.h2
38 files changed, 313 insertions, 477 deletions
diff --git a/src/chatlog.c b/src/chatlog.c
index c154d916..90cac638 100644
--- a/src/chatlog.c
+++ b/src/chatlog.c
@@ -96,13 +96,12 @@ chat_log_otr_msg_out(const char* const barejid, const char* const msg, const cha
 {
     if (prefs_get_boolean(PREF_CHLOG)) {
         char* mybarejid = connection_get_barejid();
-        char* pref_otr_log = prefs_get_string(PREF_OTR_LOG);
+        auto_gchar gchar* pref_otr_log = prefs_get_string(PREF_OTR_LOG);
         if (strcmp(pref_otr_log, "on") == 0) {
             _chat_log_chat(mybarejid, barejid, msg, PROF_OUT_LOG, NULL, resource);
         } else if (strcmp(pref_otr_log, "redact") == 0) {
             _chat_log_chat(mybarejid, barejid, "[redacted]", PROF_OUT_LOG, NULL, resource);
         }
-        g_free(pref_otr_log);
         free(mybarejid);
     }
 }
@@ -112,13 +111,12 @@ chat_log_pgp_msg_out(const char* const barejid, const char* const msg, const cha
 {
     if (prefs_get_boolean(PREF_CHLOG)) {
         char* mybarejid = connection_get_barejid();
-        char* pref_pgp_log = prefs_get_string(PREF_PGP_LOG);
+        auto_gchar gchar* pref_pgp_log = prefs_get_string(PREF_PGP_LOG);
         if (strcmp(pref_pgp_log, "on") == 0) {
             _chat_log_chat(mybarejid, barejid, msg, PROF_OUT_LOG, NULL, resource);
         } else if (strcmp(pref_pgp_log, "redact") == 0) {
             _chat_log_chat(mybarejid, barejid, "[redacted]", PROF_OUT_LOG, NULL, resource);
         }
-        g_free(pref_pgp_log);
         free(mybarejid);
     }
 }
@@ -128,13 +126,12 @@ chat_log_omemo_msg_out(const char* const barejid, const char* const msg, const c
 {
     if (prefs_get_boolean(PREF_CHLOG)) {
         char* mybarejid = connection_get_barejid();
-        char* pref_omemo_log = prefs_get_string(PREF_OMEMO_LOG);
+        auto_gchar gchar* pref_omemo_log = prefs_get_string(PREF_OMEMO_LOG);
         if (strcmp(pref_omemo_log, "on") == 0) {
             _chat_log_chat(mybarejid, barejid, msg, PROF_OUT_LOG, NULL, resource);
         } else if (strcmp(pref_omemo_log, "redact") == 0) {
             _chat_log_chat(mybarejid, barejid, "[redacted]", PROF_OUT_LOG, NULL, resource);
         }
-        g_free(pref_omemo_log);
         free(mybarejid);
     }
 }
@@ -144,7 +141,7 @@ chat_log_otr_msg_in(ProfMessage* message)
 {
     if (prefs_get_boolean(PREF_CHLOG)) {
         char* mybarejid = connection_get_barejid();
-        char* pref_otr_log = prefs_get_string(PREF_OTR_LOG);
+        auto_gchar gchar* pref_otr_log = prefs_get_string(PREF_OTR_LOG);
         if (message->enc == PROF_MSG_ENC_NONE || (strcmp(pref_otr_log, "on") == 0)) {
             if (message->type == PROF_MSG_TYPE_MUCPM) {
                 _chat_log_chat(mybarejid, message->from_jid->barejid, message->plain, PROF_IN_LOG, message->timestamp, message->from_jid->resourcepart);
@@ -158,7 +155,6 @@ chat_log_otr_msg_in(ProfMessage* message)
                 _chat_log_chat(mybarejid, message->from_jid->barejid, "[redacted]", PROF_IN_LOG, message->timestamp, NULL);
             }
         }
-        g_free(pref_otr_log);
         free(mybarejid);
     }
 }
@@ -168,7 +164,7 @@ chat_log_pgp_msg_in(ProfMessage* message)
 {
     if (prefs_get_boolean(PREF_CHLOG)) {
         char* mybarejid = connection_get_barejid();
-        char* pref_pgp_log = prefs_get_string(PREF_PGP_LOG);
+        auto_gchar gchar* pref_pgp_log = prefs_get_string(PREF_PGP_LOG);
         if (strcmp(pref_pgp_log, "on") == 0) {
             if (message->type == PROF_MSG_TYPE_MUCPM) {
                 _chat_log_chat(mybarejid, message->from_jid->barejid, message->plain, PROF_IN_LOG, message->timestamp, message->from_jid->resourcepart);
@@ -182,7 +178,6 @@ chat_log_pgp_msg_in(ProfMessage* message)
                 _chat_log_chat(mybarejid, message->from_jid->barejid, "[redacted]", PROF_IN_LOG, message->timestamp, NULL);
             }
         }
-        g_free(pref_pgp_log);
         free(mybarejid);
     }
 }
@@ -192,7 +187,7 @@ chat_log_omemo_msg_in(ProfMessage* message)
 {
     if (prefs_get_boolean(PREF_CHLOG)) {
         char* mybarejid = connection_get_barejid();
-        char* pref_omemo_log = prefs_get_string(PREF_OMEMO_LOG);
+        auto_gchar gchar* pref_omemo_log = prefs_get_string(PREF_OMEMO_LOG);
         if (strcmp(pref_omemo_log, "on") == 0) {
             if (message->type == PROF_MSG_TYPE_MUCPM) {
                 _chat_log_chat(mybarejid, message->from_jid->barejid, message->plain, PROF_IN_LOG, message->timestamp, message->from_jid->resourcepart);
@@ -206,7 +201,6 @@ chat_log_omemo_msg_in(ProfMessage* message)
                 _chat_log_chat(mybarejid, message->from_jid->barejid, "[redacted]", PROF_IN_LOG, message->timestamp, message->from_jid->resourcepart);
             }
         }
-        g_free(pref_omemo_log);
         free(mybarejid);
     }
 }
@@ -272,7 +266,7 @@ _chat_log_chat(const char* const login, const char* const other, const char* con
         g_date_time_ref(timestamp);
     }
 
-    gchar* date_fmt = g_date_time_format_iso8601(timestamp);
+    auto_gchar gchar* date_fmt = g_date_time_format_iso8601(timestamp);
     FILE* chatlogp = fopen(dated_log->filename, "a");
     g_chmod(dated_log->filename, S_IRUSR | S_IWUSR);
     if (chatlogp) {
@@ -304,7 +298,6 @@ _chat_log_chat(const char* const login, const char* const other, const char* con
         }
     }
 
-    g_free(date_fmt);
     g_date_time_unref(timestamp);
 }
 
@@ -342,7 +335,7 @@ groupchat_log_omemo_msg_out(const gchar* const room, const gchar* const msg)
 {
     if (prefs_get_boolean(PREF_CHLOG)) {
         char* mybarejid = connection_get_barejid();
-        char* pref_omemo_log = prefs_get_string(PREF_OMEMO_LOG);
+        auto_gchar gchar* pref_omemo_log = prefs_get_string(PREF_OMEMO_LOG);
         char* mynick = muc_nick(room);
 
         if (strcmp(pref_omemo_log, "on") == 0) {
@@ -351,7 +344,6 @@ groupchat_log_omemo_msg_out(const gchar* const room, const gchar* const msg)
             _groupchat_log_chat(mybarejid, room, mynick, "[redacted]");
         }
 
-        g_free(pref_omemo_log);
         free(mybarejid);
     }
 }
@@ -360,17 +352,14 @@ void
 groupchat_log_omemo_msg_in(const gchar* const room, const gchar* const nick, const gchar* const msg)
 {
     if (prefs_get_boolean(PREF_CHLOG)) {
-        char* mybarejid = connection_get_barejid();
-        char* pref_omemo_log = prefs_get_string(PREF_OMEMO_LOG);
+        auto_char char* mybarejid = connection_get_barejid();
+        auto_gchar gchar* pref_omemo_log = prefs_get_string(PREF_OMEMO_LOG);
 
         if (strcmp(pref_omemo_log, "on") == 0) {
             _groupchat_log_chat(mybarejid, room, nick, msg);
         } else if (strcmp(pref_omemo_log, "redact") == 0) {
             _groupchat_log_chat(mybarejid, room, nick, "[redacted]");
         }
-
-        g_free(pref_omemo_log);
-        free(mybarejid);
     }
 }
 
@@ -393,7 +382,7 @@ _groupchat_log_chat(const gchar* const login, const gchar* const room, const gch
 
     GDateTime* dt_tmp = g_date_time_new_now_local();
 
-    gchar* date_fmt = g_date_time_format_iso8601(dt_tmp);
+    auto_gchar gchar* date_fmt = g_date_time_format_iso8601(dt_tmp);
 
     FILE* grpchatlogp = fopen(dated_log->filename, "a");
     g_chmod(dated_log->filename, S_IRUSR | S_IWUSR);
@@ -411,7 +400,6 @@ _groupchat_log_chat(const gchar* const login, const gchar* const room, const gch
         }
     }
 
-    g_free(date_fmt);
     g_date_time_unref(dt_tmp);
 }
 
@@ -425,21 +413,16 @@ chat_log_close(void)
 static char*
 _get_log_filename(const char* const other, const char* const login, GDateTime* dt, gboolean is_room)
 {
-    gchar* chatlogs_dir = files_file_in_account_data_path(DIR_CHATLOGS, login, is_room ? "rooms" : NULL);
-    gchar* logfile_name = g_date_time_format(dt, "%Y_%m_%d.log");
-    gchar* other_ = str_replace(other, "@", "_at_");
-    gchar* logs_path = g_strdup_printf("%s/%s", chatlogs_dir, other_);
+    auto_gchar gchar* chatlogs_dir = files_file_in_account_data_path(DIR_CHATLOGS, login, is_room ? "rooms" : NULL);
+    auto_gchar gchar* logfile_name = g_date_time_format(dt, "%Y_%m_%d.log");
+    auto_gchar gchar* other_ = str_replace(other, "@", "_at_");
+    auto_gchar gchar* logs_path = g_strdup_printf("%s/%s", chatlogs_dir, other_);
     gchar* logfile_path = NULL;
 
     if (create_dir(logs_path)) {
         logfile_path = g_strdup_printf("%s/%s", logs_path, logfile_name);
     }
 
-    g_free(logs_path);
-    g_free(other_);
-    g_free(logfile_name);
-    g_free(chatlogs_dir);
-
     return logfile_path;
 }
 
diff --git a/src/command/cmd_ac.c b/src/command/cmd_ac.c
index deda8abe..48d712e4 100644
--- a/src/command/cmd_ac.c
+++ b/src/command/cmd_ac.c
@@ -125,7 +125,7 @@ static char* _logging_autocomplete(ProfWin* window, const char* const input, gbo
 static char* _color_autocomplete(ProfWin* window, const char* const input, gboolean previous);
 static char* _avatar_autocomplete(ProfWin* window, const char* const input, gboolean previous);
 static char* _correction_autocomplete(ProfWin* window, const char* const input, gboolean previous);
-static char* _correct_autocomplete(ProfWin* window, const char* const input, gboolean previous);
+static gchar* _correct_autocomplete(ProfWin* window, const char* const input, gboolean previous);
 static char* _software_autocomplete(ProfWin* window, const char* const input, gboolean previous);
 static char* _url_autocomplete(ProfWin* window, const char* const input, gboolean previous);
 static char* _executable_autocomplete(ProfWin* window, const char* const input, gboolean previous);
@@ -277,6 +277,7 @@ static Autocomplete correction_ac;
 static Autocomplete avatar_ac;
 static Autocomplete url_ac;
 static Autocomplete executable_ac;
+static Autocomplete executable_param_ac;
 static Autocomplete intype_ac;
 static Autocomplete mood_ac;
 static Autocomplete mood_type_ac;
@@ -1115,6 +1116,11 @@ cmd_ac_init(void)
     autocomplete_add(executable_ac, "urlopen");
     autocomplete_add(executable_ac, "urlsave");
     autocomplete_add(executable_ac, "editor");
+    autocomplete_add(executable_ac, "vcard_photo");
+
+    executable_param_ac = autocomplete_new();
+    autocomplete_add(executable_param_ac, "set");
+    autocomplete_add(executable_param_ac, "default");
 
     intype_ac = autocomplete_new();
     autocomplete_add(intype_ac, "console");
@@ -1701,6 +1707,7 @@ cmd_ac_reset(ProfWin* window)
     autocomplete_reset(avatar_ac);
     autocomplete_reset(url_ac);
     autocomplete_reset(executable_ac);
+    autocomplete_reset(executable_param_ac);
     autocomplete_reset(intype_ac);
     autocomplete_reset(mood_ac);
     autocomplete_reset(mood_type_ac);
@@ -1888,6 +1895,7 @@ cmd_ac_uninit(void)
     autocomplete_free(avatar_ac);
     autocomplete_free(url_ac);
     autocomplete_free(executable_ac);
+    autocomplete_free(executable_param_ac);
     autocomplete_free(intype_ac);
     autocomplete_free(adhoc_cmd_ac);
     autocomplete_free(lastactivity_ac);
@@ -4291,17 +4299,15 @@ _avatar_autocomplete(ProfWin* window, const char* const input, gboolean previous
 static char*
 _correction_autocomplete(ProfWin* window, const char* const input, gboolean previous)
 {
-    char* result = NULL;
-
-    result = autocomplete_param_with_ac(input, "/correction", correction_ac, TRUE, previous);
+    char* result = autocomplete_param_with_ac(input, "/correction", correction_ac, TRUE, previous);
 
     return result;
 }
 
-static char*
+static gchar*
 _correct_autocomplete(ProfWin* window, const char* const input, gboolean previous)
 {
-    char* result = g_strdup_printf("/correct %s", win_get_last_sent_message(window));
+    gchar* result = g_strdup_printf("/correct %s", win_get_last_sent_message(window));
 
     return result;
 }
@@ -4381,6 +4387,31 @@ _executable_autocomplete(ProfWin* window, const char* const input, gboolean prev
 {
     char* result = NULL;
 
+    result = autocomplete_param_with_ac(input, "/executable avatar", executable_param_ac, TRUE, previous);
+    if (result) {
+        return result;
+    }
+
+    result = autocomplete_param_with_ac(input, "/executable urlopen", executable_param_ac, TRUE, previous);
+    if (result) {
+        return result;
+    }
+
+    result = autocomplete_param_with_ac(input, "/executable urlsave", executable_param_ac, TRUE, previous);
+    if (result) {
+        return result;
+    }
+
+    result = autocomplete_param_with_ac(input, "/executable vcard_photo", executable_param_ac, TRUE, previous);
+    if (result) {
+        return result;
+    }
+
+    result = autocomplete_param_with_ac(input, "/executable editor", executable_param_ac, TRUE, previous);
+    if (result) {
+        return result;
+    }
+
     result = autocomplete_param_with_ac(input, "/executable", executable_ac, TRUE, previous);
 
     return result;
diff --git a/src/command/cmd_defs.c b/src/command/cmd_defs.c
index 3b9fccfd..e8a97398 100644
--- a/src/command/cmd_defs.c
+++ b/src/command/cmd_defs.c
@@ -2438,7 +2438,7 @@ static const struct cmd_t command_defs[] = {
               { "set <path>", "Set avatar to the image at <path>." },
               { "disable", "Disable avatar publishing; your avatar will not display to others." },
               { "get <barejid>", "Download the avatar. barejid is the JID to download avatar from." },
-              { "open <barejid>", "Download avatar and open it with command." })
+              { "open <barejid>", "Download avatar and open it with command. See /executable." })
       CMD_EXAMPLES(
               "/avatar set ~/images/avatar.png",
               "/avatar disable",
@@ -2535,23 +2535,27 @@ static const struct cmd_t command_defs[] = {
       CMD_TAGS(
               CMD_TAG_DISCOVERY)
       CMD_SYN(
-              "/executable avatar <cmd>",
+              "/executable avatar set <cmdtemplate>",
+              "/executable avatar default",
               "/executable urlopen set <cmdtemplate>",
               "/executable urlopen default",
               "/executable urlsave set <cmdtemplate>",
               "/executable urlsave default",
               "/executable editor set <cmdtemplate>",
+              "/executable editor default",
               "/executable vcard_photo set <cmdtemplate>",
               "/executable vcard_photo default")
       CMD_DESC(
               "Configure executable that should be called upon a certain command.")
       CMD_ARGS(
-              { "avatar", "Set executable that is run by /avatar open. Use your favorite image viewer." },
+              { "avatar set", "Set executable that is run by /avatar open. Use your favorite image viewer." },
+              { "avatar default", "Restore to default settings." },
               { "urlopen set", "Set executable that is run by /url open. Takes a command template that replaces %u and %p with the URL and path respectively." },
               { "urlopen default", "Restore to default settings." },
               { "urlsave set", "Set executable that is run by /url save. Takes a command template that replaces %u and %p with the URL and path respectively." },
               { "urlsave default", "Use the built-in download method for saving." },
               { "editor set", "Set editor to be used with /editor. Needs a terminal editor or a script to run a graphical editor." },
+              { "editor default", "Restore to default settings." },
               { "vcard_photo set", "Set executable that is run by /vcard photo open. Takes a command template that replaces %p with the path" },
               { "vcard_photo default", "Restore to default settings." })
       CMD_EXAMPLES(
@@ -2578,10 +2582,10 @@ static const struct cmd_t command_defs[] = {
               "/url open <url>",
               "/url save <url> [<path>]")
       CMD_DESC(
-              "Deal with URLs")
+              "Open or save URLs. This works with OMEMO encrypted files as well.")
       CMD_ARGS(
               { "open", "Open URL with predefined executable." },
-              { "save", "Save URL to optional path, default path is current directory" })
+              { "save", "Save URL to optional path, default path is current directory." })
       CMD_EXAMPLES(
               "/url open https://profanity-im.github.io",
               "/url save https://profanity-im.github.io/guide/latest/userguide.html /home/user/Download/")
diff --git a/src/command/cmd_funcs.c b/src/command/cmd_funcs.c
index 70546a0b..e5ff8116 100644
--- a/src/command/cmd_funcs.c
+++ b/src/command/cmd_funcs.c
@@ -6578,7 +6578,8 @@ cmd_reconnect(ProfWin* window, const char* const command, gchar** args)
     int intval = 0;
     char* err_msg = NULL;
     if (g_strcmp0(value, "now") == 0) {
-        session_reconnect_now();
+        cons_show("Reconnecting now.");
+        cl_ev_reconnect();
     } else if (strtoi_range(value, &intval, 0, INT_MAX, &err_msg)) {
         prefs_set_reconnect(intval);
         if (intval == 0) {
@@ -9343,9 +9344,6 @@ cmd_avatar(ProfWin* window, const char* const command, gchar** args)
             avatar_get_by_nick(args[1], false);
         } else if (g_strcmp0(args[0], "open") == 0) {
             avatar_get_by_nick(args[1], true);
-        } else if (g_strcmp0(args[0], "cmd") == 0) {
-            prefs_set_string(PREF_AVATAR_CMD, args[1]);
-            cons_show("Avatar cmd set to: %s", args[1]);
         } else {
             cons_bad_cmd_usage(command);
         }
@@ -9645,15 +9643,7 @@ cmd_url_save(ProfWin* window, const char* const command, gchar** args)
 }
 
 gboolean
-cmd_executable_avatar(ProfWin* window, const char* const command, gchar** args)
-{
-    prefs_set_string(PREF_AVATAR_CMD, args[1]);
-    cons_show("`avatar` command set to invoke '%s'", args[1]);
-    return TRUE;
-}
-
-gboolean
-cmd_executable_urlopen(ProfWin* window, const char* const command, gchar** args)
+_cmd_executable_template(const preference_t setting, const char* command, gchar** args)
 {
     guint num_args = g_strv_length(args);
     if (num_args < 2) {
@@ -9663,14 +9653,17 @@ cmd_executable_urlopen(ProfWin* window, const char* const command, gchar** args)
 
     if (g_strcmp0(args[1], "set") == 0 && num_args >= 3) {
         gchar* str = g_strjoinv(" ", &args[2]);
-        prefs_set_string(PREF_URL_OPEN_CMD, str);
-        cons_show("`url open` command set to invoke '%s'", str);
+        prefs_set_string(setting, str);
+        cons_show("`%s` command set to invoke '%s'", command, str);
         g_free(str);
 
     } else if (g_strcmp0(args[1], "default") == 0) {
-        prefs_set_string(PREF_URL_OPEN_CMD, NULL);
-        gchar* def = prefs_get_string(PREF_URL_OPEN_CMD);
-        cons_show("`url open` command set to invoke %s (default)", def);
+        prefs_set_string(setting, NULL);
+        gchar* def = prefs_get_string(setting);
+        if (def == NULL) {
+            def = g_strdup("built-in method");
+        }
+        cons_show("`%s` command set to invoke %s (default)", command, def);
         g_free(def);
     } else {
         cons_bad_cmd_usage(command);
@@ -9680,63 +9673,35 @@ cmd_executable_urlopen(ProfWin* window, const char* const command, gchar** args)
 }
 
 gboolean
-cmd_executable_urlsave(ProfWin* window, const char* const command, gchar** args)
+cmd_executable_avatar(ProfWin* window, const char* const command, gchar** args)
 {
+    return _cmd_executable_template(PREF_AVATAR_CMD, args[0], args);
+}
 
-    guint num_args = g_strv_length(args);
-    if (num_args < 2) {
-        cons_bad_cmd_usage(command);
-        return TRUE;
-    }
-
-    if (g_strcmp0(args[1], "set") == 0 && num_args >= 3) {
-        gchar* str = g_strjoinv(" ", &args[2]);
-        prefs_set_string(PREF_URL_SAVE_CMD, str);
-        cons_show("`url save` command set to invoke '%s'", str);
-        g_free(str);
-
-    } else if (g_strcmp0(args[1], "default") == 0) {
-        prefs_set_string(PREF_URL_SAVE_CMD, NULL);
-        cons_show("`url save` will use built-in download method (default)");
-    } else {
-        cons_bad_cmd_usage(command);
-    }
+gboolean
+cmd_executable_urlopen(ProfWin* window, const char* const command, gchar** args)
+{
+    return _cmd_executable_template(PREF_URL_OPEN_CMD, args[0], args);
+}
 
-    return TRUE;
+gboolean
+cmd_executable_urlsave(ProfWin* window, const char* const command, gchar** args)
+{
+    return _cmd_executable_template(PREF_URL_SAVE_CMD, args[0], args);
 }
 
 gboolean
 cmd_executable_editor(ProfWin* window, const char* const command, gchar** args)
 {
-    guint num_args = g_strv_length(args);
-
-    if (g_strcmp0(args[1], "set") == 0 && num_args >= 3) {
-        prefs_set_string(PREF_COMPOSE_EDITOR, args[2]);
-        cons_show("`editor` command set to invoke '%s'", args[2]);
-    } else {
-        cons_bad_cmd_usage(command);
-    }
-
-    return TRUE;
+    return _cmd_executable_template(PREF_COMPOSE_EDITOR, args[0], args);
 }
 
 gboolean
 cmd_executable_vcard_photo(ProfWin* window, const char* const command, gchar** args)
 {
-    if (g_strcmp0(args[1], "set") == 0 && args[2] != NULL) {
-        prefs_set_string(PREF_VCARD_PHOTO_CMD, args[2]);
-        cons_show("`vcard photo open` command set to invoke '%s'", args[2]);
-    } else if (g_strcmp0(args[1], "default") == 0) {
-        prefs_set_string(PREF_VCARD_PHOTO_CMD, NULL);
-        char* cmd = prefs_get_string(PREF_VCARD_PHOTO_CMD);
-        cons_show("`vcard photo open` command set to invoke '%s' (default)", cmd);
-        g_free(cmd);
-    } else {
-        cons_bad_cmd_usage(command);
-    }
-
-    return TRUE;
+    return _cmd_executable_template(PREF_VCARD_PHOTO_CMD, args[0], args);
 }
+
 gboolean
 cmd_mam(ProfWin* window, const char* const command, gchar** args)
 {
diff --git a/src/common.c b/src/common.c
index 1ba27a63..c1a47c2d 100644
--- a/src/common.c
+++ b/src/common.c
@@ -457,14 +457,11 @@ GSList*
 get_mentions(gboolean whole_word, gboolean case_sensitive, const char* const message, const char* const nick)
 {
     GSList* mentions = NULL;
-    gchar* message_search = case_sensitive ? g_strdup(message) : g_utf8_strdown(message, -1);
-    gchar* mynick_search = case_sensitive ? g_strdup(nick) : g_utf8_strdown(nick, -1);
+    auto_gchar gchar* message_search = case_sensitive ? g_strdup(message) : g_utf8_strdown(message, -1);
+    auto_gchar gchar* mynick_search = case_sensitive ? g_strdup(nick) : g_utf8_strdown(nick, -1);
 
     mentions = prof_occurrences(mynick_search, message_search, 0, whole_word, &mentions);
 
-    g_free(message_search);
-    g_free(mynick_search);
-
     return mentions;
 }
 
@@ -483,11 +480,10 @@ call_external(gchar** argv)
                                   NULL, NULL, NULL,
                                   &spawn_error);
     if (!is_successful) {
-        gchar* cmd = g_strjoinv(" ", argv);
+        auto_gchar gchar* cmd = g_strjoinv(" ", argv);
         log_error("Spawning '%s' failed with error '%s'", cmd, spawn_error ? spawn_error->message : "Unknown, spawn_error is NULL");
 
         g_error_free(spawn_error);
-        g_free(cmd);
     }
 
     return is_successful;
@@ -579,7 +575,7 @@ get_expanded_path(const char* path)
 gchar*
 unique_filename_from_url(const char* url, const char* path)
 {
-    gchar* realpath;
+    auto_gchar gchar* realpath = NULL;
 
     // Default to './' as path when none has been provided.
     if (path == NULL) {
@@ -590,7 +586,7 @@ unique_filename_from_url(const char* url, const char* path)
 
     // Resolves paths such as './../.' for path.
     GFile* target = g_file_new_for_commandline_arg(realpath);
-    gchar* filename = NULL;
+    auto_gchar gchar* filename = NULL;
 
     if (_has_directory_suffix(realpath) || g_file_test(realpath, G_FILE_TEST_IS_DIR)) {
         // The target should be used as a directory. Assume that the basename
@@ -605,14 +601,10 @@ unique_filename_from_url(const char* url, const char* path)
 
     gchar* unique_filename = _unique_filename(filename);
     if (unique_filename == NULL) {
-        g_free(filename);
-        g_free(realpath);
         return NULL;
     }
 
     g_object_unref(target);
-    g_free(filename);
-    g_free(realpath);
 
     return unique_filename;
 }
diff --git a/src/common.h b/src/common.h
index e9a3d0cd..da6bd2b6 100644
--- a/src/common.h
+++ b/src/common.h
@@ -47,6 +47,9 @@
 
 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
 
+#define PROF_STRINGIFY_(n) #n
+#define PROF_STRINGIFY(n)  PROF_STRINGIFY_(n)
+
 void auto_free_gchar(gchar** str);
 #define auto_gchar __attribute__((__cleanup__(auto_free_gchar)))
 void auto_free_gcharv(gchar*** args);
@@ -60,6 +63,9 @@ void auto_free_char(char** str);
 #define STR_MAYBE_NULL(p) (p)
 #endif
 
+/* Our own define of MB_CUR_MAX but this time at compile time */
+#define PROF_MB_CUR_MAX 8
+
 // assume malloc stores at most 8 bytes for size of allocated memory
 // and page size is at least 4KB
 #define READ_BUF_SIZE 4088
diff --git a/src/config/files.c b/src/config/files.c
index e88ae687..75940c90 100644
--- a/src/config/files.c
+++ b/src/config/files.c
@@ -114,7 +114,7 @@ files_get_inputrc_file(void)
     return NULL;
 }
 
-char*
+gchar*
 files_get_log_file(const char* const log_file)
 {
     gchar* xdg_data = _files_get_xdg_data_home();
@@ -139,7 +139,7 @@ files_get_log_file(const char* const log_file)
         g_string_append(logfile, ".log");
     }
 
-    char* result = g_strdup(logfile->str);
+    gchar* result = g_strdup(logfile->str);
 
     free(xdg_data);
     g_string_free(logfile, TRUE);
diff --git a/src/config/preferences.c b/src/config/preferences.c
index 2d975e39..c10f4d83 100644
--- a/src/config/preferences.c
+++ b/src/config/preferences.c
@@ -101,21 +101,15 @@ _prefs_load(void)
 
     // move pre 0.5.0 autoaway.message to autoaway.awaymessage
     if (g_key_file_has_key(prefs, PREF_GROUP_PRESENCE, "autoaway.message", NULL)) {
-        char* message = g_key_file_get_string(prefs, PREF_GROUP_PRESENCE, "autoaway.message", NULL);
+        auto_gchar gchar* message = g_key_file_get_string(prefs, PREF_GROUP_PRESENCE, "autoaway.message", NULL);
         g_key_file_set_string(prefs, PREF_GROUP_PRESENCE, "autoaway.awaymessage", message);
         g_key_file_remove_key(prefs, PREF_GROUP_PRESENCE, "autoaway.message", NULL);
-        g_free(message);
     }
 
     // migrate pre 0.5.0 time settings
     if (g_key_file_has_key(prefs, PREF_GROUP_UI, "time", NULL)) {
-        char* time = g_key_file_get_string(prefs, PREF_GROUP_UI, "time", NULL);
-        char* val = NULL;
-        if (time) {
-            val = time;
-        } else {
-            val = "off";
-        }
+        auto_gchar gchar* time = g_key_file_get_string(prefs, PREF_GROUP_UI, "time", NULL);
+        char* val = time ? time : "off";
         g_key_file_set_string(prefs, PREF_GROUP_UI, "time.console", val);
         g_key_file_set_string(prefs, PREF_GROUP_UI, "time.chat", val);
         g_key_file_set_string(prefs, PREF_GROUP_UI, "time.muc", val);
@@ -123,12 +117,11 @@ _prefs_load(void)
         g_key_file_set_string(prefs, PREF_GROUP_UI, "time.private", val);
         g_key_file_set_string(prefs, PREF_GROUP_UI, "time.xmlconsole", val);
         g_key_file_remove_key(prefs, PREF_GROUP_UI, "time", NULL);
-        g_free(time);
     }
 
     // move pre 0.5.0 notify settings
     if (g_key_file_has_key(prefs, PREF_GROUP_NOTIFICATIONS, "room", NULL)) {
-        char* value = g_key_file_get_string(prefs, PREF_GROUP_NOTIFICATIONS, "room", NULL);
+        auto_gchar gchar* value = g_key_file_get_string(prefs, PREF_GROUP_NOTIFICATIONS, "room", NULL);
         if (g_strcmp0(value, "on") == 0) {
             g_key_file_set_boolean(prefs, PREF_GROUP_NOTIFICATIONS, "room", TRUE);
         } else if (g_strcmp0(value, "off") == 0) {
@@ -137,7 +130,6 @@ _prefs_load(void)
             g_key_file_set_boolean(prefs, PREF_GROUP_NOTIFICATIONS, "room", FALSE);
             g_key_file_set_boolean(prefs, PREF_GROUP_NOTIFICATIONS, "room.mention", TRUE);
         }
-        g_free(value);
     }
 
     // move pre 0.6.0 titlebar settings to wintitle
@@ -154,7 +146,7 @@ _prefs_load(void)
 
     // after 0.8.1: titlebar use jid|name -> titlebar show|hide jid|name
     if (g_key_file_has_key(prefs, PREF_GROUP_UI, "titlebar.muc.title", NULL)) {
-        char* value = g_key_file_get_string(prefs, PREF_GROUP_UI, "titlebar.muc.title", NULL);
+        auto_gchar gchar* value = g_key_file_get_string(prefs, PREF_GROUP_UI, "titlebar.muc.title", NULL);
         if (g_strcmp0(value, "name") == 0) {
             g_key_file_set_boolean(prefs, PREF_GROUP_UI, "titlebar.muc.title.name", TRUE);
         } else if (g_strcmp0(value, "jid") == 0) {
@@ -164,7 +156,7 @@ _prefs_load(void)
 
     // 0.9.0 introduced /urlopen. It was saved under "logging" section. Now we have a new "executables" section.
     if (g_key_file_has_key(prefs, PREF_GROUP_LOGGING, "urlopen.cmd", NULL)) {
-        char* val = g_key_file_get_string(prefs, PREF_GROUP_LOGGING, "urlopen.cmd", NULL);
+        auto_gchar gchar* val = g_key_file_get_string(prefs, PREF_GROUP_LOGGING, "urlopen.cmd", NULL);
 
         GString* value = g_string_new("false;");
         value = g_string_append(value, val);
@@ -178,7 +170,7 @@ _prefs_load(void)
 
     // 0.9.0 introduced configurable /avatar. It was saved under "logging" section. Now we have a new "executables" section.
     if (g_key_file_has_key(prefs, PREF_GROUP_LOGGING, "avatar.cmd", NULL)) {
-        char* value = g_key_file_get_string(prefs, PREF_GROUP_LOGGING, "avatar.cmd", NULL);
+        auto_gchar gchar* value = g_key_file_get_string(prefs, PREF_GROUP_LOGGING, "avatar.cmd", NULL);
         g_key_file_set_string(prefs, PREF_GROUP_EXECUTABLES, "avatar.cmd", value);
         g_key_file_remove_key(prefs, PREF_GROUP_LOGGING, "avatar.cmd", NULL);
     }
@@ -192,7 +184,7 @@ _prefs_load(void)
     // file type or scheme matching. Move value saved under 'DEF' locale to a
     // simple key-value string not under any locale.
     {
-        char** values = g_key_file_get_locale_string_list(prefs, PREF_GROUP_EXECUTABLES, "url.open.cmd", "DEF", NULL, NULL);
+        auto_gcharv gchar** values = g_key_file_get_locale_string_list(prefs, PREF_GROUP_EXECUTABLES, "url.open.cmd", "DEF", NULL, NULL);
         if (values && !g_key_file_has_key(prefs, PREF_GROUP_EXECUTABLES, "url.open.cmd", NULL)) {
             // First value in array is `require_save` option -- we ignore that
             // one as there is no such option anymore.
@@ -201,16 +193,13 @@ _prefs_load(void)
             g_key_file_set_string(prefs, PREF_GROUP_EXECUTABLES, "url.open.cmd", executable);
             g_key_file_set_comment(prefs, PREF_GROUP_EXECUTABLES, "url.open.cmd", " Migrated from url.open.cmd[DEF]. `require_save` option has been removed in v0.10 and was discarded.", NULL);
             g_key_file_remove_key(prefs, PREF_GROUP_EXECUTABLES, "url.open.cmd[DEF]", NULL);
-
-            g_strfreev(values);
         }
 
-        char* value = g_key_file_get_locale_string(prefs, PREF_GROUP_EXECUTABLES, "url.save.cmd", "DEF", NULL);
+        auto_gchar gchar* value = g_key_file_get_locale_string(prefs, PREF_GROUP_EXECUTABLES, "url.save.cmd", "DEF", NULL);
         if (value && !g_key_file_has_key(prefs, PREF_GROUP_EXECUTABLES, "url.save.cmd", NULL)) {
             g_key_file_set_string(prefs, PREF_GROUP_EXECUTABLES, "url.save.cmd", value);
             g_key_file_set_comment(prefs, PREF_GROUP_EXECUTABLES, "url.save.cmd", " Migrated from url.save.cmd[DEF].", NULL);
             g_key_file_remove_key(prefs, PREF_GROUP_EXECUTABLES, "url.save.cmd[DEF]", NULL);
-            g_free(value);
         }
     }
 
@@ -342,20 +331,18 @@ prefs_message_get_triggers(const char* const message)
 {
     GList* result = NULL;
 
-    char* message_lower = g_utf8_strdown(message, -1);
+    auto_gchar gchar* message_lower = g_utf8_strdown(message, -1);
     gsize len = 0;
     gchar** triggers = g_key_file_get_string_list(prefs, PREF_GROUP_NOTIFICATIONS, "room.trigger.list", &len, NULL);
 
     for (int i = 0; i < len; i++) {
-        char* trigger_lower = g_utf8_strdown(triggers[i], -1);
+        auto_gchar gchar* trigger_lower = g_utf8_strdown(triggers[i], -1);
         if (g_strrstr(message_lower, trigger_lower)) {
             result = g_list_append(result, strdup(triggers[i]));
         }
-        g_free(trigger_lower);
     }
 
     g_strfreev(triggers);
-    g_free(message_lower);
 
     return result;
 }
@@ -540,7 +527,7 @@ prefs_get_string(preference_t pref)
     const char* key = _get_key(pref);
     char* def = _get_default_string(pref);
 
-    char* result = g_key_file_get_string(prefs, group, key, NULL);
+    gchar* result = g_key_file_get_string(prefs, group, key, NULL);
 
     if (result == NULL) {
         if (def) {
@@ -632,10 +619,9 @@ prefs_get_tls_certpath(void)
     const char* group = _get_group(PREF_TLS_CERTPATH);
     const char* key = _get_key(PREF_TLS_CERTPATH);
 
-    char* setting = g_key_file_get_string(prefs, group, key, NULL);
+    auto_gchar gchar* setting = g_key_file_get_string(prefs, group, key, NULL);
 
     if (g_strcmp0(setting, "none") == 0) {
-        g_free(setting);
         return NULL;
     }
 
@@ -660,7 +646,6 @@ prefs_get_tls_certpath(void)
     }
 
     char* result = strdup(setting);
-    g_free(setting);
 
     return result;
 }
@@ -1684,15 +1669,13 @@ prefs_get_aliases(void)
 
         for (int i = 0; i < len; i++) {
             char* name = keys[i];
-            char* value = g_key_file_get_string(prefs, PREF_GROUP_ALIAS, name, NULL);
+            auto_gchar gchar* value = g_key_file_get_string(prefs, PREF_GROUP_ALIAS, name, NULL);
 
             if (value) {
                 ProfAlias* alias = malloc(sizeof(struct prof_alias_t));
                 alias->name = strdup(name);
                 alias->value = strdup(value);
 
-                free(value);
-
                 result = g_list_insert_sorted(result, alias, (GCompareFunc)_alias_cmp);
             }
         }
@@ -2314,7 +2297,7 @@ _get_default_string(preference_t pref)
     case PREF_COLOR_NICK:
         return "false";
     case PREF_AVATAR_CMD:
-        return "xdg-open";
+        return "xdg-open %p";
     case PREF_URL_OPEN_CMD:
         return "xdg-open %u";
     case PREF_VCARD_PHOTO_CMD:
diff --git a/src/config/theme.c b/src/config/theme.c
index a39362fb..4909f6b7 100644
--- a/src/config/theme.c
+++ b/src/config/theme.c
@@ -686,7 +686,7 @@ _theme_prep_fgnd(char* setting, GString* lookup_str, gboolean* bold)
 char*
 theme_get_string(char* str)
 {
-    char* res = g_key_file_get_string(theme, "colours", str, NULL);
+    gchar* res = g_key_file_get_string(theme, "colours", str, NULL);
     if (!res) {
         return strdup(g_hash_table_lookup(defaults, str));
     } else {
diff --git a/src/config/tlscerts.c b/src/config/tlscerts.c
index 008d28f6..d2f053fa 100644
--- a/src/config/tlscerts.c
+++ b/src/config/tlscerts.c
@@ -121,26 +121,17 @@ tlscerts_list(void)
     for (int i = 0; i < g_strv_length(groups); i++) {
         char* fingerprint = strdup(groups[i]);
         int version = g_key_file_get_integer(tlscerts, fingerprint, "version", NULL);
-        char* serialnumber = g_key_file_get_string(tlscerts, fingerprint, "serialnumber", NULL);
-        char* subjectname = g_key_file_get_string(tlscerts, fingerprint, "subjectname", NULL);
-        char* issuername = g_key_file_get_string(tlscerts, fingerprint, "issuername", NULL);
-        char* notbefore = g_key_file_get_string(tlscerts, fingerprint, "start", NULL);
-        char* notafter = g_key_file_get_string(tlscerts, fingerprint, "end", NULL);
-        char* keyalg = g_key_file_get_string(tlscerts, fingerprint, "keyalg", NULL);
-        char* signaturealg = g_key_file_get_string(tlscerts, fingerprint, "signaturealg", NULL);
+        auto_gchar gchar* serialnumber = g_key_file_get_string(tlscerts, fingerprint, "serialnumber", NULL);
+        auto_gchar gchar* subjectname = g_key_file_get_string(tlscerts, fingerprint, "subjectname", NULL);
+        auto_gchar gchar* issuername = g_key_file_get_string(tlscerts, fingerprint, "issuername", NULL);
+        auto_gchar gchar* notbefore = g_key_file_get_string(tlscerts, fingerprint, "start", NULL);
+        auto_gchar gchar* notafter = g_key_file_get_string(tlscerts, fingerprint, "end", NULL);
+        auto_gchar gchar* keyalg = g_key_file_get_string(tlscerts, fingerprint, "keyalg", NULL);
+        auto_gchar gchar* signaturealg = g_key_file_get_string(tlscerts, fingerprint, "signaturealg", NULL);
 
         TLSCertificate* cert = tlscerts_new(fingerprint, version, serialnumber, subjectname, issuername, notbefore,
                                             notafter, keyalg, signaturealg, NULL);
 
-        free(fingerprint);
-        free(serialnumber);
-        free(subjectname);
-        free(issuername);
-        free(notbefore);
-        free(notafter);
-        free(keyalg);
-        free(signaturealg);
-
         res = g_list_append(res, cert);
     }
 
@@ -316,25 +307,16 @@ tlscerts_get_trusted(const char* const fingerprint)
     }
 
     int version = g_key_file_get_integer(tlscerts, fingerprint, "version", NULL);
-    char* serialnumber = g_key_file_get_string(tlscerts, fingerprint, "serialnumber", NULL);
-    char* subjectname = g_key_file_get_string(tlscerts, fingerprint, "subjectname", NULL);
-    char* issuername = g_key_file_get_string(tlscerts, fingerprint, "issuername", NULL);
-    char* notbefore = g_key_file_get_string(tlscerts, fingerprint, "start", NULL);
-    char* notafter = g_key_file_get_string(tlscerts, fingerprint, "end", NULL);
-    char* keyalg = g_key_file_get_string(tlscerts, fingerprint, "keyalg", NULL);
-    char* signaturealg = g_key_file_get_string(tlscerts, fingerprint, "signaturealg", NULL);
+    auto_gchar gchar* serialnumber = g_key_file_get_string(tlscerts, fingerprint, "serialnumber", NULL);
+    auto_gchar gchar* subjectname = g_key_file_get_string(tlscerts, fingerprint, "subjectname", NULL);
+    auto_gchar gchar* issuername = g_key_file_get_string(tlscerts, fingerprint, "issuername", NULL);
+    auto_gchar gchar* notbefore = g_key_file_get_string(tlscerts, fingerprint, "start", NULL);
+    auto_gchar gchar* notafter = g_key_file_get_string(tlscerts, fingerprint, "end", NULL);
+    auto_gchar gchar* keyalg = g_key_file_get_string(tlscerts, fingerprint, "keyalg", NULL);
+    auto_gchar gchar* signaturealg = g_key_file_get_string(tlscerts, fingerprint, "signaturealg", NULL);
 
     TLSCertificate* cert = tlscerts_new(fingerprint, version, serialnumber, subjectname, issuername, notbefore,
                                         notafter, keyalg, signaturealg, NULL);
-
-    free(serialnumber);
-    free(subjectname);
-    free(issuername);
-    free(notbefore);
-    free(notafter);
-    free(keyalg);
-    free(signaturealg);
-
     return cert;
 }
 
diff --git a/src/database.c b/src/database.c
index ce936170..80439b9a 100644
--- a/src/database.c
+++ b/src/database.c
@@ -250,7 +250,7 @@ log_database_get_limits_info(const gchar* const contact_barejid, gboolean is_las
 // null the current time is used. from_start gets first few messages if true
 // otherwise the last ones. Flip flips the order of the results
 GSList*
-log_database_get_previous_chat(const gchar* const contact_barejid, char* start_time, char* end_time, gboolean from_start, gboolean flip)
+log_database_get_previous_chat(const gchar* const contact_barejid, const char* start_time, char* end_time, gboolean from_start, gboolean flip)
 {
     sqlite3_stmt* stmt = NULL;
     const char* jid = connection_get_fulljid();
diff --git a/src/database.h b/src/database.h
index 3b5fd647..1e73c874 100644
--- a/src/database.h
+++ b/src/database.h
@@ -47,7 +47,7 @@ void log_database_add_incoming(ProfMessage* message);
 void log_database_add_outgoing_chat(const char* const id, const char* const barejid, const char* const message, const char* const replace_id, prof_enc_t enc);
 void log_database_add_outgoing_muc(const char* const id, const char* const barejid, const char* const message, const char* const replace_id, prof_enc_t enc);
 void log_database_add_outgoing_muc_pm(const char* const id, const char* const barejid, const char* const message, const char* const replace_id, prof_enc_t enc);
-GSList* log_database_get_previous_chat(const gchar* const contact_barejid, char* start_time, char* end_time, gboolean from_start, gboolean flip);
+GSList* log_database_get_previous_chat(const gchar* const contact_barejid, const char* start_time, char* end_time, gboolean from_start, gboolean flip);
 ProfMessage* log_database_get_limits_info(const gchar* const contact_barejid, gboolean is_last);
 void log_database_close(void);
 
diff --git a/src/event/client_events.c b/src/event/client_events.c
index 67ffec20..c393ccd4 100644
--- a/src/event/client_events.c
+++ b/src/event/client_events.c
@@ -47,6 +47,7 @@
 #include "plugins/plugins.h"
 #include "ui/window_list.h"
 #include "xmpp/chat_session.h"
+#include "xmpp/session.h"
 #include "xmpp/xmpp.h"
 
 #ifdef HAVE_LIBOTR
@@ -85,9 +86,8 @@ cl_ev_connect_account(ProfAccount* account)
 void
 cl_ev_disconnect(void)
 {
-    char* mybarejid = connection_get_barejid();
+    auto_char char* mybarejid = connection_get_barejid();
     cons_show("%s logged out successfully.", mybarejid);
-    free(mybarejid);
 
     ui_close_all_wins();
     ev_disconnect_cleanup();
@@ -96,6 +96,18 @@ cl_ev_disconnect(void)
 }
 
 void
+cl_ev_reconnect(void)
+{
+    if (connection_get_status() != JABBER_DISCONNECTED) {
+        connection_disconnect();
+        ev_disconnect_cleanup();
+        // on intentional disconnect reset the counter
+        ev_reset_connection_counter();
+    }
+    session_reconnect_now();
+}
+
+void
 cl_ev_presence_send(const resource_presence_t presence_type, const int idle_secs)
 {
     char* signed_status = NULL;
diff --git a/src/event/client_events.h b/src/event/client_events.h
index fed2bb37..a35e97b5 100644
--- a/src/event/client_events.h
+++ b/src/event/client_events.h
@@ -42,6 +42,7 @@ jabber_conn_status_t cl_ev_connect_jid(const char* const jid, const char* const
 jabber_conn_status_t cl_ev_connect_account(ProfAccount* account);
 
 void cl_ev_disconnect(void);
+void cl_ev_reconnect(void);
 
 void cl_ev_presence_send(const resource_presence_t presence_type, const int idle_secs);
 
diff --git a/src/event/server_events.c b/src/event/server_events.c
index 0f6df2ac..c10f69c3 100644
--- a/src/event/server_events.c
+++ b/src/event/server_events.c
@@ -1332,8 +1332,8 @@ sv_ev_bookmark_autojoin(Bookmark* bookmark)
 
     log_debug("Autojoin %s with nick=%s", bookmark->barejid, nick);
     if (!muc_active(bookmark->barejid)) {
-        presence_join_room(bookmark->barejid, nick, bookmark->password);
         muc_join(bookmark->barejid, nick, bookmark->password, TRUE);
+        presence_join_room(bookmark->barejid, nick, bookmark->password);
         iq_room_affiliation_list(bookmark->barejid, "member", false);
         iq_room_affiliation_list(bookmark->barejid, "admin", false);
         iq_room_affiliation_list(bookmark->barejid, "owner", false);
@@ -1346,7 +1346,7 @@ static void
 _cut(ProfMessage* message, const char* cut)
 {
     if (strstr(message->plain, cut)) {
-        char** split = g_strsplit(message->plain, cut, -1);
+        gchar** split = g_strsplit(message->plain, cut, -1);
         free(message->plain);
         message->plain = g_strjoinv("", split);
         g_strfreev(split);
diff --git a/src/main.c b/src/main.c
index 6cbca667..6edfd3c8 100644
--- a/src/main.c
+++ b/src/main.c
@@ -160,9 +160,8 @@ main(int argc, char** argv)
 #endif
 
 #ifdef HAVE_PYTHON
-        gchar* python_version = python_get_version_number();
+        auto_gchar gchar* python_version = python_get_version_number();
         g_print("Python plugins: Enabled (%s)\n", python_version);
-        g_free(python_version);
 #else
         g_print("Python plugins: Disabled\n");
 #endif
diff --git a/src/omemo/omemo.c b/src/omemo/omemo.c
index 69a9fc30..303fa689 100644
--- a/src/omemo/omemo.c
+++ b/src/omemo/omemo.c
@@ -350,14 +350,12 @@ omemo_generate_crypto_materials(ProfAccount* account)
     signal_protocol_key_helper_generate_identity_key_pair(&omemo_ctx.identity_key_pair, omemo_ctx.signal);
 
     ec_public_key_serialize(&omemo_ctx.identity_key_store.public, ratchet_identity_key_pair_get_public(omemo_ctx.identity_key_pair));
-    char* identity_key_public = g_base64_encode(signal_buffer_data(omemo_ctx.identity_key_store.public), signal_buffer_len(omemo_ctx.identity_key_store.public));
+    auto_gchar gchar* identity_key_public = g_base64_encode(signal_buffer_data(omemo_ctx.identity_key_store.public), signal_buffer_len(omemo_ctx.identity_key_store.public));
     g_key_file_set_string(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_IDENTITY, OMEMO_STORE_KEY_IDENTITY_KEY_PUBLIC, identity_key_public);
-    g_free(identity_key_public);
 
     ec_private_key_serialize(&omemo_ctx.identity_key_store.private, ratchet_identity_key_pair_get_private(omemo_ctx.identity_key_pair));
-    char* identity_key_private = g_base64_encode(signal_buffer_data(omemo_ctx.identity_key_store.private), signal_buffer_len(omemo_ctx.identity_key_store.private));
+    auto_gchar gchar* identity_key_private = g_base64_encode(signal_buffer_data(omemo_ctx.identity_key_store.private), signal_buffer_len(omemo_ctx.identity_key_store.private));
     g_key_file_set_string(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_IDENTITY, OMEMO_STORE_KEY_IDENTITY_KEY_PRIVATE, identity_key_private);
-    g_free(identity_key_private);
 
     /* Registration ID */
     signal_protocol_key_helper_generate_registration_id(&omemo_ctx.registration_id, 0, omemo_ctx.signal);
@@ -1322,19 +1320,19 @@ omemo_untrust(const char* const jid, const char* const fingerprint_formatted)
     }
     free(identity);
 
-    char* fingerprint = _omemo_unformat_fingerprint(fingerprint_formatted);
+    auto_char 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;
+        return;
     }
 
     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;
+        return;
     }
     signal_protocol_address address = {
         .name = jid,
@@ -1345,13 +1343,9 @@ omemo_untrust(const char* const jid, const char* const fingerprint_formatted)
     delete_session(&address, omemo_ctx.session_store);
 
     /* Remove from keyfile */
-    char* device_id_str = g_strdup_printf("%d", device_id);
+    auto_gchar gchar* 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);
 }
 
 static void
@@ -1540,7 +1534,7 @@ _load_identity(void)
 
     /* Identity key */
     error = NULL;
-    char* identity_key_public_b64 = g_key_file_get_string(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_IDENTITY, OMEMO_STORE_KEY_IDENTITY_KEY_PUBLIC, &error);
+    auto_gchar gchar* identity_key_public_b64 = g_key_file_get_string(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_IDENTITY, OMEMO_STORE_KEY_IDENTITY_KEY_PUBLIC, &error);
     if (!identity_key_public_b64) {
         log_error("[OMEMO] cannot load identity public key: %s", error->message);
         return FALSE;
@@ -1548,11 +1542,10 @@ _load_identity(void)
 
     size_t identity_key_public_len;
     unsigned char* identity_key_public = g_base64_decode(identity_key_public_b64, &identity_key_public_len);
-    g_free(identity_key_public_b64);
     omemo_ctx.identity_key_store.public = signal_buffer_create(identity_key_public, identity_key_public_len);
 
     error = NULL;
-    char* identity_key_private_b64 = g_key_file_get_string(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_IDENTITY, OMEMO_STORE_KEY_IDENTITY_KEY_PRIVATE, &error);
+    auto_gchar gchar* identity_key_private_b64 = g_key_file_get_string(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_IDENTITY, OMEMO_STORE_KEY_IDENTITY_KEY_PRIVATE, &error);
     if (!identity_key_private_b64) {
         log_error("[OMEMO] cannot load identity private key: %s", error->message);
         return FALSE;
@@ -1560,7 +1553,6 @@ _load_identity(void)
 
     size_t identity_key_private_len;
     unsigned char* identity_key_private = g_base64_decode(identity_key_private_b64, &identity_key_private_len);
-    g_free(identity_key_private_b64);
     omemo_ctx.identity_key_store.private = signal_buffer_create(identity_key_private, identity_key_private_len);
 
     ec_public_key* public_key;
@@ -1579,10 +1571,9 @@ _load_identity(void)
     keys = g_key_file_get_keys(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_PREKEYS, NULL, NULL);
     if (keys) {
         for (i = 0; keys[i] != NULL; i++) {
-            char* pre_key_b64 = g_key_file_get_string(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_PREKEYS, keys[i], NULL);
+            auto_gchar gchar* pre_key_b64 = g_key_file_get_string(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_PREKEYS, keys[i], NULL);
             size_t pre_key_len;
             unsigned char* pre_key = g_base64_decode(pre_key_b64, &pre_key_len);
-            g_free(pre_key_b64);
             signal_buffer* buffer = signal_buffer_create(pre_key, pre_key_len);
             g_free(pre_key);
             g_hash_table_insert(omemo_ctx.pre_key_store, GINT_TO_POINTER(strtoul(keys[i], NULL, 10)), buffer);
@@ -1601,10 +1592,9 @@ _load_identity(void)
     keys = g_key_file_get_keys(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_SIGNED_PREKEYS, NULL, NULL);
     if (keys) {
         for (i = 0; keys[i] != NULL; i++) {
-            char* signed_pre_key_b64 = g_key_file_get_string(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_SIGNED_PREKEYS, keys[i], NULL);
+            auto_gchar gchar* signed_pre_key_b64 = g_key_file_get_string(omemo_ctx.identity_keyfile, OMEMO_STORE_GROUP_SIGNED_PREKEYS, keys[i], NULL);
             size_t signed_pre_key_len;
             unsigned char* signed_pre_key = g_base64_decode(signed_pre_key_b64, &signed_pre_key_len);
-            g_free(signed_pre_key_b64);
             signal_buffer* buffer = signal_buffer_create(signed_pre_key, signed_pre_key_len);
             g_free(signed_pre_key);
             g_hash_table_insert(omemo_ctx.signed_pre_key_store, GINT_TO_POINTER(strtoul(keys[i], NULL, 10)), buffer);
@@ -1629,7 +1619,7 @@ static void
 _load_trust(void)
 {
     char** keys = NULL;
-    char** groups = g_key_file_get_groups(omemo_ctx.trust_keyfile, NULL);
+    gchar** groups = g_key_file_get_groups(omemo_ctx.trust_keyfile, NULL);
     if (groups) {
         int i;
         for (i = 0; groups[i] != NULL; i++) {
@@ -1644,10 +1634,9 @@ _load_trust(void)
             keys = g_key_file_get_keys(omemo_ctx.trust_keyfile, groups[i], NULL, NULL);
             int j;
             for (j = 0; keys[j] != NULL; j++) {
-                char* key_b64 = g_key_file_get_string(omemo_ctx.trust_keyfile, groups[i], keys[j], NULL);
+                auto_gchar gchar* key_b64 = g_key_file_get_string(omemo_ctx.trust_keyfile, groups[i], keys[j], NULL);
                 size_t key_len;
                 unsigned char* key = g_base64_decode(key_b64, &key_len);
-                g_free(key_b64);
                 signal_buffer* buffer = signal_buffer_create(key, key_len);
                 g_free(key);
                 uint32_t device_id = strtoul(keys[j], NULL, 10);
@@ -1663,7 +1652,7 @@ static void
 _load_sessions(void)
 {
     int i;
-    char** groups = g_key_file_get_groups(omemo_ctx.sessions_keyfile, NULL);
+    auto_gcharv gchar** groups = g_key_file_get_groups(omemo_ctx.sessions_keyfile, NULL);
     if (groups) {
         for (i = 0; groups[i] != NULL; i++) {
             int j;
@@ -1675,20 +1664,17 @@ _load_sessions(void)
                 g_hash_table_insert(omemo_ctx.session_store, strdup(groups[i]), device_store);
             }
 
-            char** keys = g_key_file_get_keys(omemo_ctx.sessions_keyfile, groups[i], NULL, NULL);
+            auto_gcharv gchar** keys = g_key_file_get_keys(omemo_ctx.sessions_keyfile, groups[i], NULL, NULL);
             for (j = 0; keys[j] != NULL; j++) {
                 uint32_t id = strtoul(keys[j], NULL, 10);
-                char* record_b64 = g_key_file_get_string(omemo_ctx.sessions_keyfile, groups[i], keys[j], NULL);
+                auto_gchar gchar* record_b64 = g_key_file_get_string(omemo_ctx.sessions_keyfile, groups[i], keys[j], NULL);
                 size_t record_len;
                 unsigned char* record = g_base64_decode(record_b64, &record_len);
-                g_free(record_b64);
                 signal_buffer* buffer = signal_buffer_create(record, record_len);
                 g_free(record);
                 g_hash_table_insert(device_store, GINT_TO_POINTER(id), buffer);
             }
-            g_strfreev(keys);
         }
-        g_strfreev(groups);
     }
 }
 
@@ -1696,7 +1682,7 @@ static void
 _load_known_devices(void)
 {
     int i;
-    char** groups = g_key_file_get_groups(omemo_ctx.known_devices_keyfile, NULL);
+    auto_gcharv gchar** groups = g_key_file_get_groups(omemo_ctx.known_devices_keyfile, NULL);
     if (groups) {
         for (i = 0; groups[i] != NULL; i++) {
             int j;
@@ -1708,16 +1694,13 @@ _load_known_devices(void)
                 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);
+            auto_gcharv gchar** 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);
+                auto_gchar gchar* 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);
     }
 }
 
@@ -1734,9 +1717,8 @@ _cache_device_identity(const char* const jid, uint32_t device_id, ec_public_key*
     log_debug("[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);
+    auto_gchar gchar* 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();
 
     Autocomplete ac = g_hash_table_lookup(omemo_ctx.fingerprint_ac, jid);
diff --git a/src/omemo/store.c b/src/omemo/store.c
index 3825c95d..08291460 100644
--- a/src/omemo/store.c
+++ b/src/omemo/store.c
@@ -143,11 +143,9 @@ store_session(const signal_protocol_address* address,
     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);
+    auto_gchar gchar* record_b64 = g_base64_encode(record, record_len);
+    auto_gchar gchar* 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();
 
@@ -187,9 +185,8 @@ delete_session(const signal_protocol_address* address, void* user_data)
 
     g_hash_table_remove(device_store, GINT_TO_POINTER(address->device_id));
 
-    char* device_id_str = g_strdup_printf("%d", address->device_id);
+    auto_gchar gchar* 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;
@@ -238,11 +235,9 @@ store_pre_key(uint32_t pre_key_id, uint8_t* record, size_t 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);
+    auto_gchar gchar* pre_key_id_str = g_strdup_printf("%d", pre_key_id);
+    auto_gchar gchar* 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();
 
@@ -265,9 +260,8 @@ remove_pre_key(uint32_t pre_key_id, void* 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);
+    auto_gchar gchar* 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();
 
@@ -306,11 +300,9 @@ store_signed_pre_key(uint32_t signed_pre_key_id, uint8_t* record,
     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);
+    auto_gchar gchar* signed_pre_key_id_str = g_strdup_printf("%d", signed_pre_key_id);
+    auto_gchar gchar* 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();
 
@@ -333,9 +325,8 @@ remove_signed_pre_key(uint32_t signed_pre_key_id, void* 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);
+    auto_gchar gchar* 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();
 
@@ -393,11 +384,9 @@ save_identity(const signal_protocol_address* address, uint8_t* key_data,
     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);
+    auto_gchar gchar* key_b64 = g_base64_encode(key_data, key_len);
+    auto_gchar gchar* device_id = g_strdup_printf("%d", address->device_id);
     g_key_file_set_string(omemo_trust_keyfile(), address->name, device_id, key_b64);
-    g_free(device_id);
-    g_free(key_b64);
 
     omemo_trust_keyfile_save();
 
diff --git a/src/plugins/python_plugins.c b/src/plugins/python_plugins.c
index c32d177c..cf1e4a87 100644
--- a/src/plugins/python_plugins.c
+++ b/src/plugins/python_plugins.c
@@ -874,9 +874,8 @@ static void
 _python_undefined_error(ProfPlugin* plugin, char* hook, char* type)
 {
     GString* err_msg = g_string_new("Plugin error - ");
-    char* module_name = g_strndup(plugin->name, strlen(plugin->name) - 2);
+    auto_gchar gchar* module_name = g_strndup(plugin->name, strlen(plugin->name) - 2);
     g_string_append(err_msg, module_name);
-    free(module_name);
     g_string_append(err_msg, hook);
     g_string_append(err_msg, "(): return value undefined, expected ");
     g_string_append(err_msg, type);
@@ -889,9 +888,8 @@ static void
 _python_type_error(ProfPlugin* plugin, char* hook, char* type)
 {
     GString* err_msg = g_string_new("Plugin error - ");
-    char* module_name = g_strndup(plugin->name, strlen(plugin->name) - 2);
+    auto_gchar gchar* module_name = g_strndup(plugin->name, strlen(plugin->name) - 2);
     g_string_append(err_msg, module_name);
-    free(module_name);
     g_string_append(err_msg, hook);
     g_string_append(err_msg, "(): incorrect return type, expected ");
     g_string_append(err_msg, type);
diff --git a/src/profanity.c b/src/profanity.c
index 2c6ad12d..42983993 100644
--- a/src/profanity.c
+++ b/src/profanity.c
@@ -151,10 +151,9 @@ _connect_default(const char* const account)
     if (account) {
         cmd_execute_connect(window, account);
     } else {
-        char* pref_connect_account = prefs_get_string(PREF_CONNECT_ACCOUNT);
+        auto_gchar gchar* pref_connect_account = prefs_get_string(PREF_CONNECT_ACCOUNT);
         if (pref_connect_account) {
             cmd_execute_connect(window, pref_connect_account);
-            g_free(pref_connect_account);
         }
     }
 }
@@ -198,9 +197,8 @@ _init(char* log_level, char* config_file, char* log_file, char* theme_name)
     if (theme_name) {
         theme_init(theme_name);
     } else {
-        char* theme = prefs_get_string(PREF_THEME);
+        auto_gchar gchar* theme = prefs_get_string(PREF_THEME);
         theme_init(theme);
-        g_free(theme);
     }
 
     ui_init();
diff --git a/src/tools/autocomplete.c b/src/tools/autocomplete.c
index 27532081..8c7818e7 100644
--- a/src/tools/autocomplete.c
+++ b/src/tools/autocomplete.c
@@ -315,7 +315,7 @@ static char*
 _autocomplete_param_common(const char* const input, char* command, autocomplete_func func, Autocomplete ac, gboolean quote, gboolean previous, void* context)
 {
     int len;
-    auto_char char* command_cpy = g_strdup_printf("%s ", command);
+    auto_gchar gchar* command_cpy = g_strdup_printf("%s ", command);
     if (!command_cpy) {
         return NULL;
     }
diff --git a/src/tools/editor.c b/src/tools/editor.c
index 0efb4d6b..4cd70d9d 100644
--- a/src/tools/editor.c
+++ b/src/tools/editor.c
@@ -54,19 +54,18 @@ get_message_from_editor(gchar* message, gchar** returned_message)
     /* Make sure that there's no junk in the return-pointer in error cases */
     *returned_message = NULL;
 
-    gchar* filename = NULL;
+    auto_gchar gchar* filename = NULL;
     GError* glib_error = NULL;
     auto_char char* jid = connection_get_barejid();
     if (jid) {
         filename = files_file_in_account_data_path(DIR_EDITOR, jid, "compose.md");
     } else {
         log_debug("[Editor] could not get JID");
-        gchar* data_dir = files_get_data_path(DIR_EDITOR);
+        auto_gchar gchar* data_dir = files_get_data_path(DIR_EDITOR);
         if (!create_dir(data_dir)) {
             return TRUE;
         }
         filename = g_strdup_printf("%s/compose.md", data_dir);
-        g_free(data_dir);
     }
     if (!filename) {
         log_error("[Editor] something went wrong while creating compose file");
@@ -83,21 +82,17 @@ get_message_from_editor(gchar* message, gchar** returned_message)
         if (glib_error) {
             g_error_free(glib_error);
         }
-        g_free(filename);
         return TRUE;
     }
 
-    char* editor = prefs_get_string(PREF_COMPOSE_EDITOR);
-    gchar* editor_with_filename = g_strdup_printf("%s %s", editor, filename);
-    gchar** editor_argv = g_strsplit(editor_with_filename, " ", 0);
-
-    g_free(editor_with_filename);
+    auto_gchar gchar* editor = prefs_get_string(PREF_COMPOSE_EDITOR);
+    auto_gchar gchar* editor_with_filename = g_strdup_printf("%s %s", editor, filename);
+    auto_gcharv gchar** editor_argv = g_strsplit(editor_with_filename, " ", 0);
 
     // Fork / exec
     pid_t pid = fork();
     if (pid == 0) {
         int x = execvp(editor_argv[0], editor_argv);
-        g_strfreev(editor_argv);
         if (x == -1) {
             log_error("[Editor] Failed to exec %s", editor);
         }
@@ -115,8 +110,6 @@ get_message_from_editor(gchar* message, gchar** returned_message)
             if (glib_error) {
                 g_error_free(glib_error);
             }
-            g_free(filename);
-            g_free(editor);
             return TRUE;
         }
         /* Remove all trailing new-line characters */
@@ -127,10 +120,7 @@ get_message_from_editor(gchar* message, gchar** returned_message)
         } else {
             log_debug("[Editor] deleted file: %s", filename);
         }
-        g_free(filename);
     }
 
-    g_free(editor);
-
     return FALSE;
 }
diff --git a/src/tools/http_upload.c b/src/tools/http_upload.c
index 4dae41c5..289dba15 100644
--- a/src/tools/http_upload.c
+++ b/src/tools/http_upload.c
@@ -399,15 +399,13 @@ file_mime_type(const char* const filename)
     size_t file_header_size = fread(file_header, 1, FILE_HEADER_BYTES, fh);
     fclose(fh);
 
-    char* content_type = g_content_type_guess(filename, (unsigned char*)file_header, file_header_size, NULL);
+    auto_gchar gchar* content_type = g_content_type_guess(filename, (unsigned char*)file_header, file_header_size, NULL);
     if (content_type != NULL) {
-        char* mime_type = g_content_type_get_mime_type(content_type);
+        auto_gchar gchar* mime_type = g_content_type_get_mime_type(content_type);
         out_mime_type = strdup(mime_type);
-        g_free(mime_type);
     } else {
         return strdup(FALLBACK_MIMETYPE);
     }
-    g_free(content_type);
     return out_mime_type;
 }
 
diff --git a/src/ui/chatwin.c b/src/ui/chatwin.c
index 0c540998..b470dbd6 100644
--- a/src/ui/chatwin.c
+++ b/src/ui/chatwin.c
@@ -598,7 +598,7 @@ _chatwin_history(ProfChatWin* chatwin, const char* const contact_barejid)
 // first entry's timestamp in the buffer is used. Flip true to prepend to buffer.
 // Timestamps should be in iso8601
 gboolean
-chatwin_db_history(ProfChatWin* chatwin, char* start_time, char* end_time, gboolean flip)
+chatwin_db_history(ProfChatWin* chatwin, const char* start_time, char* end_time, gboolean flip)
 {
     if (!end_time) {
         end_time = buffer_size(((ProfWin*)chatwin)->layout->buffer) == 0 ? NULL : g_date_time_format_iso8601(buffer_get_entry(((ProfWin*)chatwin)->layout->buffer, 0)->time);
diff --git a/src/ui/confwin.c b/src/ui/confwin.c
index 85abe748..a49b7010 100644
--- a/src/ui/confwin.c
+++ b/src/ui/confwin.c
@@ -70,7 +70,7 @@ confwin_show_form(ProfConfWin* confwin)
                 win_println(window, THEME_DEFAULT, "-", "%s", value);
             }
         } else if (g_strcmp0(field->type, "hidden") != 0 && field->var) {
-            char* tag = g_hash_table_lookup(confwin->form->var_to_tag, field->var);
+            gchar* tag = g_hash_table_lookup(confwin->form->var_to_tag, field->var);
             _confwin_form_field(window, tag, field);
         }
 
diff --git a/src/ui/inputwin.c b/src/ui/inputwin.c
index bc689d5f..9b652f1c 100644
--- a/src/ui/inputwin.c
+++ b/src/ui/inputwin.c
@@ -36,6 +36,7 @@
 #define _XOPEN_SOURCE_EXTENDED
 #include "config.h"
 
+#include <assert.h>
 #include <stdio.h>
 #include <sys/select.h>
 #include <stdlib.h>
@@ -140,6 +141,11 @@ static int _inp_rl_send_to_editor(int count, int key);
 void
 create_input_window(void)
 {
+    /* MB_CUR_MAX is evaluated at runtime depending on the current
+     * locale, therefore we check that our own version is big enough
+     * and bail out if it isn't.
+     */
+    assert(MB_CUR_MAX <= PROF_MB_CUR_MAX);
 #ifdef NCURSES_REENTRANT
     set_escdelay(25);
 #else
@@ -331,7 +337,7 @@ _inp_write(char* line, int offset)
 
     for (size_t i = 0; line[i] != '\0'; i++) {
         char* c = &line[i];
-        char retc[MB_CUR_MAX];
+        char retc[PROF_MB_CUR_MAX] = { 0 };
 
         size_t ch_len = mbrlen(c, MB_CUR_MAX, NULL);
         if ((ch_len == (size_t)-2) || (ch_len == (size_t)-1)) {
diff --git a/src/ui/mucwin.c b/src/ui/mucwin.c
index 550b05e2..a2c31421 100644
--- a/src/ui/mucwin.c
+++ b/src/ui/mucwin.c
@@ -391,7 +391,7 @@ _mucwin_print_mention(ProfWin* window, const char* const message, const char* co
     while (curr) {
         pos = GPOINTER_TO_INT(curr->data);
 
-        char* before_str = g_utf8_substring(message, last_pos, pos);
+        auto_gchar gchar* before_str = g_utf8_substring(message, last_pos, pos);
 
         if (last_pos == 0 && strncmp(before_str, "/me ", 4) == 0) {
             win_print_them(window, THEME_ROOMMENTION, ch, flags, "");
@@ -404,11 +404,9 @@ _mucwin_print_mention(ProfWin* window, const char* const message, const char* co
             }
             win_append_highlight(window, THEME_ROOMMENTION, "%s", before_str);
         }
-        g_free(before_str);
 
-        char* mynick_str = g_utf8_substring(message, pos, pos + mynick_len);
+        auto_gchar gchar* mynick_str = g_utf8_substring(message, pos, pos + mynick_len);
         win_append_highlight(window, THEME_ROOMMENTION_TERM, "%s", mynick_str);
-        g_free(mynick_str);
 
         last_pos = pos + mynick_len;
 
@@ -418,7 +416,7 @@ _mucwin_print_mention(ProfWin* window, const char* const message, const char* co
     glong message_len = g_utf8_strlen(message, -1);
     if (last_pos < message_len) {
         // get tail without allocating a new string
-        char* rest = g_utf8_offset_to_pointer(message, last_pos);
+        gchar* rest = g_utf8_offset_to_pointer(message, last_pos);
         win_appendln_highlight(window, THEME_ROOMMENTION, "%s", rest);
     } else {
         win_appendln_highlight(window, THEME_ROOMMENTION, "");
@@ -449,15 +447,15 @@ _mucwin_print_triggers(ProfWin* window, const char* const message, GList* trigge
         curr = g_list_next(curr);
     }
 
-    char* message_lower = g_utf8_strdown(message, -1);
+    auto_gchar gchar* message_lower = g_utf8_strdown(message, -1);
 
     // find earliest trigger in message
     int first_trigger_pos = -1;
     int first_trigger_len = -1;
     curr = weighted_triggers;
     while (curr) {
-        char* trigger_lower = g_utf8_strdown(curr->data, -1);
-        char* trigger_ptr = g_strstr_len(message_lower, -1, trigger_lower);
+        auto_gchar gchar* trigger_lower = g_utf8_strdown(curr->data, -1);
+        gchar* trigger_ptr = g_strstr_len(message_lower, -1, trigger_lower);
 
         // not found, try next
         if (trigger_ptr == NULL) {
@@ -472,11 +470,9 @@ _mucwin_print_triggers(ProfWin* window, const char* const message, GList* trigge
             first_trigger_len = strlen(trigger_lower);
         }
 
-        g_free(trigger_lower);
         curr = g_list_next(curr);
     }
 
-    g_free(message_lower);
     g_list_free(weighted_triggers);
 
     // no triggers found
diff --git a/src/ui/ui.h b/src/ui/ui.h
index d9534ed5..79fafd75 100644
--- a/src/ui/ui.h
+++ b/src/ui/ui.h
@@ -145,7 +145,7 @@ void chatwin_set_incoming_char(ProfChatWin* chatwin, const char* const ch);
 void chatwin_unset_incoming_char(ProfChatWin* chatwin);
 void chatwin_set_outgoing_char(ProfChatWin* chatwin, const char* const ch);
 void chatwin_unset_outgoing_char(ProfChatWin* chatwin);
-gboolean chatwin_db_history(ProfChatWin* chatwin, char* start_time, char* end_time, gboolean flip);
+gboolean chatwin_db_history(ProfChatWin* chatwin, const char* start_time, char* end_time, gboolean flip);
 
 // MUC window
 ProfMucWin* mucwin_new(const char* const barejid);
diff --git a/src/xmpp/capabilities.c b/src/xmpp/capabilities.c
index 638b2c90..3bd567cd 100644
--- a/src/xmpp/capabilities.c
+++ b/src/xmpp/capabilities.c
@@ -368,14 +368,14 @@ _caps_by_ver(const char* const ver)
         return NULL;
     }
 
-    char* category = g_key_file_get_string(cache, ver, "category", NULL);
-    char* type = g_key_file_get_string(cache, ver, "type", NULL);
-    char* name = g_key_file_get_string(cache, ver, "name", NULL);
+    auto_gchar gchar* category = g_key_file_get_string(cache, ver, "category", NULL);
+    auto_gchar gchar* type = g_key_file_get_string(cache, ver, "type", NULL);
+    auto_gchar gchar* name = g_key_file_get_string(cache, ver, "name", NULL);
 
-    char* software = g_key_file_get_string(cache, ver, "software", NULL);
-    char* software_version = g_key_file_get_string(cache, ver, "software_version", NULL);
-    char* os = g_key_file_get_string(cache, ver, "os", NULL);
-    char* os_version = g_key_file_get_string(cache, ver, "os_version", NULL);
+    auto_gchar gchar* software = g_key_file_get_string(cache, ver, "software", NULL);
+    auto_gchar gchar* software_version = g_key_file_get_string(cache, ver, "software_version", NULL);
+    auto_gchar gchar* os = g_key_file_get_string(cache, ver, "os", NULL);
+    auto_gchar gchar* os_version = g_key_file_get_string(cache, ver, "os_version", NULL);
 
     gsize features_len = 0;
     gchar** features_list = g_key_file_get_string_list(cache, ver, "features", &features_len, NULL);
@@ -391,13 +391,6 @@ _caps_by_ver(const char* const ver)
         software, software_version, os, os_version,
         features);
 
-    g_free(category);
-    g_free(type);
-    g_free(name);
-    g_free(software);
-    g_free(software_version);
-    g_free(os);
-    g_free(os_version);
     if (features_list) {
         g_strfreev(features_list);
     }
diff --git a/src/xmpp/connection.c b/src/xmpp/connection.c
index eef395f1..2a022cc4 100644
--- a/src/xmpp/connection.c
+++ b/src/xmpp/connection.c
@@ -964,6 +964,7 @@ _connection_handler(xmpp_conn_t* const xmpp_conn, const xmpp_conn_event_t status
         conn.domain = strdup(my_jid->domainpart);
         jid_destroy(my_jid);
 
+        connection_clear_data();
         conn.features_by_jid = g_hash_table_new_full(g_str_hash, g_str_equal, free, (GDestroyNotify)g_hash_table_destroy);
         g_hash_table_insert(conn.features_by_jid, strdup(conn.domain), g_hash_table_new_full(g_str_hash, g_str_equal, free, NULL));
 
@@ -990,6 +991,7 @@ _connection_handler(xmpp_conn_t* const xmpp_conn, const xmpp_conn_event_t status
         conn.domain = strdup(my_raw_jid->domainpart);
         jid_destroy(my_raw_jid);
 
+        connection_clear_data();
         conn.features_by_jid = g_hash_table_new_full(g_str_hash, g_str_equal, free, (GDestroyNotify)g_hash_table_destroy);
         g_hash_table_insert(conn.features_by_jid, strdup(conn.domain), g_hash_table_new_full(g_str_hash, g_str_equal, free, NULL));
 
diff --git a/src/xmpp/connection.h b/src/xmpp/connection.h
index 79bee1d4..d4ade03a 100644
--- a/src/xmpp/connection.h
+++ b/src/xmpp/connection.h
@@ -48,7 +48,6 @@ jabber_conn_status_t connection_connect(const char* const fulljid, const char* c
                                         const char* const tls_policy, const char* const auth_policy);
 jabber_conn_status_t connection_register(const char* const altdomain, int port, const char* const tls_policy,
                                          const char* const username, const char* const password);
-void connection_disconnect(void);
 void connection_set_disconnected(void);
 
 void connection_set_priority(const int priority);
diff --git a/src/xmpp/iq.c b/src/xmpp/iq.c
index f09deead..159ba609 100644
--- a/src/xmpp/iq.c
+++ b/src/xmpp/iq.c
@@ -265,6 +265,7 @@ iq_handlers_init(void)
         xmpp_timed_handler_add(conn, _autoping_timed_send, millis, ctx);
     }
 
+    iq_rooms_cache_clear();
     iq_handlers_clear();
 
     id_handlers = g_hash_table_new_full(g_str_hash, g_str_equal, free, (GDestroyNotify)_iq_id_handler_free);
@@ -1614,6 +1615,7 @@ _version_get_handler(xmpp_stanza_t* const stanza)
     const char* from = xmpp_stanza_get_from(stanza);
     ProfAccount* account = accounts_get_account(session_get_account_name());
     auto_char char* client = account->client != NULL ? strdup(account->client) : NULL;
+    account_free(account);
     bool is_custom_client = client != NULL;
     gchar* custom_version_str = NULL;
     if (is_custom_client) {
@@ -2651,6 +2653,18 @@ iq_mam_request_older(ProfChatWin* win)
     return;
 }
 
+static void
+_mam_userdata_free(MamRsmUserdata* data)
+{
+    free(data->end_datestr);
+    data->end_datestr = NULL;
+    free(data->start_datestr);
+    data->start_datestr = NULL;
+    free(data->barejid);
+    data->barejid = NULL;
+    free(data);
+}
+
 void
 _iq_mam_request(ProfChatWin* win, GDateTime* startdate, GDateTime* enddate)
 {
@@ -2694,7 +2708,7 @@ _iq_mam_request(ProfChatWin* win, GDateTime* startdate, GDateTime* enddate)
         data->fetch_next = fetch_next;
         data->win = win;
 
-        iq_id_handler_add(xmpp_stanza_get_id(iq), _mam_rsm_id_handler, NULL, data);
+        iq_id_handler_add(xmpp_stanza_get_id(iq), _mam_rsm_id_handler, (ProfIqFreeCallback)_mam_userdata_free, data);
     }
 
     iq_send_stanza(iq);
@@ -2714,9 +2728,11 @@ iq_mam_request(ProfChatWin* win, GDateTime* enddate)
     if (!received_disco_items) {
         LateDeliveryUserdata* cur_del_data = malloc(sizeof(LateDeliveryUserdata));
         cur_del_data->win = win;
-        cur_del_data->enddate = g_date_time_ref(enddate);
-        cur_del_data->startdate = g_date_time_ref(startdate);
+        cur_del_data->enddate = enddate;
+        cur_del_data->startdate = startdate;
         late_delivery_windows = g_slist_append(late_delivery_windows, cur_del_data);
+        log_debug("Save MAM request of %s for later", win->barejid);
+        return;
     }
 
     _iq_mam_request(win, startdate, enddate);
@@ -2742,7 +2758,7 @@ _mam_rsm_id_handler(xmpp_stanza_t* const stanza, void* const userdata)
 
             buffer_remove_entry(window->layout->buffer, 0);
 
-            char* start_str = NULL;
+            auto_char char* start_str = NULL;
             if (data->start_datestr) {
                 start_str = strdup(data->start_datestr);
                 // Convert to iso8601
@@ -2757,24 +2773,10 @@ _mam_rsm_id_handler(xmpp_stanza_t* const stanza, void* const userdata)
 
             if (is_complete || !data->fetch_next) {
                 chatwin_db_history(data->win, is_complete ? NULL : start_str, end_str, TRUE);
-                // TODO free memory
-                if (start_str) {
-                    free(start_str);
-                    free(data->start_datestr);
-                }
-
-                if (end_str) {
-                    free(data->end_datestr);
-                }
-
-                free(data->barejid);
-                free(data);
                 return 0;
             }
 
             chatwin_db_history(data->win, start_str, end_str, TRUE);
-            if (start_str)
-                free(start_str);
 
             xmpp_stanza_t* set = xmpp_stanza_get_child_by_name_and_ns(fin, STANZA_TYPE_SET, STANZA_NS_RSM);
             if (set) {
@@ -2787,14 +2789,22 @@ _mam_rsm_id_handler(xmpp_stanza_t* const stanza, void* const userdata)
                 // 4.3.2. send same stanza with set,max stanza
                 xmpp_ctx_t* const ctx = connection_get_ctx();
 
-                if (end_str) {
+                if (data->end_datestr) {
                     free(data->end_datestr);
+                    data->end_datestr = NULL;
                 }
-                data->end_datestr = NULL;
-                xmpp_stanza_t* iq = stanza_create_mam_iq(ctx, data->barejid, data->start_datestr, data->end_datestr, firstid, NULL);
+                xmpp_stanza_t* iq = stanza_create_mam_iq(ctx, data->barejid, data->start_datestr, NULL, firstid, NULL);
                 free(firstid);
 
-                iq_id_handler_add(xmpp_stanza_get_id(iq), _mam_rsm_id_handler, NULL, data);
+                MamRsmUserdata* ndata = malloc(sizeof(*ndata));
+                *ndata = *data;
+                if (data->end_datestr)
+                    ndata->end_datestr = strdup(data->end_datestr);
+                if (data->start_datestr)
+                    ndata->start_datestr = strdup(data->start_datestr);
+                if (data->barejid)
+                    ndata->barejid = strdup(data->barejid);
+                iq_id_handler_add(xmpp_stanza_get_id(iq), _mam_rsm_id_handler, (ProfIqFreeCallback)_mam_userdata_free, ndata);
 
                 iq_send_stanza(iq);
                 xmpp_stanza_release(iq);
diff --git a/src/xmpp/jid.c b/src/xmpp/jid.c
index e9aa595e..51c710f1 100644
--- a/src/xmpp/jid.c
+++ b/src/xmpp/jid.c
@@ -90,10 +90,9 @@ jid_create(const gchar* const str)
     if (slashp) {
         result->resourcepart = g_strdup(slashp + 1);
         result->domainpart = g_utf8_substring(domain_start, 0, g_utf8_pointer_to_offset(domain_start, slashp));
-        char* barejidraw = g_utf8_substring(trimmed, 0, g_utf8_pointer_to_offset(trimmed, slashp));
+        auto_gchar gchar* barejidraw = g_utf8_substring(trimmed, 0, g_utf8_pointer_to_offset(trimmed, slashp));
         result->barejid = g_utf8_strdown(barejidraw, -1);
         result->fulljid = g_strdup(trimmed);
-        g_free(barejidraw);
     } else {
         result->domainpart = g_strdup(domain_start);
         result->barejid = g_utf8_strdown(trimmed, -1);
@@ -189,15 +188,13 @@ create_fulljid(const char* const barejid, const char* const resource)
 char*
 get_nick_from_full_jid(const char* const full_room_jid)
 {
-    char** tokens = g_strsplit(full_room_jid, "/", 0);
+    auto_gcharv gchar** tokens = g_strsplit(full_room_jid, "/", 0);
     char* nick_part = NULL;
 
     if (tokens) {
         if (tokens[0] && tokens[1]) {
             nick_part = strdup(tokens[1]);
         }
-
-        g_strfreev(tokens);
     }
 
     return nick_part;
diff --git a/src/xmpp/message.c b/src/xmpp/message.c
index 28997474..a6372c4a 100644
--- a/src/xmpp/message.c
+++ b/src/xmpp/message.c
@@ -638,10 +638,9 @@ message_send_chat_omemo(const char* const jid, uint32_t sid, GList* keys,
 
     xmpp_stanza_t* header = xmpp_stanza_new(ctx);
     xmpp_stanza_set_name(header, "header");
-    char* sid_text = g_strdup_printf("%d", sid);
+    auto_gchar gchar* sid_text = g_strdup_printf("%d", sid);
     log_debug("[OMEMO] Sending from device sid %s", sid_text);
     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) {
@@ -649,10 +648,9 @@ message_send_chat_omemo(const char* const jid, uint32_t sid, GList* keys,
 
         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);
+        auto_gchar gchar* rid = g_strdup_printf("%d", key->device_id);
         log_debug("[OMEMO] Sending to device rid %s", STR_MAYBE_NULL(rid));
         xmpp_stanza_set_attribute(key_stanza, "rid", rid);
-        g_free(rid);
         if (key->prekey) {
             xmpp_stanza_set_attribute(key_stanza, "prekey", "true");
         }
@@ -1625,19 +1623,16 @@ message_is_sent_by_us(const ProfMessage* const message, bool checkOID)
 
             // our client sents at CON_RAND_ID_LEN + identifier
             if (tmp_len > CON_RAND_ID_LEN) {
-                char* uuid = g_strndup(tmp_id, CON_RAND_ID_LEN);
+                auto_gchar gchar* uuid = g_strndup(tmp_id, CON_RAND_ID_LEN);
                 const char* prof_identifier = connection_get_profanity_identifier();
 
-                gchar* hmac = g_compute_hmac_for_string(G_CHECKSUM_SHA1,
-                                                        (guchar*)prof_identifier, strlen(prof_identifier),
-                                                        uuid, strlen(uuid));
+                auto_gchar gchar* hmac = g_compute_hmac_for_string(G_CHECKSUM_SHA1,
+                                                                   (guchar*)prof_identifier, strlen(prof_identifier),
+                                                                   uuid, strlen(uuid));
 
                 if (g_strcmp0(&tmp_id[CON_RAND_ID_LEN], hmac) == 0) {
                     ret = TRUE;
                 }
-
-                g_free(uuid);
-                g_free(hmac);
             }
         }
     }
diff --git a/src/xmpp/omemo.c b/src/xmpp/omemo.c
index e1bdbc3e..23c3d375 100644
--- a/src/xmpp/omemo.c
+++ b/src/xmpp/omemo.c
@@ -669,11 +669,10 @@ _omemo_bundle_publish_result(xmpp_stanza_t* const stanza, void* const userdata)
     xmpp_ctx_t* const ctx = connection_get_ctx();
     Jid* jid = jid_create(connection_get_fulljid());
     char* id = connection_create_stanza_id();
-    char* node = g_strdup_printf("%s:%d", STANZA_NS_OMEMO_BUNDLES, omemo_device_id());
+    auto_gchar gchar* node = g_strdup_printf("%s:%d", STANZA_NS_OMEMO_BUNDLES, omemo_device_id());
     log_debug("[OMEMO] node: %s", node);
 
     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);
 
@@ -712,9 +711,8 @@ _omemo_bundle_publish_configure(xmpp_stanza_t* const stanza, void* const userdat
     xmpp_ctx_t* const ctx = connection_get_ctx();
     Jid* jid = jid_create(connection_get_fulljid());
     char* id = connection_create_stanza_id();
-    char* node = g_strdup_printf("%s:%d", STANZA_NS_OMEMO_BUNDLES, omemo_device_id());
+    auto_gchar gchar* 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);
 
diff --git a/src/xmpp/session.h b/src/xmpp/session.h
index e6facb93..d8565fa4 100644
--- a/src/xmpp/session.h
+++ b/src/xmpp/session.h
@@ -47,6 +47,5 @@ void session_init_activity(void);
 void session_check_autoaway(void);
 
 void session_reconnect(gchar* altdomain, unsigned short altport);
-void session_reconnect_now(void);
 
 #endif
diff --git a/src/xmpp/stanza.c b/src/xmpp/stanza.c
index 0fe3966c..bbc15c13 100644
--- a/src/xmpp/stanza.c
+++ b/src/xmpp/stanza.c
@@ -2329,9 +2329,8 @@ stanza_create_omemo_devicelist_publish(xmpp_ctx_t* ctx, GList* const ids)
     for (GList* 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));
+        auto_gchar gchar* 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);
@@ -2366,9 +2365,8 @@ stanza_create_omemo_bundle_publish(xmpp_ctx_t* ctx, const char* const id,
 
     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);
+    auto_gchar gchar* 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);
@@ -2416,9 +2414,8 @@ stanza_create_omemo_bundle_publish(xmpp_ctx_t* ctx, const char* const id,
     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));
+        auto_gchar gchar* 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));
@@ -2464,9 +2461,8 @@ stanza_create_omemo_bundle_request(xmpp_ctx_t* ctx, const char* const id, const
 
     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);
+    auto_gchar gchar* 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);
@@ -2577,9 +2573,8 @@ stanza_create_avatar_retrieve_data_request(xmpp_ctx_t* ctx, const char* stanza_i
 
     xmpp_stanza_t* items = xmpp_stanza_new(ctx);
     xmpp_stanza_set_name(items, "items");
-    char* node = g_strdup_printf("%s", STANZA_NS_USER_AVATAR_DATA);
+    auto_gchar gchar* node = g_strdup_printf("%s", STANZA_NS_USER_AVATAR_DATA);
     xmpp_stanza_set_attribute(items, STANZA_ATTR_NODE, node);
-    g_free(node);
 
     xmpp_stanza_t* item = xmpp_stanza_new(ctx);
     xmpp_stanza_set_name(item, STANZA_NAME_ITEM);
@@ -2671,16 +2666,13 @@ stanza_create_avatar_metadata_publish_iq(xmpp_ctx_t* ctx, const char* img_data,
     xmpp_stanza_set_name(info, STANZA_NAME_INFO);
     xmpp_stanza_set_attribute(info, "id", sha1);
     xmpp_free(ctx, sha1);
-    char* bytes = g_strdup_printf("%" G_GSIZE_FORMAT, len);
-    char* h = g_strdup_printf("%d", height);
-    char* w = g_strdup_printf("%d", width);
+    auto_gchar gchar* bytes = g_strdup_printf("%" G_GSIZE_FORMAT, len);
+    auto_gchar gchar* h = g_strdup_printf("%d", height);
+    auto_gchar gchar* w = g_strdup_printf("%d", width);
     xmpp_stanza_set_attribute(info, "bytes", bytes);
     xmpp_stanza_set_attribute(info, "type", "img/png");
     xmpp_stanza_set_attribute(info, "height", h);
     xmpp_stanza_set_attribute(info, "width", w);
-    g_free(bytes);
-    g_free(h);
-    g_free(w);
 
     xmpp_stanza_add_child(metadata, info);
     xmpp_stanza_add_child(item, metadata);
@@ -2765,6 +2757,19 @@ stanza_attach_correction(xmpp_ctx_t* ctx, xmpp_stanza_t* stanza, const char* con
     return stanza;
 }
 
+static xmpp_stanza_t*
+_text_stanza(xmpp_ctx_t* ctx, const char* name, const char* text)
+{
+    xmpp_stanza_t* res = xmpp_stanza_new(ctx);
+    xmpp_stanza_set_name(res, name);
+
+    xmpp_stanza_t* t = xmpp_stanza_new(ctx);
+    xmpp_stanza_set_text(t, text);
+    xmpp_stanza_add_child_ex(res, t, 0);
+
+    return res;
+}
+
 xmpp_stanza_t*
 stanza_create_mam_iq(xmpp_ctx_t* ctx, const char* const jid, const char* const startdate, const char* const enddate, const char* const firstid, const char* const lastid)
 {
@@ -2788,146 +2793,67 @@ stanza_create_mam_iq(xmpp_ctx_t* ctx, const char* const jid, const char* const s
     xmpp_stanza_set_attribute(field_form_type, STANZA_ATTR_VAR, "FORM_TYPE");
     xmpp_stanza_set_type(field_form_type, "hidden");
 
-    xmpp_stanza_t* value_mam = xmpp_stanza_new(ctx);
-    xmpp_stanza_set_name(value_mam, STANZA_NAME_VALUE);
-
-    xmpp_stanza_t* mam_text = xmpp_stanza_new(ctx);
-    xmpp_stanza_set_text(mam_text, STANZA_NS_MAM2);
-    xmpp_stanza_add_child(value_mam, mam_text);
+    xmpp_stanza_t* value_mam = _text_stanza(ctx, STANZA_NAME_VALUE, STANZA_NS_MAM2);
 
-    xmpp_stanza_add_child(field_form_type, value_mam);
+    xmpp_stanza_add_child_ex(field_form_type, value_mam, 0);
 
     // field 'with'
     xmpp_stanza_t* field_with = xmpp_stanza_new(ctx);
     xmpp_stanza_set_name(field_with, STANZA_NAME_FIELD);
     xmpp_stanza_set_attribute(field_with, STANZA_ATTR_VAR, "with");
 
-    xmpp_stanza_t* value_with = xmpp_stanza_new(ctx);
-    xmpp_stanza_set_name(value_with, STANZA_NAME_VALUE);
-
-    xmpp_stanza_t* with_text = xmpp_stanza_new(ctx);
-    xmpp_stanza_set_text(with_text, jid);
-    xmpp_stanza_add_child(value_with, with_text);
-
-    xmpp_stanza_add_child(field_with, value_with);
-
-    // field 'start'
-    xmpp_stanza_t *field_start, *value_start, *start_date_text;
-    if (startdate) {
-        field_start = xmpp_stanza_new(ctx);
-        xmpp_stanza_set_name(field_start, STANZA_NAME_FIELD);
-        xmpp_stanza_set_attribute(field_start, STANZA_ATTR_VAR, "start");
-
-        value_start = xmpp_stanza_new(ctx);
-        xmpp_stanza_set_name(value_start, STANZA_NAME_VALUE);
-
-        start_date_text = xmpp_stanza_new(ctx);
-        xmpp_stanza_set_text(start_date_text, startdate);
-        xmpp_stanza_add_child(value_start, start_date_text);
-
-        xmpp_stanza_add_child(field_start, value_start);
-    }
-
-    xmpp_stanza_t *field_end, *value_end, *end_date_text;
-    if (enddate) {
-        field_end = xmpp_stanza_new(ctx);
-        xmpp_stanza_set_name(field_end, STANZA_NAME_FIELD);
-        xmpp_stanza_set_attribute(field_end, STANZA_ATTR_VAR, "end");
-
-        value_end = xmpp_stanza_new(ctx);
-        xmpp_stanza_set_name(value_end, STANZA_NAME_VALUE);
-
-        end_date_text = xmpp_stanza_new(ctx);
-        xmpp_stanza_set_text(end_date_text, enddate);
-        xmpp_stanza_add_child(value_end, end_date_text);
+    xmpp_stanza_t* value_with = _text_stanza(ctx, STANZA_NAME_VALUE, jid);
 
-        xmpp_stanza_add_child(field_end, value_end);
-    }
+    xmpp_stanza_add_child_ex(field_with, value_with, 0);
 
     // 4.3.2 set/rsm
-    xmpp_stanza_t *set, *max, *max_text;
-    set = xmpp_stanza_new(ctx);
+    xmpp_stanza_t* set = xmpp_stanza_new(ctx);
     xmpp_stanza_set_name(set, STANZA_TYPE_SET);
     xmpp_stanza_set_ns(set, STANZA_NS_RSM);
 
-    max = xmpp_stanza_new(ctx);
-    xmpp_stanza_set_name(max, STANZA_NAME_MAX);
-
-    max_text = xmpp_stanza_new(ctx);
-    char* txt = g_strdup_printf("%d", MESSAGES_TO_RETRIEVE);
-    xmpp_stanza_set_text(max_text, txt);
-    g_free(txt);
-
-    xmpp_stanza_add_child(max, max_text);
-    xmpp_stanza_add_child(set, max);
+    xmpp_stanza_t* max = _text_stanza(ctx, STANZA_NAME_MAX, PROF_STRINGIFY(MESSAGES_TO_RETRIEVE));
+    xmpp_stanza_add_child_ex(set, max, 0);
 
-    xmpp_stanza_t *after, *after_text;
     if (lastid) {
-        after = xmpp_stanza_new(ctx);
-        xmpp_stanza_set_name(after, STANZA_NAME_AFTER);
-
-        after_text = xmpp_stanza_new(ctx);
-        xmpp_stanza_set_text(after_text, lastid);
-
-        xmpp_stanza_add_child(after, after_text);
-        xmpp_stanza_add_child(set, after);
+        xmpp_stanza_t* after = _text_stanza(ctx, STANZA_NAME_AFTER, lastid);
+        xmpp_stanza_add_child_ex(set, after, 0);
     }
 
-    xmpp_stanza_t *before, *before_text;
     if (firstid) {
-        before = xmpp_stanza_new(ctx);
-        xmpp_stanza_set_name(before, STANZA_NAME_BEFORE);
-
-        before_text = xmpp_stanza_new(ctx);
-        xmpp_stanza_set_text(before_text, firstid);
-
-        xmpp_stanza_add_child(before, before_text);
-        xmpp_stanza_add_child(set, before);
+        xmpp_stanza_t* before = _text_stanza(ctx, STANZA_NAME_BEFORE, firstid);
+        xmpp_stanza_add_child_ex(set, before, 0);
     }
 
     // add and release
-    xmpp_stanza_add_child(iq, query);
-    xmpp_stanza_add_child(query, x);
-    xmpp_stanza_add_child(x, field_form_type);
-    xmpp_stanza_add_child(x, field_with);
+    xmpp_stanza_add_child_ex(iq, query, 0);
+    xmpp_stanza_add_child_ex(query, x, 0);
+    xmpp_stanza_add_child_ex(x, field_form_type, 0);
+    xmpp_stanza_add_child_ex(x, field_with, 0);
 
+    // field 'start'
     if (startdate) {
-        xmpp_stanza_add_child(x, field_start);
-        xmpp_stanza_release(field_start);
-        xmpp_stanza_release(value_start);
-        xmpp_stanza_release(start_date_text);
-    }
+        xmpp_stanza_t* field_start = xmpp_stanza_new(ctx);
+        xmpp_stanza_set_name(field_start, STANZA_NAME_FIELD);
+        xmpp_stanza_set_attribute(field_start, STANZA_ATTR_VAR, "start");
 
-    if (enddate) {
-        xmpp_stanza_add_child(x, field_end);
-        xmpp_stanza_release(field_end);
-        xmpp_stanza_release(value_end);
-        xmpp_stanza_release(end_date_text);
+        xmpp_stanza_t* value_start = _text_stanza(ctx, STANZA_NAME_VALUE, startdate);
+
+        xmpp_stanza_add_child_ex(field_start, value_start, 0);
+        xmpp_stanza_add_child_ex(x, field_start, 0);
     }
 
-    xmpp_stanza_add_child(query, set);
-    xmpp_stanza_release(set);
-    xmpp_stanza_release(max_text);
-    xmpp_stanza_release(max);
+    if (enddate) {
+        xmpp_stanza_t* field_end = xmpp_stanza_new(ctx);
+        xmpp_stanza_set_name(field_end, STANZA_NAME_FIELD);
+        xmpp_stanza_set_attribute(field_end, STANZA_ATTR_VAR, "end");
 
-    if (lastid) {
-        xmpp_stanza_release(after_text);
-        xmpp_stanza_release(after);
-    }
+        xmpp_stanza_t* value_end = _text_stanza(ctx, STANZA_NAME_VALUE, enddate);
 
-    if (firstid) {
-        xmpp_stanza_release(before_text);
-        xmpp_stanza_release(before);
+        xmpp_stanza_add_child_ex(field_end, value_end, 0);
+        xmpp_stanza_add_child_ex(x, field_end, 0);
     }
 
-    xmpp_stanza_release(mam_text);
-    xmpp_stanza_release(with_text);
-    xmpp_stanza_release(value_mam);
-    xmpp_stanza_release(value_with);
-    xmpp_stanza_release(field_form_type);
-    xmpp_stanza_release(field_with);
-    xmpp_stanza_release(x);
-    xmpp_stanza_release(query);
+    xmpp_stanza_add_child_ex(query, set, 0);
 
     return iq;
 }
diff --git a/src/xmpp/xmpp.h b/src/xmpp/xmpp.h
index 539126cd..2babe536 100644
--- a/src/xmpp/xmpp.h
+++ b/src/xmpp/xmpp.h
@@ -186,7 +186,9 @@ void session_disconnect(void);
 void session_shutdown(void);
 void session_process_events(void);
 char* session_get_account_name(void);
+void session_reconnect_now(void);
 
+void connection_disconnect(void);
 jabber_conn_status_t connection_get_status(void);
 char* connection_get_presence_msg(void);
 void connection_set_presence_msg(const char* const message);