source: protocols/account.c @ 5447c59

Last change on this file since 5447c59 was 7a9d968, checked in by Wilmer van der Gaast <wilmer@…>, at 2018-03-10T11:30:39Z

Merge branch 'master' into HEAD

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