about summary refs log tree commit diff stats
path: root/src/command
diff options
context:
space:
mode:
Diffstat (limited to 'src/command')
-rw-r--r--src/command/command.c200
-rw-r--r--src/command/commands.c90
-rw-r--r--src/command/commands.h1
3 files changed, 262 insertions, 29 deletions
diff --git a/src/command/command.c b/src/command/command.c
index f1432d90..ec8696fe 100644
--- a/src/command/command.c
+++ b/src/command/command.c
@@ -51,6 +51,7 @@
 #include "config/preferences.h"
 #include "config/theme.h"
 #include "config/tlscerts.h"
+#include "config/scripts.h"
 #include "contact.h"
 #include "roster_list.h"
 #include "jid.h"
@@ -108,6 +109,7 @@ static char * _receipts_autocomplete(ProfWin *window, const char * const input);
 static char * _help_autocomplete(ProfWin *window, const char * const input);
 static char * _wins_autocomplete(ProfWin *window, const char * const input);
 static char * _tls_autocomplete(ProfWin *window, const char * const input);
+static char * _script_autocomplete(ProfWin *window, const char * const input);
 
 GHashTable *commands = NULL;
 
@@ -166,12 +168,12 @@ static struct cmd_t command_defs[] =
     },
 
     { "/connect",
-        cmd_connect, parse_args, 0, 5, NULL,
+        cmd_connect, parse_args, 0, 7, NULL,
         CMD_TAGS(
             CMD_TAG_CONNECTION)
         CMD_SYN(
             "/connect [<account>]",
-            "/connect <account> [server <server>] [port <port>]")
+            "/connect <account> [server <server>] [port <port>] [tls force|allow|disable]")
         CMD_DESC(
             "Login to a chat service. "
             "If no account is specified, the default is used if one is configured. "
@@ -179,19 +181,24 @@ static struct cmd_t command_defs[] =
         CMD_ARGS(
             { "<account>",         "The local account you wish to connect with, or a JID if connecting for the first time." },
             { "server <server>",   "Supply a server if it is different to the domain part of your JID." },
-            { "port <port>",       "The port to use if different to the default (5222, or 5223 for SSL)." })
+            { "port <port>",       "The port to use if different to the default (5222, or 5223 for SSL)." },
+            { "tls force",         "Force TLS connection, and fail if one cannot be established, this is default behaviour." },
+            { "tls allow",         "Use TLS for the connection if it is available." },
+            { "tls disable",       "Disable TLS for the connection." })
         CMD_EXAMPLES(
             "/connect",
             "/connect myuser@gmail.com",
             "/connect myuser@mycompany.com server talk.google.com",
             "/connect bob@someplace port 5678",
+            "/connect me@localhost.test.org server 127.0.0.1 tls disable",
             "/connect me@chatty server chatty.com port 5443")
         },
 
     { "/tls",
         cmd_tls, parse_args, 1, 3, NULL,
         CMD_TAGS(
-            CMD_TAG_CONNECTION)
+            CMD_TAG_CONNECTION,
+            CMD_TAG_UI)
         CMD_SYN(
             "/tls allow",
             "/tls always",
@@ -200,7 +207,8 @@ static struct cmd_t command_defs[] =
             "/tls revoke <fingerprint>",
             "/tls certpath",
             "/tls certpath set <path>",
-            "/tls certpath clear")
+            "/tls certpath clear",
+            "/tls show on|off")
         CMD_DESC(
             "Handle TLS certificates. ")
         CMD_ARGS(
@@ -211,7 +219,8 @@ static struct cmd_t command_defs[] =
             { "revoke <fingerprint>", "Remove a manually trusted certificate." },
             { "certpath",             "Show the trusted certificate path." },
             { "certpath set <path>",  "Specify filesystem path containing trusted certificates." },
-            { "certpath clear",       "Clear the trusted certificate path." })
+            { "certpath clear",       "Clear the trusted certificate path." },
+            { "show on|off",          "Show or hide the TLS indicator in the titlebar." })
         CMD_NOEXAMPLES
     },
 
