source: protocols/account.c @ 6f10697

Last change on this file since 6f10697 was 6f10697, checked in by dequis <dx@…>, at 2015-01-16T19:50:23Z

Fix incorrect Free Software Foundation address

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