Changeset 00f1e93


Ignore:
Timestamp:
2012-06-05T19:09:14Z (12 years ago)
Author:
Wilmer van der Gaast <wilmer@…>
Branches:
master
Children:
d219296
Parents:
a338faa
Message:

xml_load using xmltree. About half as much code as the old stuff. Likely a
little slower, but IMHO also a little less annoying to work with.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • storage_xml.c

    ra338faa r00f1e93  
    4747{
    4848        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];
    5450        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                 else
    111                 {
    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 check
    120                    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                 else
    158                 {
    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                         else
    185                                 xd->current_set_head = &xd->irc->b->set;
    186                        
    187                         xd->current_setting = g_strdup( setting );
    188                 }
    189                 else
    190                         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                 else
    205                 {
    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 the
    226                    IRC client already rejoined all channels it was in. They
    227                    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 people
    233            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                 else
    258                 {
    259                         g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
    260                                      "Missing attributes for %s element", element_name );
    261                 }
    262         }
    263         else
    264         {
    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 check
    312                    the password, or if we didn't get the chance to check it
    313                    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         NULL
    34051};
    34152
     
    34960}
    35061
     62static 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
     80static 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
     137static 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
     162static const struct xt_handler_entry handlers[] = {
     163        { "account", "user", handle_account, },
     164        { "channel", "user", handle_channel, },
     165        { NULL,      NULL,   NULL, },
     166};
     167
    351168static storage_status_t xml_load_real( irc_t *irc, const char *my_nick, const char *password, xml_pass_st action )
    352169{
    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];
    357172        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       
    360178        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';
    364181        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 );
    367185        if( ( fd = open( fn, O_RDONLY ) ) < 0 )
    368186        {
    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 );
    377192        while( ( st = read( fd, buf, sizeof( buf ) ) ) > 0 )
    378193        {
    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 )
    380211                {
    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;
    399213                }
    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        }
    406220       
    407221        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       
     233error:
     234        return ret;
    411235}
    412236
     
    418242static storage_status_t xml_check_pass( const char *my_nick, const char *password )
    419243{
    420         /* This is a little bit risky because we have to pass NULL for the
    421            irc_t argument. This *should* be fine, if I didn't miss anything... */
    422244        return xml_load_real( NULL, my_nick, password, XML_PASS_CHECK_ONLY );
    423245}
     246
    424247
    425248static gboolean xml_save_nick( gpointer key, gpointer value, gpointer data );
Note: See TracChangeset for help on using the changeset viewer.