@@ -700,13 +709,16 @@ static struct cmd_t command_defs[] =
         CMD_TAGS(
             CMD_TAG_PRESENCE)
         CMD_SYN(
+            "/lastactivity on|off",
             "/lastactivity [<jid>]")
         CMD_DESC(
-            "Send a last activity query to the supplied JID, omitting the JID will send the query to your server.")
+            "Enable/disable sending last activity, and send last activity requests.")
         CMD_ARGS(
-            { "<jid>", "The JID of the entity to which the query will be sent." })
+            { "on|off", "Enable or disable sending of last activity." },
+            { "<jid>",  "The JID of the entity to query, omitting the JID will query your server." })
         CMD_EXAMPLES(
             "/lastactivity",
+            "/lastactivity off",
             "/lastactivity alice@securechat.org",
             "/lastactivity alice@securechat.org/laptop",
             "/lastactivity someserver.com")
@@ -1482,12 +1494,15 @@ static struct cmd_t command_defs[] =
             "/account set <account> nick <nick>",
             "/account set <account> otr <policy>",
             "/account set <account> pgpkeyid <pgpkeyid>",
+            "/account set <account> startscript <script>",
+            "/account set <account> tls force|allow|disable",
             "/account clear <account> password",
             "/account clear <account> eval_password",
             "/account clear <account> server",
             "/account clear <account> port",
             "/account clear <account> otr",
-            "/account clear <account> pgpkeyid")
+            "/account clear <account> pgpkeyid",
+            "/account clear <account> startscript")
         CMD_DESC(
             "Commands for creating and managing accounts. "
             "Calling with no arguments will display information for the current account.")
@@ -1514,12 +1529,17 @@ static struct cmd_t command_defs[] =
             { "set <account> nick <nick>",              "The default nickname to use when joining chat rooms." },
             { "set <account> otr <policy>",             "Override global OTR policy for this account, see /otr." },
             { "set <account> pgpkeyid <pgpkeyid>",      "Set the ID of the PGP key for this account, see /pgp." },
+            { "set <account> startscript <script>",     "Set the script to execute after connecting." },
+            { "set <account> tls force",                "Force TLS connection, and fail if one cannot be established, this is default behaviour." },
+            { "set <account> tls allow",                "Use TLS for the connection if it is available." },
+            { "set <account> tls disable",              "Disable TLS for the connection." },
             { "clear <account> server",                 "Remove the server setting for this account." },
             { "clear <account> port",                   "Remove the port setting for this account." },
             { "clear <account> password",               "Remove the password setting for this account." },
             { "clear <account> eval_password",          "Remove the eval_password setting for this account." },
             { "clear <account> otr",                    "Remove the OTR policy setting for this account." },
-            { "clear <account> pgpkeyid",               "Remove pgpkeyid associated with this account." })
+            { "clear <account> pgpkeyid",               "Remove pgpkeyid associated with this account." },
+            { "clear <account> startscript",            "Remove startscript associated with this account." })
         CMD_EXAMPLES(
             "/account add me",
             "/account set me jid me@chatty",
@@ -1681,6 +1701,26 @@ static struct cmd_t command_defs[] =
             "/xa",
             "/xa This meeting is going to be a long one")
     },
+
+    { "/script",
+        cmd_script, parse_args, 1, 2, NULL,
+        CMD_NOTAGS
+        CMD_SYN(
+            "/script run <script>",
+            "/script list",
+            "/script show <script>")
+        CMD_DESC(
+            "Run command scripts. "
+            "Scripts are stored in $XDG_DATA_HOME/profanity/scripts/ which is usually $HOME/.local/share/profanity/scripts/.")
+        CMD_ARGS(
+            { "script run <script>",    "Execute a script." },
+            { "script list",            "List all scripts TODO." },
+            { "script show <script>",   "Show the commands in script TODO." })
+        CMD_EXAMPLES(
+            "/script list",
+            "/script run myscript",
+            "/script show somescript")
+    },
 };
 
 static Autocomplete commands_ac;
@@ -1721,6 +1761,7 @@ static Autocomplete otr_ac;
 static Autocomplete otr_log_ac;
 static Autocomplete otr_policy_ac;
 static Autocomplete connect_property_ac;
+static Autocomplete tls_property_ac;
 static Autocomplete statuses_ac;
 static Autocomplete statuses_setting_ac;
 static Autocomplete alias_ac;
