source: protocols/account.c @ 40e6dac

Last change on this file since 40e6dac was 40e6dac, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-07-24T15:46:59Z

Adding account tags as a way to 100% uniquely identify an account.
protocol(screenname) doesn't do this and is a little bit long. These will
be used for nick_format and XML storage.

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