changeset 424:aeb24b1b5e77

Refactor the /command handling completely.
author Matti Hamalainen <ccr@tnsp.org>
date Thu, 24 May 2012 11:14:44 +0300
parents 6727fec3c326
children 143fd51048a9
files main.c
diffstat 1 files changed, 395 insertions(+), 221 deletions(-) [+]
line wrap: on
line diff
--- a/main.c	Thu May 24 11:09:48 2012 +0300
+++ b/main.c	Thu May 24 11:14:44 2012 +0300
@@ -32,6 +32,7 @@
 #define SET_DELAY          (5)
 #endif
 
+#define SET_PROFILE_PREFIX "http://www.newbienudes.com/profile/%s/"
 #define SET_NICK_SEPARATOR ':'
 
 #define SET_MAX_HISTORY (16)        // Command history length
@@ -761,7 +762,7 @@
     else if (!nn_conn_buf_strcmp(conn, "SUCCESS>"))
     {
         printMsg(NULL, "½2½Login success½0½ - ½3½%s½0½\n", tmpStr);
-        nn_conn_send_msg(conn, optUserNameEnc, "%%2FRequestUserList");
+        nn_conn_send_msg(conn, optUserNameEnc, "%2FRequestUserList");
         return 0;
     }
     else
@@ -882,231 +883,405 @@
 }
 
 
