source: protocols/account.c @ 3f44e43

Last change on this file since 3f44e43 was 3f44e43, checked in by dequis <dx@…>, at 2016-11-21T07:49:26Z

Merge branch 'master' into parson

  • Property mode set to 100644
File size: 10.9 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/* Account management functions                                         */
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 "bitlbee.h"
28#include "account.h"
29
[5ebff60]30static char *set_eval_nick_source(set_t *set, char *value);
[06b39f2]31
[5ebff60]32account_t *account_add(bee_t *bee, struct prpl *prpl, char *user, char *pass)
[b7d3cc34]33{
34        account_t *a;
[5100caa]35        set_t *s;
[5ebff60]36        char tag[strlen(prpl->name) + 10];
37
38        if (bee->accounts) {
39                for (a = bee->accounts; a->next; a = a->next) {
40                        ;
41                }
42                a = a->next = g_new0(account_t, 1);
43        } else {
44                bee->accounts = a = g_new0(account_t, 1);
[b7d3cc34]45        }
[5ebff60]46
[7b23afd]47        a->prpl = prpl;
[5ebff60]48        a->user = g_strdup(user);
49        a->pass = g_strdup(pass);
[2b14eef]50        a->auto_connect = 1;
[81e04e1]51        a->bee = bee;
[5ebff60]52
53        s = set_add(&a->set, "auto_connect", "true", set_eval_account, a);
[bb5ce568]54        s->flags |= SET_NOSAVE;
[5ebff60]55
56        s = set_add(&a->set, "auto_reconnect", "true", set_eval_bool, a);
57
58        s = set_add(&a->set, "nick_format", NULL, NULL, a);
[badd148]59        s->flags |= SET_NULL_OK;
[5ebff60]60
61        s = set_add(&a->set, "nick_source", "handle", set_eval_nick_source, a);
[bb5ce568]62        s->flags |= SET_NOSAVE; /* Just for bw compatibility! */
[5ebff60]63
64        s = set_add(&a->set, "password", NULL, set_eval_account, a);
[3ac6d9f]65        s->flags |= SET_NOSAVE | SET_NULL_OK | SET_PASSWORD | ACC_SET_LOCKABLE;
[5ebff60]66
67        s = set_add(&a->set, "tag", NULL, set_eval_account, a);
[bb5ce568]68        s->flags |= SET_NOSAVE;
[5ebff60]69
70        s = set_add(&a->set, "username", NULL, set_eval_account, a);
[3ac6d9f]71        s->flags |= SET_NOSAVE | ACC_SET_OFFLINE_ONLY | ACC_SET_LOCKABLE;
[5ebff60]72        set_setstr(&a->set, "username", user);
73
[5a8afc3]74        if (prpl == &protocol_missing) {
75                s = set_add(&a->set, "server", NULL, set_eval_account, a);
76                s->flags |= SET_NOSAVE | SET_HIDDEN | ACC_SET_OFFLINE_ONLY | ACC_SET_ONLINE_ONLY;
77        }
78
[3b3c50d9]79        /* Hardcode some more clever tag guesses. */
[5ebff60]80        strcpy(tag, prpl->name);
81        if (strcmp(prpl->name, "oscar") == 0) {
82                if (g_ascii_isdigit(a->user[0])) {
83                        strcpy(tag, "icq");
84                } else {
85                        strcpy(tag, "aim");
86                }
87        } else if (strcmp(prpl->name, "jabber") == 0) {
88                if (strstr(a->user, "@gmail.com") ||
89                    strstr(a->user, "@googlemail.com")) {
90                        strcpy(tag, "gtalk");
91                }
[3b3c50d9]92        }
[5ebff60]93
94        if (account_by_tag(bee, tag)) {
95                char *numpos = tag + strlen(tag);
[40e6dac]96                int i;
97
[5ebff60]98                for (i = 2; i < 10000; i++) {
99                        sprintf(numpos, "%d", i);
100                        if (!account_by_tag(bee, tag)) {
[40e6dac]101                                break;
[5ebff60]102                        }
[40e6dac]103                }
104        }
[5ebff60]105        set_setstr(&a->set, "tag", tag);
106
107        a->nicks = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
108
[96863f6]109        /* This function adds some more settings (and might want to do more
110           things that have to be done now, although I can't think of anything. */
[5ebff60]111        if (prpl->init) {
112                prpl->init(a);
113        }
114
115        s = set_add(&a->set, "away", NULL, set_eval_account, a);
[58adb7e]116        s->flags |= SET_NULL_OK;
[5ebff60]117
118        if (a->flags & ACC_FLAG_STATUS_MESSAGE) {
119                s = set_add(&a->set, "status", NULL, set_eval_account, a);
[58adb7e]120                s->flags |= SET_NULL_OK;
121        }
[5ebff60]122
[58adb7e]123        return a;
[b7d3cc34]124}
125
[5ebff60]126char *set_eval_account(set_t *set, char *value)
[5100caa]127{
128        account_t *acc = set->data;
[5ebff60]129
[5100caa]130        /* Double-check: We refuse to edit on-line accounts. */
[5ebff60]131        if (set->flags & ACC_SET_OFFLINE_ONLY && acc->ic) {
[7125cb3]132                return SET_INVALID;
[5ebff60]133        }
134
135        if (strcmp(set->key, "server") == 0) {
136                g_free(acc->server);
137                if (value && *value) {
138                        acc->server = g_strdup(value);
[30ce1ce]139                        return value;
[5ebff60]140                } else {
141                        acc->server = g_strdup(set->def);
142                        return g_strdup(set->def);
[30ce1ce]143                }
[5ebff60]144        } else if (strcmp(set->key, "username") == 0) {
145                g_free(acc->user);
146                acc->user = g_strdup(value);
[3b32017]147                return value;
[5ebff60]148        } else if (strcmp(set->key, "password") == 0) {
[35987a1]149                /* set -del allows /oper to be used to change the password or,
150                   iff oauth is enabled, reset the oauth credential magic.
151                */
[5ebff60]152                if (!value) {
153                        if (set_getbool(&(acc->set), "oauth")) {
[35987a1]154                                value = "";
155                        } else {
156                                value = PASSWORD_PENDING;
[5ebff60]157                                ((irc_t *) acc->bee->ui_data)->status |= OPER_HACK_ACCOUNT_PASSWORD;
158                                irc_rootmsg((irc_t *) acc->bee->ui_data, "You may now use /OPER to set the password");
[35987a1]159                        }
160                }
[5ebff60]161
162                g_free(acc->pass);
163                acc->pass = g_strdup(value);
164                return NULL;    /* password shouldn't be visible in plaintext! */
165        } else if (strcmp(set->key, "tag") == 0) {
[40e6dac]166                account_t *oa;
[5ebff60]167
[40e6dac]168                /* Enforce uniqueness. */
[5ebff60]169                if ((oa = account_by_tag(acc->bee, value)) && oa != acc) {
[40e6dac]170                        return SET_INVALID;
[5ebff60]171                }
172
173                g_free(acc->tag);
174                acc->tag = g_strdup(value);
[40e6dac]175                return value;
[5ebff60]176        } else if (strcmp(set->key, "auto_connect") == 0) {
177                if (!is_bool(value)) {
[7125cb3]178                        return SET_INVALID;
[5ebff60]179                }
180
181                acc->auto_connect = bool2int(value);
[5100caa]182                return value;
[5ebff60]183        } else if (strcmp(set->key, "away") == 0 ||
184                   strcmp(set->key, "status") == 0) {
185                if (acc->ic && acc->ic->flags & OPT_LOGGED_IN) {
[58adb7e]186                        /* If we're currently on-line, set the var now already
187                           (bit of a hack) and send an update. */
[5ebff60]188                        g_free(set->value);
189                        set->value = g_strdup(value);
190
191                        imc_away_send_update(acc->ic);
[58adb7e]192                }
[5ebff60]193
[58adb7e]194                return value;
195        }
[5ebff60]196
[7125cb3]197        return SET_INVALID;
[5100caa]198}
199
[06b39f2]200/* For bw compatibility, have this write-only setting. */
[5ebff60]201static char *set_eval_nick_source(set_t *set, char *value)
[06b39f2]202{
203        account_t *a = set->data;
[5ebff60]204
205        if (strcmp(value, "full_name") == 0) {
206                set_setstr(&a->set, "nick_format", "%full_name");
207        } else if (strcmp(value, "first_name") == 0) {
208                set_setstr(&a->set, "nick_format", "%first_name");
209        } else {
210                set_setstr(&a->set, "nick_format", "%-@nick");
211        }
212
[06b39f2]213        return value;
214}
215
[5ebff60]216account_t *account_get(bee_t *bee, const char *id)
[b7d3cc34]217{
218        account_t *a, *ret = NULL;
[85616c3]219        char *handle, *s;
[b7d3cc34]220        int nr;
[5ebff60]221
[40e6dac]222        /* Tags get priority above anything else. */
[5ebff60]223        if ((a = account_by_tag(bee, id))) {
[40e6dac]224                return a;
[5ebff60]225        }
226
[85616c3]227        /* This checks if the id string ends with (...) */
[5ebff60]228        if ((handle = strchr(id, '(')) && (s = strchr(handle, ')')) && s[1] == 0) {
[85616c3]229                struct prpl *proto;
[5ebff60]230
[85616c3]231                *s = *handle = 0;
[5ebff60]232                handle++;
233
234                if ((proto = find_protocol(id))) {
235                        for (a = bee->accounts; a; a = a->next) {
236                                if (a->prpl == proto &&
237                                    a->prpl->handle_cmp(handle, a->user) == 0) {
[85616c3]238                                        ret = a;
[5ebff60]239                                }
240                        }
[85616c3]241                }
[5ebff60]242
[85616c3]243                /* Restore the string. */
[5ebff60]244                handle--;
[85616c3]245                *handle = '(';
246                *s = ')';
[5ebff60]247
248                if (ret) {
[85616c3]249                        return ret;
[5ebff60]250                }
[85616c3]251        }
[5ebff60]252
253        if (sscanf(id, "%d", &nr) == 1 && nr < 1000) {
254                for (a = bee->accounts; a; a = a->next) {
255                        if ((nr--) == 0) {
256                                return(a);
257                        }
258                }
259
260                return(NULL);
[b7d3cc34]261        }
[5ebff60]262
263        for (a = bee->accounts; a; a = a->next) {
264                if (g_strcasecmp(id, a->prpl->name) == 0) {
265                        if (!ret) {
[b7d3cc34]266                                ret = a;
[5ebff60]267                        } else {
268                                return(NULL);   /* We don't want to match more than one... */
269                        }
270                } else if (strstr(a->user, id)) {
271                        if (!ret) {
[b7d3cc34]272                                ret = a;
[5ebff60]273                        } else {
274                                return(NULL);
275                        }
[b7d3cc34]276                }
277        }
[5ebff60]278
279        return(ret);
[b7d3cc34]280}
281
[5ebff60]282account_t *account_by_tag(bee_t *bee, const char *tag)
[40e6dac]283{
284        account_t *a;
[5ebff60]285
286        for (a = bee->accounts; a; a = a->next) {
287                if (a->tag && g_strcasecmp(tag, a->tag) == 0) {
[40e6dac]288                        return a;
[5ebff60]289                }
290        }
291
[40e6dac]292        return NULL;
293}
294
[5ebff60]295void account_del(bee_t *bee, account_t *acc)
[b7d3cc34]296{
297        account_t *a, *l = NULL;
[5ebff60]298
299        if (acc->ic) {
[fa75134]300                /* Caller should have checked, accounts still in use can't be deleted. */
301                return;
[5ebff60]302        }
303
304        for (a = bee->accounts; a; a = (l = a)->next) {
305                if (a == acc) {
306                        if (l) {
[b7d3cc34]307                                l->next = a->next;
[5ebff60]308                        } else {
[81e04e1]309                                bee->accounts = a->next;
[5ebff60]310                        }
311
[81e04e1]312                        /** FIXME
313                        for( c = bee->chatrooms; c; c = nc )
[f86a3d5]314                        {
[5ebff60]315                                nc = c->next;
316                                if( acc == c->acc )
317                                        chat_del( bee, c );
[f86a3d5]318                        }
[81e04e1]319                        */
[5ebff60]320
321                        while (a->set) {
322                                set_del(&a->set, a->set->key);
323                        }
324
325                        g_hash_table_destroy(a->nicks);
326
327                        g_free(a->tag);
328                        g_free(a->user);
329                        g_free(a->pass);
330                        g_free(a->server);
331                        if (a->reconnect) {     /* This prevents any reconnect still queued to happen */
332                                cancel_auto_reconnect(a);
333                        }
334                        g_free(a);
335
[b7d3cc34]336                        break;
337                }
[5ebff60]338        }
[b7d3cc34]339}
340
[5ebff60]341static gboolean account_on_timeout(gpointer d, gint fd, b_input_condition cond);
[748bcdd]342
[5ebff60]343void account_on(bee_t *bee, account_t *a)
[b7d3cc34]344{
[5ebff60]345        if (a->ic) {
[b7d3cc34]346                /* Trying to enable an already-enabled account */
347                return;
348        }
[5ebff60]349
350        cancel_auto_reconnect(a);
351
[b7d3cc34]352        a->reconnect = 0;
[5ebff60]353        a->prpl->login(a);
354
355        if (a->ic && !(a->ic->flags & (OPT_SLOW_LOGIN | OPT_LOGGED_IN))) {
356                a->ic->keepalive = b_timeout_add(120000, account_on_timeout, a->ic);
357        }
[b7d3cc34]358}
359
[5ebff60]360void account_off(bee_t *bee, account_t *a)
[b7d3cc34]361{
[5ebff60]362        imc_logout(a->ic, FALSE);
[0da65d5]363        a->ic = NULL;
[5ebff60]364        if (a->reconnect) {
[b7d3cc34]365                /* Shouldn't happen */
[5ebff60]366                cancel_auto_reconnect(a);
[b7d3cc34]367        }
368}
[280e655]369
[5ebff60]370static gboolean account_on_timeout(gpointer d, gint fd, b_input_condition cond)
[748bcdd]371{
372        struct im_connection *ic = d;
[5ebff60]373
374        if (!(ic->flags & (OPT_SLOW_LOGIN | OPT_LOGGED_IN))) {
375                imcb_error(ic, "Connection timeout");
376                imc_logout(ic, TRUE);
[4cb21b7]377        }
[5ebff60]378
[748bcdd]379        return FALSE;
380}
381
[5ebff60]382struct account_reconnect_delay {
[280e655]383        int start;
384        char op;
385        int step;
[4230221]386        int max;
387};
388
[5ebff60]389int account_reconnect_delay_parse(char *value, struct account_reconnect_delay *p)
[4230221]390{
[5ebff60]391        memset(p, 0, sizeof(*p));
[4230221]392        /* A whole day seems like a sane "maximum maximum". */
393        p->max = 86400;
[5ebff60]394
[58adb7e]395        /* Format: /[0-9]+([*+][0-9]+(<[0-9+])?)?/ */
[5ebff60]396        while (*value && g_ascii_isdigit(*value)) {
[4230221]397                p->start = p->start * 10 + *value++ - '0';
[5ebff60]398        }
399
[4230221]400        /* Sure, call me evil for implementing my own fscanf here, but it's
[7125cb3]401           dead simple and I immediately know where to continue parsing. */
[5ebff60]402
403        if (*value == 0) {
[4230221]404                /* If the string ends now, the delay is constant. */
405                return 1;
[5ebff60]406        } else if (*value != '+' && *value != '*') {
[4230221]407                /* Otherwise allow either a + or a * */
408                return 0;
[5ebff60]409        }
410
[4230221]411        p->op = *value++;
[5ebff60]412
[4230221]413        /* + or * the delay by this number every time. */
[5ebff60]414        while (*value && g_ascii_isdigit(*value)) {
[4230221]415                p->step = p->step * 10 + *value++ - '0';
[5ebff60]416        }
417
418        if (*value == 0) {
[4230221]419                /* Use the default maximum (one day). */
420                return 1;
[5ebff60]421        } else if (*value != '<') {
[4230221]422                return 0;
[5ebff60]423        }
424
[4230221]425        p->max = 0;
[5ebff60]426        value++;
427        while (*value && g_ascii_isdigit(*value)) {
[4230221]428                p->max = p->max * 10 + *value++ - '0';
[5ebff60]429        }
430
[4230221]431        return p->max > 0;
432}
433
[5ebff60]434char *set_eval_account_reconnect_delay(set_t *set, char *value)
[4230221]435{
436        struct account_reconnect_delay p;
[5ebff60]437
438        return account_reconnect_delay_parse(value, &p) ? value : SET_INVALID;
[280e655]439}
440
[5ebff60]441int account_reconnect_delay(account_t *a)
[280e655]442{
[5ebff60]443        char *setting = set_getstr(&a->bee->set, "auto_reconnect_delay");
[4230221]444        struct account_reconnect_delay p;
[5ebff60]445
446        if (account_reconnect_delay_parse(setting, &p)) {
447                if (a->auto_reconnect_delay == 0) {
[4230221]448                        a->auto_reconnect_delay = p.start;
[5ebff60]449                } else if (p.op == '+') {
[4230221]450                        a->auto_reconnect_delay += p.step;
[5ebff60]451                } else if (p.op == '*') {
[4230221]452                        a->auto_reconnect_delay *= p.step;
[5ebff60]453                }
454
455                if (a->auto_reconnect_delay > p.max) {
[4230221]456                        a->auto_reconnect_delay = p.max;
[5ebff60]457                }
458        } else {
[4230221]459                a->auto_reconnect_delay = 0;
[280e655]460        }
[5ebff60]461
[4230221]462        return a->auto_reconnect_delay;
[280e655]463}
Note: See TracBrowser for help on using the repository browser.