source: protocols/account.c @ 7a80925

Last change on this file since 7a80925 was e31e5b8, checked in by Wilmer van der Gaast <wilmer@…>, at 2013-04-20T13:05:55Z

Merging "storage" branch which I wrote long ago. It separates generation of
XML-formatted user configs from disk I/O so we can try to start using other
mechanisms to store them (a REST API or something, for example).

  • Property mode set to 100644
File size: 10.6 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., 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 |= 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 |= SET_NOSAVE; /* Just for bw compatibility! */
64       
65        s = set_add( &a->set, "password", NULL, set_eval_account, a );
66        s->flags |= SET_NOSAVE | SET_NULL_OK | SET_PASSWORD;
67       
68        s = set_add( &a->set, "tag", NULL, set_eval_account, a );
69        s->flags |= SET_NOSAVE;
70       
71        s = set_add( &a->set, "username", NULL, set_eval_account, a );
72        s->flags |= 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                /* set -del should be allowed now, but I don't want to have any
157                   NULL pointers to have to deal with. */
158                if( !value )
159                        value = "";
160               
161                g_free( acc->pass );
162                acc->pass = g_strdup( value );
163                return NULL;    /* password shouldn't be visible in plaintext! */
164        }
165        else if( strcmp( set->key, "tag" ) == 0 )
166        {
167                account_t *oa;
168               
169                /* Enforce uniqueness. */
170                if( ( oa = account_by_tag( acc->bee, value ) ) && oa != acc )
171                        return SET_INVALID;
172               
173                g_free( acc->tag );
174                acc->tag = g_strdup( value );
175                return value;
176        }
177        else if( strcmp( set->key, "auto_connect" ) == 0 )
178        {
179                if( !is_bool( value ) )
180                        return SET_INVALID;
181               
182                acc->auto_connect = bool2int( value );
183                return value;
184        }
185        else if( strcmp( set->key, "away" ) == 0 ||
186                 strcmp( set->key, "status" ) == 0 )
187        {
188                if( acc->ic && acc->ic->flags & OPT_LOGGED_IN )
189                {
190                        /* If we're currently on-line, set the var now already
191                           (bit of a hack) and send an update. */
192                        g_free( set->value );
193                        set->value = g_strdup( value );
194                       
195                        imc_away_send_update( acc->ic );
196                }
197               
198                return value;
199        }
200       
201        return SET_INVALID;
202}
203
204/* For bw compatibility, have this write-only setting. */
205static char *set_eval_nick_source( set_t *set, char *value )
206{
207        account_t *a = set->data;
208       
209        if( strcmp( value, "full_name" ) == 0 )
210                set_setstr( &a->set, "nick_format", "%full_name" );
211        else if( strcmp( value, "first_name" ) == 0 )
212                set_setstr( &a->set, "nick_format", "%first_name" );
213        else
214                set_setstr( &a->set, "nick_format", "%-@nick" );
215       
216        return value;
217}
218
219account_t *account_get( bee_t *bee, const char *id )
220{
221        account_t *a, *ret = NULL;
222        char *handle, *s;
223        int nr;
224       
225        /* Tags get priority above anything else. */
226        if( ( a = account_by_tag( bee, id ) ) )
227                return a;
228       
229        /* This checks if the id string ends with (...) */
230        if( ( handle = strchr( id, '(' ) ) && ( s = strchr( handle, ')' ) ) && s[1] == 0 )
231        {
232                struct prpl *proto;
233               
234                *s = *handle = 0;
235                handle ++;
236               
237                if( ( proto = find_protocol( id ) ) )
238                {
239                        for( a = bee->accounts; a; a = a->next )
240                                if( a->prpl == proto &&
241                                    a->prpl->handle_cmp( handle, a->user ) == 0 )
242                                        ret = a;
243                }
244               
245                /* Restore the string. */
246                handle --;
247                *handle = '(';
248                *s = ')';
249               
250                if( ret )
251                        return ret;
252        }
253       
254        if( sscanf( id, "%d", &nr ) == 1 && nr < 1000 )
255        {
256                for( a = bee->accounts; a; a = a->next )
257                        if( ( nr-- ) == 0 )
258                                return( a );
259               
260                return( NULL );
261        }
262       
263        for( a = bee->accounts; a; a = a->next )
264        {
265                if( g_strcasecmp( id, a->prpl->name ) == 0 )
266                {
267                        if( !ret )
268                                ret = a;
269                        else
270                                return( NULL ); /* We don't want to match more than one... */
271                }
272                else if( strstr( a->user, id ) )
273                {
274                        if( !ret )
275                                ret = a;
276                        else
277                                return( NULL );
278                }
279        }
280       
281        return( ret );
282}
283
284account_t *account_by_tag( bee_t *bee, const char *tag )
285{
286        account_t *a;
287       
288        for( a = bee->accounts; a; a = a->next )
289                if( a->tag && g_strcasecmp( tag, a->tag ) == 0 )
290                        return a;
291       
292        return NULL;
293}
294
295void account_del( bee_t *bee, account_t *acc )
296{
297        account_t *a, *l = NULL;
298       
299        if( acc->ic )
300                /* Caller should have checked, accounts still in use can't be deleted. */
301                return;
302       
303        for( a = bee->accounts; a; a = (l=a)->next )
304                if( a == acc )
305                {
306                        if( l )
307                                l->next = a->next;
308                        else
309                                bee->accounts = a->next;
310                       
311                        /** FIXME
312                        for( c = bee->chatrooms; c; c = nc )
313                        {
314                                nc = c->next;
315                                if( acc == c->acc )
316                                        chat_del( bee, c );
317                        }
318                        */
319                       
320                        while( a->set )
321                                set_del( &a->set, a->set->key );
322                       
323                        g_hash_table_destroy( a->nicks );
324                       
325                        g_free( a->tag );
326                        g_free( a->user );
327                        g_free( a->pass );
328                        g_free( a->server );
329                        if( a->reconnect )      /* This prevents any reconnect still queued to happen */
330                                cancel_auto_reconnect( a );
331                        g_free( a );
332                       
333                        break;
334                }
335}
336
337static gboolean account_on_timeout( gpointer d, gint fd, b_input_condition cond );
338
339void account_on( bee_t *bee, account_t *a )
340{
341        if( a->ic )
342        {
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
356void account_off( bee_t *bee, account_t *a )
357{
358        imc_logout( a->ic, FALSE );
359        a->ic = NULL;
360        if( a->reconnect )
361        {
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        {
373                imcb_error( ic, "Connection timeout" );
374                imc_logout( ic, TRUE );
375        }
376       
377        return FALSE;
378}
379
380struct account_reconnect_delay
381{
382        int start;
383        char op;
384        int step;
385        int max;
386};
387
388int account_reconnect_delay_parse( char *value, struct account_reconnect_delay *p )
389{
390        memset( p, 0, sizeof( *p ) );
391        /* A whole day seems like a sane "maximum maximum". */
392        p->max = 86400;
393       
394        /* Format: /[0-9]+([*+][0-9]+(<[0-9+])?)?/ */
395        while( *value && isdigit( *value ) )
396                p->start = p->start * 10 + *value++ - '0';
397       
398        /* Sure, call me evil for implementing my own fscanf here, but it's
399           dead simple and I immediately know where to continue parsing. */
400       
401        if( *value == 0 )
402                /* If the string ends now, the delay is constant. */
403                return 1;
404        else if( *value != '+' && *value != '*' )
405                /* Otherwise allow either a + or a * */
406                return 0;
407       
408        p->op = *value++;
409       
410        /* + or * the delay by this number every time. */
411        while( *value && isdigit( *value ) )
412                p->step = p->step * 10 + *value++ - '0';
413       
414        if( *value == 0 )
415                /* Use the default maximum (one day). */
416                return 1;
417        else if( *value != '<' )
418                return 0;
419       
420        p->max = 0;
421        value ++;
422        while( *value && isdigit( *value ) )
423                p->max = p->max * 10 + *value++ - '0';
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        {
442                if( a->auto_reconnect_delay == 0 )
443                        a->auto_reconnect_delay = p.start;
444                else if( p.op == '+' )
445                        a->auto_reconnect_delay += p.step;
446                else if( p.op == '*' )
447                        a->auto_reconnect_delay *= p.step;
448               
449                if( a->auto_reconnect_delay > p.max )
450                        a->auto_reconnect_delay = p.max;
451        }
452        else
453        {
454                a->auto_reconnect_delay = 0;
455        }
456       
457        return a->auto_reconnect_delay;
458}
Note: See TracBrowser for help on using the repository browser.