source: storage_xml.c @ a312b6b

Last change on this file since a312b6b was a312b6b, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-06-07T11:25:46Z

Added storage_xml.c

  • 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-2006 Wilmer van der Gaast and others                *
5  \********************************************************************/
6
7/* Storage backend that uses an XMLish format for all data. */
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
29struct xml_parsedata
30{
31        irc_t *irc;
32        char *current_setting;
33        account_t *current_account;
34};
35
36static char *xml_attr( const gchar **attr_names, const gchar **attr_values, const gchar *key )
37{
38        int i;
39       
40        for( i = 0; attr_names[i]; i ++ )
41                if( g_strcasecmp( attr_names[i], key ) == 0 )
42                        return attr_values[i];
43       
44        return NULL;
45}
46
47static void xml_start_element( GMarkupParseContext *ctx, const gchar *element_name, const gchar **attr_names, const gchar **attr_values, gpointer data, GError **error )
48{
49        struct xml_parsedata *xd = data;
50        irc_t *irc = data->irc;
51       
52        if( g_strcasecmp( element_name, "user" ) == 0 )
53        {
54                char *nick = xml_attr( attr_names, attr_values, "nick" );
55               
56                if( nick && g_strcasecmp( nick, irc->nick ) == 0 )
57                {
58                        /* Okay! */
59                }
60        }
61        else if( g_strcasecmp( element_name, "account" ) == 0 )
62        {
63                char *protocol, *handle, *password;
64                struct prpl *prpl = NULL;
65               
66                handle = xml_attr( attr_names, attr_values, "handle" );
67                password = xml_attr( attr_names, attr_values, "password" );
68               
69                protocol = xml_attr( attr_names, attr_values, "protocol" );
70                if( protocol )
71                        prpl = find_protocol( protocol );
72               
73                if( handle && password && prpl )
74                {
75                        xd->current_account = account_add( irc, prpl, handle, password )
76                }
77        }
78        else if( g_strcasecmp( element_name, "setting" ) == 0 )
79        {
80                if( xd->current_account == NULL )
81                {
82                        current_setting = xml_attr( attr_names, attr_values, "name" );
83                }
84        }
85        else if( g_strcasecmp( element_name, "buddy" ) == 0 )
86        {
87        }
88        else if( g_strcasecmp( element_name, "password" ) == 0 )
89        {
90        }
91        else
92        {
93                /* Return "unknown element" error. */
94        }
95}
96
97static void xml_end_element( GMarkupParseContext *ctx, const gchar *element_name, gpointer data, GError **error )
98{
99}
100
101static void xml_text( GMarkupParseContext *ctx, const gchar *text, gsize text_len, gpointer data, GError **error )
102{
103        struct xml_parsedata *xd = data;
104        irc_t *irc = data->irc;
105       
106        if( xd->current_setting )
107        {
108                set_setstr( irc, xd->current_setting, text );
109        }
110}
111
112static void xml_error( GMarkupParseContext *ctx, GError *error, gpointer data )
113{
114}
115
116GMarkupParser xml_parser =
117{
118        xml_start_element,
119        xml_end_element,
120        xml_text,
121        NULL,
122        xml_error
123};
124
125static void xml_init( void )
126{
127        if( access( global.conf->configdir, F_OK ) != 0 )
128                log_message( LOGLVL_WARNING, "The configuration directory %s does not exist. Configuration won't be saved.", CONFIG );
129        else if( access( global.conf->configdir, R_OK ) != 0 || access( global.conf->configdir, W_OK ) != 0 )
130                log_message( LOGLVL_WARNING, "Permission problem: Can't read/write from/to %s.", global.conf->configdir );
131}
132
133static storage_status_t xml_load ( const char *my_nick, const char* password, irc_t *irc )
134{
135        GMarkupParseContext *ctx;
136       
137        ctx = g_markup_parse_context_new( parser, 0, xd, NULL );
138        if( irc->status >= USTATUS_IDENTIFIED )
139                return( 1 );
140       
141        g_snprintf( s, 511, "%s%s%s", global.conf->configdir, my_nick, ".accounts" );
142        fp = fopen( s, "r" );
143        if( !fp ) return STORAGE_NO_SUCH_USER;
144       
145        fscanf( fp, "%32[^\n]s", s );
146
147        if (checkpass (password, s) != 0) 
148        {
149                fclose( fp );
150                return STORAGE_INVALID_PASSWORD;
151        }
152       
153        /* Do this now. If the user runs with AuthMode = Registered, the
154           account command will not work otherwise. */
155        irc->status = USTATUS_IDENTIFIED;
156       
157        while( fscanf( fp, "%511[^\n]s", s ) > 0 )
158        {
159                fgetc( fp );
160                line = deobfucrypt( s, password );
161                if (line == NULL) return STORAGE_OTHER_ERROR;
162                root_command_string( irc, ru, line, 0 );
163                g_free( line );
164        }
165        fclose( fp );
166       
167        g_snprintf( s, 511, "%s%s%s", global.conf->configdir, my_nick, ".nicks" );
168        fp = fopen( s, "r" );
169        if( !fp ) return STORAGE_NO_SUCH_USER;
170        while( fscanf( fp, "%s %d %s", s, &proto, nick ) > 0 )
171        {
172                struct prpl *prpl;
173
174                prpl = find_protocol_by_id(proto);
175
176                if (!prpl)
177                        continue;
178
179                http_decode( s );
180                nick_set( irc, s, prpl, nick );
181        }
182        fclose( fp );
183       
184        if( set_getint( irc, "auto_connect" ) )
185        {
186                strcpy( s, "account on" );      /* Can't do this directly because r_c_s alters the string */
187                root_command_string( irc, ru, s, 0 );
188        }
189       
190        return STORAGE_OK;
191}
192
193static storage_status_t text_save( irc_t *irc, int overwrite )
194{
195        char s[512];
196        char path[512], new_path[512];
197        char *line;
198        nick_t *n;
199        set_t *set;
200        mode_t ou = umask( 0077 );
201        account_t *a;
202        FILE *fp;
203        char *hash;
204
205        if (!overwrite) {
206                g_snprintf( path, 511, "%s%s%s", global.conf->configdir, irc->nick, ".accounts" );
207                if (access( path, F_OK ) != -1)
208                        return STORAGE_ALREADY_EXISTS;
209       
210                g_snprintf( path, 511, "%s%s%s", global.conf->configdir, irc->nick, ".nicks" );
211                if (access( path, F_OK ) != -1)
212                        return STORAGE_ALREADY_EXISTS;
213        }
214       
215        /*\
216         *  [SH] Nothing should be saved if no password is set, because the
217         *  password is not set if it was wrong, or if one is not identified
218         *  yet. This means that a malicious user could easily overwrite
219         *  files owned by someone else:
220         *  a Bad Thing, methinks
221        \*/
222
223        /* [WVG] No? Really? */
224
225        /*\
226         *  [SH] Okay, okay, it wasn't really Wilmer who said that, it was
227         *  me. I just thought it was funny.
228        \*/
229       
230        hash = hashpass( irc->password );
231        if( hash == NULL )
232        {
233                irc_usermsg( irc, "Please register yourself if you want to save your settings." );
234                return STORAGE_OTHER_ERROR;
235        }
236       
237        g_snprintf( path, 511, "%s%s%s", global.conf->configdir, irc->nick, ".nicks~" );
238        fp = fopen( path, "w" );
239        if( !fp ) return STORAGE_OTHER_ERROR;
240        for( n = irc->nicks; n; n = n->next )
241        {
242                strcpy( s, n->handle );
243                s[169] = 0; /* Prevent any overflow (169 ~ 512 / 3) */
244                http_encode( s );
245                g_snprintf( s + strlen( s ), 510 - strlen( s ), " %d %s", find_protocol_id(n->proto->name), n->nick );
246                if( fprintf( fp, "%s\n", s ) != strlen( s ) + 1 )
247                {
248                        irc_usermsg( irc, "fprintf() wrote too little. Disk full?" );
249                        fclose( fp );
250                        return STORAGE_OTHER_ERROR;
251                }
252        }
253        if( fclose( fp ) != 0 )
254        {
255                irc_usermsg( irc, "fclose() reported an error. Disk full?" );
256                return STORAGE_OTHER_ERROR;
257        }
258 
259        g_snprintf( new_path, 512, "%s%s%s", global.conf->configdir, irc->nick, ".nicks" );
260        if( unlink( new_path ) != 0 )
261        {
262                if( errno != ENOENT )
263                {
264                        irc_usermsg( irc, "Error while removing old .nicks file" );
265                        return STORAGE_OTHER_ERROR;
266                }
267        }
268        if( rename( path, new_path ) != 0 )
269        {
270                irc_usermsg( irc, "Error while renaming new .nicks file" );
271                return STORAGE_OTHER_ERROR;
272        }
273       
274        g_snprintf( path, 511, "%s%s%s", global.conf->configdir, irc->nick, ".accounts~" );
275        fp = fopen( path, "w" );
276        if( !fp ) return STORAGE_OTHER_ERROR;
277        if( fprintf( fp, "%s", hash ) != strlen( hash ) )
278        {
279                irc_usermsg( irc, "fprintf() wrote too little. Disk full?" );
280                fclose( fp );
281                return STORAGE_OTHER_ERROR;
282        }
283        g_free( hash );
284
285        for( a = irc->accounts; a; a = a->next )
286        {
287                if( !strcmp(a->prpl->name, "oscar") )
288                        g_snprintf( s, sizeof( s ), "account add oscar \"%s\" \"%s\" %s", a->user, a->pass, a->server );
289                else
290                        g_snprintf( s, sizeof( s ), "account add %s \"%s\" \"%s\" \"%s\"",
291                                    a->prpl->name, a->user, a->pass, a->server ? a->server : "" );
292               
293                line = obfucrypt( s, irc->password );
294                if( *line )
295                {
296                        if( fprintf( fp, "%s\n", line ) != strlen( line ) + 1 )
297                        {
298                                irc_usermsg( irc, "fprintf() wrote too little. Disk full?" );
299                                fclose( fp );
300                                return STORAGE_OTHER_ERROR;
301                        }
302                }
303                g_free( line );
304        }
305       
306        for( set = irc->set; set; set = set->next )
307        {
308                if( set->value && set->def )
309                {
310                        g_snprintf( s, sizeof( s ), "set %s \"%s\"", set->key, set->value );
311                        line = obfucrypt( s, irc->password );
312                        if( *line )
313                        {
314                                if( fprintf( fp, "%s\n", line ) != strlen( line ) + 1 )
315                                {
316                                        irc_usermsg( irc, "fprintf() wrote too little. Disk full?" );
317                                        fclose( fp );
318                                        return STORAGE_OTHER_ERROR;
319                                }
320                        }
321                        g_free( line );
322                }
323        }
324       
325        if( strcmp( irc->mynick, ROOT_NICK ) != 0 )
326        {
327                g_snprintf( s, sizeof( s ), "rename %s %s", ROOT_NICK, irc->mynick );
328                line = obfucrypt( s, irc->password );
329                if( *line )
330                {
331                        if( fprintf( fp, "%s\n", line ) != strlen( line ) + 1 )
332                        {
333                                irc_usermsg( irc, "fprintf() wrote too little. Disk full?" );
334                                fclose( fp );
335                                return STORAGE_OTHER_ERROR;
336                        }
337                }
338                g_free( line );
339        }
340        if( fclose( fp ) != 0 )
341        {
342                irc_usermsg( irc, "fclose() reported an error. Disk full?" );
343                return STORAGE_OTHER_ERROR;
344        }
345       
346        g_snprintf( new_path, 512, "%s%s%s", global.conf->configdir, irc->nick, ".accounts" );
347        if( unlink( new_path ) != 0 )
348        {
349                if( errno != ENOENT )
350                {
351                        irc_usermsg( irc, "Error while removing old .accounts file" );
352                        return STORAGE_OTHER_ERROR;
353                }
354        }
355        if( rename( path, new_path ) != 0 )
356        {
357                irc_usermsg( irc, "Error while renaming new .accounts file" );
358                return STORAGE_OTHER_ERROR;
359        }
360       
361        umask( ou );
362       
363        return STORAGE_OK;
364}
365
366static storage_status_t text_check_pass( const char *nick, const char *password )
367{
368        char s[512];
369        FILE *fp;
370       
371        g_snprintf( s, 511, "%s%s%s", global.conf->configdir, nick, ".accounts" );
372        fp = fopen( s, "r" );
373        if (!fp)
374                return STORAGE_NO_SUCH_USER;
375
376        fscanf( fp, "%32[^\n]s", s );
377        fclose( fp );
378
379        if (checkpass( password, s) == -1)
380                return STORAGE_INVALID_PASSWORD;
381
382        return STORAGE_OK;
383}
384
385static storage_status_t text_remove( const char *nick, const char *password )
386{
387        char s[512];
388        storage_status_t status;
389
390        status = text_check_pass( nick, password );
391        if (status != STORAGE_OK)
392                return status;
393
394        g_snprintf( s, 511, "%s%s%s", global.conf->configdir, nick, ".accounts" );
395        if (unlink( s ) == -1)
396                return STORAGE_OTHER_ERROR;
397       
398        g_snprintf( s, 511, "%s%s%s", global.conf->configdir, nick, ".nicks" );
399        if (unlink( s ) == -1)
400                return STORAGE_OTHER_ERROR;
401
402        return STORAGE_OK;
403}
404
405storage_t storage_xml = {
406        .name = "xml",
407        .init = xml_init,
408        .check_pass = xml_check_pass,
409        .remove = xml_remove,
410        .load = xml_load,
411        .save = xml_save
412};
Note: See TracBrowser for help on using the repository browser.