Changes in / [96ace1b:728a981]


Ignore:
Files:
18 edited

Legend:

Unmodified
Added
Removed
  • .bzrignore

    r96ace1b r728a981  
    1111encode
    1212bitlbee.pc
     13.gdb_history
  • doc/user-guide/commands.xml

    r96ace1b r728a981  
    197197                <syntax>block &lt;nick&gt;</syntax>
    198198                <syntax>block &lt;connection&gt; &lt;handle&gt;</syntax>
     199                <syntax>block &lt;connection&gt;</syntax>
    199200
    200201                <description>
    201202                        <para>
    202203                                Puts the specified user on your ignore list. Either specify the user's nick when you have him/her in your contact list or a connection number and a user handle.
     204                        </para>
     205                       
     206                        <para>
     207                                When called with only a connection specification as an argument, the command displays the current block list for that connection.
    203208                        </para>
    204209                </description>
     
    214219                                Reverse of block. Unignores the specified user or user handle on specified connection.
    215220                        </para>
     221                       
     222                        <para>
     223                                When called with only a connection specification as an argument, the command displays the current allow list for that connection.
     224                        </para>
    216225                </description>
    217226        </bitlbee-command>
  • help.c

    r96ace1b r728a981  
    116116                h = h->next;
    117117        }
    118         if( h )
     118        if( h && h->length > 0 )
    119119        {
    120120                char *s = g_new( char, h->length + 1 );
     
    123123                {
    124124                        g_free( h );
    125                         *help=NULL;
    126                         return( NULL );
     125                        *help = NULL;
     126                        return NULL;
    127127                }
    128128                mtime = stat->st_mtime;
    129129               
    130                 if( mtime > h->mtime ) {
    131                         return( NULL );
    132                         return( g_strdup( "Help file changed during this session. Please restart to get help back." ) );
    133                 }
     130                if( mtime > h->mtime )
     131                        return NULL;
     132               
    134133                s[h->length] = 0;
    135134                if( h->fd >= 0 )
     
    142141                        strncpy( s, h->offset.mem_offset, h->length );
    143142                }
    144                 return( s );
     143                return s;
    145144        }
    146145       
    147         return( NULL );
     146        return NULL;
    148147}
  • ipc.c

    r96ace1b r728a981  
    8484void ipc_master_cmd_restart( irc_t *data, char **cmd )
    8585{
    86         struct bitlbee_child *child = (void*) data;
    87        
    8886        if( global.conf->runmode != RUNMODE_FORKDAEMON )
    8987        {
  • irc.c

    r96ace1b r728a981  
    232232        irc_connection_list = g_slist_remove( irc_connection_list, irc );
    233233       
    234         for (account = irc->accounts; account; account = account->next)
     234        for (account = irc->accounts; account; account = account->next) {
    235235                if (account->gc)
    236                         signoff(account->gc);
     236                        account_offline(account->gc);
     237                else if (account->reconnect)
     238                        g_source_remove(account->reconnect);
     239        }
    237240       
    238241        g_free(irc->sendbuffer);
  • irc.h

    r96ace1b r728a981  
    3333#define IRC_PING_STRING "PinglBee"
    3434
    35 #define UMODES "iasw"
     35#define UMODES "abisw"
    3636#define UMODES_PRIV "Ro"
    3737#define CMODES "nt"
  • irc_commands.c

    r96ace1b r728a981  
    509509        irc->last_pong = gettime();
    510510        irc->pinging = 0;
     511}
     512
     513static void irc_cmd_version( irc_t *irc, char **cmd )
     514{
     515        irc_reply( irc, 351, "bitlbee-%s. %s :%s/%s ", BITLBEE_VERSION, irc->myhost, ARCH, CPU );
    511516}
    512517
     
    568573        { "motd",        0, irc_cmd_motd,        IRC_CMD_LOGGED_IN },
    569574        { "pong",        0, irc_cmd_pong,        IRC_CMD_LOGGED_IN },
     575        { "version",     0, irc_cmd_version,     IRC_CMD_LOGGED_IN },
    570576        { "completions", 0, irc_cmd_completions, IRC_CMD_LOGGED_IN },
    571577        { "die",         0, NULL,                IRC_CMD_OPER_ONLY | IRC_CMD_TO_MASTER },
  • protocols/jabber/jabber.c

    r96ace1b r728a981  
    12321232        }
    12331233
    1234         x = jutil_presnew(0, NULL, "Online");
    1235         gjab_send(gjc, x);
    1236         xmlnode_free(x);
     1234        account_online(GJ_GC(gjc));
    12371235}
    12381236
     
    12501248                } else {
    12511249                        gjab_reqroster(gjc);
    1252                         account_online(GJ_GC(gjc));
    12531250                       
    12541251                        ((struct jabber_data *)GJ_GC(gjc)->proto_data)->did_import = TRUE;
  • protocols/msn/msn.h

    r96ace1b r728a981  
    6666        GSList *msgq;
    6767        GSList *switchboards;
     68        const struct msn_away_state *away_state;
     69       
    6870        int buddycount;
    69         const struct msn_away_state *away_state;
     71        int groupcount;
     72        char **grouplist;
    7073};
    7174
  • protocols/msn/msn_util.c

    r96ace1b r728a981  
    4646int msn_logged_in( struct gaim_connection *gc )
    4747{
    48         struct msn_data *md = gc->proto_data;
    49         char buf[1024];
    50        
    5148        account_online( gc );
    52        
    53         /* account_online() sets an away state if there is any, so only
    54            execute this code if we're not away. */
    55         if( md->away_state == msn_away_state_list )
    56         {
    57                 g_snprintf( buf, sizeof( buf ), "CHG %d %s %d\r\n", ++md->trId, md->away_state->code, 0 );
    58                 return( msn_write( gc, buf, strlen( buf ) ) );
    59         }
    6049       
    6150        return( 0 );
  • protocols/msn/ns.c

    r96ace1b r728a981  
    257257                {
    258258                        md->buddycount = atoi( cmd[3] );
     259                        md->groupcount = atoi( cmd[4] );
     260                        if( md->groupcount > 0 )
     261                                md->grouplist = g_new0( char *, md->groupcount );
    259262                       
    260263                        if( !*cmd[3] || md->buddycount == 0 )
     
    269272                }
    270273        }
    271         else if( strcmp( cmd[0], "GTC" ) == 0 )
    272         {
    273         }
    274         else if( strcmp( cmd[0], "BLP" ) == 0 )
    275         {
    276         }
    277         else if( strcmp( cmd[0], "PRP" ) == 0 )
    278         {
    279         }
    280         else if( strcmp( cmd[0], "LSG" ) == 0 )
    281         {
    282         }
    283274        else if( strcmp( cmd[0], "LST" ) == 0 )
    284275        {
     
    297288                if( list & 1 ) /* FL */
    298289                {
    299                         add_buddy( gc, NULL, cmd[1], cmd[2] );
     290                        char *group = NULL;
     291                        int num;
     292                       
     293                        if( cmd[4] != NULL && sscanf( cmd[4], "%d", &num ) == 1 )
     294                                group = md->grouplist[num];
     295                       
     296                        add_buddy( gc, group, cmd[1], cmd[2] );
    300297                }
    301298                if( list & 2 ) /* AL */
     
    327324                }
    328325        }
    329         else if( strcmp( cmd[0], "BPR" ) == 0 )
    330         {
    331         }
    332         else if( strcmp( cmd[0], "CHG" ) == 0 )
    333         {
     326        else if( strcmp( cmd[0], "LSG" ) == 0 )
     327        {
     328                int num;
     329               
     330                if( num_parts != 4 )
     331                {
     332                        hide_login_progress_error( gc, "Syntax error" );
     333                        signoff( gc );
     334                        return( 0 );
     335                }
     336               
     337                http_decode( cmd[2] );
     338                num = atoi( cmd[1] );
     339               
     340                if( num < md->groupcount )
     341                        md->grouplist[num] = g_strdup( cmd[2] );
    334342        }
    335343        else if( strcmp( cmd[0], "CHL" ) == 0 )
     
    357365                return( msn_write( gc, buf, strlen( buf ) ) );
    358366        }
    359         else if( strcmp( cmd[0], "QRY" ) == 0 )
    360         {
    361         }
    362         else if( strcmp( cmd[0], "QNG" ) == 0 )
    363         {
    364         }
    365367        else if( strcmp( cmd[0], "ILN" ) == 0 )
    366368        {
     
    478480                        msn_buddy_ask( gc, cmd[4], cmd[5] );
    479481                }
    480         }
    481         else if( strcmp( cmd[0], "REM" ) == 0 )
    482         {
    483482        }
    484483        else if( strcmp( cmd[0], "OUT" ) == 0 )
  • protocols/msn/sb.c

    r96ace1b r728a981  
    202202        debug( "Destroying switchboard: %s", sb->who ? sb->who : sb->key ? sb->key : "" );
    203203       
    204         if( sb->key ) g_free( sb->key );
    205         if( sb->who ) g_free( sb->who );
    206        
    207204        if( sb->msgq )
    208205        {
     
    222219                serv_got_crap( gc, "Warning: Closing down MSN switchboard connection with "
    223220                                   "unsent message to %s, you'll have to resend it.",
    224                                    m->who ? m->who : "(unknown)" );
    225         }
     221                                   sb->who ? sb->who : "(unknown)" );
     222        }
     223       
     224        if( sb->key ) g_free( sb->key );
     225        if( sb->who ) g_free( sb->who );
    226226       
    227227        if( sb->chat )
  • protocols/nogaim.c

    r96ace1b r728a981  
    1414 * (except for the function names).
    1515 *
    16  * Copyright 2002-2004 Wilmer van der Gaast <lintux@lintux.cx>
     16 * Copyright 2002-2006 Wilmer van der Gaast <wilmer@gaast.net> and others
    1717 */
    1818
     
    3939#include <iconv.h>
    4040
    41 static char *proto_away_alias[7][5] =
     41static char *proto_away_alias[8][5] =
    4242{
    4343        { "Away from computer", "Away", "Extended away", NULL },
     
    4747        { "On the phone", "Phone", "On phone", NULL },
    4848        { "Out to lunch", "Lunch", "Food", NULL },
     49        { "Invisible", "Hidden" },
    4950        { NULL }
    5051};
     
    305306{
    306307        va_list params;
    307         char text[1024], buf[1024], acc_id[33];
     308        char text[1024], buf[1024], *acc_id;
    308309        char *msg;
    309310        account_t *a;
     
    330331        /* If we found one, add the screenname to the acc_id. */
    331332        if( a )
    332                 g_snprintf( acc_id, 32, "%s(%s)", gc->prpl->name, gc->username );
     333                acc_id = g_strdup_printf( "%s(%s)", gc->prpl->name, gc->username );
    333334        else
    334                 g_snprintf( acc_id, 32, "%s", gc->prpl->name );
     335                acc_id = g_strdup( gc->prpl->name );
    335336       
    336337        irc_usermsg( gc->irc, "%s - %s", acc_id, msg );
     338       
     339        g_free( acc_id );
    337340}
    338341
     
    352355       
    353356        /* MSN servers sometimes redirect you to a different server and do
    354            the whole login sequence again, so subsequent calls to this
     357           the whole login sequence again, so these "late" calls to this
    355358           function should be handled correctly. (IOW, ignored) */
    356359        if( gc->flags & OPT_LOGGED_IN )
     
    364367        gc->flags |= OPT_LOGGED_IN;
    365368       
    366         if( u && u->away ) proto_away( gc, u->away );
    367        
    368         if( !strcmp(gc->prpl->name, "icq") )
     369        /* Also necessary when we're not away, at least for some of the
     370           protocols. */
     371        proto_away( gc, u->away );
     372       
     373        if( strcmp( gc->prpl->name, "ICQ" ) == 0 )
    369374        {
    370375                for( u = gc->irc->users; u; u = u->next )
     
    470475/* list.c */
    471476
    472 int bud_list_cache_exists( struct gaim_connection *gc )
    473 {
    474         return( 0 );
    475 }
    476 
    477 void do_import( struct gaim_connection *gc, void *null )
    478 {
    479         return;
    480 }
    481 
    482477void add_buddy( struct gaim_connection *gc, char *group, char *handle, char *realname )
    483478{
     
    531526        u->gc = gc;
    532527        u->handle = g_strdup( handle );
     528        if( group ) u->group = g_strdup( group );
    533529        u->send_handler = buddy_send_handler;
    534530        u->last_typing_notice = 0;
     
    552548       
    553549        return( b );
    554 }
    555 
    556 void do_export( struct gaim_connection *gc )
    557 {
    558         return;
    559550}
    560551
     
    882873       
    883874        return( c );
    884 }
    885 
    886 void serv_finish_login( struct gaim_connection *gc )
    887 {
    888         return;
    889875}
    890876
  • protocols/oscar/oscar.c

    r96ace1b r728a981  
    356356        if (isdigit(*user->username)) {
    357357                odata->icq = TRUE;
    358                 /* this is odd but it's necessary for a proper do_import and do_export */
     358                /* This is odd but it's necessary for a proper do_import and do_export.
     359                   We don't do those anymore, but let's stick with it, just in case
     360                   it accidentally fixes something else too... */
    359361                gc->password[8] = 0;
    360362        } else {
     
    11481150                reason = msg + 6;
    11491151       
    1150         dialog_msg = g_strdup_printf("The user %u wants to add you to their buddy list for the following reason:\n\n%s", uin, reason ? reason : "No reason given.");
     1152        dialog_msg = g_strdup_printf("The user %u wants to add you to their buddy list for the following reason: %s", uin, reason ? reason : "No reason given.");
    11511153        data->gc = gc;
    11521154        data->uin = uin;
     
    17371739        odata->rights.maxdenies = (guint)maxdenies;
    17381740
    1739 //      serv_finish_login(gc);
    1740 
    1741         if (bud_list_cache_exists(gc))
    1742                 do_import(gc, NULL);
    1743 
    17441741        aim_clientready(sess, fr->conn);
    17451742
     
    20962093        } /* End of for loop */
    20972094
    2098         if (tmp)
    2099                 do_export(gc);
    21002095        aim_ssi_enable(sess, fr->conn);
    21012096       
  • protocols/ssl_openssl.c

    r96ace1b r728a981  
    55  \********************************************************************/
    66
    7 /* SSL module - OpenTLS version                                          */
     7/* SSL module - OpenSSL version                                         */
    88
    99/*
     
    4141struct scd
    4242{
    43         SslInputFunction func;
     43        ssl_input_function func;
    4444        gpointer data;
    4545        int fd;
    4646        gboolean established;
    4747       
     48        int inpa;
     49        int lasterr;            /* Necessary for SSL_get_error */
    4850        SSL *ssl;
    4951        SSL_CTX *ssl_ctx;
     
    5456
    5557
    56 void *ssl_connect( char *host, int port, SslInputFunction func, gpointer data )
     58void *ssl_connect( char *host, int port, ssl_input_function func, gpointer data )
    5759{
    5860        struct scd *conn = g_new0( struct scd, 1 );
     
    9395}
    9496
     97static void ssl_handshake( gpointer data, gint source, GaimInputCondition cond );
     98
    9599static void ssl_connected( gpointer data, gint source, GaimInputCondition cond )
    96100{
     
    98102       
    99103        if( source == -1 )
    100                 goto ssl_connected_failure;
    101        
     104                return ssl_handshake( data, -1, cond );
     105       
     106        /* Make it non-blocking at least during the handshake... */
     107        sock_make_nonblocking( conn->fd );
    102108        SSL_set_fd( conn->ssl, conn->fd );
    103109       
    104         if( SSL_connect( conn->ssl ) < 0 )
    105                 goto ssl_connected_failure;
     110        return ssl_handshake( data, source, cond );
     111}       
     112
     113static void ssl_handshake( gpointer data, gint source, GaimInputCondition cond )
     114{
     115        struct scd *conn = data;
     116        int st;
     117       
     118        if( conn->inpa != -1 )
     119        {
     120                gaim_input_remove( conn->inpa );
     121                conn->inpa = -1;
     122        }
     123       
     124        if( ( st = SSL_connect( conn->ssl ) ) < 0 )
     125        {
     126                conn->lasterr = SSL_get_error( conn->ssl, st );
     127                if( conn->lasterr != SSL_ERROR_WANT_READ && conn->lasterr != SSL_ERROR_WANT_WRITE )
     128                        goto ssl_connected_failure;
     129               
     130                conn->inpa = gaim_input_add( conn->fd, ssl_getdirection( conn ), ssl_handshake, data );
     131                return;
     132        }
    106133       
    107134        conn->established = TRUE;
     135        sock_make_blocking( conn->fd );         /* For now... */
    108136        conn->func( conn->data, conn, cond );
    109137        return;
     
    127155int ssl_read( void *conn, char *buf, int len )
    128156{
     157        int st;
     158       
    129159        if( !((struct scd*)conn)->established )
    130                 return( 0 );
    131        
    132         return( SSL_read( ((struct scd*)conn)->ssl, buf, len ) );
     160        {
     161                ssl_errno = SSL_NOHANDSHAKE;
     162                return -1;
     163        }
     164       
     165        st = SSL_read( ((struct scd*)conn)->ssl, buf, len );
     166       
     167        ssl_errno = SSL_OK;
     168        if( st <= 0 )
     169        {
     170                ((struct scd*)conn)->lasterr = SSL_get_error( ((struct scd*)conn)->ssl, st );
     171                if( ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_READ || ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_WRITE )
     172                        ssl_errno = SSL_AGAIN;
     173        }
     174       
     175        return st;
    133176}
    134177
    135178int ssl_write( void *conn, const char *buf, int len )
    136179{
     180        int st;
     181       
    137182        if( !((struct scd*)conn)->established )
    138                 return( 0 );
    139        
    140         return( SSL_write( ((struct scd*)conn)->ssl, buf, len ) );
     183        {
     184                ssl_errno = SSL_NOHANDSHAKE;
     185                return -1;
     186        }
     187       
     188        st = SSL_write( ((struct scd*)conn)->ssl, buf, len );
     189       
     190        ssl_errno = SSL_OK;
     191        if( st <= 0 )
     192        {
     193                ((struct scd*)conn)->lasterr = SSL_get_error( ((struct scd*)conn)->ssl, st );
     194                if( ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_READ || ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_WRITE )
     195                        ssl_errno = SSL_AGAIN;
     196        }
     197       
     198        return st;
    141199}
    142200
     
    144202{
    145203        struct scd *conn = conn_;
     204       
     205        if( conn->inpa != -1 )
     206                gaim_input_remove( conn->inpa );
    146207       
    147208        if( conn->established )
     
    159220        return( ((struct scd*)conn)->fd );
    160221}
     222
     223GaimInputCondition ssl_getdirection( void *conn )
     224{
     225        return( ((struct scd*)conn)->lasterr == SSL_ERROR_WANT_WRITE ? GAIM_INPUT_WRITE : GAIM_INPUT_READ );
     226}
  • query.c

    r96ace1b r728a981  
    3939        q->no = no;
    4040        q->data = data;
     41       
     42        if( strchr( irc->umode, 'b' ) != NULL )
     43        {
     44                char *s;
     45               
     46                /* At least for the machine-parseable version, get rid of
     47                   newlines to make "parsing" easier. */
     48                for( s = q->question; *s; s ++ )
     49                        if( *s == '\r' || *s == '\n' )
     50                                *s = ' ';
     51        }
    4152       
    4253        if( irc->queries )
     
    127138                disp = 1;
    128139        }
    129         //Using irc_usermsg instead of serv_got_crap because \x02A is a char too, so a SPACE is needed.
    130140        if( ans )
    131141        {
  • root_commands.c

    r96ace1b r728a981  
    484484        account_t *a;
    485485       
    486         if( !cmd[2] )
     486        if( !cmd[2] && ( a = account_get( irc, cmd[1] ) ) && a->gc )
     487        {
     488                char *format;
     489                GSList *l;
     490               
     491                if( strchr( irc->umode, 'b' ) != NULL )
     492                        format = "%s\t%s";
     493                else
     494                        format = "%-32.32  %-16.16s";
     495               
     496                irc_usermsg( irc, format, "Handle", "Nickname" );
     497                for( l = a->gc->deny; l; l = l->next )
     498                {
     499                        user_t *u = user_findhandle( a->gc, l->data );
     500                        irc_usermsg( irc, format, l->data, u ? u->nick : "(none)" );
     501                }
     502                irc_usermsg( irc, "End of list." );
     503               
     504                return;
     505        }
     506        else if( !cmd[2] )
    487507        {
    488508                user_t *u = user_find( irc, cmd[1] );
     
    523543        account_t *a;
    524544       
    525         if( !cmd[2] )
     545        if( !cmd[2] && ( a = account_get( irc, cmd[1] ) ) && a->gc )
     546        {
     547                char *format;
     548                GSList *l;
     549               
     550                if( strchr( irc->umode, 'b' ) != NULL )
     551                        format = "%s\t%s";
     552                else
     553                        format = "%-32.32  %-16.16s";
     554               
     555                irc_usermsg( irc, format, "Handle", "Nickname" );
     556                for( l = a->gc->deny; l; l = l->next )
     557                {
     558                        user_t *u = user_findhandle( a->gc, l->data );
     559                        irc_usermsg( irc, format, l->data, u ? u->nick : "(none)" );
     560                }
     561                irc_usermsg( irc, "End of list." );
     562               
     563                return;
     564        }
     565        else if( !cmd[2] )
    526566        {
    527567                user_t *u = user_find( irc, cmd[1] );
     
    635675        int online = 0, away = 0, offline = 0;
    636676        user_t *u;
    637         char s[64];
     677        char s[256];
     678        char *format;
    638679        int n_online = 0, n_away = 0, n_offline = 0;
    639680       
     
    649690                online =  away = 1;
    650691       
    651         irc_usermsg( irc, "%-16.16s  %-40.40s  %s", "Nick", "User/Host/Network", "Status" );
    652        
    653         if( online == 1 ) for( u = irc->users; u; u = u->next ) if( u->gc && u->online && !u->away )
    654         {
    655                 g_snprintf( s, 63, "%s@%s (%s)", u->user, u->host, u->gc->user->prpl->name );
    656                 irc_usermsg( irc, "%-16.16s  %-40.40s  %s", u->nick, s, "Online" );
     692        if( strchr( irc->umode, 'b' ) != NULL )
     693                format = "%s\t%s\t%s";
     694        else
     695                format = "%-16.16s  %-40.40s  %s";
     696       
     697        irc_usermsg( irc, format, "Nick", "User/Host/Network", "Status" );
     698       
     699        for( u = irc->users; u; u = u->next ) if( u->gc && u->online && !u->away )
     700        {
     701                if( online == 1 )
     702                {
     703                        g_snprintf( s, sizeof( s ) - 1, "%s@%s (%s)", u->user, u->host, u->gc->user->prpl->name );
     704                        irc_usermsg( irc, format, u->nick, s, "Online" );
     705                }
     706               
    657707                n_online ++;
    658708        }
    659709
    660         if( away == 1 ) for( u = irc->users; u; u = u->next ) if( u->gc && u->online && u->away )
    661         {
    662                 g_snprintf( s, 63, "%s@%s (%s)", u->user, u->host, u->gc->user->prpl->name );
    663                 irc_usermsg( irc, "%-16.16s  %-40.40s  %s", u->nick, s, u->away );
     710        for( u = irc->users; u; u = u->next ) if( u->gc && u->online && u->away )
     711        {
     712                if( away == 1 )
     713                {
     714                        g_snprintf( s, sizeof( s ) - 1, "%s@%s (%s)", u->user, u->host, u->gc->user->prpl->name );
     715                        irc_usermsg( irc, format, u->nick, s, u->away );
     716                }
    664717                n_away ++;
    665718        }
    666719       
    667         if( offline == 1 ) for( u = irc->users; u; u = u->next ) if( u->gc && !u->online )
    668         {
    669                 g_snprintf( s, 63, "%s@%s (%s)", u->user, u->host, u->gc->user->prpl->name );
    670                 irc_usermsg( irc, "%-16.16s  %-40.40s  %s", u->nick, s, "Offline" );
     720        for( u = irc->users; u; u = u->next ) if( u->gc && !u->online )
     721        {
     722                if( offline == 1 )
     723                {
     724                        g_snprintf( s, sizeof( s ) - 1, "%s@%s (%s)", u->user, u->host, u->gc->user->prpl->name );
     725                        irc_usermsg( irc, format, u->nick, s, "Offline" );
     726                }
    671727                n_offline ++;
    672728        }
  • user.h

    r96ace1b r728a981  
    3737       
    3838        char *handle;
     39        char *group;
    3940        struct gaim_connection *gc;
    4041
Note: See TracChangeset for help on using the changeset viewer.