source: root_commands.c @ fc650a8

Last change on this file since fc650a8 was 7733b8c, checked in by dequis <dx@…>, at 2015-02-21T06:10:54Z

Add the concept of jabber sub-protocols

Currently including: jabber (none), gtalk, facebook, hipchat.

They provide a default server field and an oauth service definition.

Also the "account tag guesses" become subprotocol guesses

  • Property mode set to 100644
File size: 36.7 KB
RevLine 
[5ebff60]1/********************************************************************\
[b7d3cc34]2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
[0e788f5]4  * Copyright 2002-2013 Wilmer van der Gaast and others                *
[b7d3cc34]5  \********************************************************************/
6
7/* User manager (root) commands                                         */
8
9/*
10  This program is free software; you can redistribute it and/or modify
11  it under the terms of the GNU General Public License as published by
12  the Free Software Foundation; either version 2 of the License, or
13  (at your option) any later version.
14
15  This program is distributed in the hope that it will be useful,
16  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  GNU General Public License for more details.
19
20  You should have received a copy of the GNU General Public License with
21  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
[6f10697]22  if not, write to the Free Software Foundation, Inc., 51 Franklin St.,
23  Fifth Floor, Boston, MA  02110-1301  USA
[b7d3cc34]24*/
25
26#define BITLBEE_CORE
27#include "commands.h"
28#include "bitlbee.h"
29#include "help.h"
[f1c2b21]30#include "ipc.h"
[b7d3cc34]31
[5ebff60]32void root_command_string(irc_t *irc, char *command)
[7e563ed]33{
[5ebff60]34        root_command(irc, split_command_parts(command, 0));
[7e563ed]35}
36
[5ebff60]37#define MIN_ARGS(x, y ...)                                                    \
[3b99524]38        do                                                                     \
39        {                                                                      \
[07054a5]40                int blaat;                                                     \
[5ebff60]41                for (blaat = 0; blaat <= x; blaat++) {                         \
42                        if (cmd[blaat] == NULL)                               \
[3b99524]43                        {                                                      \
[5ebff60]44                                irc_rootmsg(irc, "Not enough parameters given (need %d).", x); \
[3b99524]45                                return y;                                      \
[5ebff60]46                        } }                                                      \
47        } while (0)
[3b99524]48
[5ebff60]49void root_command(irc_t *irc, char *cmd[])
50{
[6c56f42]51        int i, len;
[5ebff60]52
53        if (!cmd[0]) {
[f73b969]54                return;
[5ebff60]55        }
56
57        len = strlen(cmd[0]);
58        for (i = 0; root_commands[i].command; i++) {
59                if (g_strncasecmp(root_commands[i].command, cmd[0], len) == 0) {
60                        if (root_commands[i + 1].command &&
61                            g_strncasecmp(root_commands[i + 1].command, cmd[0], len) == 0) {
62                                /* Only match on the first letters if the match is unique. */
63                                break;
64                        }
65
66                        MIN_ARGS(root_commands[i].required_parameters);
67
68                        root_commands[i].execute(irc, cmd);
[f73b969]69                        return;
[7e563ed]70                }
[5ebff60]71        }
72
73        irc_rootmsg(irc, "Unknown command: %s. Please use \x02help commands\x02 to get a list of available commands.",
74                    cmd[0]);
[7e563ed]75}
76
[5ebff60]77static void cmd_help(irc_t *irc, char **cmd)
[b7d3cc34]78{
79        char param[80];
80        int i;
81        char *s;
[5ebff60]82
83        memset(param, 0, sizeof(param));
84        for (i = 1; (cmd[i] != NULL && (strlen(param) < (sizeof(param) - 1))); i++) {
85                if (i != 1) {   // prepend space except for the first parameter
[b7d3cc34]86                        strcat(param, " ");
[5ebff60]87                }
88                strncat(param, cmd[i], sizeof(param) - strlen(param) - 1);
[b7d3cc34]89        }
90
[5ebff60]91        s = help_get(&(global.help), param);
92        if (!s) {
93                s = help_get(&(global.help), "");
[b7d3cc34]94        }
[5ebff60]95
96        if (s) {
97                irc_rootmsg(irc, "%s", s);
98                g_free(s);
99        } else {
100                irc_rootmsg(irc, "Error opening helpfile.");
[b7d3cc34]101        }
102}
103
[5ebff60]104static void cmd_account(irc_t *irc, char **cmd);
105static void bitlbee_whatsnew(irc_t *irc);
[90bbb0e]106
[5ebff60]107static void cmd_identify(irc_t *irc, char **cmd)
[b7d3cc34]108{
[92cb8c4]109        storage_status_t status;
110        gboolean load = TRUE;
111        char *password = cmd[1];
[5ebff60]112
113        if (irc->status & USTATUS_IDENTIFIED) {
114                irc_rootmsg(irc, "You're already logged in.");
[e9cf291]115                return;
116        }
[5ebff60]117
118        if (cmd[1] == NULL) {
119        } else if (strncmp(cmd[1], "-no", 3) == 0) {
[92cb8c4]120                load = FALSE;
121                password = cmd[2];
[5ebff60]122                if (password == NULL) {
[fda194f]123                        irc->status |= OPER_HACK_IDENTIFY_NOLOAD;
[5ebff60]124                }
125        } else if (strncmp(cmd[1], "-force", 6) == 0) {
[92cb8c4]126                password = cmd[2];
[5ebff60]127                if (password == NULL) {
[fda194f]128                        irc->status |= OPER_HACK_IDENTIFY_FORCE;
[5ebff60]129                }
130        } else if (irc->b->accounts != NULL) {
131                irc_rootmsg(irc,
132                            "You're trying to identify yourself, but already have "
133                            "at least one IM account set up. "
134                            "Use \x02identify -noload\x02 or \x02identify -force\x02 "
135                            "instead (see \x02help identify\x02).");
[92cb8c4]136                return;
137        }
[5ebff60]138
139        if (password == NULL) {
140                irc_rootmsg(irc, "About to identify, use /OPER to enter the password");
[060d066]141                irc->status |= OPER_HACK_IDENTIFY;
142                return;
[92cb8c4]143        }
[5ebff60]144
145        if (load) {
146                status = storage_load(irc, password);
147        } else {
148                status = storage_check_pass(irc->user->nick, password);
149        }
150
[09adf08]151        switch (status) {
152        case STORAGE_INVALID_PASSWORD:
[5ebff60]153                irc_rootmsg(irc, "Incorrect password");
[09adf08]154                break;
155        case STORAGE_NO_SUCH_USER:
[5ebff60]156                irc_rootmsg(irc, "The nick is (probably) not registered");
[09adf08]157                break;
158        case STORAGE_OK:
[5ebff60]159                irc_rootmsg(irc, "Password accepted%s",
160                            load ? ", settings and accounts loaded" : "");
161                irc_setpass(irc, password);
[3183c21]162                irc->status |= USTATUS_IDENTIFIED;
[5ebff60]163                irc_umode_set(irc, "+R", 1);
164
165                bitlbee_whatsnew(irc);
166
[e92c4f4]167                /* The following code is a bit hairy now. With takeover
168                   support, we shouldn't immediately auto_connect in case
169                   we're going to offer taking over an existing session.
170                   Do it in 200ms since that should give the parent process
171                   enough time to come back to us. */
[5ebff60]172                if (load) {
173                        irc_channel_auto_joins(irc, NULL);
174                        if (!set_getbool(&irc->default_channel->set, "auto_join")) {
175                                irc_channel_del_user(irc->default_channel, irc->user,
176                                                     IRC_CDU_PART, "auto_join disabled "
177                                                     "for this channel.");
178                        }
179                        if (set_getbool(&irc->b->set, "auto_connect")) {
180                                irc->login_source_id = b_timeout_add(200,
181                                                                     cmd_identify_finish, irc);
182                        }
[6c2404e]183                }
[5ebff60]184
[e92c4f4]185                /* If ipc_child_identify() returns FALSE, it means we're
186                   already sure that there's no takeover target (only
187                   possible in 1-process daemon mode). Start auto_connect
188                   immediately. */
[5ebff60]189                if (!ipc_child_identify(irc) && load) {
190                        cmd_identify_finish(irc, 0, 0);
191                }
192
[09adf08]193                break;
[c121f89]194        case STORAGE_OTHER_ERROR:
[09adf08]195        default:
[5ebff60]196                irc_rootmsg(irc, "Unknown error while loading configuration");
[09adf08]197                break;
[b7d3cc34]198        }
199}
200
[5ebff60]201gboolean cmd_identify_finish(gpointer data, gint fd, b_input_condition cond)
[6c2404e]202{
203        char *account_on[] = { "account", "on", NULL };
204        irc_t *irc = data;
[5ebff60]205
206        if (set_getbool(&irc->b->set, "auto_connect")) {
207                cmd_account(irc, account_on);
208        }
209
210        b_event_remove(irc->login_source_id);
[f545372]211        irc->login_source_id = -1;
[6c2404e]212        return FALSE;
213}
214
[5ebff60]215static void cmd_register(irc_t *irc, char **cmd)
[b7d3cc34]216{
[8d93b4a]217        char s[16];
[5ebff60]218
219        if (global.conf->authmode == AUTHMODE_REGISTERED) {
220                irc_rootmsg(irc, "This server does not allow registering new accounts");
[f73b969]221                return;
[b7d3cc34]222        }
[5ebff60]223
224        if (cmd[1] == NULL) {
225                irc_rootmsg(irc, "About to register, use /OPER to enter the password");
[060d066]226                irc->status |= OPER_HACK_REGISTER;
227                return;
228        }
[1ee6c18]229
[5ebff60]230        switch (storage_save(irc, cmd[1], FALSE)) {
231        case STORAGE_ALREADY_EXISTS:
232                irc_rootmsg(irc, "Nick is already registered");
233                break;
[a1f17d4]234
[5ebff60]235        case STORAGE_OK:
236                irc_rootmsg(irc, "Account successfully created");
237                irc_setpass(irc, cmd[1]);
238                irc->status |= USTATUS_IDENTIFIED;
239                irc_umode_set(irc, "+R", 1);
240
241                /* Set this var now, or anyone who logs in to his/her
242                   newly created account for the first time gets the
243                   whatsnew story. */
244                g_snprintf(s, sizeof(s), "%d", BITLBEE_VERSION_CODE);
245                set_setstr(&irc->b->set, "last_version", s);
246                break;
247
248        default:
249                irc_rootmsg(irc, "Error registering");
250                break;
[b7d3cc34]251        }
252}
253
[5ebff60]254static void cmd_drop(irc_t *irc, char **cmd)
[b7d3cc34]255{
[a1f17d4]256        storage_status_t status;
[5ebff60]257
258        status = storage_remove(irc->user->nick, cmd[1]);
[a1f17d4]259        switch (status) {
260        case STORAGE_NO_SUCH_USER:
[5ebff60]261                irc_rootmsg(irc, "That account does not exist");
[f73b969]262                break;
[a1f17d4]263        case STORAGE_INVALID_PASSWORD:
[5ebff60]264                irc_rootmsg(irc, "Password invalid");
[f73b969]265                break;
[a1f17d4]266        case STORAGE_OK:
[5ebff60]267                irc_setpass(irc, NULL);
[79e826a]268                irc->status &= ~USTATUS_IDENTIFIED;
[5ebff60]269                irc_umode_set(irc, "-R", 1);
270                irc_rootmsg(irc, "Account `%s' removed", irc->user->nick);
[f73b969]271                break;
[a1f17d4]272        default:
[5ebff60]273                irc_rootmsg(irc, "Error: `%d'", status);
[f73b969]274                break;
[b7d3cc34]275        }
276}
[1f92a58]277
[5ebff60]278static void cmd_save(irc_t *irc, char **cmd)
[1f92a58]279{
[5ebff60]280        if ((irc->status & USTATUS_IDENTIFIED) == 0) {
281                irc_rootmsg(irc, "Please create an account first (see \x02help register\x02)");
282        } else if (storage_save(irc, NULL, TRUE) == STORAGE_OK) {
283                irc_rootmsg(irc, "Configuration saved");
284        } else {
285                irc_rootmsg(irc, "Configuration could not be saved!");
286        }
[1f92a58]287}
[b7d3cc34]288
[5ebff60]289static void cmd_showset(irc_t *irc, set_t **head, char *key)
[f3579fd]290{
[09d4922]291        set_t *set;
[f536a99]292        char *val;
[5ebff60]293
294        if ((val = set_getstr(head, key))) {
295                irc_rootmsg(irc, "%s = `%s'", key, val);
296        } else if (!(set = set_find(head, key))) {
297                irc_rootmsg(irc, "Setting `%s' does not exist.", key);
298                if (*head == irc->b->set) {
299                        irc_rootmsg(irc, "It might be an account or channel setting. "
300                                    "See \x02help account set\x02 and \x02help channel set\x02.");
301                }
302        } else if (set->flags & SET_PASSWORD) {
303                irc_rootmsg(irc, "%s = `********' (hidden)", key);
304        } else {
305                irc_rootmsg(irc, "%s is empty", key);
[5613af7]306        }
[f3579fd]307}
308
[5ebff60]309typedef set_t** (*cmd_set_findhead)(irc_t*, char*);
310typedef int (*cmd_set_checkflags)(irc_t*, set_t *set);
[e7bc722]311
[5ebff60]312static int cmd_set_real(irc_t *irc, char **cmd, set_t **head, cmd_set_checkflags checkflags)
[e7bc722]313{
[e907683]314        char *set_name = NULL, *value = NULL;
315        gboolean del = FALSE;
[5ebff60]316
317        if (cmd[1] && g_strncasecmp(cmd[1], "-del", 4) == 0) {
318                MIN_ARGS(2, 0);
[e907683]319                set_name = cmd[2];
320                del = TRUE;
[5ebff60]321        } else {
[e907683]322                set_name = cmd[1];
323                value = cmd[2];
[e7bc722]324        }
[5ebff60]325
326        if (set_name && (value || del)) {
327                set_t *s = set_find(head, set_name);
[e7bc722]328                int st;
[5ebff60]329
330                if (s && checkflags && checkflags(irc, s) == 0) {
[e7bc722]331                        return 0;
[5ebff60]332                }
333
334                if (del) {
335                        st = set_reset(head, set_name);
336                } else {
337                        st = set_setstr(head, set_name, value);
338                }
339
340                if (set_getstr(head, set_name) == NULL &&
341                    set_find(head, set_name)) {
[e907683]342                        /* This happens when changing the passwd, for example.
343                           Showing these msgs instead gives slightly clearer
344                           feedback. */
[5ebff60]345                        if (st) {
346                                irc_rootmsg(irc, "Setting changed successfully");
347                        } else {
348                                irc_rootmsg(irc, "Failed to change setting");
349                        }
350                } else {
351                        cmd_showset(irc, head, set_name);
[e7bc722]352                }
[5ebff60]353        } else if (set_name) {
354                cmd_showset(irc, head, set_name);
355        } else {
[e7bc722]356                set_t *s = *head;
[5ebff60]357                while (s) {
358                        if (set_isvisible(s)) {
359                                cmd_showset(irc, &s, s->key);
360                        }
[e7bc722]361                        s = s->next;
362                }
363        }
[5ebff60]364
[e7bc722]365        return 1;
366}
367
[5ebff60]368static int cmd_account_set_checkflags(irc_t *irc, set_t *s)
[c05eb5b]369{
370        account_t *a = s->data;
[5ebff60]371
372        if (a->ic && s && s->flags & ACC_SET_OFFLINE_ONLY) {
373                irc_rootmsg(irc, "This setting can only be changed when the account is %s-line", "off");
[c05eb5b]374                return 0;
[5ebff60]375        } else if (!a->ic && s && s->flags & ACC_SET_ONLINE_ONLY) {
376                irc_rootmsg(irc, "This setting can only be changed when the account is %s-line", "on");
[c05eb5b]377                return 0;
378        }
[5ebff60]379
[c05eb5b]380        return 1;
381}
382
[5ebff60]383static void cmd_account(irc_t *irc, char **cmd)
[b7d3cc34]384{
385        account_t *a;
[c7eb771]386        int len;
[5ebff60]387
388        if (global.conf->authmode == AUTHMODE_REGISTERED && !(irc->status & USTATUS_IDENTIFIED)) {
389                irc_rootmsg(irc, "This server only accepts registered users");
[f73b969]390                return;
[b7d3cc34]391        }
[5ebff60]392
393        len = strlen(cmd[1]);
394
395        if (len >= 1 && g_strncasecmp(cmd[1], "add", len) == 0) {
[7b23afd]396                struct prpl *prpl;
[7733b8c]397                char *protocol_name = cmd[2];
[5ebff60]398
399                MIN_ARGS(3);
400
401                if (cmd[4] == NULL) {
402                        for (a = irc->b->accounts; a; a = a->next) {
403                                if (strcmp(a->pass, PASSWORD_PENDING) == 0) {
404                                        irc_rootmsg(irc, "Enter password for account %s "
405                                                    "first (use /OPER)", a->tag);
[9564e55]406                                        return;
407                                }
[5ebff60]408                        }
409
[35987a1]410                        irc->status |= OPER_HACK_ACCOUNT_PASSWORD;
[da60f28]411                }
[5ebff60]412
[7733b8c]413                /* These used to be just hardcoded account tag guesses,
414                 * now they are promoted to hardecoded subprotocol guesses */
415                if (strcmp(protocol_name, "jabber") == 0) {
416                        if (strstr(cmd[3], "@gmail.com") || strstr(cmd[3], "@googlemail.com")) {
417                                protocol_name = "gtalk";
418                        } else if (strstr(cmd[3], "@chat.facebook.com")) {
419                                protocol_name = "fb";
420                        }
421                }
422
423                prpl = find_protocol(protocol_name);
[5ebff60]424
425                if (prpl == NULL) {
426                        irc_rootmsg(irc, "Unknown protocol");
[f73b969]427                        return;
[b7d3cc34]428                }
[5ebff60]429
430                for (a = irc->b->accounts; a; a = a->next) {
431                        if (a->prpl == prpl && prpl->handle_cmp(a->user, cmd[3]) == 0) {
432                                irc_rootmsg(irc, "Warning: You already have an account with "
433                                            "protocol `%s' and username `%s'. Are you accidentally "
434                                            "trying to add it twice?", prpl->name, cmd[3]);
435                        }
436                }
437
438                a = account_add(irc->b, prpl, cmd[3], cmd[4] ? cmd[4] : PASSWORD_PENDING);
439                if (cmd[5]) {
440                        irc_rootmsg(irc, "Warning: Passing a servername/other flags to `account add' "
441                                    "is now deprecated. Use `account set' instead.");
442                        set_setstr(&a->set, "server", cmd[5]);
[30ce1ce]443                }
[5ebff60]444
445                irc_rootmsg(irc, "Account successfully added with tag %s", a->tag);
446
447                if (cmd[4] == NULL) {
448                        set_t *oauth = set_find(&a->set, "oauth");
449                        if (oauth && bool2int(set_value(oauth))) {
[ce199b7]450                                *a->pass = '\0';
[5ebff60]451                                irc_rootmsg(irc, "No need to enter a password for this "
452                                            "account since it's using OAuth");
453                        } else {
454                                irc_rootmsg(irc, "You can now use the /OPER command to "
455                                            "enter the password");
456                                if (oauth) {
457                                        irc_rootmsg(irc, "Alternatively, enable OAuth if "
458                                                    "the account supports it: account %s "
459                                                    "set oauth on", a->tag);
460                                }
[ce199b7]461                        }
462                }
[5ebff60]463
[e907683]464                return;
[5ebff60]465        } else if (len >= 1 && g_strncasecmp(cmd[1], "list", len) == 0) {
[b7d3cc34]466                int i = 0;
[5ebff60]467
468                if (strchr(irc->umode, 'b')) {
469                        irc_rootmsg(irc, "Account list:");
470                }
471
472                for (a = irc->b->accounts; a; a = a->next) {
[b7d3cc34]473                        char *con;
[5ebff60]474
475                        if (a->ic && (a->ic->flags & OPT_LOGGED_IN)) {
[b7d3cc34]476                                con = " (connected)";
[5ebff60]477                        } else if (a->ic) {
[b7d3cc34]478                                con = " (connecting)";
[5ebff60]479                        } else if (a->reconnect) {
[b7d3cc34]480                                con = " (awaiting reconnect)";
[5ebff60]481                        } else {
[b7d3cc34]482                                con = "";
[5ebff60]483                        }
484
485                        irc_rootmsg(irc, "%2d (%s): %s, %s%s", i, a->tag, a->prpl->name, a->user, con);
486
487                        i++;
[b7d3cc34]488                }
[5ebff60]489                irc_rootmsg(irc, "End of account list");
490
[e907683]491                return;
[5ebff60]492        } else if (cmd[2]) {
[e907683]493                /* Try the following two only if cmd[2] == NULL */
[5ebff60]494        } else if (len >= 2 && g_strncasecmp(cmd[1], "on", len) == 0) {
495                if (irc->b->accounts) {
496                        irc_rootmsg(irc, "Trying to get all accounts connected...");
497
498                        for (a = irc->b->accounts; a; a = a->next) {
499                                if (!a->ic && a->auto_connect) {
500                                        if (strcmp(a->pass, PASSWORD_PENDING) == 0) {
501                                                irc_rootmsg(irc, "Enter password for account %s "
502                                                            "first (use /OPER)", a->tag);
503                                        } else {
504                                                account_on(irc->b, a);
505                                        }
[9564e55]506                                }
[5ebff60]507                        }
508                } else {
509                        irc_rootmsg(irc, "No accounts known. Use `account add' to add one.");
[b7d3cc34]510                }
[5ebff60]511
[e907683]512                return;
[5ebff60]513        } else if (len >= 2 && g_strncasecmp(cmd[1], "off", len) == 0) {
514                irc_rootmsg(irc, "Deactivating all active (re)connections...");
515
516                for (a = irc->b->accounts; a; a = a->next) {
517                        if (a->ic) {
518                                account_off(irc->b, a);
519                        } else if (a->reconnect) {
520                                cancel_auto_reconnect(a);
521                        }
[e907683]522                }
[5ebff60]523
[e907683]524                return;
525        }
[5ebff60]526
527        MIN_ARGS(2);
528        len = strlen(cmd[2]);
529
[e907683]530        /* At least right now, don't accept on/off/set/del as account IDs even
531           if they're a proper match, since people not familiar with the new
532           syntax yet may get a confusing/nasty surprise. */
[5ebff60]533        if (g_strcasecmp(cmd[1], "on") == 0 ||
534            g_strcasecmp(cmd[1], "off") == 0 ||
535            g_strcasecmp(cmd[1], "set") == 0 ||
536            g_strcasecmp(cmd[1], "del") == 0 ||
537            (a = account_get(irc->b, cmd[1])) == NULL) {
538                irc_rootmsg(irc, "Could not find account `%s'.", cmd[1]);
539
[e907683]540                return;
541        }
[5ebff60]542
543        if (len >= 1 && g_strncasecmp(cmd[2], "del", len) == 0) {
544                if (a->ic) {
545                        irc_rootmsg(irc, "Account is still logged in, can't delete");
546                } else {
547                        account_del(irc->b, a);
548                        irc_rootmsg(irc, "Account deleted");
[e907683]549                }
[5ebff60]550        } else if (len >= 2 && g_strncasecmp(cmd[2], "on", len) == 0) {
551                if (a->ic) {
552                        irc_rootmsg(irc, "Account already online");
553                } else if (strcmp(a->pass, PASSWORD_PENDING) == 0) {
554                        irc_rootmsg(irc, "Enter password for account %s "
555                                    "first (use /OPER)", a->tag);
556                } else {
557                        account_on(irc->b, a);
[e907683]558                }
[5ebff60]559        } else if (len >= 2 && g_strncasecmp(cmd[2], "off", len) == 0) {
560                if (a->ic) {
561                        account_off(irc->b, a);
562                } else if (a->reconnect) {
563                        cancel_auto_reconnect(a);
564                        irc_rootmsg(irc, "Reconnect cancelled");
565                } else {
566                        irc_rootmsg(irc, "Account already offline");
[e907683]567                }
[5ebff60]568        } else if (len >= 1 && g_strncasecmp(cmd[2], "set", len) == 0) {
569                cmd_set_real(irc, cmd + 2, &a->set, cmd_account_set_checkflags);
570        } else {
571                irc_rootmsg(irc,
572                            "Unknown command: %s [...] %s. Please use \x02help commands\x02 to get a list of available commands.", "account",
573                            cmd[2]);
[b7d3cc34]574        }
575}
576
[5ebff60]577static void cmd_channel(irc_t *irc, char **cmd)
[c133d4b8]578{
579        irc_channel_t *ic;
[c7eb771]580        int len;
[5ebff60]581
582        len = strlen(cmd[1]);
583
584        if (len >= 1 && g_strncasecmp(cmd[1], "list", len) == 0) {
[36562b0]585                GSList *l;
586                int i = 0;
[5ebff60]587
588                if (strchr(irc->umode, 'b')) {
589                        irc_rootmsg(irc, "Channel list:");
590                }
591
592                for (l = irc->channels; l; l = l->next) {
[36562b0]593                        irc_channel_t *ic = l->data;
[5ebff60]594
595                        irc_rootmsg(irc, "%2d. %s, %s channel%s", i, ic->name,
596                                    set_getstr(&ic->set, "type"),
597                                    ic->flags & IRC_CHANNEL_JOINED ? " (joined)" : "");
598
599                        i++;
[36562b0]600                }
[5ebff60]601                irc_rootmsg(irc, "End of channel list");
602
[e907683]603                return;
[36562b0]604        }
[5ebff60]605
606        if ((ic = irc_channel_get(irc, cmd[1])) == NULL) {
[4f22a68c]607                /* If this doesn't match any channel, maybe this is the short
608                   syntax (only works when used inside a channel). */
[5ebff60]609                if ((ic = irc->root->last_channel) &&
610                    (len = strlen(cmd[1])) &&
611                    g_strncasecmp(cmd[1], "set", len) == 0) {
612                        cmd_set_real(irc, cmd + 1, &ic->set, NULL);
613                } else {
614                        irc_rootmsg(irc, "Could not find channel `%s'", cmd[1]);
615                }
616
[e907683]617                return;
618        }
[5ebff60]619
620        MIN_ARGS(2);
621        len = strlen(cmd[2]);
622
623        if (len >= 1 && g_strncasecmp(cmd[2], "set", len) == 0) {
624                cmd_set_real(irc, cmd + 2, &ic->set, NULL);
625        } else if (len >= 1 && g_strncasecmp(cmd[2], "del", len) == 0) {
626                if (!(ic->flags & IRC_CHANNEL_JOINED) &&
627                    ic != ic->irc->default_channel) {
628                        irc_rootmsg(irc, "Channel %s deleted.", ic->name);
629                        irc_channel_free(ic);
630                } else {
631                        irc_rootmsg(irc, "Couldn't remove channel (main channel %s or "
632                                    "channels you're still in cannot be deleted).",
633                                    irc->default_channel->name);
[a4d920b]634                }
[5ebff60]635        } else {
636                irc_rootmsg(irc,
637                            "Unknown command: %s [...] %s. Please use \x02help commands\x02 to get a list of available commands.", "channel",
638                            cmd[1]);
[c133d4b8]639        }
640}
641
[5ebff60]642static void cmd_add(irc_t *irc, char **cmd)
[b7d3cc34]643{
644        account_t *a;
[f0cb961]645        int add_on_server = 1;
[06eef80]646        char *handle = NULL, *s;
[5ebff60]647
648        if (g_strcasecmp(cmd[1], "-tmp") == 0) {
649                MIN_ARGS(3);
[f0cb961]650                add_on_server = 0;
[5ebff60]651                cmd++;
[f8de26f]652        }
[5ebff60]653
654        if (!(a = account_get(irc->b, cmd[1]))) {
655                irc_rootmsg(irc, "Invalid account");
[f73b969]656                return;
[5ebff60]657        } else if (!(a->ic && (a->ic->flags & OPT_LOGGED_IN))) {
658                irc_rootmsg(irc, "That account is not on-line");
[f73b969]659                return;
[b7d3cc34]660        }
[5ebff60]661
662        if (cmd[3]) {
663                if (!nick_ok(irc, cmd[3])) {
664                        irc_rootmsg(irc, "The requested nick `%s' is invalid", cmd[3]);
[f73b969]665                        return;
[5ebff60]666                } else if (irc_user_by_name(irc, cmd[3])) {
667                        irc_rootmsg(irc, "The requested nick `%s' already exists", cmd[3]);
[f73b969]668                        return;
[5ebff60]669                } else {
670                        nick_set_raw(a, cmd[2], cmd[3]);
[b7d3cc34]671                }
672        }
[5ebff60]673
674        if ((a->flags & ACC_FLAG_HANDLE_DOMAINS) && cmd[2][0] != '_' &&
675            (!(s = strchr(cmd[2], '@')) || s[1] == '\0')) {
[06eef80]676                /* If there's no @ or it's the last char, append the user's
677                   domain name now. Exclude handles starting with a _ so
678                   adding _xmlconsole will keep working. */
[5ebff60]679                if (s) {
[06eef80]680                        *s = '\0';
[5ebff60]681                }
682                if ((s = strchr(a->user, '@'))) {
683                        cmd[2] = handle = g_strconcat(cmd[2], s, NULL);
684                }
[06eef80]685        }
[5ebff60]686
687        if (add_on_server) {
[f1d488e]688                irc_channel_t *ic;
689                char *s, *group = NULL;;
[5ebff60]690
691                if ((ic = irc->root->last_channel) &&
692                    (s = set_getstr(&ic->set, "fill_by")) &&
693                    strcmp(s, "group") == 0 &&
694                    (group = set_getstr(&ic->set, "group"))) {
695                        irc_rootmsg(irc, "Adding `%s' to contact list (group %s)",
696                                    cmd[2], group);
697                } else {
698                        irc_rootmsg(irc, "Adding `%s' to contact list", cmd[2]);
699                }
700
701                a->prpl->add_buddy(a->ic, cmd[2], group);
702        } else {
[f1d488e]703                bee_user_t *bu;
704                irc_user_t *iu;
[5ebff60]705
[dbb0ce3]706                /* Only for add -tmp. For regular adds, this callback will
707                   be called once the IM server confirms. */
[5ebff60]708                if ((bu = bee_user_new(irc->b, a->ic, cmd[2], BEE_USER_LOCAL)) &&
709                    (iu = bu->ui_data)) {
710                        irc_rootmsg(irc, "Temporarily assigned nickname `%s' "
711                                    "to contact `%s'", iu->nick, cmd[2]);
712                }
[f1d488e]713        }
[5ebff60]714
715        g_free(handle);
[b7d3cc34]716}
717
[5ebff60]718static void cmd_remove(irc_t *irc, char **cmd)
[dbb0ce3]719{
720        irc_user_t *iu;
721        bee_user_t *bu;
722        char *s;
[5ebff60]723
724        if (!(iu = irc_user_by_name(irc, cmd[1])) || !(bu = iu->bu)) {
725                irc_rootmsg(irc, "Buddy `%s' not found", cmd[1]);
[dbb0ce3]726                return;
727        }
[5ebff60]728        s = g_strdup(bu->handle);
729
730        bu->ic->acc->prpl->remove_buddy(bu->ic, bu->handle, NULL);
731        nick_del(bu);
732        if (g_slist_find(irc->users, iu)) {
733                bee_user_free(irc->b, bu);
734        }
735
736        irc_rootmsg(irc, "Buddy `%s' (nick %s) removed from contact list", s, cmd[1]);
737        g_free(s);
738
[dbb0ce3]739        return;
740}
741
[5ebff60]742static void cmd_info(irc_t *irc, char **cmd)
[b7d3cc34]743{
[0da65d5]744        struct im_connection *ic;
[b7d3cc34]745        account_t *a;
[5ebff60]746
747        if (!cmd[2]) {
748                irc_user_t *iu = irc_user_by_name(irc, cmd[1]);
749                if (!iu || !iu->bu) {
750                        irc_rootmsg(irc, "Nick `%s' does not exist", cmd[1]);
[f73b969]751                        return;
[b7d3cc34]752                }
[aa44bdd]753                ic = iu->bu->ic;
754                cmd[2] = iu->bu->handle;
[5ebff60]755        } else if (!(a = account_get(irc->b, cmd[1]))) {
756                irc_rootmsg(irc, "Invalid account");
[f73b969]757                return;
[5ebff60]758        } else if (!((ic = a->ic) && (a->ic->flags & OPT_LOGGED_IN))) {
759                irc_rootmsg(irc, "That account is not on-line");
[f73b969]760                return;
[b7d3cc34]761        }
[5ebff60]762
763        if (!ic->acc->prpl->get_info) {
764                irc_rootmsg(irc, "Command `%s' not supported by this protocol", cmd[0]);
765        } else {
766                ic->acc->prpl->get_info(ic, cmd[2]);
[f73b969]767        }
[b7d3cc34]768}
769
[5ebff60]770static void cmd_rename(irc_t *irc, char **cmd)
[b7d3cc34]771{
[9a9b520]772        irc_user_t *iu, *old;
[5ebff60]773        gboolean del = g_strcasecmp(cmd[1], "-del") == 0;
774
775        iu = irc_user_by_name(irc, cmd[del ? 2 : 1]);
776
777        if (iu == NULL) {
778                irc_rootmsg(irc, "Nick `%s' does not exist", cmd[1]);
779        } else if (del) {
780                if (iu->bu) {
781                        bee_irc_user_nick_reset(iu);
782                }
783                irc_rootmsg(irc, "Nickname reset to `%s'", iu->nick);
784        } else if (iu == irc->user) {
785                irc_rootmsg(irc, "Use /nick to change your own nickname");
786        } else if (!nick_ok(irc, cmd[2])) {
787                irc_rootmsg(irc, "Nick `%s' is invalid", cmd[2]);
788        } else if ((old = irc_user_by_name(irc, cmd[2])) && old != iu) {
789                irc_rootmsg(irc, "Nick `%s' already exists", cmd[2]);
790        } else {
791                if (!irc_user_set_nick(iu, cmd[2])) {
792                        irc_rootmsg(irc, "Error while changing nick");
[57c96f7]793                        return;
794                }
[5ebff60]795
796                if (iu == irc->root) {
[7125cb3]797                        /* If we're called internally (user did "set root_nick"),
798                           let's not go O(INF). :-) */
[5ebff60]799                        if (strcmp(cmd[0], "set_rename") != 0) {
800                                set_setstr(&irc->b->set, "root_nick", cmd[2]);
801                        }
802                } else if (iu->bu) {
803                        nick_set(iu->bu, cmd[2]);
[f73b969]804                }
[5ebff60]805
806                irc_rootmsg(irc, "Nick successfully changed");
[b7d3cc34]807        }
808}
809
[5ebff60]810char *set_eval_root_nick(set_t *set, char *new_nick)
[1195cec]811{
812        irc_t *irc = set->data;
[5ebff60]813
814        if (strcmp(irc->root->nick, new_nick) != 0) {
[0a6e5d1]815                char *cmd[] = { "set_rename", irc->root->nick, new_nick, NULL };
[5ebff60]816
817                cmd_rename(irc, cmd);
[1195cec]818        }
[5ebff60]819
820        return strcmp(irc->root->nick, new_nick) == 0 ? new_nick : SET_INVALID;
[1195cec]821}
[0baed0d]822
[5ebff60]823static void cmd_block(irc_t *irc, char **cmd)
[b7d3cc34]824{
[0da65d5]825        struct im_connection *ic;
[b7d3cc34]826        account_t *a;
[5ebff60]827
828        if (!cmd[2] && (a = account_get(irc->b, cmd[1])) && a->ic) {
[87b6a3e]829                char *format;
830                GSList *l;
[5ebff60]831
832                if (strchr(irc->umode, 'b') != NULL) {
[87b6a3e]833                        format = "%s\t%s";
[5ebff60]834                } else {
[57ef864]835                        format = "%-32.32s  %-16.16s";
[5ebff60]836                }
837
838                irc_rootmsg(irc, format, "Handle", "Nickname");
839                for (l = a->ic->deny; l; l = l->next) {
840                        bee_user_t *bu = bee_user_by_handle(irc->b, a->ic, l->data);
[2272cb3]841                        irc_user_t *iu = bu ? bu->ui_data : NULL;
[5ebff60]842                        irc_rootmsg(irc, format, l->data, iu ? iu->nick : "(none)");
[87b6a3e]843                }
[5ebff60]844                irc_rootmsg(irc, "End of list.");
845
[87b6a3e]846                return;
[5ebff60]847        } else if (!cmd[2]) {
848                irc_user_t *iu = irc_user_by_name(irc, cmd[1]);
849                if (!iu || !iu->bu) {
850                        irc_rootmsg(irc, "Nick `%s' does not exist", cmd[1]);
[f73b969]851                        return;
[b7d3cc34]852                }
[2272cb3]853                ic = iu->bu->ic;
854                cmd[2] = iu->bu->handle;
[5ebff60]855        } else if (!(a = account_get(irc->b, cmd[1]))) {
856                irc_rootmsg(irc, "Invalid account");
[f73b969]857                return;
[5ebff60]858        } else if (!((ic = a->ic) && (a->ic->flags & OPT_LOGGED_IN))) {
859                irc_rootmsg(irc, "That account is not on-line");
[f73b969]860                return;
[b7d3cc34]861        }
[5ebff60]862
863        if (!ic->acc->prpl->add_deny || !ic->acc->prpl->rem_permit) {
864                irc_rootmsg(irc, "Command `%s' not supported by this protocol", cmd[0]);
865        } else {
866                imc_rem_allow(ic, cmd[2]);
867                imc_add_block(ic, cmd[2]);
868                irc_rootmsg(irc, "Buddy `%s' moved from allow- to block-list", cmd[2]);
[b7d3cc34]869        }
870}
871
[5ebff60]872static void cmd_allow(irc_t *irc, char **cmd)
[b7d3cc34]873{
[0da65d5]874        struct im_connection *ic;
[b7d3cc34]875        account_t *a;
[5ebff60]876
877        if (!cmd[2] && (a = account_get(irc->b, cmd[1])) && a->ic) {
[87b6a3e]878                char *format;
879                GSList *l;
[5ebff60]880
881                if (strchr(irc->umode, 'b') != NULL) {
[87b6a3e]882                        format = "%s\t%s";
[5ebff60]883                } else {
[57ef864]884                        format = "%-32.32s  %-16.16s";
[5ebff60]885                }
886
887                irc_rootmsg(irc, format, "Handle", "Nickname");
888                for (l = a->ic->permit; l; l = l->next) {
889                        bee_user_t *bu = bee_user_by_handle(irc->b, a->ic, l->data);
[2272cb3]890                        irc_user_t *iu = bu ? bu->ui_data : NULL;
[5ebff60]891                        irc_rootmsg(irc, format, l->data, iu ? iu->nick : "(none)");
[87b6a3e]892                }
[5ebff60]893                irc_rootmsg(irc, "End of list.");
894
[87b6a3e]895                return;
[5ebff60]896        } else if (!cmd[2]) {
897                irc_user_t *iu = irc_user_by_name(irc, cmd[1]);
898                if (!iu || !iu->bu) {
899                        irc_rootmsg(irc, "Nick `%s' does not exist", cmd[1]);
[f73b969]900                        return;
[b7d3cc34]901                }
[2272cb3]902                ic = iu->bu->ic;
903                cmd[2] = iu->bu->handle;
[5ebff60]904        } else if (!(a = account_get(irc->b, cmd[1]))) {
905                irc_rootmsg(irc, "Invalid account");
[f73b969]906                return;
[5ebff60]907        } else if (!((ic = a->ic) && (a->ic->flags & OPT_LOGGED_IN))) {
908                irc_rootmsg(irc, "That account is not on-line");
[f73b969]909                return;
[b7d3cc34]910        }
[5ebff60]911
912        if (!ic->acc->prpl->rem_deny || !ic->acc->prpl->add_permit) {
913                irc_rootmsg(irc, "Command `%s' not supported by this protocol", cmd[0]);
914        } else {
915                imc_rem_block(ic, cmd[2]);
916                imc_add_allow(ic, cmd[2]);
917
918                irc_rootmsg(irc, "Buddy `%s' moved from block- to allow-list", cmd[2]);
[b7d3cc34]919        }
920}
921
[5ebff60]922static void cmd_yesno(irc_t *irc, char **cmd)
[b7d3cc34]923{
924        query_t *q = NULL;
925        int numq = 0;
[5ebff60]926
927        if (irc->queries == NULL) {
[65a4a64]928                /* Alright, alright, let's add a tiny easter egg here. */
929                static irc_t *last_irc = NULL;
930                static time_t last_time = 0;
931                static int times = 0;
932                static const char *msg[] = {
933                        "Oh yeah, that's right.",
934                        "Alright, alright. Now go back to work.",
935                        "Buuuuuuuuuuuuuuuurp... Excuse me!",
936                        "Yes?",
937                        "No?",
938                };
[5ebff60]939
940                if (last_irc == irc && time(NULL) - last_time < 15) {
941                        if ((++times >= 3)) {
942                                irc_rootmsg(irc, "%s", msg[rand() % (sizeof(msg) / sizeof(char*))]);
[65a4a64]943                                last_irc = NULL;
944                                times = 0;
945                                return;
946                        }
[5ebff60]947                } else {
948                        last_time = time(NULL);
[65a4a64]949                        last_irc = irc;
950                        times = 0;
951                }
[5ebff60]952
953                irc_rootmsg(irc, "Did I ask you something?");
[f73b969]954                return;
[b7d3cc34]955        }
[5ebff60]956
[b7d3cc34]957        /* If there's an argument, the user seems to want to answer another question than the
958           first/last (depending on the query_order setting) one. */
[5ebff60]959        if (cmd[1]) {
960                if (sscanf(cmd[1], "%d", &numq) != 1) {
961                        irc_rootmsg(irc, "Invalid query number");
[f73b969]962                        return;
[b7d3cc34]963                }
[5ebff60]964
965                for (q = irc->queries; q; q = q->next, numq--) {
966                        if (numq == 0) {
[b7d3cc34]967                                break;
[5ebff60]968                        }
969                }
970
971                if (!q) {
972                        irc_rootmsg(irc, "Uhm, I never asked you something like that...");
[f73b969]973                        return;
[b7d3cc34]974                }
975        }
[5ebff60]976
977        if (g_strcasecmp(cmd[0], "yes") == 0) {
978                query_answer(irc, q, 1);
979        } else if (g_strcasecmp(cmd[0], "no") == 0) {
980                query_answer(irc, q, 0);
981        }
[b7d3cc34]982}
983
[5ebff60]984static void cmd_set(irc_t *irc, char **cmd)
[b7d3cc34]985{
[5ebff60]986        cmd_set_real(irc, cmd, &irc->b->set, NULL);
[b7d3cc34]987}
988
[5ebff60]989static void cmd_blist(irc_t *irc, char **cmd)
[b7d3cc34]990{
[f93fd2d]991        int online = 0, away = 0, offline = 0, ismatch = 0;
[4c3519a]992        GSList *l;
[f93fd2d]993        GRegex *regex = NULL;
994        GError *error = NULL;
[aefa533e]995        char s[256];
996        char *format;
[b7d3cc34]997        int n_online = 0, n_away = 0, n_offline = 0;
[5ebff60]998
999        if (cmd[1] && g_strcasecmp(cmd[1], "all") == 0) {
[b7d3cc34]1000                online = offline = away = 1;
[5ebff60]1001        } else if (cmd[1] && g_strcasecmp(cmd[1], "offline") == 0) {
[b7d3cc34]1002                offline = 1;
[5ebff60]1003        } else if (cmd[1] && g_strcasecmp(cmd[1], "away") == 0) {
[b7d3cc34]1004                away = 1;
[5ebff60]1005        } else if (cmd[1] && g_strcasecmp(cmd[1], "online") == 0) {
[b7d3cc34]1006                online = 1;
[5ebff60]1007        } else {
[449a51d]1008                online = away = 1;
[f93fd2d]1009        }
[5ebff60]1010
1011        if (cmd[2]) {
1012                regex = g_regex_new(cmd[2], G_REGEX_CASELESS, 0, &error);
1013        }
1014
1015        if (error) {
1016                irc_rootmsg(irc, error->message);
1017                g_error_free(error);
1018        }
1019
1020        if (strchr(irc->umode, 'b') != NULL) {
[aefa533e]1021                format = "%s\t%s\t%s";
[5ebff60]1022        } else {
[aefa533e]1023                format = "%-16.16s  %-40.40s  %s";
[5ebff60]1024        }
1025
1026        irc_rootmsg(irc, format, "Nick", "Handle/Account", "Status");
1027
1028        if (irc->root->last_channel &&
1029            strcmp(set_getstr(&irc->root->last_channel->set, "type"), "control") != 0) {
[ac2717b]1030                irc->root->last_channel = NULL;
[5ebff60]1031        }
1032
1033        for (l = irc->users; l; l = l->next) {
[4c3519a]1034                irc_user_t *iu = l->data;
1035                bee_user_t *bu = iu->bu;
[5ebff60]1036
1037                if (!regex || g_regex_match(regex, iu->nick, 0, NULL)) {
[f93fd2d]1038                        ismatch = 1;
[5ebff60]1039                } else {
[f93fd2d]1040                        ismatch = 0;
[5ebff60]1041                }
1042
1043                if (!bu || (irc->root->last_channel && !irc_channel_wants_user(irc->root->last_channel, iu))) {
[4c3519a]1044                        continue;
[5ebff60]1045                }
1046
1047                if ((bu->flags & (BEE_USER_ONLINE | BEE_USER_AWAY)) == BEE_USER_ONLINE) {
1048                        if (ismatch == 1 && online == 1) {
[f93fd2d]1049                                char st[256] = "Online";
[5ebff60]1050
1051                                if (bu->status_msg) {
1052                                        g_snprintf(st, sizeof(st) - 1, "Online (%s)", bu->status_msg);
1053                                }
1054
1055                                g_snprintf(s, sizeof(s) - 1, "%s %s", bu->handle, bu->ic->acc->tag);
1056                                irc_rootmsg(irc, format, iu->nick, s, st);
[f93fd2d]1057                        }
[5ebff60]1058
1059                        n_online++;
[aefa533e]1060                }
[5ebff60]1061
1062                if ((bu->flags & BEE_USER_ONLINE) && (bu->flags & BEE_USER_AWAY)) {
1063                        if (ismatch == 1 && away == 1) {
1064                                g_snprintf(s, sizeof(s) - 1, "%s %s", bu->handle, bu->ic->acc->tag);
1065                                irc_rootmsg(irc, format, iu->nick, s, irc_user_get_away(iu));
[f93fd2d]1066                        }
[5ebff60]1067                        n_away++;
[aefa533e]1068                }
[5ebff60]1069
1070                if (!(bu->flags & BEE_USER_ONLINE)) {
1071                        if (ismatch == 1 && offline == 1) {
1072                                g_snprintf(s, sizeof(s) - 1, "%s %s", bu->handle, bu->ic->acc->tag);
1073                                irc_rootmsg(irc, format, iu->nick, s, "Offline");
[f93fd2d]1074                        }
[5ebff60]1075                        n_offline++;
[aefa533e]1076                }
[b7d3cc34]1077        }
[5ebff60]1078
1079        irc_rootmsg(irc, "%d buddies (%d available, %d away, %d offline)", n_online + n_away + n_offline, n_online,
1080                    n_away, n_offline);
1081
1082        if (regex) {
1083                g_regex_unref(regex);
1084        }
[b7d3cc34]1085}
1086
[5ebff60]1087static void cmd_qlist(irc_t *irc, char **cmd)
[b7d3cc34]1088{
1089        query_t *q = irc->queries;
1090        int num;
[5ebff60]1091
1092        if (!q) {
1093                irc_rootmsg(irc, "There are no pending questions.");
[f73b969]1094                return;
[b7d3cc34]1095        }
[5ebff60]1096
1097        irc_rootmsg(irc, "Pending queries:");
1098
1099        for (num = 0; q; q = q->next, num++) {
1100                if (q->ic) { /* Not necessary yet, but it might come later */
1101                        irc_rootmsg(irc, "%d, %s: %s", num, q->ic->acc->tag, q->question);
1102                } else {
1103                        irc_rootmsg(irc, "%d, BitlBee: %s", num, q->question);
1104                }
1105        }
[b7d3cc34]1106}
1107
[5ebff60]1108static void cmd_chat(irc_t *irc, char **cmd)
[a9a7287]1109{
1110        account_t *acc;
[5ebff60]1111
1112        if (g_strcasecmp(cmd[1], "add") == 0) {
[07054a5]1113                char *channel, *s;
[7b71feb]1114                struct irc_channel *ic;
[5ebff60]1115
1116                MIN_ARGS(3);
1117
1118                if (!(acc = account_get(irc->b, cmd[2]))) {
1119                        irc_rootmsg(irc, "Invalid account");
[a9a7287]1120                        return;
[5ebff60]1121                } else if (!acc->prpl->chat_join) {
1122                        irc_rootmsg(irc, "Named chatrooms not supported on that account.");
[5a75d15]1123                        return;
1124                }
[5ebff60]1125
1126                if (cmd[4] == NULL) {
1127                        channel = g_strdup(cmd[3]);
1128                        if ((s = strchr(channel, '@'))) {
[07054a5]1129                                *s = 0;
[5ebff60]1130                        }
1131                } else {
1132                        channel = g_strdup(cmd[4]);
[07054a5]1133                }
[5ebff60]1134
1135                if (strchr(CTYPES, channel[0]) == NULL) {
1136                        s = g_strdup_printf("#%s", channel);
1137                        g_free(channel);
[07054a5]1138                        channel = s;
[5ebff60]1139
1140                        irc_channel_name_strip(channel);
[5a75d15]1141                }
[5ebff60]1142
1143                if ((ic = irc_channel_new(irc, channel)) &&
1144                    set_setstr(&ic->set, "type", "chat") &&
1145                    set_setstr(&ic->set, "chat_type", "room") &&
1146                    set_setstr(&ic->set, "account", cmd[2]) &&
1147                    set_setstr(&ic->set, "room", cmd[3])) {
1148                        irc_rootmsg(irc, "Chatroom successfully added.");
1149                } else {
1150                        if (ic) {
1151                                irc_channel_free(ic);
1152                        }
1153
1154                        irc_rootmsg(irc, "Could not add chatroom.");
[d995c9b]1155                }
[5ebff60]1156                g_free(channel);
1157        } else if (g_strcasecmp(cmd[1], "with") == 0) {
[c1a8a16]1158                irc_user_t *iu;
[5ebff60]1159
1160                MIN_ARGS(2);
1161
1162                if ((iu = irc_user_by_name(irc, cmd[2])) &&
1163                    iu->bu && iu->bu->ic->acc->prpl->chat_with) {
1164                        if (!iu->bu->ic->acc->prpl->chat_with(iu->bu->ic, iu->bu->handle)) {
1165                                irc_rootmsg(irc, "(Possible) failure while trying to open "
1166                                            "a groupchat with %s.", iu->nick);
[39f93f0]1167                        }
[5ebff60]1168                } else {
1169                        irc_rootmsg(irc, "Can't open a groupchat with %s.", cmd[2]);
[39f93f0]1170                }
[5ebff60]1171        } else if (g_strcasecmp(cmd[1], "list") == 0 ||
1172                   g_strcasecmp(cmd[1], "set") == 0 ||
1173                   g_strcasecmp(cmd[1], "del") == 0) {
1174                irc_rootmsg(irc,
1175                            "Warning: The \002chat\002 command was mostly replaced with the \002channel\002 command.");
1176                cmd_channel(irc, cmd);
1177        } else {
1178                irc_rootmsg(irc,
1179                            "Unknown command: %s %s. Please use \x02help commands\x02 to get a list of available commands.", "chat",
1180                            cmd[1]);
[a9a7287]1181        }
[fa29d093]1182}
1183
[5ebff60]1184static void cmd_group(irc_t *irc, char **cmd)
[f1d488e]1185{
1186        GSList *l;
1187        int len;
[5ebff60]1188
1189        len = strlen(cmd[1]);
1190        if (g_strncasecmp(cmd[1], "list", len) == 0) {
[f1d488e]1191                int n = 0;
[5ebff60]1192
1193                if (strchr(irc->umode, 'b')) {
1194                        irc_rootmsg(irc, "Group list:");
1195                }
1196
1197                for (l = irc->b->groups; l; l = l->next) {
[f1d488e]1198                        bee_group_t *bg = l->data;
[5ebff60]1199                        irc_rootmsg(irc, "%d. %s", n++, bg->name);
[f1d488e]1200                }
[5ebff60]1201                irc_rootmsg(irc, "End of group list");
1202        } else if (g_strncasecmp(cmd[1], "info", len) == 0) {
[e4f5ca8]1203                bee_group_t *bg;
[c2cc24c]1204                int n = 0;
1205
1206                MIN_ARGS(2);
[5ebff60]1207                bg = bee_group_by_name(irc->b, cmd[2], FALSE);
1208
1209                if (bg) {
1210                        if (strchr(irc->umode, 'b')) {
1211                                irc_rootmsg(irc, "Members of %s:", cmd[2]);
1212                        }
1213                        for (l = irc->b->users; l; l = l->next) {
[c2cc24c]1214                                bee_user_t *bu = l->data;
[5ebff60]1215                                if (bu->group == bg) {
1216                                        irc_rootmsg(irc, "%d. %s", n++, bu->nick ? : bu->handle);
1217                                }
[c2cc24c]1218                        }
[5ebff60]1219                        irc_rootmsg(irc, "End of member list");
1220                } else {
1221                        irc_rootmsg(irc,
1222                                    "Unknown group: %s. Please use \x02group list\x02 to get a list of available groups.",
1223                                    cmd[2]);
[c2cc24c]1224                }
[5ebff60]1225        } else {
1226                irc_rootmsg(irc,
1227                            "Unknown command: %s %s. Please use \x02help commands\x02 to get a list of available commands.", "group",
1228                            cmd[1]);
[f1d488e]1229        }
1230}
1231
[5ebff60]1232static void cmd_transfer(irc_t *irc, char **cmd)
[2c2df7d]1233{
1234        GSList *files = irc->file_transfers;
[5ebff60]1235
[2c2df7d]1236        enum { LIST, REJECT, CANCEL };
1237        int subcmd = LIST;
1238        int fid;
1239
[5ebff60]1240        if (!files) {
1241                irc_rootmsg(irc, "No pending transfers");
[2c2df7d]1242                return;
1243        }
1244
[5ebff60]1245        if (cmd[1] && (strcmp(cmd[1], "reject") == 0)) {
[2c2df7d]1246                subcmd = REJECT;
[5ebff60]1247        } else if (cmd[1] && (strcmp(cmd[1], "cancel") == 0) &&
1248                   cmd[2] && (sscanf(cmd[2], "%d", &fid) == 1)) {
[2c2df7d]1249                subcmd = CANCEL;
1250        }
1251
[5ebff60]1252        for (; files; files = g_slist_next(files)) {
[2c2df7d]1253                file_transfer_t *file = files->data;
[5ebff60]1254
1255                switch (subcmd) {
[2c2df7d]1256                case LIST:
[5ebff60]1257                        if (file->status == FT_STATUS_LISTENING) {
1258                                irc_rootmsg(irc,
1259                                            "Pending file(id %d): %s (Listening...)", file->local_id, file->file_name);
1260                        } else {
[2c2df7d]1261                                int kb_per_s = 0;
[5ebff60]1262                                time_t diff = time(NULL) - file->started ? : 1;
1263                                if ((file->started > 0) && (file->bytes_transferred > 0)) {
[2c2df7d]1264                                        kb_per_s = file->bytes_transferred / 1024 / diff;
[5ebff60]1265                                }
1266
1267                                irc_rootmsg(irc,
1268                                            "Pending file(id %d): %s (%10zd/%zd kb, %d kb/s)", file->local_id,
1269                                            file->file_name,
1270                                            file->bytes_transferred / 1024, file->file_size / 1024, kb_per_s);
[2c2df7d]1271                        }
1272                        break;
1273                case REJECT:
[5ebff60]1274                        if (file->status == FT_STATUS_LISTENING) {
1275                                irc_rootmsg(irc, "Rejecting file transfer for %s", file->file_name);
1276                                imcb_file_canceled(file->ic, file, "Denied by user");
[2c2df7d]1277                        }
1278                        break;
1279                case CANCEL:
[5ebff60]1280                        if (file->local_id == fid) {
1281                                irc_rootmsg(irc, "Canceling file transfer for %s", file->file_name);
1282                                imcb_file_canceled(file->ic, file, "Canceled by user");
[2c2df7d]1283                        }
1284                        break;
1285                }
1286        }
1287}
1288
[5ebff60]1289static void cmd_nick(irc_t *irc, char **cmd)
[3cd4016]1290{
[5ebff60]1291        irc_rootmsg(irc, "This command is deprecated. Try: account %s set display_name", cmd[1]);
[3cd4016]1292}
1293
[180ab31]1294/* Maybe this should be a stand-alone command as well? */
[5ebff60]1295static void bitlbee_whatsnew(irc_t *irc)
[180ab31]1296{
[5ebff60]1297        int last = set_getint(&irc->b->set, "last_version");
[674a01d]1298        char s[16], *msg;
[5ebff60]1299
1300        if (last >= BITLBEE_VERSION_CODE) {
[180ab31]1301                return;
[5ebff60]1302        }
1303
1304        msg = help_get_whatsnew(&(global.help), last);
1305
1306        if (msg) {
1307                irc_rootmsg(irc, "%s: This seems to be your first time using this "
1308                            "this version of BitlBee. Here's a list of new "
1309                            "features you may like to know about:\n\n%s\n",
1310                            irc->user->nick, msg);
1311        }
1312
1313        g_free(msg);
1314
1315        g_snprintf(s, sizeof(s), "%d", BITLBEE_VERSION_CODE);
1316        set_setstr(&irc->b->set, "last_version", s);
[180ab31]1317}
1318
[6c56f42]1319/* IMPORTANT: Keep this list sorted! The short command logic needs that. */
[8358691]1320command_t root_commands[] = {
[d860a8d]1321        { "account",        1, cmd_account,        0 },
[6c56f42]1322        { "add",            2, cmd_add,            0 },
[2272cb3]1323        { "allow",          1, cmd_allow,          0 },
[4c3519a]1324        { "blist",          0, cmd_blist,          0 },
[2272cb3]1325        { "block",          1, cmd_block,          0 },
[c133d4b8]1326        { "channel",        1, cmd_channel,        0 },
1327        { "chat",           1, cmd_chat,           0 },
[6c56f42]1328        { "drop",           1, cmd_drop,           0 },
[9d4352c]1329        { "ft",             0, cmd_transfer,       0 },
[f1d488e]1330        { "group",          1, cmd_group,          0 },
[5ebff60]1331        { "help",           0, cmd_help,           0 },
[060d066]1332        { "identify",       0, cmd_identify,       0 },
[aa44bdd]1333        { "info",           1, cmd_info,           0 },
[3cd4016]1334        { "nick",           1, cmd_nick,           0 },
[6c56f42]1335        { "no",             0, cmd_yesno,          0 },
[9d4352c]1336        { "qlist",          0, cmd_qlist,          0 },
[060d066]1337        { "register",       0, cmd_register,       0 },
[dbb0ce3]1338        { "remove",         1, cmd_remove,         0 },
[0298d11]1339        { "rename",         2, cmd_rename,         0 },
[6c56f42]1340        { "save",           0, cmd_save,           0 },
[0298d11]1341        { "set",            0, cmd_set,            0 },
[9d4352c]1342        { "transfer",       0, cmd_transfer,       0 },
[0298d11]1343        { "yes",            0, cmd_yesno,          0 },
[8358691]1344        /* Not expecting too many plugins adding root commands so just make a
1345           dumb array with some empty entried at the end. */
1346        { NULL },
1347        { NULL },
1348        { NULL },
1349        { NULL },
1350        { NULL },
1351        { NULL },
1352        { NULL },
1353        { NULL },
1354        { NULL },
[0298d11]1355};
[5ebff60]1356static const int num_root_commands = sizeof(root_commands) / sizeof(command_t);
[8358691]1357
[5ebff60]1358gboolean root_command_add(const char *command, int params, void (*func)(irc_t *, char **args), int flags)
[8358691]1359{
1360        int i;
[5ebff60]1361
1362        if (root_commands[num_root_commands - 2].command) {
[8358691]1363                /* Planning fail! List is full. */
1364                return FALSE;
[5ebff60]1365        }
1366
1367        for (i = 0; root_commands[i].command; i++) {
1368                if (g_strcasecmp(root_commands[i].command, command) == 0) {
[8358691]1369                        return FALSE;
[5ebff60]1370                } else if (g_strcasecmp(root_commands[i].command, command) > 0) {
[8358691]1371                        break;
[5ebff60]1372                }
[8358691]1373        }
[5ebff60]1374        memmove(root_commands + i + 1, root_commands + i,
1375                sizeof(command_t) * (num_root_commands - i - 1));
1376
1377        root_commands[i].command = g_strdup(command);
[8358691]1378        root_commands[i].required_parameters = params;
1379        root_commands[i].execute = func;
1380        root_commands[i].flags = flags;
[5ebff60]1381
[8358691]1382        return TRUE;
1383}
Note: See TracBrowser for help on using the repository browser.