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
RevLine 
[a312b6b]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
[c121f89]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
[a312b6b]38struct xml_parsedata
39{
40        irc_t *irc;
41        char *current_setting;
42        account_t *current_account;
[c121f89]43        char *given_nick;
44        char *given_pass;
45        xml_pass_st pass_st;
[a312b6b]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 )
[c121f89]54                        return (char*) attr_values[i];
[a312b6b]55       
56        return NULL;
57}
58
[c121f89]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
[a312b6b]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;
[c121f89]71        irc_t *irc = xd->irc;
[a312b6b]72       
73        if( g_strcasecmp( element_name, "user" ) == 0 )
74        {
75                char *nick = xml_attr( attr_names, attr_values, "nick" );
[c121f89]76                char *pass = xml_attr( attr_names, attr_values, "password" );
[a312b6b]77               
[c121f89]78                if( !nick || !pass )
[a312b6b]79                {
[c121f89]80                        g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
81                                     "Missing attributes for %s element", element_name );
[a312b6b]82                }
[c121f89]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. */
[a312b6b]99        }
100        else if( g_strcasecmp( element_name, "account" ) == 0 )
101        {
[c121f89]102                char *protocol, *handle, *server, *password;
[a312b6b]103                struct prpl *prpl = NULL;
104               
105                handle = xml_attr( attr_names, attr_values, "handle" );
106                password = xml_attr( attr_names, attr_values, "password" );
[c121f89]107                server = xml_attr( attr_names, attr_values, "server" );
[a312b6b]108               
109                protocol = xml_attr( attr_names, attr_values, "protocol" );
110                if( protocol )
111                        prpl = find_protocol( protocol );
112               
[c121f89]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
[a312b6b]120                {
[c121f89]121                        xd->current_account = account_add( irc, prpl, handle, password );
122                        if( server )
123                                xd->current_account->server = g_strdup( server );
[a312b6b]124                }
125        }
126        else if( g_strcasecmp( element_name, "setting" ) == 0 )
127        {
128                if( xd->current_account == NULL )
129                {
[c121f89]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 );
[a312b6b]143                }
144        }
145        else if( g_strcasecmp( element_name, "buddy" ) == 0 )
146        {
[c121f89]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                }
[a312b6b]161        }
162        else
163        {
[c121f89]164                g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
165                             "Unkown element: %s", element_name );
[a312b6b]166        }
167}
168
169static void xml_end_element( GMarkupParseContext *ctx, const gchar *element_name, gpointer data, GError **error )
170{
[5898ef8]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        }
[a312b6b]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;
[c121f89]188        irc_t *irc = xd->irc;
[a312b6b]189       
[c121f89]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 )
[a312b6b]197        {
[c121f89]198                set_setstr( irc, xd->current_setting, (char*) text );
199                g_free( xd->current_setting );
200                xd->current_setting = NULL;
[a312b6b]201        }
202}
203
204GMarkupParser xml_parser =
205{
206        xml_start_element,
207        xml_end_element,
208        xml_text,
209        NULL,
[5898ef8]210        NULL
[a312b6b]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
[c121f89]221static storage_status_t xml_load( const char *my_nick, const char *password, irc_t *irc )
[a312b6b]222{
223        GMarkupParseContext *ctx;
[c121f89]224        struct xml_parsedata *xd;
225        char *fn, buf[512];
226        GError *gerr = NULL;
227        int fd, st;
[a312b6b]228       
[5898ef8]229        if( irc->status & USTATUS_IDENTIFIED )
[a312b6b]230                return( 1 );
231       
[c121f89]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 );
[a312b6b]237       
[c121f89]238        fn = g_strdup_printf( "%s%s%s", global.conf->configdir, xd->given_nick, ".xml" );
239        if( ( fd = open( fn, O_RDONLY ) ) < 0 )
[a312b6b]240        {
[c121f89]241                xml_destroy_xd( xd );
242                g_free( fn );
243                return STORAGE_NO_SUCH_USER;
[a312b6b]244        }
[c121f89]245        g_free( fn );
[a312b6b]246       
[c121f89]247        ctx = g_markup_parse_context_new( &xml_parser, 0, xd, xml_destroy_xd );
[a312b6b]248       
[c121f89]249        while( ( st = read( fd, buf, sizeof( buf ) ) ) > 0 )
[a312b6b]250        {
[c121f89]251                if( !g_markup_parse_context_parse( ctx, buf, st, &gerr ) || gerr )
252                {
253                        g_markup_parse_context_free( ctx );
[5898ef8]254                        close( fd );
[c121f89]255                       
[5898ef8]256                        /* Slightly dirty... */
[c121f89]257                        if( gerr && strcmp( gerr->message, XML_PASS_ERRORMSG ) == 0 )
258                                return STORAGE_INVALID_PASSWORD;
259                        else
[5898ef8]260                        {
261                                if( gerr )
262                                        irc_usermsg( irc, "Error from XML-parser: %s", gerr->message );
263                               
[c121f89]264                                return STORAGE_OTHER_ERROR;
[5898ef8]265                        }
[c121f89]266                }
[a312b6b]267        }
268       
[c121f89]269        g_markup_parse_context_free( ctx );
[5898ef8]270        close( fd );
[c121f89]271       
[5898ef8]272        irc->status |= USTATUS_IDENTIFIED;
[a312b6b]273       
274        if( set_getint( irc, "auto_connect" ) )
275        {
[c121f89]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 );
[a312b6b]279        }
280       
281        return STORAGE_OK;
282}
283
[5898ef8]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
[c121f89]301static storage_status_t xml_save( irc_t *irc, int overwrite )
[a312b6b]302{
[5898ef8]303        char path[512], *path2;
304        set_t *set;
305        nick_t *nick;
306        account_t *acc;
307        int fd;
[a312b6b]308       
[5898ef8]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;
[a312b6b]319        }
[5898ef8]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       
[a312b6b]365        return STORAGE_OK;
[5898ef8]366
367write_error:
368        irc_usermsg( irc, "Write error. Disk full?" );
369        close( fd );
370       
371        return STORAGE_OTHER_ERROR;
[a312b6b]372}
373
374storage_t storage_xml = {
375        .name = "xml",
376        .init = xml_init,
[c121f89]377//      .check_pass = xml_check_pass,
378//      .remove = xml_remove,
[a312b6b]379        .load = xml_load,
380        .save = xml_save
381};
Note: See TracBrowser for help on using the repository browser.