+int nncmd_open_profile(nn_conn_t *conn, char *name)
+{
+    char *uri, *enc_name = nn_encode_str1(name);
+#ifdef __WIN32
+    HINSTANCE status;
+#else
+    int status;
+    int fds[2];
+    pid_t pid;
+#endif
+    (void) conn;
+
+    printMsg(currWin, "Opening profile for: '%s'\n", name);
+
+#ifdef __WIN32
+    uri = th_strdup_printf(SET_PROFILE_PREFIX, enc_name);
+    status = ShellExecute(NULL, "open", tmpBuf, NULL, NULL, SW_SHOWNA);
+    if (status <= (HINSTANCE) 32)
+    {
+        printMsgQ(currWin, "Could not launch default web browser: %d\n", status);
+    }
+#else
+
+    uri = th_strdup_printf("openurl(" SET_PROFILE_PREFIX ",new-tab)", enc_name);
+
+    if (pipe(fds) == -1)
+    {
+        int ret = errno;
+        printMsgQ(currWin, "Could not open process communication pipe! (%d, %s)\n", ret, strerror(ret));
+        return 0;
+    }
+
+    if ((pid = fork()) < 0)
+    {
+        printMsgQ(currWin, "Could not create sub-process!\n");
+    }
+    else if (pid == 0)
+    {
+        dup2(fds[1], STDOUT_FILENO);
+        dup2(fds[0], STDERR_FILENO);
+        execlp(setBrowser, setBrowser, "-remote", uri, (void *)NULL);
+        _exit(errno);
+    }
+
+    wait(&status);
+#endif
+
+    th_free(uri);
+    th_free(enc_name);
+    return 0;
+}
+
+
+int nncmd_ignore(nn_conn_t *conn, char *name)
+{
+    (void) conn;
+
+    if (name[0])
+    {
+        // Add or remove someone to/from ignore
+        qlist_t *user = th_llist_find_func(setIgnoreList, name, compareUsername);
+        if (user != NULL)
+        {
+            printMsgQ(currWin, "Removed user '%s' from ignore.\n", name);
+            th_llist_delete_node(&setIgnoreList, user);
+        }
+        else
+        {
+            printMsgQ(currWin, "Now ignoring '%s'.\n", name);
+            th_llist_append(&setIgnoreList, th_strdup(name));
+        }
+    }
+    else
+    {
+        // Just list whomever is in ignore now
+        qlist_t *user = setIgnoreList;
+        ssize_t nuser = th_llist_length(setIgnoreList);
+        char *result = th_strdup_printf("Users ignored (%d): ", nuser);
+        while (user != NULL)
+        {
+            if (user->data != NULL)
+            {
+                th_pstr_printf(&result, "%s'%s'", result, (char *) user->data);
+                if (--nuser > 0)
+                    th_pstr_printf(&result, "%s, ", result);
+            }
+            user = user->next;
+        }
+        printMsgQ(currWin, "%s\n", result);
+        th_free(result);
+    }
+
+    return 0;
+}
+
+
+int nncmd_set_color(nn_conn_t *conn, char *arg)
+{
+    int val;
+    (void) conn;
+
+    if ((val = th_get_hex_triplet(arg)) < 0)
+    {
+        printMsgQ(currWin, "Invalid color value '%s'\n", arg);
+        return 1;
+    }
+
+    optUserColor = val;
+    printMsgQ(currWin, "Setting color to #%06x\n", optUserColor);
+    nn_conn_send_msg_v(conn, optUserNameEnc, "%%2FSetFontColor%%20%%2Dcolor%%20%06X", optUserColor);
+    return 0;
+}
+
+
+int nncmd_open_query(nn_conn_t *conn, char *name)
+{
+    (void) conn;
+
+    if (name[0])
+    {
+        nn_user_t *user = nn_user_find(nnUsers, nn_username_encode(name));
+        if (user != NULL)
+        {
+            name = nn_username_decode(th_strdup(user->name));
+            printMsgQ(currWin, "Opening PRV query for '%s'.\n", name);
+            if (nnwin_open(name, TRUE))
+                printMsgQ(currWin, "In PRV query with '%s'.\n", name);
+            th_free(name);
+            return 0;
+        }
+        else
+        {
+            printMsgQ(currWin, "Could not find username '%s'.\n", name);
+            return 1;
+        }
+    }
+    else
+    {
+        printMsgQ(currWin, "Usage: /query username\n");
+        printMsgQ(currWin, "To close a PRV query, use /close [username]\n");
+        printMsgQ(currWin, "/close without username will close the current PRV window, if any.\n");
+        return 1;
+    }
+}
+
+
+int nncmd_close_query(nn_conn_t *conn, char *name)
+{
+    (void) conn;
+
+    if (name[0])
+    {
+        nn_window_t *win = nnwin_find(name);
+        if (win != NULL)
+        {
+            nnwin_close(win);
+            printMsgQ(currWin, "Closed PRV query to '%s'.\n", name);
+        }
+        else
+        {
+            printMsgQ(currWin, "No PRV query by name '%s'.\n", name);
+        }
+    }
+    else
+    {
+        if (currWin != chatWindows[0])
+        {
+            nnwin_close(currWin);
+            currWin = chatWindows[0];
+        }
+        else
+        {
+            printMsgQ(currWin, "Usage: /close [username]\n");
+            printMsgQ(currWin, "/close without username will close the current PRV window. if any.\n");
+        }
+    }
+
+    return 0;
+}
+
+
+int nncmd_window_info(nn_conn_t *conn, char *arg)
+{
+    (void) conn;
+
+    if (arg[0])
+    {
+        int val = atoi(arg);
+        if (val >= 1 && val < SET_MAX_WINDOWS)
+        {
+            if (chatWindows[val - 1] != NULL)
+                currWin = chatWindows[val - 1];
+        }
+        else
+        {
+            printMsgQ(currWin, "Invalid window number '%s'\n", arg);
+            return 1;
+        }
+    }
+    else
+    {
+        printMsgQ(currWin, "Window   : #%d\n", currWin->num);
+        printMsgQ(currWin, "ID       : %s\n", currWin->id);
+    }
+    return 0;
+}
+
+
+int nncmd_list_all_users(nn_conn_t *conn, char *buf)
+{
+    (void) buf;
+
+    // Alias /listallusers
+    return nn_conn_send_msg(conn, optUserNameEnc, "%%2Flistallusers");
+}
+
+
+int nncmd_names(nn_conn_t *conn, char *buf)
+{
+    (void) conn;
+    (void) buf;
+    
+    printMsgQ(currWin, "Not implemented yet.\n");
+    return 0;
+}
+
+
+int nncmd_save_config(nn_conn_t *conn, char *buf)
+{
+    (void) conn;
+    (void) buf;
+
+    FILE *cfgfile = fopen(setConfigFile, "w");
+    if (cfgfile == NULL)
+    {
+        printMsgQ(currWin, "Could not create configuration to file '%s': %s\n",
+            setConfigFile, strerror(errno));
+        return 0;
+    }
+
+    printMsgQ(currWin, "Configuration saved in file '%s', res=%d\n",
+        setConfigFile,
+        th_cfg_write(cfgfile, setConfigFile, cfg));
+
+    fclose(cfgfile);
+    return 0;
+}
+
+
+enum
+{
+    CMDARG_NONE,
+    CMDARG_STRING,
+    CMDARG_OPTIONAL,
+    CMDARG_NICK,
+};
+
+typedef struct
+{
+    char *cmd;
+    int flags;
+    ssize_t len;
+    int (*handler)(nn_conn_t *, char *buf);
+} nn_usercmd_t;
+
+
+static nn_usercmd_t userCmds[] =
+{
+    // Server side commands, we just implement completion
+    { "me",       CMDARG_STRING,   -1, NULL },
+    { "status",   CMDARG_STRING,   -1, NULL },
+    { "list",     CMDARG_NONE,     -1, nncmd_list_all_users },
+
+    // List internal username list
+    { "who",      CMDARG_NONE,     -1, nncmd_names },
+    { "names",    CMDARG_NONE,     -1, nncmd_names },
+
+    { "w",        CMDARG_NICK,     -1, nncmd_open_profile },
+    { "profile",  CMDARG_NICK,     -1, nncmd_open_profile },
+
+    { "query",    CMDARG_NICK,     -1, nncmd_open_query },
+    { "close",    CMDARG_OPTIONAL, -1, nncmd_close_query },
+    { "win",      CMDARG_OPTIONAL, -1, nncmd_window_info },
+
+    { "ignore",   CMDARG_NICK,     -1, nncmd_ignore },
+    { "color",    CMDARG_STRING,   -1, nncmd_set_color },
+    { "save",     CMDARG_NONE,     -1, nncmd_save_config },
+};
+
+static const int nuserCmds = sizeof(userCmds) / sizeof(userCmds[0]);
+
+
+int nn_handle_command(nn_conn_t *conn, char *buf)
+{
+    static BOOL userCmdsInit = FALSE;
+    int i;
+
+    // Initialize command structure
+    if (!userCmdsInit)
+    {
+        for (i = 0; i < nuserCmds; i++)
+            userCmds[i].len = strlen(userCmds[i].cmd);
+
+        userCmdsInit = TRUE;
+    }
+
+
+    for (i = 0; i < nuserCmds; i++)
+    {
+        nn_usercmd_t *cmd = &userCmds[i];
+        if (!th_strncasecmp(buf, cmd->cmd, cmd->len))
+        {
+            char *nbuf = str_trim_left(buf + cmd->len);
+
+            switch (cmd->flags)
+            {
+                case CMDARG_NICK:
+                case CMDARG_STRING:
+                    if (!nbuf[0])
+                    {
+                        printMsgQ(currWin, "Command: /%s requires an argument.\n", cmd->cmd);
+                        return 1;
+                    }
+                    break;
+                
+                case CMDARG_NONE:
+                    if (nbuf[0])
+                    {
+                        printMsgQ(currWin, "Command: /%s does not take arguments.\n", cmd->cmd);
+                        return 1;
+                    }
+                    break;
+
+                case CMDARG_OPTIONAL:
+                    break;
+            }
+
+            // Check if there is a handler function
+            if (cmd->handler)
+            {
+                printMsgQ(currWin, "%s[%p]->handler('%s')\n", cmd->cmd, cmd->handler, nbuf);
+                // Internal commands have a handler
+                return cmd->handler(conn, nbuf);
+            }
+            else
+            {
+                // Server-side commands are just pass-through here
+                char *tmp = nn_dblencode_str(buf);
+                BOOL result;
+                if (tmp == NULL) return -2;
+                result = nn_conn_send_msg(conn, optUserNameEnc, tmp);
+                th_free(tmp);
+                return result ? 0 : -1;
+            }
+        }
+    }
+    
+    printMsgQ(currWin, "Unknown command: /%s\n", buf);
+    return 1;
+}
+
+
 int nn_handle_input(nn_conn_t *conn, char *buf, size_t bufLen)
 {
-    char *tmpStr, tmpBuf[4096];
     BOOL result;
+    char *tmp;
 
-    // Trim right
-    bufLen--;
-    buf[bufLen--] = 0;
-    while (bufLen > 0 && th_isspace(buf[bufLen]))
-        buf[bufLen--] = 0;
+    // Trim right side
+    if (bufLen > 0) buf[--bufLen] = 0;
+    while (bufLen > 0 && th_isspace(buf[bufLen - 1]))
+        buf[--bufLen] = 0;
+
+    if (buf[0] == 0)
+        return 1;
 
     // Decode completed usernames
     nn_username_decode(buf);
 
-    // Check for special user commands
-    if (*buf == 0)
-    {
-        return 1;
-    }
-    else if (!th_strncasecmp(buf, "/color ", 7))
-    {
-        // Change color
-        int tmpInt;
-        if ((tmpInt = th_get_hex_triplet(str_trim_left(buf + 7))) < 0)
-        {
-            printMsgQ(currWin, "Invalid color value '%s'\n", buf+7);
-            return 1;
-        }
-        optUserColor = tmpInt;
-        printMsgQ(currWin, "Setting color to #%06x\n", optUserColor);
-        nn_conn_send_msg(conn, optUserNameEnc, "%%2FSetFontColor%%20%%2Dcolor%%20%06X", optUserColor);
-        return 0;
-    }
-    else if (!th_strncasecmp(buf, "/ignore", 7))
-    {
-        char *name = str_trim_left(buf + 7);
-        if (strlen(name) > 0)
-        {
-            // Add or remove someone to/from ignore
-            qlist_t *user = th_llist_find_func(setIgnoreList, name, compareUsername);
-            if (user != NULL)
-            {
-                printMsgQ(currWin, "Removed user '%s' from ignore.\n", name);
-                th_llist_delete_node(&setIgnoreList, user);
-            }
-            else
-            {
-                printMsgQ(currWin, "Now ignoring '%s'.\n", name);
-                th_llist_append(&setIgnoreList, th_strdup(name));
-            }
-        }
-        else
-        {
-            // Just list whomever is in ignore now
-            qlist_t *user = setIgnoreList;
-            ssize_t nuser = th_llist_length(setIgnoreList);
-            char *result = th_strdup_printf("Users ignored (%d): ", nuser);
-            while (user != NULL)
-            {
-                if (user->data != NULL)
-                {
-                    th_pstr_printf(&result, "%s'%s'", result, (char *) user->data);
-                    if (--nuser > 0)
-                        th_pstr_printf(&result, "%s, ", result);
-                }
-                user = user->next;
-            }
-            printMsgQ(currWin, "%s\n", result);
-            th_free(result);
-        }
-        return 0;
-    }
-    else if (!th_strncasecmp(buf, "/query", 6))
-    {
-        char *name = str_trim_left(buf + 6);
-        if (strlen(name) > 0)
-        {
-            nn_user_t *user = nn_user_find(nnUsers, nn_username_encode(name));
-            if (user != NULL)
-            {
-                name = nn_username_decode(th_strdup(user->name));
-                printMsgQ(currWin, "Opening PRV query for '%s'.\n", name);
-                if (nnwin_open(name, TRUE))
-                    printMsgQ(currWin, "In PRV query with '%s'.\n", name);
-                th_free(name);
-            }
-        }
-        else
-        {
-            printMsgQ(currWin, "Usage: /query username\n");
-            printMsgQ(currWin, "To close a PRV query, use /close [username]\n");
-            printMsgQ(currWin, "/close without username will close the current PRV window.\n");
-        }
-        return 0;
-    }
-    else if (!th_strncasecmp(buf, "/win", 4))
-    {
-        // Change color
-        char *tmp = str_trim_left(buf + 4);
-        if (strlen(tmp) > 0)
-        {
-            int val = atoi(tmp);
-            if (val >= 1 && val < SET_MAX_WINDOWS)
-            {
-                if (chatWindows[val - 1] != NULL)
-                    currWin = chatWindows[val - 1];
-            }
-            else
-            {
-                printMsgQ(currWin, "Invalid window number '%s'\n", tmp);
-                return 1;
-            }
-        }
-        else
-        {
-            printMsgQ(currWin, "Window   : #%d\n", currWin->num);
-            printMsgQ(currWin, "ID       : %s\n", currWin->id);
-        }
-        return 0;
-    }
-    else if (!th_strncasecmp(buf, "/close", 6))
-    {
-        char *name = str_trim_left(buf + 6);
-        if (strlen(name) > 0)
-        {
-            nn_window_t *win = nnwin_find(name);
-            if (win != NULL)
-            {
-                nnwin_close(win);
-                printMsgQ(currWin, "Closed PRV query to '%s'.\n", name);
-            }
-            else
-            {
-                printMsgQ(currWin, "No PRV query by name '%s'.\n", name);
-            }
-        }
-        else
-        {
-            if (currWin != chatWindows[0])
-            {
-                nnwin_close(currWin);
-                currWin = chatWindows[0];
-            }
-        }
-        return 0;
-    }
-    else if (!th_strncasecmp(buf, "/save", 5))
-    {
-        // Save configuration
-        FILE *cfgfile = fopen(setConfigFile, "w");
-        if (cfgfile == NULL)
-        {
-            printMsgQ(currWin, "Could not create configuration to file '%s': %s\n",
-                setConfigFile, strerror(errno));
-            return 0;
-        }
-        printMsgQ(currWin, "Configuration saved in file '%s', res=%d\n",
-            setConfigFile,
-            th_cfg_write(cfgfile, setConfigFile, cfg));
+    // Check for commands
+    if (buf[0] == '/')
+        return nn_handle_command(conn, buf + 1);
 
-        fclose(cfgfile);
-        return 0;
-    }
-    else if (!th_strncasecmp(buf, "/w ", 3))
-    {
-        // Open given username's profile via firefox in a new tab
-        char *name = str_trim_left(buf + 3);
-
-        printMsg(currWin, "Opening profile for: '%s'\n", name);
-
-        tmpStr = nn_encode_str1(name);
-#ifdef __WIN32
-        {
-            HINSTANCE status;
-            snprintf(tmpBuf, sizeof(tmpBuf), "http://www.newbienudes.com/profile/%s/", tmpStr);
-            th_free(tmpStr);
-            status = ShellExecute(NULL, "open", tmpBuf, NULL, NULL, SW_SHOWNA);
-            if (status <= (HINSTANCE) 32)
-                printMsgQ(currWin, "Could not launch default web browser: %d\n", status);
-        }
-#else
-        {
-            int status;
-            int fds[2];
-            pid_t pid;
-            snprintf(tmpBuf, sizeof(tmpBuf), "openurl(http://www.newbienudes.com/profile/%s/,new-tab)", tmpStr);
-            th_free(tmpStr);
-
-            if (pipe(fds) == -1)
-            {
-                int ret = errno;
-                printMsgQ(currWin, "Could not open process communication pipe! (%d, %s)\n", ret, strerror(ret));
-                return 0;
-            }
-
-            if ((pid = fork()) < 0)
-            {
-                printMsgQ(currWin, "Could not create sub-process!\n");
-            }
-            else if (pid == 0)
-            {
-                dup2(fds[1], STDOUT_FILENO);
-                dup2(fds[0], STDERR_FILENO);
-                execlp(setBrowser, setBrowser, "-remote", tmpBuf, (void *)NULL);
-                _exit(errno);
-            }
-
-            wait(&status);
-        }
-#endif
-        return 0;
-    }
-    else if (!th_strncasecmp(buf, "/who", 4))
-    {
-        // Alias /who to /listallusers
-        snprintf(tmpBuf, sizeof(tmpBuf), "/listallusers");
-        buf = tmpBuf;
-    }
-
+    // If current window is not the main room window, send private
     if (currWin != chatWindows[0])
     {
         if (currWin->id != NULL)
         {
-            snprintf(tmpBuf, sizeof(tmpBuf), "/prv -to %s -msg %s", currWin->id, buf);
-            buf = tmpBuf;
+            char *tmp, *msg = th_strdup_printf("/prv -to %s -msg %s", currWin->id, buf);
+            if (msg == NULL) return -3;
+            tmp = nn_dblencode_str(msg);
+            if (tmp == NULL)
+            {
+                th_free(msg);
+                return -2;
+            }
+            result = nn_conn_send_msg(conn, optUserNameEnc, tmp);
+            th_free(tmp);
+            th_free(msg);
+            return result ? 0 : -1;
         }
         else
         {
@@ -1115,12 +1290,11 @@
         }
     }
 
-    // Send double-encoded
-    tmpStr = nn_dblencode_str(nn_username_decode(buf));
-    if (tmpStr == 0) return -2;
-    result = nn_conn_send_msg(conn, optUserNameEnc, "%s", tmpStr);
-    th_free(tmpStr);
-
+    // Send double-encoded message
+    tmp = nn_dblencode_str(buf);
+    if (tmp == NULL) return -2;
+    result = nn_conn_send_msg(conn, optUserNameEnc, tmp);
+    th_free(tmp);
     return result ? 0 : -1;
 }
 
@@ -1682,7 +1856,7 @@
     // Send login command
     optUserNameEnc = nn_dblencode_str(optUserName);
     tmpStr = nn_dblencode_str(optSite);
-    nn_conn_send_msg(conn, optUserNameEnc, "%%2Flogin%%20%%2Dsite%%20%s%%20%%2Dpassword%%20%s", tmpStr, optPassword);
+    nn_conn_send_msg_v(conn, optUserNameEnc, "%%2Flogin%%20%%2Dsite%%20%s%%20%%2Dpassword%%20%s", tmpStr, optPassword);
     th_free(tmpStr);
 
     // Initialize random numbers
@@ -2088,7 +2262,7 @@
             if (!colorSet)
             {
                 colorSet = TRUE;
-                nn_conn_send_msg(conn, optUserNameEnc, "%%2FSetFontColor%%20%%2Dcolor%%20%06X", optUserColor);
+                nn_conn_send_msg_v(conn, optUserNameEnc, "%%2FSetFontColor%%20%%2Dcolor%%20%06X", optUserColor);
             }
 
             nnwin_update_statusline();