source: protocols/account.c @ b7cd22d

Last change on this file since b7cd22d was 1783ab6, checked in by dequis <dx@…>, at 2014-07-24T03:51:07Z

Gadugadu local contact storage (ticket #917)

Patch originally by Michał Siejak, adapted for 3.2.1

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