source: storage_xml.c @ 10efa91

Last change on this file since 10efa91 was 5898ef8, checked in by Wilmer van der Gaast <wilmer@…>, at 2006-06-15T12:46:31Z

xml_save(), decent error handling and merging fixed autosaving.

  • Property mode set to 100644
File size: 10.1 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
29typedef enum
30{
31        XML_PASS_CHECK_ONLY = -1,
32        XML_PASS_UNKNOWN = 0,
33        XML_PASS_OK
34} xml_pass_st;
35
36#define XML_PASS_ERRORMSG "Wrong username or password"
37
38struct xml_parsedata
39{
40        irc_t *irc;
41        char *current_setting;
42        account_t *current_account;
43        char *given_nick;
44        char *given_pass;
45        xml_pass_st pass_st;
46};
47
48static char *xml_attr( const gchar **attr_names, const gchar **attr_values, const gchar *key )
49{
50        int i;
51       
52        for( i = 0; attr_names[i]; i ++ )
53                if( g_strcasecmp( attr_names[i], key ) == 0 )
54                        return (char*) attr_values[i];
55       
56        return NULL;
57}
58
59static void xml_destroy_xd( gpointer data )
60{
61        struct xml_parsedata *xd = data;
62       
63        g_free( xd->given_nick );
64        g_free( xd->given_pass );
65        g_free( xd );
66}
67
68static void xml_start_element( GMarkupParseContext *ctx, const gchar *element_name, const gchar **attr_names, const gchar **attr_values, gpointer data, GError **error )
69{
70        struct xml_parsedata *xd = data;
71        irc_t *irc = xd->irc;
72       
73        if( g_strcasecmp( element_name, "user" ) == 0 )
74        {
75                char *nick = xml_attr( attr_names, attr_values, "nick" );
76                char *pass = xml_attr( attr_names, attr_values, "password" );
77               
78                if( !nick || !pass )
79                {
80                        g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
81                                     "Missing attributes for %s element", element_name );
82                }
83                else if( strcmp( nick, xd->given_nick ) == 0 &&
84                         strcmp( pass, xd->given_pass ) == 0 )
85                {
86                        if( xd->pass_st != XML_PASS_CHECK_ONLY )
87                                xd->pass_st = XML_PASS_OK;
88                }
89                else
90                {
91                        g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
92                                     XML_PASS_ERRORMSG );
93                }
94        }
95        else if( xd->pass_st < XML_PASS_OK )
96        {
97                /* Let's not parse anything else if we only have to check
98                   the password. */
99        }
100        else if( g_strcasecmp( element_name, "account" ) == 0 )
101        {
102                char *protocol, *handle, *server, *password;
103                struct prpl *prpl = NULL;
104               
105                handle = xml_attr( attr_names, attr_values, "handle" );
106                password = xml_attr( attr_names, attr_values, "password" );
107                server = xml_attr( attr_names, attr_values, "server" );
108               
109                protocol = xml_attr( attr_names, attr_values, "protocol" );
110                if( protocol )
111                        prpl = find_protocol( protocol );
112               
113                if( !handle || !password )
114                        g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
115                                     "Missing attributes for %s element", element_name );
116                else if( !prpl )
117                        g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
118                                     "Missing or unknown protocol %s element", element_name );
119                else
120                {
121                        xd->current_account = account_add( irc, prpl, handle, password );
122                        if( server )
123                                xd->current_account->server = g_strdup( server );
124                }
125        }
126        else if( g_strcasecmp( element_name, "setting" ) == 0 )
127        {
128                if( xd->current_account == NULL )
129                {
130                        char *setting;
131                       
132                        if( xd->current_setting )
133                        {
134                                g_free( xd->current_setting );
135                                xd->current_setting = NULL;
136                        }
137                       
138                        if( ( setting = xml_attr( attr_names, attr_values, "name" ) ) )
139                                xd->current_setting = g_strdup( setting );
140                        else
141                                g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
142                                             "Missing attributes for %s element", element_name );
143                }
144        }
145        else if( g_strcasecmp( element_name, "buddy" ) == 0 )
146        {
147                char *handle, *nick;
148               
149                handle = xml_attr( attr_names, attr_values, "handle" );
150                nick = xml_attr( attr_names, attr_values, "nick" );
151               
152                if( xd->current_account && handle && nick )
153                {
154                        nick_set( irc, handle, xd->current_account->prpl, nick );
155                }
156                else
157                {
158                        g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
159                                     "Missing attributes for %s element", element_name );
160                }
161        }
162        else
163        {
164                g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
165                             "Unkown element: %s", element_name );
166        }
167}
168
169static void xml_end_element( GMarkupParseContext *ctx, const gchar *element_name, gpointer data, GError **error )
170{
171        struct xml_parsedata *xd = data;
172        // irc_t *irc = xd->irc;
173       
174        if( g_strcasecmp( element_name, "setting" ) == 0 && xd->current_setting )
175        {
176                g_free( xd->current_setting );
177                xd->current_setting = NULL;
178        }
179        else if( g_strcasecmp( element_name, "account" ) == 0 )
180        {
181                xd->current_account = NULL;
182        }
183}
184
185static void xml_text( GMarkupParseContext *ctx, const gchar *text, gsize text_len, gpointer data, GError **error )
186{
187        struct xml_parsedata *xd = data;
188        irc_t *irc = xd->irc;
189       
190        if( xd->pass_st < XML_PASS_OK )
191        {
192                /* Let's not parse anything else if we only have to check
193                   the password. */
194        }
195        else if( g_strcasecmp( g_markup_parse_context_get_element( ctx ), "setting" ) == 0 &&
196                 xd->current_setting && xd->current_account == NULL )
197        {
198                set_setstr( irc, xd->current_setting, (char*) text );
199                g_free( xd->current_setting );
200                xd->current_setting = NULL;
201        }
202}
203
204GMarkupParser xml_parser =
205{
206        xml_start_element,
207        xml_end_element,
208        xml_text,
209        NULL,
210        NULL
211};
212
213static void xml_init( void )
214{
215        if( access( global.conf->configdir, F_OK ) != 0 )
216                log_message( LOGLVL_WARNING, "The configuration directory %s does not exist. Configuration won't be saved.", CONFIG );
217        else if( access( global.conf->configdir, R_OK ) != 0 || access( global.conf->configdir, W_OK ) != 0 )
218                log_message( LOGLVL_WARNING, "Permission problem: Can't read/write from/to %s.", global.conf->configdir );
219}
220
221static storage_status_t xml_load( const char *my_nick, const char *password, irc_t *irc )
222{
223        GMarkupParseContext *ctx;
224        struct xml_parsedata *xd;
225        char *fn, buf[512];
226        GError *gerr = NULL;
227        int fd, st;
228       
229        if( irc->status & USTATUS_IDENTIFIED )
230                return( 1 );
231       
232        xd = g_new0( struct xml_parsedata, 1 );
233        xd->irc = irc;
234        xd->given_nick = g_strdup( my_nick );
235        xd->given_pass = g_strdup( password );
236        nick_lc( xd->given_nick );
237       
238        fn = g_strdup_printf( "%s%s%s", global.conf->configdir, xd->given_nick, ".xml" );
239        if( ( fd = open( fn, O_RDONLY ) ) < 0 )
240        {
241                xml_destroy_xd( xd );
242                g_free( fn );
243                return STORAGE_NO_SUCH_USER;
244        }
245        g_free( fn );
246       
247        ctx = g_markup_parse_context_new( &xml_parser, 0, xd, xml_destroy_xd );
248       
249        while( ( st = read( fd, buf, sizeof( buf ) ) ) > 0 )
250        {
251                if( !g_markup_parse_context_parse( ctx, buf, st, &gerr ) || gerr )
252                {
253                        g_markup_parse_context_free( ctx );
254                        close( fd );
255                       
256                        /* Slightly dirty... */
257                        if( gerr && strcmp( gerr->message, XML_PASS_ERRORMSG ) == 0 )
258                                return STORAGE_INVALID_PASSWORD;
259                        else
260                        {
261                                if( gerr )
262                                        irc_usermsg( irc, "Error from XML-parser: %s", gerr->message );
263                               
264                                return STORAGE_OTHER_ERROR;
265                        }
266                }
267        }
268       
269        g_markup_parse_context_free( ctx );
270        close( fd );
271       
272        irc->status |= USTATUS_IDENTIFIED;
273       
274        if( set_getint( irc, "auto_connect" ) )
275        {
276                /* Can't do this directly because r_c_s alters the string */
277                strcpy( buf, "account on" );
278                root_command_string( irc, NULL, buf, 0 );
279        }
280       
281        return STORAGE_OK;
282}
283
284static int xml_printf( int fd, char *fmt, ... )
285{
286        va_list params;
287        char *out;
288        int len;
289       
290        va_start( params, fmt );
291        out = g_markup_vprintf_escaped( fmt, params );
292        va_end( params );
293       
294        len = strlen( out );
295        len -= write( fd, out, len );
296        g_free( out );
297       
298        return len == 0;
299}
300
301static storage_status_t xml_save( irc_t *irc, int overwrite )
302{
303        char path[512], *path2;
304        set_t *set;
305        nick_t *nick;
306        account_t *acc;
307        int fd;
308       
309        g_snprintf( path, sizeof( path ) - 2, "%s%s%s", global.conf->configdir, irc->nick, ".xml" );
310       
311        if( !overwrite && access( path, F_OK ) != -1 )
312                return STORAGE_ALREADY_EXISTS;
313       
314        strcat( path, "~" );
315        if( ( fd = open( path, O_WRONLY | O_CREAT, 0600 ) ) < 0 )
316        {
317                irc_usermsg( irc, "Error while opening configuration file." );
318                return STORAGE_OTHER_ERROR;
319        }
320       
321        if( !xml_printf( fd, "<user nick=\"%s\" password=\"%s\">\n", irc->nick, irc->password ) )
322                goto write_error;
323       
324        for( set = irc->set; set; set = set->next )
325                if( set->value && set->def )
326                        if( !xml_printf( fd, "\t<setting name=\"%s\">%s</setting>\n", set->key, set->value ) )
327                                goto write_error;
328       
329        for( acc = irc->accounts; acc; acc = acc->next )
330        {
331                if( !xml_printf( fd, "\t<account protocol=\"%s\" handle=\"%s\" password=\"%s\" autoconnect=\"%s\"", acc->prpl->name, acc->user, acc->pass, "yes" ) )
332                        goto write_error;
333                if( acc->server && acc->server[0] && !xml_printf( fd, " server=\"%s\"", acc->server ) )
334                        goto write_error;
335                if( !xml_printf( fd, ">\n" ) )
336                        goto write_error;
337               
338                for( nick = irc->nicks; nick; nick = nick->next )
339                        if( nick->proto == acc->prpl )
340                                if( !xml_printf( fd, "\t\t<buddy handle=\"%s\" nick=\"%s\" />\n", nick->handle, nick->nick ) )
341                                        goto write_error;
342               
343                if( !xml_printf( fd, "\t</account>\n" ) )
344                        goto write_error;
345        }
346       
347        if( !xml_printf( fd, "</user>\n" ) )
348                goto write_error;
349       
350        close( fd );
351       
352        path2 = g_strndup( path, strlen( path ) - 1 );
353        if( rename( path, path2 ) != 0 )
354        {
355                irc_usermsg( irc, "Error while renaming temporary configuration file." );
356               
357                g_free( path2 );
358                unlink( path );
359               
360                return STORAGE_OTHER_ERROR;
361        }
362       
363        g_free( path2 );
364       
365        return STORAGE_OK;
366
367write_error:
368        irc_usermsg( irc, "Write error. Disk full?" );
369        close( fd );
370       
371        return STORAGE_OTHER_ERROR;
372}
373
374storage_t storage_xml = {
375        .name = "xml",
376        .init = xml_init,
377//      .check_pass = xml_check_pass,
378//      .remove = xml_remove,
379        .load = xml_load,
380        .save = xml_save
381};
Note: See TracBrowser for help on using the repository browser.