source: protocols/account.c @ 495d21b

Last change on this file since 495d21b was 748bcdd, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-11-21T15:23:54Z

Time out if logging in takes too long (2m for now). Except for Twitter
OAuth login, which requires user action. This mostly solves problems with
OSCAR login silently failing, but may also be useful in other places.

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