source: account.c @ 842cd8d

Last change on this file since 842cd8d was 58adb7e, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-03-07T00:22:33Z

Added global and per-account settings "away" and "status" so the user can
set these things individually.

  • Property mode set to 100644
File size: 8.1 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#include "chat.h"
30
31account_t *account_add( irc_t *irc, struct prpl *prpl, char *user, char *pass )
32{
33        account_t *a;
34        set_t *s;
35       
36        if( irc->accounts )
37        {
38                for( a = irc->accounts; a->next; a = a->next );
39                a = a->next = g_new0( account_t, 1 );
40        }
41        else
42        {
43                irc->accounts = a = g_new0 ( account_t, 1 );
44        }
45       
46        a->prpl = prpl;
47        a->user = g_strdup( user );
48        a->pass = g_strdup( pass );
49        a->auto_connect = 1;
50        a->irc = irc;
51       
52        s = set_add( &a->set, "auto_connect", "true", set_eval_account, a );
53        s->flags |= ACC_SET_NOSAVE;
54       
55        s = set_add( &a->set, "auto_reconnect", "true", set_eval_bool, a );
56       
57        s = set_add( &a->set, "password", NULL, set_eval_account, a );
58        s->flags |= ACC_SET_NOSAVE | SET_NULL_OK;
59       
60        s = set_add( &a->set, "username", NULL, set_eval_account, a );
61        s->flags |= ACC_SET_NOSAVE | ACC_SET_OFFLINE_ONLY;
62        set_setstr( &a->set, "username", user );
63       
64        a->nicks = g_hash_table_new_full( g_str_hash, g_str_equal, g_free, g_free );
65       
66        /* This function adds some more settings (and might want to do more
67           things that have to be done now, although I can't think of anything. */
68        if( prpl->init )
69                prpl->init( a );
70       
71        s = set_add( &a->set, "away", NULL, set_eval_account, a );
72        s->flags |= SET_NULL_OK;
73       
74        if( a->flags & ACC_FLAG_STATUS_MESSAGE )
75        {
76                s = set_add( &a->set, "status", NULL, set_eval_account, a );
77                s->flags |= SET_NULL_OK;
78        }
79       
80        return a;
81}
82
83char *set_eval_account( set_t *set, char *value )
84{
85        account_t *acc = set->data;
86       
87        /* Double-check: We refuse to edit on-line accounts. */
88        if( set->flags & ACC_SET_OFFLINE_ONLY && acc->ic )
89                return SET_INVALID;
90       
91        if( strcmp( set->key, "server" ) == 0 )
92        {
93                g_free( acc->server );
94                if( value && *value )
95                {
96                        acc->server = g_strdup( value );
97                        return value;
98                }
99                else
100                {
101                        acc->server = g_strdup( set->def );
102                        return g_strdup( set->def );
103                }
104        }
105        else if( strcmp( set->key, "username" ) == 0 )
106        {
107                g_free( acc->user );
108                acc->user = g_strdup( value );
109                return value;
110        }
111        else if( strcmp( set->key, "password" ) == 0 )
112        {
113                if( value )
114                {
115                        g_free( acc->pass );
116                        acc->pass = g_strdup( value );
117                        return NULL;    /* password shouldn't be visible in plaintext! */
118                }
119                else
120                {
121                        /* NULL can (should) be stored in the set_t
122                           variable, but is otherwise not correct. */
123                        return SET_INVALID;
124                }
125        }
126        else if( strcmp( set->key, "auto_connect" ) == 0 )
127        {
128                if( !is_bool( value ) )
129                        return SET_INVALID;
130               
131                acc->auto_connect = bool2int( value );
132                return value;
133        }
134        else if( strcmp( set->key, "away" ) == 0 ||
135                 strcmp( set->key, "status" ) == 0 )
136        {
137                if( acc->ic && acc->ic->flags & OPT_LOGGED_IN )
138                {
139                        /* If we're currently on-line, set the var now already
140                           (bit of a hack) and send an update. */
141                        g_free( set->value );
142                        set->value = g_strdup( value );
143                       
144                        imc_away_send_update( acc->ic );
145                }
146               
147                return value;
148        }
149       
150        return SET_INVALID;
151}
152
153account_t *account_get( irc_t *irc, char *id )
154{
155        account_t *a, *ret = NULL;
156        char *handle, *s;
157        int nr;
158       
159        /* This checks if the id string ends with (...) */
160        if( ( handle = strchr( id, '(' ) ) && ( s = strchr( handle, ')' ) ) && s[1] == 0 )
161        {
162                struct prpl *proto;
163               
164                *s = *handle = 0;
165                handle ++;
166               
167                if( ( proto = find_protocol( id ) ) )
168                {
169                        for( a = irc->accounts; a; a = a->next )
170                                if( a->prpl == proto &&
171                                    a->prpl->handle_cmp( handle, a->user ) == 0 )
172                                        ret = a;
173                }
174               
175                /* Restore the string. */
176                handle --;
177                *handle = '(';
178                *s = ')';
179               
180                if( ret )
181                        return ret;
182        }
183       
184        if( sscanf( id, "%d", &nr ) == 1 && nr < 1000 )
185        {
186                for( a = irc->accounts; a; a = a->next )
187                        if( ( nr-- ) == 0 )
188                                return( a );
189               
190                return( NULL );
191        }
192       
193        for( a = irc->accounts; a; a = a->next )
194        {
195                if( g_strcasecmp( id, a->prpl->name ) == 0 )
196                {
197                        if( !ret )
198                                ret = a;
199                        else
200                                return( NULL ); /* We don't want to match more than one... */
201                }
202                else if( strstr( a->user, id ) )
203                {
204                        if( !ret )
205                                ret = a;
206                        else
207                                return( NULL );
208                }
209        }
210       
211        return( ret );
212}
213
214void account_del( irc_t *irc, account_t *acc )
215{
216        account_t *a, *l = NULL;
217        struct chat *c, *nc;
218       
219        if( acc->ic )
220                /* Caller should have checked, accounts still in use can't be deleted. */
221                return;
222       
223        for( a = irc->accounts; a; a = (l=a)->next )
224                if( a == acc )
225                {
226                        if( l )
227                                l->next = a->next;
228                        else
229                                irc->accounts = a->next;
230                       
231                        for( c = irc->chatrooms; c; c = nc )
232                        {
233                                nc = c->next;
234                                if( acc == c->acc )
235                                        chat_del( irc, c );
236                        }
237                       
238                        while( a->set )
239                                set_del( &a->set, a->set->key );
240                       
241                        g_hash_table_destroy( a->nicks );
242                       
243                        g_free( a->user );
244                        g_free( a->pass );
245                        g_free( a->server );
246                        if( a->reconnect )      /* This prevents any reconnect still queued to happen */
247                                cancel_auto_reconnect( a );
248                        g_free( a );
249                       
250                        break;
251                }
252}
253
254void account_on( irc_t *irc, account_t *a )
255{
256        if( a->ic )
257        {
258                /* Trying to enable an already-enabled account */
259                return;
260        }
261       
262        cancel_auto_reconnect( a );
263       
264        a->reconnect = 0;
265        a->prpl->login( a );
266}
267
268void account_off( irc_t *irc, account_t *a )
269{
270        imc_logout( a->ic, FALSE );
271        a->ic = NULL;
272        if( a->reconnect )
273        {
274                /* Shouldn't happen */
275                cancel_auto_reconnect( a );
276        }
277}
278
279struct account_reconnect_delay
280{
281        int start;
282        char op;
283        int step;
284        int max;
285};
286
287int account_reconnect_delay_parse( char *value, struct account_reconnect_delay *p )
288{
289        memset( p, 0, sizeof( *p ) );
290        /* A whole day seems like a sane "maximum maximum". */
291        p->max = 86400;
292       
293        /* Format: /[0-9]+([*+][0-9]+(<[0-9+])?)?/ */
294        while( *value && isdigit( *value ) )
295                p->start = p->start * 10 + *value++ - '0';
296       
297        /* Sure, call me evil for implementing my own fscanf here, but it's
298           dead simple and I immediately know where to continue parsing. */
299       
300        if( *value == 0 )
301                /* If the string ends now, the delay is constant. */
302                return 1;
303        else if( *value != '+' && *value != '*' )
304                /* Otherwise allow either a + or a * */
305                return 0;
306       
307        p->op = *value++;
308       
309        /* + or * the delay by this number every time. */
310        while( *value && isdigit( *value ) )
311                p->step = p->step * 10 + *value++ - '0';
312       
313        if( *value == 0 )
314                /* Use the default maximum (one day). */
315                return 1;
316        else if( *value != '<' )
317                return 0;
318       
319        p->max = 0;
320        value ++;
321        while( *value && isdigit( *value ) )
322                p->max = p->max * 10 + *value++ - '0';
323       
324        return p->max > 0;
325}
326
327char *set_eval_account_reconnect_delay( set_t *set, char *value )
328{
329        struct account_reconnect_delay p;
330       
331        return account_reconnect_delay_parse( value, &p ) ? value : SET_INVALID;
332}
333
334int account_reconnect_delay( account_t *a )
335{
336        char *setting = set_getstr( &a->irc->set, "auto_reconnect_delay" );
337        struct account_reconnect_delay p;
338       
339        if( account_reconnect_delay_parse( setting, &p ) )
340        {
341                if( a->auto_reconnect_delay == 0 )
342                        a->auto_reconnect_delay = p.start;
343                else if( p.op == '+' )
344                        a->auto_reconnect_delay += p.step;
345                else if( p.op == '*' )
346                        a->auto_reconnect_delay *= p.step;
347               
348                if( a->auto_reconnect_delay > p.max )
349                        a->auto_reconnect_delay = p.max;
350        }
351        else
352        {
353                a->auto_reconnect_delay = 0;
354        }
355       
356        return a->auto_reconnect_delay;
357}
Note: See TracBrowser for help on using the repository browser.