Changeset e31e5b8


Ignore:
Timestamp:
2013-04-20T13:05:55Z (11 years ago)
Author:
Wilmer van der Gaast <wilmer@…>
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.
Message:

Merging "storage" branch which I wrote long ago. It separates generation of
XML-formatted user configs from disk I/O so we can try to start using other
mechanisms to store them (a REST API or something, for example).

Files:
2 deleted
16 edited

Legend:

Unmodified
Added
Removed
  • bitlbee.c

    r9b2a8c1 re31e5b8  
    267267        if( st == size )
    268268        {
    269                 if( irc->status & USTATUS_SHUTDOWN )
    270                 {
    271                         irc_free( irc );
    272                 }
    273                 else
    274                 {
    275                         g_free( irc->sendbuffer );
    276                         irc->sendbuffer = NULL;
    277                         irc->w_watch_source_id = 0;
    278                 }
     269                g_free( irc->sendbuffer );
     270                irc->sendbuffer = NULL;
     271                irc->w_watch_source_id = 0;
    279272               
    280273                return FALSE;
  • configure

    r9b2a8c1 re31e5b8  
    3939
    4040events=glib
    41 ldap=0
    4241ssl=auto
    4342
     
    325324                ret=0;
    326325        fi;
    327 }
    328 
    329 detect_ldap()
    330 {
    331         TMPFILE=$(mktemp /tmp/bitlbee-configure.XXXXXX)
    332         if $CC -o $TMPFILE -shared -lldap 2>/dev/null >/dev/null; then
    333                 cat<<EOF>>Makefile.settings
    334 EFLAGS+=-lldap
    335 CFLAGS+=
    336 EOF
    337                 ldap=1
    338                 rm -f $TMPFILE
    339                 ret=1
    340         else
    341                 ldap=0
    342                 ret=0
    343         fi
    344326}
    345327
     
    457439STORAGES="xml"
    458440
    459 if [ "$ldap" = "auto" ]; then
    460         detect_ldap
    461 fi
    462 
    463 if [ "$ldap" = 0 ]; then
    464         echo "#undef WITH_LDAP" >> config.h
    465 elif [ "$ldap" = 1 ]; then
    466         echo
    467         echo 'LDAP support is a work in progress and does NOT work AT ALL right now.'
    468         echo
    469         exit 1
    470        
    471         echo "#define WITH_LDAP 1" >> config.h
    472         STORAGES="$STORAGES ldap"
    473 fi
    474 
    475441for i in $STORAGES; do
    476442        STORAGE_OBJS="$STORAGE_OBJS storage_$i.o"
  • lib/xmltree.c

    r9b2a8c1 re31e5b8  
    282282}
    283283
    284 static void xt_to_string_real( struct xt_node *node, GString *str )
     284static void xt_to_string_real( struct xt_node *node, GString *str, int indent )
    285285{
    286286        char *buf;
     
    288288        int i;
    289289       
     290        if( indent > 1 )
     291                g_string_append_len( str, "\n\t\t\t\t\t\t\t\t",
     292                                     indent < 8 ? indent : 8 );
     293       
    290294        g_string_append_printf( str, "<%s", node->name );
    291295       
     
    312316       
    313317        for( c = node->children; c; c = c->next )
    314                 xt_to_string_real( c, str );
     318                xt_to_string_real( c, str, indent ? indent + 1 : 0 );
     319       
     320        if( indent > 0 && node->children )
     321                g_string_append_len( str, "\n\t\t\t\t\t\t\t\t",
     322                                     indent < 8 ? indent : 8 );
    315323       
    316324        g_string_append_printf( str, "</%s>", node->name );
     
    320328{
    321329        GString *ret;
    322         char *real;
    323330       
    324331        ret = g_string_new( "" );
    325         xt_to_string_real( node, ret );
    326        
    327         real = ret->str;
    328         g_string_free( ret, FALSE );
    329        
    330         return real;
     332        xt_to_string_real( node, ret, 0 );
     333        return g_string_free( ret, FALSE );
     334}
     335
     336/* WITH indentation! */
     337char *xt_to_string_i( struct xt_node *node )
     338{
     339        GString *ret;
     340       
     341        ret = g_string_new( "" );
     342        xt_to_string_real( node, ret, 1 );
     343        return g_string_free( ret, FALSE );
    331344}
    332345
    333346void xt_print( struct xt_node *node )
    334347{
    335         int i;
    336         struct xt_node *c;
    337        
    338         /* Indentation */
    339         for( c = node; c->parent; c = c->parent )
    340                 fprintf( stderr, "    " );
    341        
    342         /* Start the tag */
    343         fprintf( stderr, "<%s", node->name );
    344        
    345         /* Print the attributes */
    346         for( i = 0; node->attr[i].key; i ++ )
    347         {
    348                 char *v = g_markup_escape_text( node->attr[i].value, -1 );
    349                 fprintf( stderr, " %s=\"%s\"", node->attr[i].key, v );
    350                 g_free( v );
    351         }
    352        
    353         /* /> in case there's really *nothing* inside this tag, otherwise
    354            just >. */
    355         /* If this tag doesn't have any content at all... */
    356         if( node->text == NULL && node->children == NULL )
    357         {
    358                 fprintf( stderr, "/>\n" );
    359                 return;
    360                 /* Then we're finished! */
    361         }
    362        
    363         /* Otherwise... */
    364         fprintf( stderr, ">" );
    365        
    366         /* Only print the text if it contains more than whitespace (TEST). */
    367         if( node->text_len > 0 )
    368         {
    369                 for( i = 0; node->text[i] && isspace( node->text[i] ); i ++ );
    370                 if( node->text[i] )
    371                 {
    372                         char *v = g_markup_escape_text( node->text, -1 );
    373                         fprintf( stderr, "%s", v );
    374                         g_free( v );
    375                 }
    376         }
    377        
    378         if( node->children )
    379                 fprintf( stderr, "\n" );
    380        
    381         for( c = node->children; c; c = c->next )
    382                 xt_print( c );
    383        
    384         if( node->children )
    385                 for( c = node; c->parent; c = c->parent )
    386                         fprintf( stderr, "    " );
    387        
    388         /* Non-empty tag is now finished. */
    389         fprintf( stderr, "</%s>\n", node->name );
     348        char *str = xt_to_string_i( node );
     349        fprintf( stderr, "%s", str );
     350        g_free( str );
    390351}
    391352
  • lib/xmltree.h

    r9b2a8c1 re31e5b8  
    8484struct xt_node *xt_from_string( const char *in, int text_len );
    8585char *xt_to_string( struct xt_node *node );
     86char *xt_to_string_i( struct xt_node *node );
    8687void xt_print( struct xt_node *node );
    8788struct xt_node *xt_dup( struct xt_node *node );
  • protocols/account.c

    r9b2a8c1 re31e5b8  
    5353       
    5454        s = set_add( &a->set, "auto_connect", "true", set_eval_account, a );
    55         s->flags |= ACC_SET_NOSAVE;
     55        s->flags |= SET_NOSAVE;
    5656       
    5757        s = set_add( &a->set, "auto_reconnect", "true", set_eval_bool, a );
     
    6161       
    6262        s = set_add( &a->set, "nick_source", "handle", set_eval_nick_source, a );
    63         s->flags |= ACC_SET_NOSAVE; /* Just for bw compatibility! */
     63        s->flags |= SET_NOSAVE; /* Just for bw compatibility! */
    6464       
    6565        s = set_add( &a->set, "password", NULL, set_eval_account, a );
    66         s->flags |= ACC_SET_NOSAVE | SET_NULL_OK | SET_PASSWORD;
     66        s->flags |= SET_NOSAVE | SET_NULL_OK | SET_PASSWORD;
    6767       
    6868        s = set_add( &a->set, "tag", NULL, set_eval_account, a );
    69         s->flags |= ACC_SET_NOSAVE;
     69        s->flags |= SET_NOSAVE;
    7070       
    7171        s = set_add( &a->set, "username", NULL, set_eval_account, a );
    72         s->flags |= ACC_SET_NOSAVE | ACC_SET_OFFLINE_ONLY;
     72        s->flags |= SET_NOSAVE | ACC_SET_OFFLINE_ONLY;
    7373        set_setstr( &a->set, "username", user );
    7474       
  • protocols/account.h

    r9b2a8c1 re31e5b8  
    6161typedef enum
    6262{
    63         ACC_SET_NOSAVE = 0x01,          /* Don't save this setting (i.e. stored elsewhere). */
    6463        ACC_SET_OFFLINE_ONLY = 0x02,    /* Allow changes only if the acct is offline. */
    6564        ACC_SET_ONLINE_ONLY = 0x04,     /* Allow changes only if the acct is online. */
  • protocols/jabber/jabber.c

    r9b2a8c1 re31e5b8  
    8080       
    8181        s = set_add( &acc->set, "server", NULL, set_eval_account, acc );
    82         s->flags |= ACC_SET_NOSAVE | ACC_SET_OFFLINE_ONLY | SET_NULL_OK;
     82        s->flags |= SET_NOSAVE | ACC_SET_OFFLINE_ONLY | SET_NULL_OK;
    8383       
    8484        s = set_add( &acc->set, "ssl", "false", set_eval_bool, acc );
  • protocols/msn/msn.c

    r9b2a8c1 re31e5b8  
    3939       
    4040        s = set_add( &acc->set, "display_name", NULL, set_eval_display_name, acc );
    41         s->flags |= ACC_SET_NOSAVE | ACC_SET_ONLINE_ONLY;
     41        s->flags |= SET_NOSAVE | ACC_SET_ONLINE_ONLY;
    4242       
    4343        set_add( &acc->set, "mail_notifications", "false", set_eval_bool, acc );
  • protocols/oscar/oscar.c

    r9b2a8c1 re31e5b8  
    378378                    icq ? AIM_DEFAULT_LOGIN_SERVER_ICQ
    379379                        : AIM_DEFAULT_LOGIN_SERVER_AIM, set_eval_account, acc);
    380         s->flags |= ACC_SET_NOSAVE | ACC_SET_OFFLINE_ONLY;
     380        s->flags |= SET_NOSAVE | ACC_SET_OFFLINE_ONLY;
    381381       
    382382        if (icq) {
  • protocols/skype/skype.c

    r9b2a8c1 re31e5b8  
    15611561        s = set_add(&acc->set, "display_name", NULL, skype_set_display_name,
    15621562                acc);
    1563         s->flags |= ACC_SET_NOSAVE | ACC_SET_ONLINE_ONLY;
     1563        s->flags |= SET_NOSAVE | ACC_SET_ONLINE_ONLY;
    15641564
    15651565        s = set_add(&acc->set, "mood_text", NULL, skype_set_mood_text, acc);
     
    15671567
    15681568        s = set_add(&acc->set, "call", NULL, skype_set_call, acc);
    1569         s->flags |= ACC_SET_NOSAVE | ACC_SET_ONLINE_ONLY;
     1569        s->flags |= SET_NOSAVE | ACC_SET_ONLINE_ONLY;
    15701570
    15711571        s = set_add(&acc->set, "balance", NULL, skype_set_balance, acc);
    1572         s->flags |= ACC_SET_NOSAVE | ACC_SET_ONLINE_ONLY;
     1572        s->flags |= SET_NOSAVE | ACC_SET_ONLINE_ONLY;
    15731573
    15741574        s = set_add(&acc->set, "skypeout_offline", "true", set_eval_bool, acc);
  • protocols/twitter/twitter_lib.c

    r9b2a8c1 re31e5b8  
    3939#include <ctype.h>
    4040#include <errno.h>
    41 
    42 /* GLib < 2.12.0 doesn't have g_ascii_strtoll(), work around using system strtoll(). */
    43 /* GLib < 2.12.4 can be buggy: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=488013 */
    44 #if !GLIB_CHECK_VERSION(2,12,5)
    45 #include <stdlib.h>
    46 #include <limits.h>
    47 #define g_ascii_strtoll strtoll
    48 #endif
    4941
    5042#define TXL_STATUS 1
  • root_commands.c

    r9b2a8c1 re31e5b8  
    281281{
    282282        if( ( irc->status & USTATUS_IDENTIFIED ) == 0 )
    283                 irc_rootmsg( irc, "Please create an account first" );
     283                irc_rootmsg( irc, "Please create an account first (see \x02help register\x02)" );
    284284        else if( storage_save( irc, NULL, TRUE ) == STORAGE_OK )
    285285                irc_rootmsg( irc, "Configuration saved" );
  • set.h

    r9b2a8c1 re31e5b8  
    4545typedef enum
    4646{
    47         SET_NOSAVE = 0x0001,
    48         SET_NULL_OK = 0x0100,
    49         SET_HIDDEN = 0x0200,
    50         SET_PASSWORD = 0x0400,
    51         SET_HIDDEN_DEFAULT = 0x0800,
     47        SET_NOSAVE = 0x0001,   /* Don't save this setting (i.e. stored elsewhere). */
     48        SET_NULL_OK = 0x0100,  /* set->value == NULL is allowed. */
     49        SET_HIDDEN = 0x0200,   /* Don't show up in setting lists. Mostly for internal storage. */
     50        SET_PASSWORD = 0x0400, /* Value shows up in settings list as "********". */
     51        SET_HIDDEN_DEFAULT = 0x0800, /* Hide unless changed from default. */
    5252} set_flags_t;
    5353
  • storage.c

    r9b2a8c1 re31e5b8  
    195195        return ret;
    196196}
    197 
    198 #if 0
    199 Not using this yet. Test thoroughly before adding UI hooks to this function.
    200 
    201 storage_status_t storage_rename (const char *onick, const char *nnick, const char *password)
    202 {
    203         storage_status_t status;
    204         GList *gl = global.storage;
    205         storage_t *primary_storage = gl->data;
    206         irc_t *irc;
    207 
    208         /* First, try to rename in the current write backend, assuming onick
    209          * is stored there */
    210         status = primary_storage->rename(onick, nnick, password);
    211         if (status != STORAGE_NO_SUCH_USER)
    212                 return status;
    213 
    214         /* Try to load from a migration backend and save to the current backend.
    215          * Explicitly remove the account from the migration backend as otherwise
    216          * it'd still be usable under the old name */
    217        
    218         irc = g_new0(irc_t, 1);
    219         status = storage_load(onick, password, irc);
    220         if (status != STORAGE_OK) {
    221                 irc_free(irc);
    222                 return status;
    223         }
    224 
    225         g_free(irc->nick);
    226         irc->nick = g_strdup(nnick);
    227 
    228         status = storage_save(irc, FALSE);
    229         if (status != STORAGE_OK) {
    230                 irc_free(irc);
    231                 return status;
    232         }
    233         irc_free(irc);
    234 
    235         storage_remove(onick, password);
    236 
    237         return STORAGE_OK;
    238 }
    239 #endif
  • storage.h

    r9b2a8c1 re31e5b8  
    5959storage_status_t storage_remove (const char *nick, const char *password);
    6060
    61 /* storage_status_t storage_rename (const char *onick, const char *nnick, const char *password); */
    62 
    6361void register_storage_backend(storage_t *);
    6462G_GNUC_MALLOC GList *storage_init(const char *primary, char **migrate);
  • storage_xml.c

    r9b2a8c1 re31e5b8  
    22  * BitlBee -- An IRC to other IM-networks gateway                     *
    33  *                                                                    *
    4   * Copyright 2002-2006 Wilmer van der Gaast and others                *
     4  * Copyright 2002-2012 Wilmer van der Gaast and others                *
    55  \********************************************************************/
    66
     
    2929#include "arc.h"
    3030#include "md5.h"
    31 
    32 #if GLIB_CHECK_VERSION(2,8,0)
     31#include "xmltree.h"
     32
    3333#include <glib/gstdio.h>
    34 #else
    35 /* GLib < 2.8.0 doesn't have g_access, so just use the system access(). */
    36 #include <unistd.h>
    37 #define g_access access
    38 #endif
    3934
    4035typedef enum
     
    4742
    4843/* To make it easier later when extending the format: */
    49 #define XML_FORMAT_VERSION 1
     44#define XML_FORMAT_VERSION "1"
    5045
    5146struct xml_parsedata
    5247{
    5348        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];
    5950        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                 else
    116                 {
    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 check
    125                    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                 else
    163                 {
    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                         else
    190                                 xd->current_set_head = &xd->irc->b->set;
    191                        
    192                         xd->current_setting = g_strdup( setting );
    193                 }
    194                 else
    195                         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                 else
    210                 {
    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 the
    231                    IRC client already rejoined all channels it was in. They
    232                    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 people
    238            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                 else
    263                 {
    264                         g_set_error( error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
    265                                      "Missing attributes for %s element", element_name );
    266                 }
    267         }
    268         else
    269         {
    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 check
    317                    the password, or if we didn't get the chance to check it
    318                    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         NULL
    34551};
    34652
     
    35460}
    35561
     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( !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
     83static 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
     140static 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
     165static const struct xt_handler_entry handlers[] = {
     166        { "account", "user", handle_account, },
     167        { "channel", "user", handle_channel, },
     168        { NULL,      NULL,   NULL, },
     169};
     170
    356171static storage_status_t xml_load_real( irc_t *irc, const char *my_nick, const char *password, xml_pass_st action )
    357172{
    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];
    362175        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       
    365180        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';
    369183        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 );
    372187        if( ( fd = open( fn, O_RDONLY ) ) < 0 )
    373188        {
    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       
     235error:
     236        xt_free( xp );
    378237        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;
    416239}
    417240
     
    423246static storage_status_t xml_check_pass( const char *my_nick, const char *password )
    424247{
    425         /* This is a little bit risky because we have to pass NULL for the
    426            irc_t argument. This *should* be fine, if I didn't miss anything... */
    427248        return xml_load_real( NULL, my_nick, password, XML_PASS_CHECK_ONLY );
    428249}
    429250
    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
     252static gboolean xml_generate_nick( gpointer key, gpointer value, gpointer data );
     253static void xml_generate_settings( struct xt_node *cur, set_t **head );
     254
     255struct xt_node *xml_generate( irc_t *irc )
     256{
     257        char *pass_buf = NULL;
    458258        account_t *acc;
    459         int fd;
    460259        md5_byte_t pass_md5[21];
    461260        md5_state_t md5_state;
    462261        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;
    478263       
    479264        /* Generate a salted md5sum of the password. Use 5 bytes for the salt
     
    488273        pass_buf = base64_encode( pass_md5, 21 );
    489274       
    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 );
    492279       
    493280        g_free( pass_buf );
    494281       
    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 );
    499283       
    500284        for( acc = irc->b->accounts; acc; acc = acc->next )
     
    508292                g_free( pass_cr );
    509293               
    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               
    517303                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;
    528304               
    529305                /* This probably looks pretty strange. g_hash_table_foreach
    530306                   is quite a PITA already (but it can't get much better in
    531                    C without using #define, I'm afraid), and since it
     307                   C without using #define, I'm afraid), and it
    532308                   doesn't seem to be possible to abort the foreach on write
    533309                   errors, so instead let's use the _find function and
    534310                   return TRUE on write errors. Which means, if we found
    535311                   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 );
    541317        }
    542318       
     
    548324                        continue;
    549325               
    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
     338static 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
     348static 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
     361static 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;
    568391       
    569392        path2 = g_strndup( path, strlen( path ) - 7 );
    570393        if( rename( path, path2 ) != 0 )
    571394        {
    572                 irc_rootmsg( irc, "Error while renaming temporary configuration file." );
    573                
    574395                g_free( path2 );
    575                 unlink( path );
    576                
    577                 return STORAGE_OTHER_ERROR;
    578         }
    579        
     396                goto error;
     397        }
    580398        g_free( path2 );
    581399       
    582         return STORAGE_OK;
    583 
    584 write_error:
    585         g_free( pass_buf );
    586        
     400        goto finish;
     401
     402error:
    587403        irc_rootmsg( irc, "Write error. Disk full?" );
     404        ret = STORAGE_OTHER_ERROR;
     405
     406finish:
    588407        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
    597415
    598416static storage_status_t xml_remove( const char *nick, const char *password )
Note: See TracChangeset for help on using the changeset viewer.