Changeset 00f1e93
- Timestamp:
- 2012-06-05T19:09:14Z (12 years ago)
- Branches:
- master
- Children:
- d219296
- Parents:
- a338faa
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
storage_xml.c
ra338faa r00f1e93 47 47 { 48 48 irc_t *irc; 49 char *current_setting; 50 account_t *current_account; 51 irc_channel_t *current_channel; 52 set_t **current_set_head; 53 char *given_nick; 49 char given_nick[MAX_NICK_LENGTH+1]; 54 50 char *given_pass; 55 xml_pass_st pass_st;56 int unknown_tag;57 };58 59 static char *xml_attr( const gchar **attr_names, const gchar **attr_values, const gchar *key )60 {61 int i;62 63 for( i = 0; attr_names[i]; i ++ )64 if( g_strcasecmp( attr_names[i], key ) == 0 )65 return (char*) attr_values[i];66 67 return NULL;68 }69 70 static void xml_destroy_xd( gpointer data )71 {72 struct xml_parsedata *xd = data;73 74 g_free( xd->given_nick );75 g_free( xd->given_pass );76 g_free( xd );77 }78 79 static void xml_start_element( GMarkupParseContext *ctx, const gchar *element_name, const gchar **attr_names, const gchar **attr_values, gpointer data, GError **error )80 {81 struct xml_parsedata *xd = data;82 irc_t *irc = xd->irc;83 84 if( xd->unknown_tag > 0 )85 {86 xd->unknown_tag ++;87 }88 else if( g_strcasecmp( element_name, "user" ) == 0 )89 {90 char *nick = xml_attr( attr_names, attr_values, "nick" );91 char *pass = xml_attr( attr_names, attr_values, "password" );92 int st;93 94 if( !nick || !pass )95 {96 g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,97 "Missing attributes for %s element", element_name );98 }99 else if( ( st = md5_verify_password( xd->given_pass, pass ) ) == -1 )100 {101 xd->pass_st = XML_PASS_WRONG;102 g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,103 "Error while decoding password attribute" );104 }105 else if( st == 0 )106 {107 if( xd->pass_st != XML_PASS_CHECK_ONLY )108 xd->pass_st = XML_PASS_OK;109 }110 else111 {112 xd->pass_st = XML_PASS_WRONG;113 g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,114 "Password mismatch" );115 }116 }117 else if( xd->pass_st < XML_PASS_OK )118 {119 /* Let's not parse anything else if we only have to check120 the password. */121 }122 else if( g_strcasecmp( element_name, "account" ) == 0 )123 {124 char *protocol, *handle, *server, *password = NULL, *autoconnect, *tag;125 char *pass_b64 = NULL;126 unsigned char *pass_cr = NULL;127 int pass_len;128 struct prpl *prpl = NULL;129 130 handle = xml_attr( attr_names, attr_values, "handle" );131 pass_b64 = xml_attr( attr_names, attr_values, "password" );132 server = xml_attr( attr_names, attr_values, "server" );133 autoconnect = xml_attr( attr_names, attr_values, "autoconnect" );134 tag = xml_attr( attr_names, attr_values, "tag" );135 136 protocol = xml_attr( attr_names, attr_values, "protocol" );137 if( protocol )138 prpl = find_protocol( protocol );139 140 if( !handle || !pass_b64 || !protocol )141 g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,142 "Missing attributes for %s element", element_name );143 else if( !prpl )144 g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,145 "Unknown protocol: %s", protocol );146 else if( ( pass_len = base64_decode( pass_b64, (unsigned char**) &pass_cr ) ) &&147 arc_decode( pass_cr, pass_len, &password, xd->given_pass ) >= 0 )148 {149 xd->current_account = account_add( irc->b, prpl, handle, password );150 if( server )151 set_setstr( &xd->current_account->set, "server", server );152 if( autoconnect )153 set_setstr( &xd->current_account->set, "auto_connect", autoconnect );154 if( tag )155 set_setstr( &xd->current_account->set, "tag", tag );156 }157 else158 {159 /* Actually the _decode functions don't even return error codes,160 but maybe they will later... */161 g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,162 "Error while decrypting account password" );163 }164 165 g_free( pass_cr );166 g_free( password );167 }168 else if( g_strcasecmp( element_name, "setting" ) == 0 )169 {170 char *setting;171 172 if( xd->current_setting )173 {174 g_free( xd->current_setting );175 xd->current_setting = NULL;176 }177 178 if( ( setting = xml_attr( attr_names, attr_values, "name" ) ) )179 {180 if( xd->current_channel != NULL )181 xd->current_set_head = &xd->current_channel->set;182 else if( xd->current_account != NULL )183 xd->current_set_head = &xd->current_account->set;184 else185 xd->current_set_head = &xd->irc->b->set;186 187 xd->current_setting = g_strdup( setting );188 }189 else190 g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,191 "Missing attributes for %s element", element_name );192 }193 else if( g_strcasecmp( element_name, "buddy" ) == 0 )194 {195 char *handle, *nick;196 197 handle = xml_attr( attr_names, attr_values, "handle" );198 nick = xml_attr( attr_names, attr_values, "nick" );199 200 if( xd->current_account && handle && nick )201 {202 nick_set_raw( xd->current_account, handle, nick );203 }204 else205 {206 g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,207 "Missing attributes for %s element", element_name );208 }209 }210 else if( g_strcasecmp( element_name, "channel" ) == 0 )211 {212 char *name, *type;213 214 name = xml_attr( attr_names, attr_values, "name" );215 type = xml_attr( attr_names, attr_values, "type" );216 217 if( !name || !type )218 {219 g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,220 "Missing attributes for %s element", element_name );221 return;222 }223 224 /* The channel may exist already, for example if it's &bitlbee.225 Also, it's possible that the user just reconnected and the226 IRC client already rejoined all channels it was in. They227 should still get the right settings. */228 if( ( xd->current_channel = irc_channel_by_name( irc, name ) ) ||229 ( xd->current_channel = irc_channel_new( irc, name ) ) )230 set_setstr( &xd->current_channel->set, "type", type );231 }232 /* Backward compatibility: Keep this around for a while for people233 switching from BitlBee 1.2.4+. */234 else if( g_strcasecmp( element_name, "chat" ) == 0 )235 {236 char *handle, *channel;237 238 handle = xml_attr( attr_names, attr_values, "handle" );239 channel = xml_attr( attr_names, attr_values, "channel" );240 241 if( xd->current_account && handle && channel )242 {243 irc_channel_t *ic;244 245 if( ( ic = irc_channel_new( irc, channel ) ) &&246 set_setstr( &ic->set, "type", "chat" ) &&247 set_setstr( &ic->set, "chat_type", "room" ) &&248 set_setstr( &ic->set, "account", xd->current_account->tag ) &&249 set_setstr( &ic->set, "room", handle ) )250 {251 /* Try to pick up some settings where possible. */252 xd->current_channel = ic;253 }254 else if( ic )255 irc_channel_free( ic );256 }257 else258 {259 g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,260 "Missing attributes for %s element", element_name );261 }262 }263 else264 {265 xd->unknown_tag ++;266 irc_rootmsg( irc, "Warning: Unknown XML tag found in configuration file (%s). "267 "This may happen when downgrading BitlBee versions. "268 "This tag will be skipped and the information will be lost "269 "once you save your settings.", element_name );270 /*271 g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,272 "Unkown element: %s", element_name );273 */274 }275 }276 277 static void xml_end_element( GMarkupParseContext *ctx, const gchar *element_name, gpointer data, GError **error )278 {279 struct xml_parsedata *xd = data;280 281 if( xd->unknown_tag > 0 )282 {283 xd->unknown_tag --;284 }285 else if( g_strcasecmp( element_name, "setting" ) == 0 && xd->current_setting )286 {287 g_free( xd->current_setting );288 xd->current_setting = NULL;289 }290 else if( g_strcasecmp( element_name, "account" ) == 0 )291 {292 xd->current_account = NULL;293 }294 else if( g_strcasecmp( element_name, "channel" ) == 0 ||295 g_strcasecmp( element_name, "chat" ) == 0 )296 {297 xd->current_channel = NULL;298 }299 }300 301 static void xml_text( GMarkupParseContext *ctx, const gchar *text_orig, gsize text_len, gpointer data, GError **error )302 {303 char text[text_len+1];304 struct xml_parsedata *xd = data;305 306 strncpy( text, text_orig, text_len );307 text[text_len] = 0;308 309 if( xd->pass_st < XML_PASS_OK )310 {311 /* Let's not parse anything else if we only have to check312 the password, or if we didn't get the chance to check it313 yet. */314 }315 else if( g_strcasecmp( g_markup_parse_context_get_element( ctx ), "setting" ) == 0 && xd->current_setting )316 {317 if( xd->current_account )318 {319 set_t *s = set_find( xd->current_set_head, xd->current_setting );320 if( s && ( s->flags & ACC_SET_ONLINE_ONLY ) )321 {322 g_free( xd->current_setting );323 xd->current_setting = NULL;324 return;325 }326 }327 set_setstr( xd->current_set_head, xd->current_setting, (char*) text );328 g_free( xd->current_setting );329 xd->current_setting = NULL;330 }331 }332 333 GMarkupParser xml_parser =334 {335 xml_start_element,336 xml_end_element,337 xml_text,338 NULL,339 NULL340 51 }; 341 52 … … 349 60 } 350 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( strcmp( node->name, "account" ) == 0 ) 71 { 72 set_t *s = set_find( head, name ); 73 if( s && ( s->flags & ACC_SET_ONLINE_ONLY ) ) 74 continue; /* U can't touch this! */ 75 } 76 set_setstr( head, name, c->text ); 77 } 78 } 79 80 static xt_status handle_account( struct xt_node *node, gpointer data ) 81 { 82 struct xml_parsedata *xd = data; 83 char *protocol, *handle, *server, *password = NULL, *autoconnect, *tag; 84 char *pass_b64 = NULL; 85 unsigned char *pass_cr = NULL; 86 int pass_len; 87 struct prpl *prpl = NULL; 88 account_t *acc; 89 struct xt_node *c; 90 91 handle = xt_find_attr( node, "handle" ); 92 pass_b64 = xt_find_attr( node, "password" ); 93 server = xt_find_attr( node, "server" ); 94 autoconnect = xt_find_attr( node, "autoconnect" ); 95 tag = xt_find_attr( node, "tag" ); 96 97 protocol = xt_find_attr( node, "protocol" ); 98 if( protocol ) 99 prpl = find_protocol( protocol ); 100 101 if( !handle || !pass_b64 || !protocol || !prpl ) 102 return XT_ABORT; 103 else if( ( pass_len = base64_decode( pass_b64, (unsigned char**) &pass_cr ) ) && 104 arc_decode( pass_cr, pass_len, &password, xd->given_pass ) >= 0 ) 105 { 106 acc = account_add( xd->irc->b, prpl, handle, password ); 107 if( server ) 108 set_setstr( &acc->set, "server", server ); 109 if( autoconnect ) 110 set_setstr( &acc->set, "auto_connect", autoconnect ); 111 if( tag ) 112 set_setstr( &acc->set, "tag", tag ); 113 } 114 else 115 return XT_ABORT; 116 117 g_free( pass_cr ); 118 g_free( password ); 119 120 handle_settings( node, &acc->set ); 121 122 for( c = node->children; ( c = xt_find_node( c, "buddy" ) ); c = c->next ) 123 { 124 char *handle, *nick; 125 126 handle = xt_find_attr( c, "handle" ); 127 nick = xt_find_attr( c, "nick" ); 128 129 if( handle && nick ) 130 nick_set_raw( acc, handle, nick ); 131 else 132 return XT_ABORT; 133 } 134 return XT_HANDLED; 135 } 136 137 static xt_status handle_channel( struct xt_node *node, gpointer data ) 138 { 139 struct xml_parsedata *xd = data; 140 irc_channel_t *ic; 141 char *name, *type; 142 143 name = xt_find_attr( node, "name" ); 144 type = xt_find_attr( node, "type" ); 145 146 if( !name || !type ) 147 return XT_ABORT; 148 149 /* The channel may exist already, for example if it's &bitlbee. 150 Also, it's possible that the user just reconnected and the 151 IRC client already rejoined all channels it was in. They 152 should still get the right settings. */ 153 if( ( ic = irc_channel_by_name( xd->irc, name ) ) || 154 ( ic = irc_channel_new( xd->irc, name ) ) ) 155 set_setstr( &ic->set, "type", type ); 156 157 handle_settings( node, &ic->set ); 158 159 return XT_HANDLED; 160 } 161 162 static const struct xt_handler_entry handlers[] = { 163 { "account", "user", handle_account, }, 164 { "channel", "user", handle_channel, }, 165 { NULL, NULL, NULL, }, 166 }; 167 351 168 static storage_status_t xml_load_real( irc_t *irc, const char *my_nick, const char *password, xml_pass_st action ) 352 169 { 353 GMarkupParseContext *ctx; 354 struct xml_parsedata *xd; 355 char *fn, buf[512]; 356 GError *gerr = NULL; 170 struct xml_parsedata xd[1]; 171 char *fn, buf[204]; 357 172 int fd, st; 358 359 xd = g_new0( struct xml_parsedata, 1 ); 173 struct xt_parser *xp; 174 struct xt_node *node; 175 storage_status_t ret = STORAGE_OTHER_ERROR; 176 char *nick; 177 360 178 xd->irc = irc; 361 xd->given_nick = g_strdup( my_nick ); 362 xd->given_pass = g_strdup( password ); 363 xd->pass_st = action; 179 strncpy( xd->given_nick, my_nick, MAX_NICK_LENGTH ); 180 xd->given_nick[MAX_NICK_LENGTH] = '\0'; 364 181 nick_lc( xd->given_nick ); 365 366 fn = g_strdup_printf( "%s%s%s", global.conf->configdir, xd->given_nick, ".xml" ); 182 xd->given_pass = password; 183 184 fn = g_strconcat( global.conf->configdir, xd->given_nick, ".xml", NULL ); 367 185 if( ( fd = open( fn, O_RDONLY ) ) < 0 ) 368 186 { 369 xml_destroy_xd( xd ); 370 g_free( fn ); 371 return STORAGE_NO_SUCH_USER; 372 } 373 g_free( fn ); 374 375 ctx = g_markup_parse_context_new( &xml_parser, 0, xd, xml_destroy_xd ); 376 187 ret = STORAGE_NO_SUCH_USER; 188 goto error; 189 } 190 191 xp = xt_new( handlers, xd ); 377 192 while( ( st = read( fd, buf, sizeof( buf ) ) ) > 0 ) 378 193 { 379 if( !g_markup_parse_context_parse( ctx, buf, st, &gerr ) || gerr ) 194 st = xt_feed( xp, buf, st ); 195 if( st != 1 ) 196 break; 197 } 198 close( fd ); 199 if( st != 0 ) 200 goto error; 201 202 node = xp->root; 203 if( node == NULL || node->next != NULL || strcmp( node->name, "user" ) != 0 ) 204 goto error; 205 206 { 207 char *nick = xt_find_attr( node, "nick" ); 208 char *pass = xt_find_attr( node, "password" ); 209 210 if( !nick || !pass ) 380 211 { 381 xml_pass_st pass_st = xd->pass_st; 382 383 g_markup_parse_context_free( ctx ); 384 close( fd ); 385 386 if( pass_st == XML_PASS_WRONG ) 387 { 388 g_clear_error( &gerr ); 389 return STORAGE_INVALID_PASSWORD; 390 } 391 else 392 { 393 if( gerr && irc ) 394 irc_rootmsg( irc, "Error from XML-parser: %s", gerr->message ); 395 396 g_clear_error( &gerr ); 397 return STORAGE_OTHER_ERROR; 398 } 212 goto error; 399 213 } 400 }401 /* Just to be sure... */402 g_clear_error( &gerr );403 404 g_markup_parse_context_free( ctx );405 close( fd );214 else if( ( st = md5_verify_password( xd->given_pass, pass ) ) != 0 ) 215 { 216 ret = STORAGE_INVALID_PASSWORD; 217 goto error; 218 } 219 } 406 220 407 221 if( action == XML_PASS_CHECK_ONLY ) 408 return STORAGE_OK; 409 410 return STORAGE_OK; 222 { 223 ret = STORAGE_OK; 224 goto error; 225 } 226 227 /* DO NOT call xt_handle() before verifying the password! */ 228 if( xt_handle( xp, NULL, -1 ) == XT_HANDLED ) 229 ret = STORAGE_OK; 230 231 handle_settings( node, &xd->irc->b->set ); 232 233 error: 234 return ret; 411 235 } 412 236 … … 418 242 static storage_status_t xml_check_pass( const char *my_nick, const char *password ) 419 243 { 420 /* This is a little bit risky because we have to pass NULL for the421 irc_t argument. This *should* be fine, if I didn't miss anything... */422 244 return xml_load_real( NULL, my_nick, password, XML_PASS_CHECK_ONLY ); 423 245 } 246 424 247 425 248 static gboolean xml_save_nick( gpointer key, gpointer value, gpointer data );
Note: See TracChangeset
for help on using the changeset viewer.