source: protocols/account.c @ d832164

Last change on this file since d832164 was 93e0901, checked in by Wilmer van der Gaast <wilmer@…>, at 2015-05-24T18:51:57Z

ACC_FLAG_LOCAL -> ACC_FLAG_LOCAL_CONTACTS.

Also, handle the flag more properly. The config loader was not the right
place to do this in.

Next up: Add a way for a protocol module to fetch the contact list
pre-login.

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