Changes in / [e31e5b8:9b2a8c1]


Ignore:
Files:
2 added
16 edited

Legend:

Unmodified
Added
Removed
  • bitlbee.c

    re31e5b8 r9b2a8c1  
    267267        if( st == size )
    268268        {
    269                 g_free( irc->sendbuffer );
    270                 irc->sendbuffer = NULL;
    271                 irc->w_watch_source_id = 0;
     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                }
    272279               
    273280                return FALSE;
  • configure

    re31e5b8 r9b2a8c1  
    3939
    4040events=glib
     41ldap=0
    4142ssl=auto
    4243
     
    324325                ret=0;
    325326        fi;
     327}
     328
     329detect_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
     334EFLAGS+=-lldap
     335CFLAGS+=
     336EOF
     337                ldap=1
     338                rm -f $TMPFILE
     339                ret=1
     340        else
     341                ldap=0
     342                ret=0
     343        fi
    326344}
    327345
     
    439457STORAGES="xml"
    440458
     459if [ "$ldap" = "auto" ]; then
     460        detect_ldap
     461fi
     462
     463if [ "$ldap" = 0 ]; then
     464        echo "#undef WITH_LDAP" >> config.h
     465elif [ "$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"
     473fi
     474
    441475for i in $STORAGES; do
    442476        STORAGE_OBJS="$STORAGE_OBJS storage_$i.o"
  • lib/xmltree.c

    re31e5b8 r9b2a8c1  
    282282}
    283283
    284 static void xt_to_string_real( struct xt_node *node, GString *str, int indent )
     284static void xt_to_string_real( struct xt_node *node, GString *str )
    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        
    294290        g_string_append_printf( str, "<%s", node->name );
    295291       
     
    316312       
    317313        for( c = node->children; c; c = c->next )
    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 );
     314                xt_to_string_real( c, str );
    323315       
    324316        g_string_append_printf( str, "</%s>", node->name );
     
    328320{
    329321        GString *ret;
     322        char *real;
    330323       
    331324        ret = g_string_new( "" );
    332         xt_to_string_real( node, ret, 0 );
    333         return g_string_free( ret, FALSE );
    334 }
    335 
    336 /* WITH indentation! */
    337 char *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 );
     325        xt_to_string_real( node, ret );
     326       
     327        real = ret->str;
     328        g_string_free( ret, FALSE );
     329       
     330        return real;
    344331}
    345332
    346333void xt_print( struct xt_node *node )
    347334{
    348         char *str = xt_to_string_i( node );
    349         fprintf( stderr, "%s", str );
    350         g_free( str );
     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 );
    351390}
    352391
  • lib/xmltree.h

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

    re31e5b8 r9b2a8c1  
    5353       
    5454        s = set_add( &a->set, "auto_connect", "true", set_eval_account, a );
    55         s->flags |= SET_NOSAVE;
     55        s->flags |= ACC_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 |= SET_NOSAVE; /* Just for bw compatibility! */
     63        s->flags |= ACC_SET_NOSAVE; /* Just for bw compatibility! */
    6464       
    6565        s = set_add( &a->set, "password", NULL, set_eval_account, a );
    66         s->flags |= SET_NOSAVE | SET_NULL_OK | SET_PASSWORD;
     66        s->flags |= ACC_SET_NOSAVE | SET_NULL_OK | SET_PASSWORD;
    6767       
    6868        s = set_add( &a->set, "tag", NULL, set_eval_account, a );
    69         s->flags |= SET_NOSAVE;
     69        s->flags |= ACC_SET_NOSAVE;
    7070       
    7171        s = set_add( &a->set, "username", NULL, set_eval_account, a );
    72         s->flags |= SET_NOSAVE | ACC_SET_OFFLINE_ONLY;
     72        s->flags |= ACC_SET_NOSAVE | ACC_SET_OFFLINE_ONLY;
    7373        set_setstr( &a->set, "username", user );
    7474       
  • protocols/account.h

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

    re31e5b8 r9b2a8c1  
    8080       
    8181        s = set_add( &acc->set, "server", NULL, set_eval_account, acc );
    82         s->flags |= SET_NOSAVE | ACC_SET_OFFLINE_ONLY | SET_NULL_OK;
     82        s->flags |= ACC_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

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

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

    re31e5b8 r9b2a8c1  
    15611561        s = set_add(&acc->set, "display_name", NULL, skype_set_display_name,
    15621562                acc);
    1563         s->flags |= SET_NOSAVE | ACC_SET_ONLINE_ONLY;
     1563        s->flags |= ACC_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 |= SET_NOSAVE | ACC_SET_ONLINE_ONLY;
     1569        s->flags |= ACC_SET_NOSAVE | ACC_SET_ONLINE_ONLY;
    15701570
    15711571        s = set_add(&acc->set, "balance", NULL, skype_set_balance, acc);
    1572         s->flags |= SET_NOSAVE | ACC_SET_ONLINE_ONLY;
     1572        s->flags |= ACC_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

    re31e5b8 r9b2a8c1  
    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
    4149
    4250#define TXL_STATUS 1
  • root_commands.c

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

    re31e5b8 r9b2a8c1  
    4545typedef enum
    4646{
    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. */
     47        SET_NOSAVE = 0x0001,
     48        SET_NULL_OK = 0x0100,
     49        SET_HIDDEN = 0x0200,
     50        SET_PASSWORD = 0x0400,
     51        SET_HIDDEN_DEFAULT = 0x0800,
    5252} set_flags_t;
    5353
  • storage.c

    re31e5b8 r9b2a8c1  
    195195        return ret;
    196196}
     197
     198#if 0
     199Not using this yet. Test thoroughly before adding UI hooks to this function.
     200
     201storage_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

    re31e5b8 r9b2a8c1  
    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
    6163void register_storage_backend(storage_t *);
    6264G_GNUC_MALLOC GList *storage_init(const char *primary, char **migrate);
  • storage_xml.c

    re31e5b8 r9b2a8c1  
    22  * BitlBee -- An IRC to other IM-networks gateway                     *
    33  *                                                                    *
    4   * Copyright 2002-2012 Wilmer van der Gaast and others                *
     4  * Copyright 2002-2006 Wilmer van der Gaast and others                *
    55  \********************************************************************/
    66
     
    2929#include "arc.h"
    3030#include "md5.h"
    31 #include "xmltree.h"
    32 
     31
     32#if GLIB_CHECK_VERSION(2,8,0)
    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
    3439
    3540typedef enum
     
    4247
    4348/* To make it easier later when extending the format: */
    44 #define XML_FORMAT_VERSION "1"
     49#define XML_FORMAT_VERSION 1
    4550
    4651struct xml_parsedata
    4752{
    4853        irc_t *irc;
    49         char given_nick[MAX_NICK_LENGTH+1];
     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;
    5059        char *given_pass;
     60        xml_pass_st pass_st;
     61        int unknown_tag;
     62};
     63
     64static 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
     75static 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
     84static 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
     282static 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
     306static 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
     338GMarkupParser xml_parser =
     339{
     340        xml_start_element,
     341        xml_end_element,
     342        xml_text,
     343        NULL,
     344        NULL
    51345};
    52346
     
    60354}
    61355
    62 static void handle_settings( struct xt_node *node, set_t **head )
    63 {
    64         struct xt_node *c;
    65        
    66         for( c = node->children; ( c = xt_find_node( c, "setting" ) ); c = c->next )
    67         {
    68                 char *name = xt_find_attr( c, "name" );
    69                
    70                 if( !name )
    71                         continue;
    72                
    73                 if( strcmp( node->name, "account" ) == 0 )
    74                 {
    75                         set_t *s = set_find( head, name );
    76                         if( s && ( s->flags & ACC_SET_ONLINE_ONLY ) )
    77                                 continue; /* U can't touch this! */
    78                 }
    79                 set_setstr( head, name, c->text );
    80         }
    81 }
    82 
    83 static xt_status handle_account( struct xt_node *node, gpointer data )
    84 {
    85         struct xml_parsedata *xd = data;
    86         char *protocol, *handle, *server, *password = NULL, *autoconnect, *tag;
    87         char *pass_b64 = NULL;
    88         unsigned char *pass_cr = NULL;
    89         int pass_len;
    90         struct prpl *prpl = NULL;
     356static storage_status_t xml_load_real( irc_t *irc, const char *my_nick, const char *password, xml_pass_st action )
     357{
     358        GMarkupParseContext *ctx;
     359        struct xml_parsedata *xd;
     360        char *fn, buf[512];
     361        GError *gerr = NULL;
     362        int fd, st;
     363       
     364        xd = g_new0( struct xml_parsedata, 1 );
     365        xd->irc = irc;
     366        xd->given_nick = g_strdup( my_nick );
     367        xd->given_pass = g_strdup( password );
     368        xd->pass_st = action;
     369        nick_lc( xd->given_nick );
     370       
     371        fn = g_strdup_printf( "%s%s%s", global.conf->configdir, xd->given_nick, ".xml" );
     372        if( ( fd = open( fn, O_RDONLY ) ) < 0 )
     373        {
     374                xml_destroy_xd( xd );
     375                g_free( fn );
     376                return STORAGE_NO_SUCH_USER;
     377        }
     378        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;
     416}
     417
     418static storage_status_t xml_load( irc_t *irc, const char *password )
     419{
     420        return xml_load_real( irc, irc->user->nick, password, XML_PASS_UNKNOWN );
     421}
     422
     423static storage_status_t xml_check_pass( const char *my_nick, const char *password )
     424{
     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... */
     427        return xml_load_real( NULL, my_nick, password, XML_PASS_CHECK_ONLY );
     428}
     429
     430static 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
     452static gboolean xml_save_nick( gpointer key, gpointer value, gpointer data );
     453
     454static storage_status_t xml_save( irc_t *irc, int overwrite )
     455{
     456        char path[512], *path2, *pass_buf = NULL;
     457        set_t *set;
    91458        account_t *acc;
    92         struct xt_node *c;
    93        
    94         handle = xt_find_attr( node, "handle" );
    95         pass_b64 = xt_find_attr( node, "password" );
    96         server = xt_find_attr( node, "server" );
    97         autoconnect = xt_find_attr( node, "autoconnect" );
    98         tag = xt_find_attr( node, "tag" );
    99        
    100         protocol = xt_find_attr( node, "protocol" );
    101         if( protocol )
    102                 prpl = find_protocol( protocol );
    103        
    104         if( !handle || !pass_b64 || !protocol || !prpl )
    105                 return XT_ABORT;
    106         else if( ( pass_len = base64_decode( pass_b64, (unsigned char**) &pass_cr ) ) &&
    107                  arc_decode( pass_cr, pass_len, &password, xd->given_pass ) >= 0 )
    108         {
    109                 acc = account_add( xd->irc->b, prpl, handle, password );
    110                 if( server )
    111                         set_setstr( &acc->set, "server", server );
    112                 if( autoconnect )
    113                         set_setstr( &acc->set, "auto_connect", autoconnect );
    114                 if( tag )
    115                         set_setstr( &acc->set, "tag", tag );
    116         }
    117         else
    118                 return XT_ABORT;
    119        
    120         g_free( pass_cr );
    121         g_free( password );
    122        
    123         handle_settings( node, &acc->set );
    124        
    125         for( c = node->children; ( c = xt_find_node( c, "buddy" ) ); c = c->next )
    126         {
    127                 char *handle, *nick;
    128                
    129                 handle = xt_find_attr( c, "handle" );
    130                 nick = xt_find_attr( c, "nick" );
    131                
    132                 if( handle && nick )
    133                         nick_set_raw( acc, handle, nick );
    134                 else
    135                         return XT_ABORT;
    136         }       
    137         return XT_HANDLED;
    138 }
    139 
    140 static xt_status handle_channel( struct xt_node *node, gpointer data )
    141 {
    142         struct xml_parsedata *xd = data;
    143         irc_channel_t *ic;
    144         char *name, *type;
    145        
    146         name = xt_find_attr( node, "name" );
    147         type = xt_find_attr( node, "type" );
    148        
    149         if( !name || !type )
    150                 return XT_ABORT;
    151        
    152         /* The channel may exist already, for example if it's &bitlbee.
    153            Also, it's possible that the user just reconnected and the
    154            IRC client already rejoined all channels it was in. They
    155            should still get the right settings. */
    156         if( ( ic = irc_channel_by_name( xd->irc, name ) ) ||
    157             ( ic = irc_channel_new( xd->irc, name ) ) )
    158                 set_setstr( &ic->set, "type", type );
    159        
    160         handle_settings( node, &ic->set );
    161        
    162         return XT_HANDLED;
    163 }
    164 
    165 static const struct xt_handler_entry handlers[] = {
    166         { "account", "user", handle_account, },
    167         { "channel", "user", handle_channel, },
    168         { NULL,      NULL,   NULL, },
    169 };
    170 
    171 static storage_status_t xml_load_real( irc_t *irc, const char *my_nick, const char *password, xml_pass_st action )
    172 {
    173         struct xml_parsedata xd[1];
    174         char *fn, buf[2048];
    175         int fd, st;
    176         struct xt_parser *xp;
    177         struct xt_node *node;
    178         storage_status_t ret = STORAGE_OTHER_ERROR;
    179        
    180         xd->irc = irc;
    181         strncpy( xd->given_nick, my_nick, MAX_NICK_LENGTH );
    182         xd->given_nick[MAX_NICK_LENGTH] = '\0';
    183         nick_lc( xd->given_nick );
    184         xd->given_pass = password;
    185        
    186         fn = g_strconcat( global.conf->configdir, xd->given_nick, ".xml", NULL );
    187         if( ( fd = open( fn, O_RDONLY ) ) < 0 )
    188         {
    189                 ret = STORAGE_NO_SUCH_USER;
    190                 goto error;
    191         }
    192        
    193         xp = xt_new( handlers, xd );
    194         while( ( st = read( fd, buf, sizeof( buf ) ) ) > 0 )
    195         {
    196                 st = xt_feed( xp, buf, st );
    197                 if( st != 1 )
    198                         break;
    199         }
    200         close( fd );
    201         if( st != 0 )
    202                 goto error;
    203        
    204         node = xp->root;
    205         if( node == NULL || node->next != NULL || strcmp( node->name, "user" ) != 0 )
    206                 goto error;
    207        
    208         {
    209                 char *nick = xt_find_attr( node, "nick" );
    210                 char *pass = xt_find_attr( node, "password" );
    211                
    212                 if( !nick || !pass )
    213                 {
    214                         goto error;
    215                 }
    216                 else if( ( st = md5_verify_password( xd->given_pass, pass ) ) != 0 )
    217                 {
    218                         ret = STORAGE_INVALID_PASSWORD;
    219                         goto error;
    220                 }
    221         }
    222        
    223         if( action == XML_PASS_CHECK_ONLY )
    224         {
    225                 ret = STORAGE_OK;
    226                 goto error;
    227         }
    228        
    229         /* DO NOT call xt_handle() before verifying the password! */
    230         if( xt_handle( xp, NULL, 1 ) == XT_HANDLED )
    231                 ret = STORAGE_OK;
    232        
    233         handle_settings( node, &xd->irc->b->set );
    234        
    235 error:
    236         xt_free( xp );
    237         g_free( fn );
    238         return ret;
    239 }
    240 
    241 static storage_status_t xml_load( irc_t *irc, const char *password )
    242 {
    243         return xml_load_real( irc, irc->user->nick, password, XML_PASS_UNKNOWN );
    244 }
    245 
    246 static storage_status_t xml_check_pass( const char *my_nick, const char *password )
    247 {
    248         return xml_load_real( NULL, my_nick, password, XML_PASS_CHECK_ONLY );
    249 }
    250 
    251 
    252 static gboolean xml_generate_nick( gpointer key, gpointer value, gpointer data );
    253 static void xml_generate_settings( struct xt_node *cur, set_t **head );
    254 
    255 struct xt_node *xml_generate( irc_t *irc )
    256 {
    257         char *pass_buf = NULL;
    258         account_t *acc;
     459        int fd;
    259460        md5_byte_t pass_md5[21];
    260461        md5_state_t md5_state;
    261462        GSList *l;
    262         struct xt_node *root, *cur;
     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        }
    263478       
    264479        /* Generate a salted md5sum of the password. Use 5 bytes for the salt
     
    273488        pass_buf = base64_encode( pass_md5, 21 );
    274489       
    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 );
     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;
    279492       
    280493        g_free( pass_buf );
    281494       
    282         xml_generate_settings( cur, &irc->b->set );
     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;
    283499       
    284500        for( acc = irc->b->accounts; acc; acc = acc->next )
     
    292508                g_free( pass_cr );
    293509               
    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                
     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                }
    303517                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;
    304528               
    305529                /* This probably looks pretty strange. g_hash_table_foreach
    306530                   is quite a PITA already (but it can't get much better in
    307                    C without using #define, I'm afraid), and it
     531                   C without using #define, I'm afraid), and since it
    308532                   doesn't seem to be possible to abort the foreach on write
    309533                   errors, so instead let's use the _find function and
    310534                   return TRUE on write errors. Which means, if we found
    311535                   something, there was an 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 );
     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;
    317541        }
    318542       
     
    324548                        continue;
    325549               
    326                 cur = xt_new_node( "channel", NULL, NULL );
    327                 xt_add_attr( cur, "name", ic->name );
    328                 xt_add_attr( cur, "type", set_getstr( &ic->set, "type" ) );
    329                
    330                 xml_generate_settings( cur, &ic->set );
    331                
    332                 xt_add_child( root, cur );
    333         }
    334        
    335         return root;
    336 }
    337 
    338 static gboolean xml_generate_nick( gpointer key, gpointer value, gpointer data )
    339 {
    340         struct xt_node *node = xt_new_node( "buddy", NULL, NULL );
    341         xt_add_attr( node, "handle", key );
    342         xt_add_attr( node, "nick", value );
    343         xt_add_child( (struct xt_node *) data, node );
    344        
    345         return FALSE;
    346 }
    347 
    348 static void xml_generate_settings( struct xt_node *cur, set_t **head )
    349 {
    350         set_t *set;
    351        
    352         for( set = *head; set; set = set->next )
    353                 if( set->value && !( set->flags & SET_NOSAVE ) )
    354                 {
    355                         struct xt_node *xset;
    356                         xt_add_child( cur, xset = xt_new_node( "setting", set->value, NULL ) );
    357                         xt_add_attr( xset, "name", set->key );
    358                 }
    359 }
    360 
    361 static storage_status_t xml_save( irc_t *irc, int overwrite )
    362 {
    363         storage_status_t ret = STORAGE_OK;
    364         char path[512], *path2 = NULL, *xml = NULL;
    365         struct xt_node *tree = NULL;
    366         size_t len;
    367         int fd;
    368        
    369         path2 = g_strdup( irc->user->nick );
    370         nick_lc( path2 );
    371         g_snprintf( path, sizeof( path ) - 20, "%s%s%s", global.conf->configdir, path2, ".xml" );
    372         g_free( path2 );
    373        
    374         if( !overwrite && g_access( path, F_OK ) == 0 )
    375                 return STORAGE_ALREADY_EXISTS;
    376        
    377         strcat( path, ".XXXXXX" );
    378         if( ( fd = mkstemp( path ) ) < 0 )
    379         {
    380                 irc_rootmsg( irc, "Error while opening configuration file." );
    381                 return STORAGE_OTHER_ERROR;
    382         }
    383        
    384         tree = xml_generate( irc );
    385         xml = xt_to_string_i( tree );
    386         len = strlen( xml );
    387         if( write( fd, xml, len ) != len ||
    388             fsync( fd ) != 0 || /* #559 */
    389             close( fd ) != 0 )
    390                 goto error;
     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 );
    391568       
    392569        path2 = g_strndup( path, strlen( path ) - 7 );
    393570        if( rename( path, path2 ) != 0 )
    394571        {
     572                irc_rootmsg( irc, "Error while renaming temporary configuration file." );
     573               
    395574                g_free( path2 );
    396                 goto error;
    397         }
     575                unlink( path );
     576               
     577                return STORAGE_OTHER_ERROR;
     578        }
     579       
    398580        g_free( path2 );
    399581       
    400         goto finish;
    401 
    402 error:
     582        return STORAGE_OK;
     583
     584write_error:
     585        g_free( pass_buf );
     586       
    403587        irc_rootmsg( irc, "Write error. Disk full?" );
    404         ret = STORAGE_OTHER_ERROR;
    405 
    406 finish:
    407588        close( fd );
    408         unlink( path );
    409         g_free( xml );
    410         xt_free_node( tree );
    411        
    412         return ret;
    413 }
    414 
     589       
     590        return STORAGE_OTHER_ERROR;
     591}
     592
     593static 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}
    415597
    416598static storage_status_t xml_remove( const char *nick, const char *password )
Note: See TracChangeset for help on using the changeset viewer.