source: protocols/account.c @ 75ad761

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