source: protocols/account.c @ c564e25

Last change on this file since c564e25 was 35987a1, checked in by Wilmer van der Gaast <wilmer@…>, at 2014-02-28T23:17:28Z

Allow use of "ac x set -del password" to use /oper to change the password
"securely". Patch from Flexo, bug #1117.

  • Property mode set to 100644
File size: 10.8 KB
RevLine 
[b7d3cc34]1  /********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
[0e788f5]4  * Copyright 2002-2013 Wilmer van der Gaast and others                *
[b7d3cc34]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
[06b39f2]30static char *set_eval_nick_source( set_t *set, char *value );
31
[81e04e1]32account_t *account_add( bee_t *bee, struct prpl *prpl, char *user, char *pass )
[b7d3cc34]33{
34        account_t *a;
[5100caa]35        set_t *s;
[40e6dac]36        char tag[strlen(prpl->name)+10];
[b7d3cc34]37       
[81e04e1]38        if( bee->accounts )
[b7d3cc34]39        {
[81e04e1]40                for( a = bee->accounts; a->next; a = a->next );
[a312b6b]41                a = a->next = g_new0( account_t, 1 );
[b7d3cc34]42        }
43        else
44        {
[81e04e1]45                bee->accounts = a = g_new0 ( account_t, 1 );
[b7d3cc34]46        }
47       
[7b23afd]48        a->prpl = prpl;
[b7d3cc34]49        a->user = g_strdup( user );
50        a->pass = g_strdup( pass );
[2b14eef]51        a->auto_connect = 1;
[81e04e1]52        a->bee = bee;
[b7d3cc34]53       
[96863f6]54        s = set_add( &a->set, "auto_connect", "true", set_eval_account, a );
[bb5ce568]55        s->flags |= SET_NOSAVE;
[5100caa]56       
[04026d4]57        s = set_add( &a->set, "auto_reconnect", "true", set_eval_bool, a );
[00a5270]58       
[2e0eaac]59        s = set_add( &a->set, "nick_format", NULL, NULL, a );
[badd148]60        s->flags |= SET_NULL_OK;
[2e0eaac]61       
[06b39f2]62        s = set_add( &a->set, "nick_source", "handle", set_eval_nick_source, a );
[bb5ce568]63        s->flags |= SET_NOSAVE; /* Just for bw compatibility! */
[286b28e]64       
[5100caa]65        s = set_add( &a->set, "password", NULL, set_eval_account, a );
[bb5ce568]66        s->flags |= SET_NOSAVE | SET_NULL_OK | SET_PASSWORD;
[5100caa]67       
[40e6dac]68        s = set_add( &a->set, "tag", NULL, set_eval_account, a );
[bb5ce568]69        s->flags |= SET_NOSAVE;
[40e6dac]70       
[5100caa]71        s = set_add( &a->set, "username", NULL, set_eval_account, a );
[bb5ce568]72        s->flags |= SET_NOSAVE | ACC_SET_OFFLINE_ONLY;
[5100caa]73        set_setstr( &a->set, "username", user );
74       
[3b3c50d9]75        /* Hardcode some more clever tag guesses. */
76        strcpy( tag, prpl->name );
77        if( strcmp( prpl->name, "oscar" ) == 0 )
[40e6dac]78        {
[3b3c50d9]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 );
[40e6dac]96                int i;
97
98                for( i = 2; i < 10000; i ++ )
99                {
[3b3c50d9]100                        sprintf( numpos, "%d", i );
[40e6dac]101                        if( !account_by_tag( bee, tag ) )
102                                break;
103                }
104        }
105        set_setstr( &a->set, "tag", tag );
106       
[5b52a48]107        a->nicks = g_hash_table_new_full( g_str_hash, g_str_equal, g_free, g_free );
108       
[96863f6]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. */
[0da65d5]111        if( prpl->init )
112                prpl->init( a );
[96863f6]113       
[58adb7e]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;
[b7d3cc34]124}
125
[5100caa]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. */
[0da65d5]131        if( set->flags & ACC_SET_OFFLINE_ONLY && acc->ic )
[7125cb3]132                return SET_INVALID;
[5100caa]133       
[3b32017]134        if( strcmp( set->key, "server" ) == 0 )
[5100caa]135        {
136                g_free( acc->server );
[3b32017]137                if( value && *value )
[30ce1ce]138                {
[5100caa]139                        acc->server = g_strdup( value );
[30ce1ce]140                        return value;
141                }
[5100caa]142                else
[30ce1ce]143                {
[7125cb3]144                        acc->server = g_strdup( set->def );
[30ce1ce]145                        return g_strdup( set->def );
146                }
[5100caa]147        }
[3b32017]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        {
[35987a1]156                /* set -del allows /oper to be used to change the password or,
157                   iff oauth is enabled, reset the oauth credential magic.
158                */
159                if( !value ) {
160                        if ( set_getbool( &(acc->set), "oauth" ) ) {
161                                value = "";
162                        } else {
163                                value = PASSWORD_PENDING;
164                                ((irc_t *)acc->bee->ui_data)->status |= OPER_HACK_ACCOUNT_PASSWORD;
165                                irc_rootmsg((irc_t *)acc->bee->ui_data, "You may now use /OPER to set the password");
166                        }
167                }
[6ee51a9]168               
169                g_free( acc->pass );
170                acc->pass = g_strdup( value );
171                return NULL;    /* password shouldn't be visible in plaintext! */
[3b32017]172        }
[40e6dac]173        else if( strcmp( set->key, "tag" ) == 0 )
174        {
175                account_t *oa;
176               
177                /* Enforce uniqueness. */
178                if( ( oa = account_by_tag( acc->bee, value ) ) && oa != acc )
179                        return SET_INVALID;
180               
181                g_free( acc->tag );
182                acc->tag = g_strdup( value );
183                return value;
184        }
[5100caa]185        else if( strcmp( set->key, "auto_connect" ) == 0 )
186        {
187                if( !is_bool( value ) )
[7125cb3]188                        return SET_INVALID;
[5100caa]189               
190                acc->auto_connect = bool2int( value );
191                return value;
192        }
[58adb7e]193        else if( strcmp( set->key, "away" ) == 0 ||
194                 strcmp( set->key, "status" ) == 0 )
195        {
196                if( acc->ic && acc->ic->flags & OPT_LOGGED_IN )
197                {
198                        /* If we're currently on-line, set the var now already
199                           (bit of a hack) and send an update. */
200                        g_free( set->value );
201                        set->value = g_strdup( value );
202                       
203                        imc_away_send_update( acc->ic );
204                }
205               
206                return value;
207        }
[5100caa]208       
[7125cb3]209        return SET_INVALID;
[5100caa]210}
211
[06b39f2]212/* For bw compatibility, have this write-only setting. */
213static char *set_eval_nick_source( set_t *set, char *value )
214{
215        account_t *a = set->data;
216       
217        if( strcmp( value, "full_name" ) == 0 )
218                set_setstr( &a->set, "nick_format", "%full_name" );
219        else if( strcmp( value, "first_name" ) == 0 )
220                set_setstr( &a->set, "nick_format", "%first_name" );
221        else
222                set_setstr( &a->set, "nick_format", "%-@nick" );
223       
224        return value;
225}
226
[40e6dac]227account_t *account_get( bee_t *bee, const char *id )
[b7d3cc34]228{
229        account_t *a, *ret = NULL;
[85616c3]230        char *handle, *s;
[b7d3cc34]231        int nr;
232       
[40e6dac]233        /* Tags get priority above anything else. */
234        if( ( a = account_by_tag( bee, id ) ) )
235                return a;
236       
[85616c3]237        /* This checks if the id string ends with (...) */
238        if( ( handle = strchr( id, '(' ) ) && ( s = strchr( handle, ')' ) ) && s[1] == 0 )
239        {
240                struct prpl *proto;
241               
242                *s = *handle = 0;
243                handle ++;
244               
245                if( ( proto = find_protocol( id ) ) )
246                {
[81e04e1]247                        for( a = bee->accounts; a; a = a->next )
[85616c3]248                                if( a->prpl == proto &&
[5b52a48]249                                    a->prpl->handle_cmp( handle, a->user ) == 0 )
[85616c3]250                                        ret = a;
251                }
252               
253                /* Restore the string. */
254                handle --;
255                *handle = '(';
256                *s = ')';
257               
258                if( ret )
259                        return ret;
260        }
261       
[b7d3cc34]262        if( sscanf( id, "%d", &nr ) == 1 && nr < 1000 )
263        {
[81e04e1]264                for( a = bee->accounts; a; a = a->next )
[b7d3cc34]265                        if( ( nr-- ) == 0 )
266                                return( a );
267               
268                return( NULL );
269        }
270       
[81e04e1]271        for( a = bee->accounts; a; a = a->next )
[b7d3cc34]272        {
[7b23afd]273                if( g_strcasecmp( id, a->prpl->name ) == 0 )
[b7d3cc34]274                {
275                        if( !ret )
276                                ret = a;
277                        else
278                                return( NULL ); /* We don't want to match more than one... */
279                }
280                else if( strstr( a->user, id ) )
281                {
282                        if( !ret )
283                                ret = a;
284                        else
285                                return( NULL );
286                }
287        }
288       
289        return( ret );
290}
291
[40e6dac]292account_t *account_by_tag( bee_t *bee, const char *tag )
293{
294        account_t *a;
295       
296        for( a = bee->accounts; a; a = a->next )
297                if( a->tag && g_strcasecmp( tag, a->tag ) == 0 )
298                        return a;
299       
300        return NULL;
301}
302
[81e04e1]303void account_del( bee_t *bee, account_t *acc )
[b7d3cc34]304{
305        account_t *a, *l = NULL;
306       
[fa75134]307        if( acc->ic )
308                /* Caller should have checked, accounts still in use can't be deleted. */
309                return;
310       
[81e04e1]311        for( a = bee->accounts; a; a = (l=a)->next )
[b7d3cc34]312                if( a == acc )
313                {
314                        if( l )
315                                l->next = a->next;
316                        else
[81e04e1]317                                bee->accounts = a->next;
[b7d3cc34]318                       
[81e04e1]319                        /** FIXME
320                        for( c = bee->chatrooms; c; c = nc )
[f86a3d5]321                        {
322                                nc = c->next;
[d995c9b]323                                if( acc == c->acc )
[81e04e1]324                                        chat_del( bee, c );
[f86a3d5]325                        }
[81e04e1]326                        */
[d995c9b]327                       
[5100caa]328                        while( a->set )
329                                set_del( &a->set, a->set->key );
330                       
[5b52a48]331                        g_hash_table_destroy( a->nicks );
332                       
[40e6dac]333                        g_free( a->tag );
[b7d3cc34]334                        g_free( a->user );
335                        g_free( a->pass );
[fa75134]336                        g_free( a->server );
[b7d3cc34]337                        if( a->reconnect )      /* This prevents any reconnect still queued to happen */
338                                cancel_auto_reconnect( a );
339                        g_free( a );
340                       
341                        break;
342                }
343}
344
[748bcdd]345static gboolean account_on_timeout( gpointer d, gint fd, b_input_condition cond );
346
[81e04e1]347void account_on( bee_t *bee, account_t *a )
[b7d3cc34]348{
[0da65d5]349        if( a->ic )
[b7d3cc34]350        {
351                /* Trying to enable an already-enabled account */
352                return;
353        }
354       
355        cancel_auto_reconnect( a );
356       
357        a->reconnect = 0;
[0a3c243]358        a->prpl->login( a );
[748bcdd]359       
[4cb21b7]360        if( a->ic && !( a->ic->flags & ( OPT_SLOW_LOGIN | OPT_LOGGED_IN ) ) )
[748bcdd]361                a->ic->keepalive = b_timeout_add( 120000, account_on_timeout, a->ic );
[b7d3cc34]362}
363
[81e04e1]364void account_off( bee_t *bee, account_t *a )
[b7d3cc34]365{
[b0eaa5b]366        imc_logout( a->ic, FALSE );
[0da65d5]367        a->ic = NULL;
[b7d3cc34]368        if( a->reconnect )
369        {
370                /* Shouldn't happen */
371                cancel_auto_reconnect( a );
372        }
373}
[280e655]374
[748bcdd]375static gboolean account_on_timeout( gpointer d, gint fd, b_input_condition cond )
376{
377        struct im_connection *ic = d;
378       
[4cb21b7]379        if( !( ic->flags & ( OPT_SLOW_LOGIN | OPT_LOGGED_IN ) ) )
380        {
381                imcb_error( ic, "Connection timeout" );
382                imc_logout( ic, TRUE );
383        }
[748bcdd]384       
385        return FALSE;
386}
387
[4230221]388struct account_reconnect_delay
[280e655]389{
390        int start;
391        char op;
392        int step;
[4230221]393        int max;
394};
395
396int account_reconnect_delay_parse( char *value, struct account_reconnect_delay *p )
397{
398        memset( p, 0, sizeof( *p ) );
399        /* A whole day seems like a sane "maximum maximum". */
400        p->max = 86400;
[280e655]401       
[58adb7e]402        /* Format: /[0-9]+([*+][0-9]+(<[0-9+])?)?/ */
[4230221]403        while( *value && isdigit( *value ) )
404                p->start = p->start * 10 + *value++ - '0';
405       
406        /* Sure, call me evil for implementing my own fscanf here, but it's
[7125cb3]407           dead simple and I immediately know where to continue parsing. */
[4230221]408       
409        if( *value == 0 )
410                /* If the string ends now, the delay is constant. */
411                return 1;
412        else if( *value != '+' && *value != '*' )
413                /* Otherwise allow either a + or a * */
414                return 0;
415       
416        p->op = *value++;
417       
418        /* + or * the delay by this number every time. */
419        while( *value && isdigit( *value ) )
420                p->step = p->step * 10 + *value++ - '0';
421       
422        if( *value == 0 )
423                /* Use the default maximum (one day). */
424                return 1;
425        else if( *value != '<' )
426                return 0;
427       
428        p->max = 0;
429        value ++;
430        while( *value && isdigit( *value ) )
431                p->max = p->max * 10 + *value++ - '0';
432       
433        return p->max > 0;
434}
435
436char *set_eval_account_reconnect_delay( set_t *set, char *value )
437{
438        struct account_reconnect_delay p;
439       
[7125cb3]440        return account_reconnect_delay_parse( value, &p ) ? value : SET_INVALID;
[280e655]441}
442
443int account_reconnect_delay( account_t *a )
444{
[81e04e1]445        char *setting = set_getstr( &a->bee->set, "auto_reconnect_delay" );
[4230221]446        struct account_reconnect_delay p;
[280e655]447       
[4230221]448        if( account_reconnect_delay_parse( setting, &p ) )
[280e655]449        {
450                if( a->auto_reconnect_delay == 0 )
[4230221]451                        a->auto_reconnect_delay = p.start;
452                else if( p.op == '+' )
453                        a->auto_reconnect_delay += p.step;
454                else if( p.op == '*' )
455                        a->auto_reconnect_delay *= p.step;
456               
457                if( a->auto_reconnect_delay > p.max )
458                        a->auto_reconnect_delay = p.max;
[280e655]459        }
[4230221]460        else
[280e655]461        {
[4230221]462                a->auto_reconnect_delay = 0;
[280e655]463        }
464       
[4230221]465        return a->auto_reconnect_delay;
[280e655]466}
Note: See TracBrowser for help on using the repository browser.