Changeset e31e5b8 for storage_xml.c
- Timestamp:
- 2013-04-20T13:05:55Z (11 years ago)
- Branches:
- master
- Children:
- dd95ce4
- Parents:
- 9b2a8c1 (diff), bfafb99 (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the(diff)
links above to see all the changes relative to each parent. - File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
storage_xml.c
r9b2a8c1 re31e5b8 2 2 * BitlBee -- An IRC to other IM-networks gateway * 3 3 * * 4 * Copyright 2002-20 06Wilmer van der Gaast and others *4 * Copyright 2002-2012 Wilmer van der Gaast and others * 5 5 \********************************************************************/ 6 6 … … 29 29 #include "arc.h" 30 30 #include "md5.h" 31 32 #if GLIB_CHECK_VERSION(2,8,0) 31 #include "xmltree.h" 32 33 33 #include <glib/gstdio.h> 34 #else35 /* GLib < 2.8.0 doesn't have g_access, so just use the system access(). */36 #include <unistd.h>37 #define g_access access38 #endif39 34 40 35 typedef enum … … 47 42 48 43 /* To make it easier later when extending the format: */ 49 #define XML_FORMAT_VERSION 144 #define XML_FORMAT_VERSION "1" 50 45 51 46 struct xml_parsedata 52 47 { 53 48 irc_t *irc; 54 char *current_setting; 55 account_t *current_account; 56 irc_channel_t *current_channel; 57 set_t **current_set_head; 58 char *given_nick; 49 char given_nick[MAX_NICK_LENGTH+1]; 59 50 char *given_pass; 60 xml_pass_st pass_st;61 int unknown_tag;62 };63 64 static char *xml_attr( const gchar **attr_names, const gchar **attr_values, const gchar *key )65 {66 int i;67 68 for( i = 0; attr_names[i]; i ++ )69 if( g_strcasecmp( attr_names[i], key ) == 0 )70 return (char*) attr_values[i];71 72 return NULL;73 }74 75 static void xml_destroy_xd( gpointer data )76 {77 struct xml_parsedata *xd = data;78 79 g_free( xd->given_nick );80 g_free( xd->given_pass );81 g_free( xd );82 }83 84 static void xml_start_element( GMarkupParseContext *ctx, const gchar *element_name, const gchar **attr_names, const gchar **attr_values, gpointer data, GError **error )85 {86 struct xml_parsedata *xd = data;87 irc_t *irc = xd->irc;88 89 if( xd->unknown_tag > 0 )90 {91 xd->unknown_tag ++;92 }93 else if( g_strcasecmp( element_name, "user" ) == 0 )94 {95 char *nick = xml_attr( attr_names, attr_values, "nick" );96 char *pass = xml_attr( attr_names, attr_values, "password" );97 int st;98 99 if( !nick || !pass )100 {101 g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,102 "Missing attributes for %s element", element_name );103 }104 else if( ( st = md5_verify_password( xd->given_pass, pass ) ) == -1 )105 {106 xd->pass_st = XML_PASS_WRONG;107 g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,108 "Error while decoding password attribute" );109 }110 else if( st == 0 )111 {112 if( xd->pass_st != XML_PASS_CHECK_ONLY )113 xd->pass_st = XML_PASS_OK;114 }115 else116 {117 xd->pass_st = XML_PASS_WRONG;118 g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,119 "Password mismatch" );120 }121 }122 else if( xd->pass_st < XML_PASS_OK )123 {124 /* Let's not parse anything else if we only have to check125 the password. */126 }127 else if( g_strcasecmp( element_name, "account" ) == 0 )128 {129 char *protocol, *handle, *server, *password = NULL, *autoconnect, *tag;130 char *pass_b64 = NULL;131 unsigned char *pass_cr = NULL;132 int pass_len;133 struct prpl *prpl = NULL;134 135 handle = xml_attr( attr_names, attr_values, "handle" );136 pass_b64 = xml_attr( attr_names, attr_values, "password" );137 server = xml_attr( attr_names, attr_values, "server" );138 autoconnect = xml_attr( attr_names, attr_values, "autoconnect" );139 tag = xml_attr( attr_names, attr_values, "tag" );140 141 protocol = xml_attr( attr_names, attr_values, "protocol" );142 if( protocol )143 prpl = find_protocol( protocol );144 145 if( !handle || !pass_b64 || !protocol )146 g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,147 "Missing attributes for %s element", element_name );148 else if( !prpl )149 g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,150 "Unknown protocol: %s", protocol );151 else if( ( pass_len = base64_decode( pass_b64, (unsigned char**) &pass_cr ) ) &&152 arc_decode( pass_cr, pass_len, &password, xd->given_pass ) >= 0 )153 {154 xd->current_account = account_add( irc->b, prpl, handle, password );155 if( server )156 set_setstr( &xd->current_account->set, "server", server );157 if( autoconnect )158 set_setstr( &xd->current_account->set, "auto_connect", autoconnect );159 if( tag )160 set_setstr( &xd->current_account->set, "tag", tag );161 }162 else163 {164 /* Actually the _decode functions don't even return error codes,165 but maybe they will later... */166 g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,167 "Error while decrypting account password" );168 }169 170 g_free( pass_cr );171 g_free( password );172 }173 else if( g_strcasecmp( element_name, "setting" ) == 0 )174 {175 char *setting;176 177 if( xd->current_setting )178 {179 g_free( xd->current_setting );180 xd->current_setting = NULL;181 }182 183 if( ( setting = xml_attr( attr_names, attr_values, "name" ) ) )184 {185 if( xd->current_channel != NULL )186 xd->current_set_head = &xd->current_channel->set;187 else if( xd->current_account != NULL )188 xd->current_set_head = &xd->current_account->set;189 else190 xd->current_set_head = &xd->irc->b->set;191 192 xd->current_setting = g_strdup( setting );193 }194 else195 g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,196 "Missing attributes for %s element", element_name );197 }198 else if( g_strcasecmp( element_name, "buddy" ) == 0 )199 {200 char *handle, *nick;201 202 handle = xml_attr( attr_names, attr_values, "handle" );203 nick = xml_attr( attr_names, attr_values, "nick" );204 205 if( xd->current_account && handle && nick )206 {207 nick_set_raw( xd->current_account, handle, nick );208 }209 else210 {211 g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,212 "Missing attributes for %s element", element_name );213 }214 }215 else if( g_strcasecmp( element_name, "channel" ) == 0 )216 {217 char *name, *type;218 219 name = xml_attr( attr_names, attr_values, "name" );220 type = xml_attr( attr_names, attr_values, "type" );221 222 if( !name || !type )223 {224 g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,225 "Missing attributes for %s element", element_name );226 return;227 }228 229 /* The channel may exist already, for example if it's &bitlbee.230 Also, it's possible that the user just reconnected and the231 IRC client already rejoined all channels it was in. They232 should still get the right settings. */233 if( ( xd->current_channel = irc_channel_by_name( irc, name ) ) ||234 ( xd->current_channel = irc_channel_new( irc, name ) ) )235 set_setstr( &xd->current_channel->set, "type", type );236 }237 /* Backward compatibility: Keep this around for a while for people238 switching from BitlBee 1.2.4+. */239 else if( g_strcasecmp( element_name, "chat" ) == 0 )240 {241 char *handle, *channel;242 243 handle = xml_attr( attr_names, attr_values, "handle" );244 channel = xml_attr( attr_names, attr_values, "channel" );245 246 if( xd->current_account && handle && channel )247 {248 irc_channel_t *ic;249 250 if( ( ic = irc_channel_new( irc, channel ) ) &&251 set_setstr( &ic->set, "type", "chat" ) &&252 set_setstr( &ic->set, "chat_type", "room" ) &&253 set_setstr( &ic->set, "account", xd->current_account->tag ) &&254 set_setstr( &ic->set, "room", handle ) )255 {256 /* Try to pick up some settings where possible. */257 xd->current_channel = ic;258 }259 else if( ic )260 irc_channel_free( ic );261 }262 else263 {264 g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,265 "Missing attributes for %s element", element_name );266 }267 }268 else269 {270 xd->unknown_tag ++;271 irc_rootmsg( irc, "Warning: Unknown XML tag found in configuration file (%s). "272 "This may happen when downgrading BitlBee versions. "273 "This tag will be skipped and the information will be lost "274 "once you save your settings.", element_name );275 /*276 g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,277 "Unkown element: %s", element_name );278 */279 }280 }281 282 static void xml_end_element( GMarkupParseContext *ctx, const gchar *element_name, gpointer data, GError **error )283 {284 struct xml_parsedata *xd = data;285 286 if( xd->unknown_tag > 0 )287 {288 xd->unknown_tag --;289 }290 else if( g_strcasecmp( element_name, "setting" ) == 0 && xd->current_setting )291 {292 g_free( xd->current_setting );293 xd->current_setting = NULL;294 }295 else if( g_strcasecmp( element_name, "account" ) == 0 )296 {297 xd->current_account = NULL;298 }299 else if( g_strcasecmp( element_name, "channel" ) == 0 ||300 g_strcasecmp( element_name, "chat" ) == 0 )301 {302 xd->current_channel = NULL;303 }304 }305 306 static void xml_text( GMarkupParseContext *ctx, const gchar *text_orig, gsize text_len, gpointer data, GError **error )307 {308 char text[text_len+1];309 struct xml_parsedata *xd = data;310 311 strncpy( text, text_orig, text_len );312 text[text_len] = 0;313 314 if( xd->pass_st < XML_PASS_OK )315 {316 /* Let's not parse anything else if we only have to check317 the password, or if we didn't get the chance to check it318 yet. */319 }320 else if( g_strcasecmp( g_markup_parse_context_get_element( ctx ), "setting" ) == 0 && xd->current_setting )321 {322 if( xd->current_account )323 {324 set_t *s = set_find( xd->current_set_head, xd->current_setting );325 if( s && ( s->flags & ACC_SET_ONLINE_ONLY ) )326 {327 g_free( xd->current_setting );328 xd->current_setting = NULL;329 return;330 }331 }332 set_setstr( xd->current_set_head, xd->current_setting, (char*) text );333 g_free( xd->current_setting );334 xd->current_setting = NULL;335 }336 }337 338 GMarkupParser xml_parser =339 {340 xml_start_element,341 xml_end_element,342 xml_text,343 NULL,344 NULL345 51 }; 346 52 … … 354 60 } 355 61 62 static void handle_settings( struct xt_node *node, set_t **head ) 63 { 64 struct xt_node *c; 65 66 for( c = node->children; ( c = xt_find_node( c, "setting" ) ); c = c->next ) 67 { 68 char *name = xt_find_attr( c, "name" ); 69 70 if( !name ) 71 continue; 72 73 if( strcmp( node->name, "account" ) == 0 ) 74 { 75 set_t *s = set_find( head, name ); 76 if( s && ( s->flags & ACC_SET_ONLINE_ONLY ) ) 77 continue; /* U can't touch this! */ 78 } 79 set_setstr( head, name, c->text ); 80 } 81 } 82 83 static xt_status handle_account( struct xt_node *node, gpointer data ) 84 { 85 struct xml_parsedata *xd = data; 86 char *protocol, *handle, *server, *password = NULL, *autoconnect, *tag; 87 char *pass_b64 = NULL; 88 unsigned char *pass_cr = NULL; 89 int pass_len; 90 struct prpl *prpl = NULL; 91 account_t *acc; 92 struct xt_node *c; 93 94 handle = xt_find_attr( node, "handle" ); 95 pass_b64 = xt_find_attr( node, "password" ); 96 server = xt_find_attr( node, "server" ); 97 autoconnect = xt_find_attr( node, "autoconnect" ); 98 tag = xt_find_attr( node, "tag" ); 99 100 protocol = xt_find_attr( node, "protocol" ); 101 if( protocol ) 102 prpl = find_protocol( protocol ); 103 104 if( !handle || !pass_b64 || !protocol || !prpl ) 105 return XT_ABORT; 106 else if( ( pass_len = base64_decode( pass_b64, (unsigned char**) &pass_cr ) ) && 107 arc_decode( pass_cr, pass_len, &password, xd->given_pass ) >= 0 ) 108 { 109 acc = account_add( xd->irc->b, prpl, handle, password ); 110 if( server ) 111 set_setstr( &acc->set, "server", server ); 112 if( autoconnect ) 113 set_setstr( &acc->set, "auto_connect", autoconnect ); 114 if( tag ) 115 set_setstr( &acc->set, "tag", tag ); 116 } 117 else 118 return XT_ABORT; 119 120 g_free( pass_cr ); 121 g_free( password ); 122 123 handle_settings( node, &acc->set ); 124 125 for( c = node->children; ( c = xt_find_node( c, "buddy" ) ); c = c->next ) 126 { 127 char *handle, *nick; 128 129 handle = xt_find_attr( c, "handle" ); 130 nick = xt_find_attr( c, "nick" ); 131 132 if( handle && nick ) 133 nick_set_raw( acc, handle, nick ); 134 else 135 return XT_ABORT; 136 } 137 return XT_HANDLED; 138 } 139 140 static xt_status handle_channel( struct xt_node *node, gpointer data ) 141 { 142 struct xml_parsedata *xd = data; 143 irc_channel_t *ic; 144 char *name, *type; 145 146 name = xt_find_attr( node, "name" ); 147 type = xt_find_attr( node, "type" ); 148 149 if( !name || !type ) 150 return XT_ABORT; 151 152 /* The channel may exist already, for example if it's &bitlbee. 153 Also, it's possible that the user just reconnected and the 154 IRC client already rejoined all channels it was in. They 155 should still get the right settings. */ 156 if( ( ic = irc_channel_by_name( xd->irc, name ) ) || 157 ( ic = irc_channel_new( xd->irc, name ) ) ) 158 set_setstr( &ic->set, "type", type ); 159 160 handle_settings( node, &ic->set ); 161 162 return XT_HANDLED; 163 } 164 165 static const struct xt_handler_entry handlers[] = { 166 { "account", "user", handle_account, }, 167 { "channel", "user", handle_channel, }, 168 { NULL, NULL, NULL, }, 169 }; 170 356 171 static storage_status_t xml_load_real( irc_t *irc, const char *my_nick, const char *password, xml_pass_st action ) 357 172 { 358 GMarkupParseContext *ctx; 359 struct xml_parsedata *xd; 360 char *fn, buf[512]; 361 GError *gerr = NULL; 173 struct xml_parsedata xd[1]; 174 char *fn, buf[2048]; 362 175 int fd, st; 363 364 xd = g_new0( struct xml_parsedata, 1 ); 176 struct xt_parser *xp; 177 struct xt_node *node; 178 storage_status_t ret = STORAGE_OTHER_ERROR; 179 365 180 xd->irc = irc; 366 xd->given_nick = g_strdup( my_nick ); 367 xd->given_pass = g_strdup( password ); 368 xd->pass_st = action; 181 strncpy( xd->given_nick, my_nick, MAX_NICK_LENGTH ); 182 xd->given_nick[MAX_NICK_LENGTH] = '\0'; 369 183 nick_lc( xd->given_nick ); 370 371 fn = g_strdup_printf( "%s%s%s", global.conf->configdir, xd->given_nick, ".xml" ); 184 xd->given_pass = password; 185 186 fn = g_strconcat( global.conf->configdir, xd->given_nick, ".xml", NULL ); 372 187 if( ( fd = open( fn, O_RDONLY ) ) < 0 ) 373 188 { 374 xml_destroy_xd( xd ); 375 g_free( fn ); 376 return STORAGE_NO_SUCH_USER; 377 } 189 ret = STORAGE_NO_SUCH_USER; 190 goto error; 191 } 192 193 xp = xt_new( handlers, xd ); 194 while( ( st = read( fd, buf, sizeof( buf ) ) ) > 0 ) 195 { 196 st = xt_feed( xp, buf, st ); 197 if( st != 1 ) 198 break; 199 } 200 close( fd ); 201 if( st != 0 ) 202 goto error; 203 204 node = xp->root; 205 if( node == NULL || node->next != NULL || strcmp( node->name, "user" ) != 0 ) 206 goto error; 207 208 { 209 char *nick = xt_find_attr( node, "nick" ); 210 char *pass = xt_find_attr( node, "password" ); 211 212 if( !nick || !pass ) 213 { 214 goto error; 215 } 216 else if( ( st = md5_verify_password( xd->given_pass, pass ) ) != 0 ) 217 { 218 ret = STORAGE_INVALID_PASSWORD; 219 goto error; 220 } 221 } 222 223 if( action == XML_PASS_CHECK_ONLY ) 224 { 225 ret = STORAGE_OK; 226 goto error; 227 } 228 229 /* DO NOT call xt_handle() before verifying the password! */ 230 if( xt_handle( xp, NULL, 1 ) == XT_HANDLED ) 231 ret = STORAGE_OK; 232 233 handle_settings( node, &xd->irc->b->set ); 234 235 error: 236 xt_free( xp ); 378 237 g_free( fn ); 379 380 ctx = g_markup_parse_context_new( &xml_parser, 0, xd, xml_destroy_xd ); 381 382 while( ( st = read( fd, buf, sizeof( buf ) ) ) > 0 ) 383 { 384 if( !g_markup_parse_context_parse( ctx, buf, st, &gerr ) || gerr ) 385 { 386 xml_pass_st pass_st = xd->pass_st; 387 388 g_markup_parse_context_free( ctx ); 389 close( fd ); 390 391 if( pass_st == XML_PASS_WRONG ) 392 { 393 g_clear_error( &gerr ); 394 return STORAGE_INVALID_PASSWORD; 395 } 396 else 397 { 398 if( gerr && irc ) 399 irc_rootmsg( irc, "Error from XML-parser: %s", gerr->message ); 400 401 g_clear_error( &gerr ); 402 return STORAGE_OTHER_ERROR; 403 } 404 } 405 } 406 /* Just to be sure... */ 407 g_clear_error( &gerr ); 408 409 g_markup_parse_context_free( ctx ); 410 close( fd ); 411 412 if( action == XML_PASS_CHECK_ONLY ) 413 return STORAGE_OK; 414 415 return STORAGE_OK; 238 return ret; 416 239 } 417 240 … … 423 246 static storage_status_t xml_check_pass( const char *my_nick, const char *password ) 424 247 { 425 /* This is a little bit risky because we have to pass NULL for the426 irc_t argument. This *should* be fine, if I didn't miss anything... */427 248 return xml_load_real( NULL, my_nick, password, XML_PASS_CHECK_ONLY ); 428 249 } 429 250 430 static int xml_printf( int fd, int indent, char *fmt, ... ) 431 { 432 va_list params; 433 char *out; 434 char tabs[9] = "\t\t\t\t\t\t\t\t"; 435 int len; 436 437 /* Maybe not very clean, but who needs more than 8 levels of indentation anyway? */ 438 if( write( fd, tabs, indent <= 8 ? indent : 8 ) != indent ) 439 return 0; 440 441 va_start( params, fmt ); 442 out = g_markup_vprintf_escaped( fmt, params ); 443 va_end( params ); 444 445 len = strlen( out ); 446 len -= write( fd, out, len ); 447 g_free( out ); 448 449 return len == 0; 450 } 451 452 static gboolean xml_save_nick( gpointer key, gpointer value, gpointer data ); 453 454 static storage_status_t xml_save( irc_t *irc, int overwrite ) 455 { 456 char path[512], *path2, *pass_buf = NULL; 457 set_t *set; 251 252 static gboolean xml_generate_nick( gpointer key, gpointer value, gpointer data ); 253 static void xml_generate_settings( struct xt_node *cur, set_t **head ); 254 255 struct xt_node *xml_generate( irc_t *irc ) 256 { 257 char *pass_buf = NULL; 458 258 account_t *acc; 459 int fd;460 259 md5_byte_t pass_md5[21]; 461 260 md5_state_t md5_state; 462 261 GSList *l; 463 464 path2 = g_strdup( irc->user->nick ); 465 nick_lc( path2 ); 466 g_snprintf( path, sizeof( path ) - 2, "%s%s%s", global.conf->configdir, path2, ".xml" ); 467 g_free( path2 ); 468 469 if( !overwrite && g_access( path, F_OK ) == 0 ) 470 return STORAGE_ALREADY_EXISTS; 471 472 strcat( path, ".XXXXXX" ); 473 if( ( fd = mkstemp( path ) ) < 0 ) 474 { 475 irc_rootmsg( irc, "Error while opening configuration file." ); 476 return STORAGE_OTHER_ERROR; 477 } 262 struct xt_node *root, *cur; 478 263 479 264 /* Generate a salted md5sum of the password. Use 5 bytes for the salt … … 488 273 pass_buf = base64_encode( pass_md5, 21 ); 489 274 490 if( !xml_printf( fd, 0, "<user nick=\"%s\" password=\"%s\" version=\"%d\">\n", irc->user->nick, pass_buf, XML_FORMAT_VERSION ) ) 491 goto write_error; 275 root = cur = xt_new_node( "user", NULL, NULL ); 276 xt_add_attr( cur, "nick", irc->user->nick ); 277 xt_add_attr( cur, "password", pass_buf ); 278 xt_add_attr( cur, "version", XML_FORMAT_VERSION ); 492 279 493 280 g_free( pass_buf ); 494 281 495 for( set = irc->b->set; set; set = set->next ) 496 if( set->value && !( set->flags & SET_NOSAVE ) ) 497 if( !xml_printf( fd, 1, "<setting name=\"%s\">%s</setting>\n", set->key, set->value ) ) 498 goto write_error; 282 xml_generate_settings( cur, &irc->b->set ); 499 283 500 284 for( acc = irc->b->accounts; acc; acc = acc->next ) … … 508 292 g_free( pass_cr ); 509 293 510 if( !xml_printf( fd, 1, "<account protocol=\"%s\" handle=\"%s\" password=\"%s\" " 511 "autoconnect=\"%d\" tag=\"%s\"", acc->prpl->name, acc->user, 512 pass_b64, acc->auto_connect, acc->tag ) ) 513 { 514 g_free( pass_b64 ); 515 goto write_error; 516 } 294 cur = xt_new_node( "account", NULL, NULL ); 295 xt_add_attr( cur, "protocol", acc->prpl->name ); 296 xt_add_attr( cur, "handle", acc->user ); 297 xt_add_attr( cur, "password", pass_b64 ); 298 xt_add_attr( cur, "autoconnect", acc->auto_connect ? "true" : "false" ); 299 xt_add_attr( cur, "tag", acc->tag ); 300 if( acc->server && acc->server[0] ) 301 xt_add_attr( cur, "server", acc->server ); 302 517 303 g_free( pass_b64 ); 518 519 if( acc->server && acc->server[0] && !xml_printf( fd, 0, " server=\"%s\"", acc->server ) )520 goto write_error;521 if( !xml_printf( fd, 0, ">\n" ) )522 goto write_error;523 524 for( set = acc->set; set; set = set->next )525 if( set->value && !( set->flags & ACC_SET_NOSAVE ) )526 if( !xml_printf( fd, 2, "<setting name=\"%s\">%s</setting>\n", set->key, set->value ) )527 goto write_error;528 304 529 305 /* This probably looks pretty strange. g_hash_table_foreach 530 306 is quite a PITA already (but it can't get much better in 531 C without using #define, I'm afraid), and sinceit307 C without using #define, I'm afraid), and it 532 308 doesn't seem to be possible to abort the foreach on write 533 309 errors, so instead let's use the _find function and 534 310 return TRUE on write errors. Which means, if we found 535 311 something, there was an error. :-) */ 536 if( g_hash_table_find( acc->nicks, xml_save_nick, & fd ) )537 goto write_error;538 539 if( !xml_printf( fd, 1, "</account>\n" ) )540 goto write_error;312 g_hash_table_find( acc->nicks, xml_generate_nick, cur ); 313 314 xml_generate_settings( cur, &acc->set ); 315 316 xt_add_child( root, cur ); 541 317 } 542 318 … … 548 324 continue; 549 325 550 if( !xml_printf( fd, 1, "<channel name=\"%s\" type=\"%s\">\n", 551 ic->name, set_getstr( &ic->set, "type" ) ) ) 552 goto write_error; 553 554 for( set = ic->set; set; set = set->next ) 555 if( set->value && strcmp( set->key, "type" ) != 0 ) 556 if( !xml_printf( fd, 2, "<setting name=\"%s\">%s</setting>\n", set->key, set->value ) ) 557 goto write_error; 558 559 if( !xml_printf( fd, 1, "</channel>\n" ) ) 560 goto write_error; 561 } 562 563 if( !xml_printf( fd, 0, "</user>\n" ) ) 564 goto write_error; 565 566 fsync( fd ); 567 close( fd ); 326 cur = xt_new_node( "channel", NULL, NULL ); 327 xt_add_attr( cur, "name", ic->name ); 328 xt_add_attr( cur, "type", set_getstr( &ic->set, "type" ) ); 329 330 xml_generate_settings( cur, &ic->set ); 331 332 xt_add_child( root, cur ); 333 } 334 335 return root; 336 } 337 338 static gboolean xml_generate_nick( gpointer key, gpointer value, gpointer data ) 339 { 340 struct xt_node *node = xt_new_node( "buddy", NULL, NULL ); 341 xt_add_attr( node, "handle", key ); 342 xt_add_attr( node, "nick", value ); 343 xt_add_child( (struct xt_node *) data, node ); 344 345 return FALSE; 346 } 347 348 static void xml_generate_settings( struct xt_node *cur, set_t **head ) 349 { 350 set_t *set; 351 352 for( set = *head; set; set = set->next ) 353 if( set->value && !( set->flags & SET_NOSAVE ) ) 354 { 355 struct xt_node *xset; 356 xt_add_child( cur, xset = xt_new_node( "setting", set->value, NULL ) ); 357 xt_add_attr( xset, "name", set->key ); 358 } 359 } 360 361 static storage_status_t xml_save( irc_t *irc, int overwrite ) 362 { 363 storage_status_t ret = STORAGE_OK; 364 char path[512], *path2 = NULL, *xml = NULL; 365 struct xt_node *tree = NULL; 366 size_t len; 367 int fd; 368 369 path2 = g_strdup( irc->user->nick ); 370 nick_lc( path2 ); 371 g_snprintf( path, sizeof( path ) - 20, "%s%s%s", global.conf->configdir, path2, ".xml" ); 372 g_free( path2 ); 373 374 if( !overwrite && g_access( path, F_OK ) == 0 ) 375 return STORAGE_ALREADY_EXISTS; 376 377 strcat( path, ".XXXXXX" ); 378 if( ( fd = mkstemp( path ) ) < 0 ) 379 { 380 irc_rootmsg( irc, "Error while opening configuration file." ); 381 return STORAGE_OTHER_ERROR; 382 } 383 384 tree = xml_generate( irc ); 385 xml = xt_to_string_i( tree ); 386 len = strlen( xml ); 387 if( write( fd, xml, len ) != len || 388 fsync( fd ) != 0 || /* #559 */ 389 close( fd ) != 0 ) 390 goto error; 568 391 569 392 path2 = g_strndup( path, strlen( path ) - 7 ); 570 393 if( rename( path, path2 ) != 0 ) 571 394 { 572 irc_rootmsg( irc, "Error while renaming temporary configuration file." );573 574 395 g_free( path2 ); 575 unlink( path ); 576 577 return STORAGE_OTHER_ERROR; 578 } 579 396 goto error; 397 } 580 398 g_free( path2 ); 581 399 582 return STORAGE_OK; 583 584 write_error: 585 g_free( pass_buf ); 586 400 goto finish; 401 402 error: 587 403 irc_rootmsg( irc, "Write error. Disk full?" ); 404 ret = STORAGE_OTHER_ERROR; 405 406 finish: 588 407 close( fd ); 589 590 return STORAGE_OTHER_ERROR; 591 } 592 593 static gboolean xml_save_nick( gpointer key, gpointer value, gpointer data ) 594 { 595 return !xml_printf( *( (int*) data ), 2, "<buddy handle=\"%s\" nick=\"%s\" />\n", key, value ); 596 } 408 unlink( path ); 409 g_free( xml ); 410 xt_free_node( tree ); 411 412 return ret; 413 } 414 597 415 598 416 static storage_status_t xml_remove( const char *nick, const char *password )
Note: See TracChangeset
for help on using the changeset viewer.