@@ -1745,6 +1786,8 @@ static Autocomplete pgp_ac;
 static Autocomplete pgp_log_ac;
 static Autocomplete tls_ac;
 static Autocomplete tls_certpath_ac;
+static Autocomplete script_ac;
+static Autocomplete script_show_ac;
 
 /*
  * Initialise command autocompleter and history
@@ -1909,6 +1952,8 @@ cmd_init(void)
     autocomplete_add(account_set_ac, "nick");
     autocomplete_add(account_set_ac, "otr");
     autocomplete_add(account_set_ac, "pgpkeyid");
+    autocomplete_add(account_set_ac, "startscript");
+    autocomplete_add(account_set_ac, "tls");
 
     account_clear_ac = autocomplete_new();
     autocomplete_add(account_clear_ac, "password");
@@ -1917,6 +1962,7 @@ cmd_init(void)
     autocomplete_add(account_clear_ac, "port");
     autocomplete_add(account_clear_ac, "otr");
     autocomplete_add(account_clear_ac, "pgpkeyid");
+    autocomplete_add(account_clear_ac, "startscript");
 
     account_default_ac = autocomplete_new();
     autocomplete_add(account_default_ac, "set");
@@ -2039,6 +2085,12 @@ cmd_init(void)
     connect_property_ac = autocomplete_new();
     autocomplete_add(connect_property_ac, "server");
     autocomplete_add(connect_property_ac, "port");
+    autocomplete_add(connect_property_ac, "tls");
+
+    tls_property_ac = autocomplete_new();
+    autocomplete_add(tls_property_ac, "force");
+    autocomplete_add(tls_property_ac, "allow");
+    autocomplete_add(tls_property_ac, "disable");
 
     join_property_ac = autocomplete_new();
     autocomplete_add(join_property_ac, "nick");
@@ -2158,10 +2210,18 @@ cmd_init(void)
     autocomplete_add(tls_ac, "trusted");
     autocomplete_add(tls_ac, "revoke");
     autocomplete_add(tls_ac, "certpath");
+    autocomplete_add(tls_ac, "show");
 
     tls_certpath_ac = autocomplete_new();
     autocomplete_add(tls_certpath_ac, "set");
     autocomplete_add(tls_certpath_ac, "clear");
+
+    script_ac = autocomplete_new();
+    autocomplete_add(script_ac, "run");
+    autocomplete_add(script_ac, "list");
+    autocomplete_add(script_ac, "show");
+
+    script_show_ac = NULL;
 }
 
 void
@@ -2205,6 +2265,7 @@ cmd_uninit(void)
     autocomplete_free(otr_log_ac);
     autocomplete_free(otr_policy_ac);
     autocomplete_free(connect_property_ac);
+    autocomplete_free(tls_property_ac);
     autocomplete_free(statuses_ac);
     autocomplete_free(statuses_setting_ac);
     autocomplete_free(alias_ac);
@@ -2229,6 +2290,8 @@ cmd_uninit(void)
     autocomplete_free(pgp_log_ac);
     autocomplete_free(tls_ac);
     autocomplete_free(tls_certpath_ac);
+    autocomplete_free(script_ac);
+    autocomplete_free(script_show_ac);
 }
 
 gboolean
@@ -2389,6 +2452,7 @@ cmd_reset_autocomplete(ProfWin *window)
     autocomplete_reset(otr_log_ac);
     autocomplete_reset(otr_policy_ac);
     autocomplete_reset(connect_property_ac);
+    autocomplete_reset(tls_property_ac);
     autocomplete_reset(statuses_ac);
     autocomplete_reset(statuses_setting_ac);
     autocomplete_reset(alias_ac);
@@ -2413,6 +2477,11 @@ cmd_reset_autocomplete(ProfWin *window)
     autocomplete_reset(pgp_log_ac);
     autocomplete_reset(tls_ac);
     autocomplete_reset(tls_certpath_ac);
+    autocomplete_reset(script_ac);
+    if (script_show_ac) {
+        autocomplete_free(script_show_ac);
+        script_show_ac = NULL;
+    }
 
     if (window->type == WIN_CHAT) {
         ProfChatWin *chatwin = (ProfChatWin*)window;
@@ -2560,7 +2629,7 @@ _cmd_complete_parameters(ProfWin *window, const char * const input)
     // autocomplete boolean settings
     gchar *boolean_choices[] = { "/beep", "/intype", "/states", "/outtype",
         "/flash", "/splash", "/chlog", "/grlog", "/history", "/vercheck",
-        "/privileges", "/presence", "/wrap", "/winstidy", "/carbons", "/encwarn" };
+        "/privileges", "/presence", "/wrap", "/winstidy", "/carbons", "/encwarn", "/lastactivity" };
 
     for (i = 0; i < ARRAY_SIZE(boolean_choices); i++) {
         result = autocomplete_param_with_func(input, boolean_choices[i], prefs_autocomplete_boolean_choice);
@@ -2667,6 +2736,7 @@ _cmd_complete_parameters(ProfWin *window, const char * const input)
     g_hash_table_insert(ac_funcs, "/receipts",      _receipts_autocomplete);
     g_hash_table_insert(ac_funcs, "/wins",          _wins_autocomplete);
     g_hash_table_insert(ac_funcs, "/tls",           _tls_autocomplete);
+    g_hash_table_insert(ac_funcs, "/script",        _script_autocomplete);
 
     int len = strlen(input);
     char parsed[len+1];
@@ -3166,6 +3236,52 @@ _theme_autocomplete(ProfWin *window, const char * const input)
 }
 
 static char *
+_script_autocomplete(ProfWin *window, const char * const input)
+{
+    char *result = NULL;
+    if ((strncmp(input, "/script show ", 13) == 0) && (strlen(input) > 13)) {
+        if (script_show_ac == NULL) {
+            script_show_ac = autocomplete_new();
+            GSList *scripts = scripts_list();
+            GSList *curr = scripts;
+            while (curr) {
+                autocomplete_add(script_show_ac, curr->data);
+                curr = g_slist_next(curr);
+            }
+            g_slist_free_full(scripts, g_free);
+        }
+        result = autocomplete_param_with_ac(input, "/script show", script_show_ac, TRUE);
+        if (result) {
+            return result;
+        }
+    }
+
+    if ((strncmp(input, "/script run ", 12) == 0) && (strlen(input) > 12)) {
+        if (script_show_ac == NULL) {
+            script_show_ac = autocomplete_new();
+            GSList *scripts = scripts_list();
+            GSList *curr = scripts;
+            while (curr) {
+                autocomplete_add(script_show_ac, curr->data);
+                curr = g_slist_next(curr);
+            }
+            g_slist_free_full(scripts, g_free);
+        }
+        result = autocomplete_param_with_ac(input, "/script run", script_show_ac, TRUE);
+        if (result) {
+            return result;
+        }
+    }
+
+    result = autocomplete_param_with_ac(input, "/script", script_ac, TRUE);
+    if (result) {
+        return result;
+    }
+
+    return NULL;
+}
+
+static char *
 _resource_autocomplete(ProfWin *window, const char * const input)
 {
     char *found = NULL;
@@ -3628,6 +3744,11 @@ _tls_autocomplete(ProfWin *window, const char * const input)
         return result;
     }
 
+    result = autocomplete_param_with_func(input, "/tls show", prefs_autocomplete_boolean_choice);
+    if (result) {
+        return result;
+    }
+
     result = autocomplete_param_with_ac(input, "/tls", tls_ac, TRUE);
     if (result) {
         return result;
@@ -3683,7 +3804,7 @@ _connect_autocomplete(ProfWin *window, const char * const input)
     char *found = NULL;
     gboolean result = FALSE;
 
-    gchar **args = parse_args(input, 2, 4, &result);
+    gchar **args = parse_args(input, 2, 6, &result);
 
     if ((strncmp(input, "/connect", 8) == 0) && (result == TRUE)) {
         GString *beginning = g_string_new("/connect ");
@@ -3693,6 +3814,12 @@ _connect_autocomplete(ProfWin *window, const char * const input)
             g_string_append(beginning, args[1]);
             g_string_append(beginning, " ");
             g_string_append(beginning, args[2]);
+            if (args[3] && args[4]) {
+                g_string_append(beginning, " ");
+                g_string_append(beginning, args[3]);
+                g_string_append(beginning, " ");
+                g_string_append(beginning, args[4]);
+            }
         }
         found = autocomplete_param_with_ac(input, beginning->str, connect_property_ac, TRUE);
         g_string_free(beginning, TRUE);
@@ -3704,6 +3831,46 @@ _connect_autocomplete(ProfWin *window, const char * const input)
 
     g_strfreev(args);
 
+    result = FALSE;
+    args = parse_args(input, 2, 7, &result);
+
+    if ((strncmp(input, "/connect", 8) == 0) && (result == TRUE)) {
+        GString *beginning = g_string_new("/connect ");
+        g_string_append(beginning, args[0]);
+        int curr = 0;
+        if (args[1]) {
+            g_string_append(beginning, " ");
+            g_string_append(beginning, args[1]);
+            curr = 1;
+            if (args[2] && args[3]) {
+                g_string_append(beginning, " ");
+                g_string_append(beginning, args[2]);
+                g_string_append(beginning, " ");
+                g_string_append(beginning, args[3]);
+                curr = 3;
+                if (args[4] && args[5]) {
+                    g_string_append(beginning, " ");
+                    g_string_append(beginning, args[4]);
+                    g_string_append(beginning, " ");
+                    g_string_append(beginning, args[5]);
+                    curr = 5;
+                }
+            }
+        }
+        if (curr != 0 && (g_strcmp0(args[curr], "tls") == 0)) {
+            found = autocomplete_param_with_ac(input, beginning->str, tls_property_ac, TRUE);
+            g_string_free(beginning, TRUE);
+            if (found) {
+                g_strfreev(args);
+                return found;
+            }
+        } else {
+            g_string_free(beginning, TRUE);
+        }
+    }
+
+    g_strfreev(args);
+
     found = autocomplete_param_with_func(input, "/connect", accounts_find_enabled);
     if (found) {
         return found;
@@ -3794,6 +3961,15 @@ _account_autocomplete(ProfWin *window, const char * const input)
                 g_strfreev(args);
                 return found;
             }
+        } else if ((g_strv_length(args) > 3) && (g_strcmp0(args[2], "tls")) == 0) {
+            g_string_append(beginning, " ");
+            g_string_append(beginning, args[2]);
+            found = autocomplete_param_with_ac(input, beginning->str, tls_property_ac, TRUE);
+            g_string_free(beginning, TRUE);
+            if (found) {
+                g_strfreev(args);
+                return found;
+            }
 #ifdef HAVE_LIBGPGME
         } else if ((g_strv_length(args) > 3) && (g_strcmp0(args[2], "pgpkeyid")) == 0) {
             g_string_append(beginning, " ");
diff --git a/src/command/commands.c b/src/command/commands.c
index 4fe77f90..4184b945 100644
--- a/src/command/commands.c
+++ b/src/command/commands.c
@@ -38,7 +38,6 @@
 #include <stdlib.h>
 #include <errno.h>
 #include <assert.h>
-#include <uuid/uuid.h>
 #include <glib.h>
 
 #include "chat_session.h"
@@ -50,6 +49,7 @@
 #include "config/preferences.h"
 #include "config/theme.h"
 #include "config/tlscerts.h"
+#include "config/scripts.h"
 #include "contact.h"
 #include "roster_list.h"
 #include "jid.h"
@@ -168,18 +168,18 @@ cmd_tls(ProfWin *window, const char * const command, gchar **args)
             }
 
             if (g_file_test(args[2], G_FILE_TEST_IS_DIR)) {
-                prefs_set_string(PREF_CERT_PATH, args[2]);
+                prefs_set_string(PREF_TLS_CERTPATH, args[2]);
                 cons_show("Certificate path set to: %s", args[2]);
             } else {
                 cons_show("Directory %s does not exist.", args[2]);
             }
             return TRUE;
         } else if (g_strcmp0(args[1], "clear") == 0) {
-            prefs_set_string(PREF_CERT_PATH, NULL);
+            prefs_set_string(PREF_TLS_CERTPATH, NULL);
             cons_show("Certificate path cleared");
             return TRUE;
         } else if (args[1] == NULL) {
-            char *path = prefs_get_string(PREF_CERT_PATH);
+            char *path = prefs_get_string(PREF_TLS_CERTPATH);
             if (path) {
                 cons_show("Trusted certificate path: %s", path);
                 prefs_free_string(path);
@@ -238,6 +238,8 @@ cmd_tls(ProfWin *window, const char * const command, gchar **args)
             }
         }
         return TRUE;
+    } else if (g_strcmp0(args[0], "show") == 0) {
+        return _cmd_set_boolean_preference(args[1], command, "TLS titlebar indicator", PREF_TLS_SHOW);
     } else {
         cons_bad_cmd_usage(command);
         return TRUE;
@@ -253,7 +255,7 @@ cmd_connect(ProfWin *window, const char * const command, gchar **args)
         return TRUE;
     }
 
-    gchar *opt_keys[] = { "server", "port", NULL };
+    gchar *opt_keys[] = { "server", "port", "tls", NULL };
     gboolean parsed;
 
     GHashTable *options = parse_options(&args[args[0] ? 1 : 0], opt_keys, &parsed);
@@ -265,6 +267,16 @@ cmd_connect(ProfWin *window, const char * const command, gchar **args)
 
     char *altdomain = g_hash_table_lookup(options, "server");
 
+    char *tls_policy = g_hash_table_lookup(options, "tls");
+    if (tls_policy &&
+            (g_strcmp0(tls_policy, "force") != 0) &&
+            (g_strcmp0(tls_policy, "allow") != 0) &&
+            (g_strcmp0(tls_policy, "disable") != 0)) {
+        cons_bad_cmd_usage(command);
+        cons_show("");
+        return TRUE;
+    }
+
     int port = 0;
     if (g_hash_table_contains(options, "port")) {
         char *port_str = g_hash_table_lookup(options, "port");
@@ -332,7 +344,7 @@ cmd_connect(ProfWin *window, const char * const command, gchar **args)
     } else {
         jid = strdup(lower);
         char *passwd = ui_ask_password();
-        conn_status = cl_ev_connect_jid(jid, passwd, altdomain, port);
+        conn_status = cl_ev_connect_jid(jid, passwd, altdomain, port, tls_policy);
         free(passwd);
     }
 
@@ -384,7 +396,7 @@ cmd_account(ProfWin *window, const char * const command, gchar **args)
         if (account_name == NULL) {
             cons_bad_cmd_usage(command);
         } else {
-            accounts_add(account_name, NULL, 0);
+            accounts_add(account_name, NULL, 0, NULL);
             cons_show("Account created.");
             cons_show("");
         }
@@ -585,6 +597,19 @@ cmd_account(ProfWin *window, const char * const command, gchar **args)
                     cons_show("PGP support is not included in this build.");
 #endif
                     cons_show("");
+                } else if (strcmp(property, "startscript") == 0) {
+                    accounts_set_script_start(account_name, value);
+                    cons_show("Updated start script for account %s: %s", account_name, value);
+                } else if (strcmp(property, "tls") == 0) {
+                    if ((g_strcmp0(value, "force") != 0)
+                            && (g_strcmp0(value, "allow") != 0)
+                            && (g_strcmp0(value, "disable") != 0)) {
+                        cons_show("TLS policy must be one of: force, allow or disable.");
+                    } else {
+                        accounts_set_tls_policy(account_name, value);
+                        cons_show("Updated TLS policy for account %s: %s", account_name, value);
+                        cons_show("");
+                    }
                 } else if (valid_resource_presence_string(property)) {
                     int intval;
                     char *err_msg = NULL;
@@ -667,6 +692,10 @@ cmd_account(ProfWin *window, const char * const command, gchar **args)
                     accounts_clear_pgp_keyid(account_name);
                     cons_show("Removed PGP key ID for account %s", account_name);
                     cons_show("");
+                } else if (strcmp(property, "startscript") == 0) {
+                    accounts_clear_script_start(account_name);
+                    cons_show("Removed start script for account %s", account_name);
+                    cons_show("");
                 } else {
                     cons_show("Invalid property: %s", property);
                     cons_show("");
@@ -682,6 +711,29 @@ cmd_account(ProfWin *window, const char * const command, gchar **args)
 }
 
 gboolean
+cmd_script(ProfWin *window, const char * const command, gchar **args)
+{
+    if ((g_strcmp0(args[0], "run") == 0) && args[1]) {
+        gboolean res = scripts_exec(args[1]);
+        if (!res) {
+            cons_show("Could not find script %s", args[1]);
+        }
+    } else if (g_strcmp0(args[0], "list") == 0) {
+        GSList *scripts = scripts_list();
+        cons_show_scripts(scripts);
+        g_slist_free_full(scripts, g_free);
+    } else if ((g_strcmp0(args[0], "show") == 0) && args[1]) {
+        GSList *commands = scripts_read(args[1]);
+        cons_show_script(args[1], commands);
+        g_slist_free_full(commands, g_free);
+    } else {
+        cons_bad_cmd_usage(command);
+    }
+
+    return TRUE;
+}
+
+gboolean
 cmd_sub(ProfWin *window, const char * const command, gchar **args)
 {
     jabber_conn_status_t conn_status = jabber_get_connection_status();
@@ -2264,22 +2316,18 @@ cmd_join(ProfWin *window, const char * const command, gchar **args)
     }
 
     if (args[0] == NULL) {
-        uuid_t uuid;
-        uuid_generate(uuid);
-        char *uuid_str = malloc(sizeof(char) * 37);
-        uuid_unparse_lower(uuid, uuid_str);
-
         char *account_name = jabber_get_account_name();
         ProfAccount *account = accounts_get_account(account_name);
 
         GString *room_str = g_string_new("");
-        g_string_append_printf(room_str, "private-chat-%s@%s", uuid_str, account->muc_service);
+        char *uuid = jabber_create_uuid();
+        g_string_append_printf(room_str, "private-chat-%s@%s", uuid, account->muc_service);
+        jabber_free_uuid(uuid);
 
         presence_join_room(room_str->str, account->muc_nick, NULL);
         muc_join(room_str->str, account->muc_nick, NULL, FALSE);
 
         g_string_free(room_str, TRUE);
-        free(uuid_str);
         account_free(account);
 
         return TRUE;
@@ -2691,12 +2739,15 @@ cmd_form(ProfWin *window, const char * const command, gchar **args)
         if (confwin->form) {
             cmd_autocomplete_remove_form_fields(confwin->form);
         }
-        wins_close_current();
+
+        int num = wins_get_num(window);
+
         ProfWin *new_current = (ProfWin*)wins_get_muc(confwin->roomjid);
         if (!new_current) {
             new_current = wins_get_console();
         }
         ui_ev_focus_win(new_current);
+        wins_close_by_num(num);
     }
 
     return TRUE;
@@ -3261,6 +3312,10 @@ cmd_disco(ProfWin *window, const char * const command, gchar **args)
 gboolean
 cmd_lastactivity(ProfWin *window, const char * const command, gchar **args)
 {
+    if ((g_strcmp0(args[0], "on") == 0) || (g_strcmp0(args[0], "off") == 0)) {
+        return _cmd_set_boolean_preference(args[0], command, "Last activity", PREF_LASTACTIVITY);
+    }
+
     jabber_conn_status_t conn_status = jabber_get_connection_status();
 
     if (conn_status != JABBER_CONNECTED) {
@@ -3276,11 +3331,12 @@ cmd_lastactivity(ProfWin *window, const char * const command, gchar **args)
 
         g_string_free(jid, TRUE);
         jid_destroy(jidp);
+
+        return TRUE;
     } else {
         iq_last_activity_request(args[0]);
+        return TRUE;
     }
-
-    return TRUE;
 }
 
 gboolean
diff --git a/src/command/commands.h b/src/command/commands.h
index b3f83a70..d75559c5 100644
--- a/src/command/commands.h
+++ b/src/command/commands.h
@@ -149,6 +149,7 @@ gboolean cmd_time(ProfWin *window, const char * const command, gchar **args);
 gboolean cmd_resource(ProfWin *window, const char * const command, gchar **args);
 gboolean cmd_inpblock(ProfWin *window, const char * const command, gchar **args);
 gboolean cmd_encwarn(ProfWin *window, const char * const command, gchar **args);
+gboolean cmd_script(ProfWin *window, const char * const command, gchar **args);
 
 gboolean cmd_form_field(ProfWin *window, char *tag, gchar **args);