source: protocols/account.c @ 2363cca

Last change on this file since 2363cca was 0e788f5, checked in by Wilmer van der Gaast <wilmer@…>, at 2013-02-21T19:15:59Z

I'm still bored on a long flight. Wrote a script to automatically update
my copyright mentions since some were getting pretty stale. Left files not
touched since before 2012 alone so that this change doesn't touch almost
EVERY source file.

  • 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 |= 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 | SET_PASSWORD;
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                /* 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.