source: protocols/account.c @ 0d8a9bb0

Last change on this file since 0d8a9bb0 was 5ebff60, checked in by dequis <dx@…>, at 2015-02-20T22:50:54Z

Reindent everything to K&R style with tabs

Used uncrustify, with the configuration file in ./doc/uncrustify.cfg

Commit author set to "Indent <please@…>" so that it's easier to
skip while doing git blame.

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