# HG changeset patch # User Matti Hamalainen # Date 1337847284 -10800 # Node ID aeb24b1b5e77767b97d25463d5c6f105bf0e7967 # Parent 6727fec3c326dfcb640d2ffdac0945105a2cf309 Refactor the /command handling completely. diff -r 6727fec3c326 -r aeb24b1b5e77 main.c --- 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();