Changes in / [6e9ae72:17f057d]


Ignore:
Files:
2 deleted
10 edited

Legend:

Unmodified
Added
Removed
  • doc/user-guide/commands.xml

    r6e9ae72 r17f057d  
    11261126                <description>
    11271127                        <para>
    1128                                 This enables OAuth authentication for accounts that support it; right now Twitter and Google Talk (if you have 2-factor authentication enabled on your account) support it.
    1129                         </para>
    1130 
    1131                         <para>
    1132                                 With OAuth enabled, you shouldn't tell BitlBee your Twitter password. Just add your account with a bogus password and type <emphasis>account on</emphasis>. BitlBee will then give you a URL to authenticate with the service. If this succeeds, you will get a PIN code which you can give back to BitlBee to finish the process.
    1133                         </para>
    1134 
    1135                         <para>
    1136                                 The resulting access token will be saved permanently, so you have to do this only once. If for any reason you want to/have to reauthenticate, you can use <emphasis>account set</emphasis> to reset the account password to something random.
     1128                                This enables OAuth authentication for Twitter accounts. From June 2010 this will be mandatory.
     1129                        </para>
     1130
     1131                        <para>
     1132                                With OAuth enabled, you shouldn't tell BitlBee your Twitter password. Just add your account with a bogus password and type <emphasis>account on</emphasis>. BitlBee will then give you a URL to authenticate with Twitter. If this succeeds, Twitter will return a PIN code which you can give back to BitlBee to finish the process.
     1133                        </para>
     1134
     1135                        <para>
     1136                                The resulting access token will be saved permanently, so you have to do this only once.
    11371137                        </para>
    11381138                </description>
  • irc_im.c

    r6e9ae72 r17f057d  
    442442{
    443443        irc_user_t *iu = data;
    444         char *msg;
     444        char *msg = g_string_free( iu->pastebuf, FALSE );
    445445        GSList *l;
    446        
    447         msg = g_string_free( iu->pastebuf, FALSE );
    448         iu->pastebuf = NULL;
    449         iu->pastebuf_timer = 0;
    450446       
    451447        for( l = irc_plugins; l; l = l->next )
     
    474470       
    475471        g_free( msg );
     472        iu->pastebuf = NULL;
     473        iu->pastebuf_timer = 0;
    476474       
    477475        return FALSE;
  • lib/Makefile

    r6e9ae72 r17f057d  
    1313
    1414# [SH] Program variables
    15 objects = arc.o base64.o $(DES) $(EVENT_HANDLER) ftutil.o http_client.o ini.o md5.o misc.o oauth.o oauth2.o proxy.o sha1.o $(SSL_CLIENT) url.o xmltree.o
     15objects = arc.o base64.o $(DES) $(EVENT_HANDLER) ftutil.o http_client.o ini.o md5.o misc.o oauth.o proxy.o sha1.o $(SSL_CLIENT) url.o xmltree.o
    1616
    1717LFLAGS += -r
  • lib/md5.c

    r6e9ae72 r17f057d  
    2424#include <sys/types.h>
    2525#include <string.h>             /* for memcpy() */
    26 #include <stdio.h>
    2726#include "md5.h"
    2827
     
    161160        memcpy(digest, ctx->buf, 16);
    162161        memset(ctx, 0, sizeof(ctx));    /* In case it's sensitive */
    163 }
    164 
    165 void md5_finish_ascii(struct MD5Context *context, char *ascii)
    166 {
    167         md5_byte_t bin[16];
    168         int i;
    169        
    170         md5_finish(context, bin);
    171         for (i = 0; i < 16; i ++)
    172                 sprintf(ascii + i * 2, "%02x", bin[i]);
    173162}
    174163
  • lib/md5.h

    r6e9ae72 r17f057d  
    4343G_MODULE_EXPORT void md5_append(struct MD5Context *context, const md5_byte_t *buf, unsigned int len);
    4444G_MODULE_EXPORT void md5_finish(struct MD5Context *context, md5_byte_t digest[16]);
    45 G_MODULE_EXPORT void md5_finish_ascii(struct MD5Context *context, char *ascii);
    4645
    4746#endif
  • lib/oauth.c

    r6e9ae72 r17f057d  
    122122        char *item;
    123123       
    124         if( !key || !value )
    125                 return;
    126        
    127124        item = g_strdup_printf( "%s=%s", key, value );
    128125        *params = g_slist_insert_sorted( *params, item, (GCompareFunc) strcmp );
     
    168165}
    169166
    170 void oauth_params_parse( GSList **params, char *in )
     167static void oauth_params_parse( GSList **params, char *in )
    171168{
    172169        char *amp, *eq, *s;
  • lib/oauth.h

    r6e9ae72 r17f057d  
    9292
    9393/* For reading misc. data. */
    94 void oauth_params_add( GSList **params, const char *key, const char *value );
    95 void oauth_params_parse( GSList **params, char *in );
    96 void oauth_params_free( GSList **params );
    97 char *oauth_params_string( GSList *params );
    9894const char *oauth_params_get( GSList **params, const char *key );
  • protocols/jabber/jabber.c

    r6e9ae72 r17f057d  
    6060        s = set_add( &acc->set, "activity_timeout", "600", set_eval_int, acc );
    6161       
    62         s = set_add( &acc->set, "oauth", "false", set_eval_bool, acc );
    63 
    6462        g_snprintf( str, sizeof( str ), "%d", jabber_port_list[0] );
    6563        s = set_add( &acc->set, "port", str, set_eval_int, acc );
     
    10199        struct im_connection *ic = imcb_new( acc );
    102100        struct jabber_data *jd = g_new0( struct jabber_data, 1 );
    103         char *s;
     101        struct ns_srv_reply **srvl = NULL, *srv = NULL;
     102        char *connect_to, *s;
     103        int i;
    104104       
    105105        /* For now this is needed in the _connected() handlers if using
     
    138138        }
    139139       
     140        /* This code isn't really pretty. Backwards compatibility never is... */
     141        s = acc->server;
     142        while( s )
     143        {
     144                static int had_port = 0;
     145               
     146                if( strncmp( s, "ssl", 3 ) == 0 )
     147                {
     148                        set_setstr( &acc->set, "ssl", "true" );
     149                       
     150                        /* Flush this part so that (if this was the first
     151                           part of the server string) acc->server gets
     152                           flushed. We don't want to have to do this another
     153                           time. :-) */
     154                        *s = 0;
     155                        s ++;
     156                       
     157                        /* Only set this if the user didn't specify a custom
     158                           port number already... */
     159                        if( !had_port )
     160                                set_setint( &acc->set, "port", 5223 );
     161                }
     162                else if( isdigit( *s ) )
     163                {
     164                        int i;
     165                       
     166                        /* The first character is a digit. It could be an
     167                           IP address though. Only accept this as a port#
     168                           if there are only digits. */
     169                        for( i = 0; isdigit( s[i] ); i ++ );
     170                       
     171                        /* If the first non-digit character is a colon or
     172                           the end of the string, save the port number
     173                           where it should be. */
     174                        if( s[i] == ':' || s[i] == 0 )
     175                        {
     176                                sscanf( s, "%d", &i );
     177                                set_setint( &acc->set, "port", i );
     178                               
     179                                /* See above. */
     180                                *s = 0;
     181                                s ++;
     182                        }
     183                       
     184                        had_port = 1;
     185                }
     186               
     187                s = strchr( s, ':' );
     188                if( s )
     189                {
     190                        *s = 0;
     191                        s ++;
     192                }
     193        }
     194       
    140195        jd->node_cache = g_hash_table_new_full( g_str_hash, g_str_equal, NULL, jabber_cache_entry_free );
    141196        jd->buddies = g_hash_table_new( g_str_hash, g_str_equal );
    142        
    143         if( set_getbool( &acc->set, "oauth" ) )
    144         {
    145                 jd->fd = jd->r_inpa = jd->w_inpa = -1;
    146                
    147                 /* For the first login with OAuth, we have to authenticate via the browser.
    148                    For subsequent logins, exchange the refresh token for a valid access
    149                    token (even though the last one maybe didn't expire yet). */
    150                 if( strncmp( acc->pass, "refresh_token=", 14 ) != 0 )
    151                 {
    152                         sasl_oauth2_init( ic );
    153                         ic->flags |= OPT_SLOW_LOGIN;
    154                 }
    155                 else
    156                         sasl_oauth2_refresh( ic, acc->pass + 14 );
    157         }
    158         else
    159                 jabber_connect( ic );
    160 }
    161 
    162 /* Separate this from jabber_login() so we can do OAuth first if necessary.
    163    Putting this in io.c would probably be more correct. */
    164 void jabber_connect( struct im_connection *ic )
    165 {
    166         account_t *acc = ic->acc;
    167         struct jabber_data *jd = ic->proto_data;
    168         int i;
    169         char *connect_to;
    170         struct ns_srv_reply **srvl = NULL, *srv = NULL;
    171197       
    172198        /* Figure out the hostname to connect to. */
     
    293319        xt_free( jd->xt );
    294320       
    295         g_free( jd->oauth2_access_token );
    296321        g_free( jd->away_message );
    297322        g_free( jd->username );
     
    311336        if( g_strcasecmp( who, JABBER_XMLCONSOLE_HANDLE ) == 0 )
    312337                return jabber_write( ic, message, strlen( message ) );
    313        
    314         if( g_strcasecmp( who, "jabber_oauth" ) == 0 )
    315         {
    316                 if( sasl_oauth2_get_refresh_token( ic, message ) )
    317                 {
    318                         return 1;
    319                 }
    320                 else
    321                 {
    322                         imcb_error( ic, "OAuth failure" );
    323                         imc_logout( ic, TRUE );
    324                         return 0;
    325                 }
    326         }
    327338       
    328339        if( ( s = strchr( who, '=' ) ) && jabber_chat_by_jid( ic, s + 1 ) )
  • protocols/jabber/jabber.h

    r6e9ae72 r17f057d  
    4747        JFLAG_XMLCONSOLE = 64,          /* If the user added an xmlconsole buddy. */
    4848        JFLAG_STARTTLS_DONE = 128,      /* If a plaintext session was converted to TLS. */
    49        
    50         JFLAG_SASL_FB = 0x10000,        /* Trying Facebook authentication. */
    5149} jabber_flags_t;
    5250
     
    9492        char *username;         /* USERNAME@server */
    9593        char *server;           /* username@SERVER -=> server/domain, not hostname */
    96        
    97         char *oauth2_access_token;
    9894       
    9995        /* After changing one of these two (or the priority setting), call
     
    236232#define XMLNS_IBB          "http://jabber.org/protocol/ibb"                      /* XEP-0047 */
    237233
    238 /* jabber.c */
    239 void jabber_connect( struct im_connection *ic );
    240 
    241234/* iq.c */
    242235xt_status jabber_pkt_iq( struct xt_node *node, gpointer data );
     
    323316xt_status sasl_pkt_result( struct xt_node *node, gpointer data );
    324317gboolean sasl_supported( struct im_connection *ic );
    325 void sasl_oauth2_init( struct im_connection *ic );
    326 int sasl_oauth2_get_refresh_token( struct im_connection *ic, const char *msg );
    327 int sasl_oauth2_refresh( struct im_connection *ic, const char *refresh_token );
    328318
    329319/* conference.c */
  • protocols/jabber/sasl.c

    r6e9ae72 r17f057d  
    2626#include "jabber.h"
    2727#include "base64.h"
    28 #include "oauth2.h"
    29 #include "oauth.h"
    3028
    3129xt_status sasl_pkt_mechanisms( struct xt_node *node, gpointer data )
     
    3533        struct xt_node *c, *reply;
    3634        char *s;
    37         int sup_plain = 0, sup_digest = 0, sup_oauth2 = 0, sup_fb = 0;
     35        int sup_plain = 0, sup_digest = 0;
    3836       
    3937        if( !sasl_supported( ic ) )
     
    6159                if( c->text && g_strcasecmp( c->text, "DIGEST-MD5" ) == 0 )
    6260                        sup_digest = 1;
    63                 if( c->text && g_strcasecmp( c->text, "X-OAUTH2" ) == 0 )
    64                         sup_oauth2 = 1;
    65                 if( c->text && g_strcasecmp( c->text, "X-FACEBOOK-PLATFORM" ) == 0 )
    66                         sup_fb = 1;
    6761               
    6862                c = c->next;
     
    7973        xt_add_attr( reply, "xmlns", XMLNS_SASL );
    8074       
    81         if( set_getbool( &ic->acc->set, "oauth" ) )
    82         {
    83                 int len;
    84                
    85                 if( !sup_oauth2 )
    86                 {
    87                         imcb_error( ic, "OAuth requested, but not supported by server" );
    88                         imc_logout( ic, FALSE );
    89                         xt_free_node( reply );
    90                         return XT_ABORT;
    91                 }
    92                
    93                 /* X-OAUTH2 is, not *the* standard OAuth2 SASL/XMPP implementation.
    94                    It's currently used by GTalk and vaguely documented on
    95                    http://code.google.com/apis/cloudprint/docs/rawxmpp.html . */
    96                 xt_add_attr( reply, "mechanism", "X-OAUTH2" );
    97                
    98                 len = strlen( jd->username ) + strlen( jd->oauth2_access_token ) + 2;
    99                 s = g_malloc( len + 1 );
    100                 s[0] = 0;
    101                 strcpy( s + 1, jd->username );
    102                 strcpy( s + 2 + strlen( jd->username ), jd->oauth2_access_token );
    103                 reply->text = base64_encode( (unsigned char *)s, len );
    104                 reply->text_len = strlen( reply->text );
    105                 g_free( s );
    106         }
    107         else if( sup_fb && strstr( ic->acc->pass, "session_key=" ) )
    108         {
    109                 xt_add_attr( reply, "mechanism", "X-FACEBOOK-PLATFORM" );
    110                 jd->flags |= JFLAG_SASL_FB;
    111         }
    112         else if( sup_digest )
     75        if( sup_digest )
    11376        {
    11477                xt_add_attr( reply, "mechanism", "DIGEST-MD5" );
     
    13396        }
    13497       
    135         if( reply && !jabber_write_packet( ic, reply ) )
     98        if( !jabber_write_packet( ic, reply ) )
    13699        {
    137100                xt_free_node( reply );
     
    234197        struct im_connection *ic = data;
    235198        struct jabber_data *jd = ic->proto_data;
    236         struct xt_node *reply_pkt = NULL;
     199        struct xt_node *reply = NULL;
    237200        char *nonce = NULL, *realm = NULL, *cnonce = NULL;
    238201        unsigned char cnonce_bin[30];
    239202        char *digest_uri = NULL;
    240203        char *dec = NULL;
    241         char *s = NULL, *reply = NULL;
     204        char *s = NULL;
    242205        xt_status ret = XT_ABORT;
    243206       
     
    247210        dec = frombase64( node->text );
    248211       
    249         if( jd->flags & JFLAG_SASL_FB )
    250         {
    251                 /* Facebook proprietary authentication. Not as useful as it seemed, but
    252                    the code's written now, may as well keep it..
    253                    
    254                    Mechanism is described on http://developers.facebook.com/docs/chat/
    255                    and in their Python module. It's all mostly useless because the tokens
    256                    expire after 24h. */
    257                 GSList *p_in = NULL, *p_out = NULL, *p;
    258                 md5_state_t md5;
    259                 char time[33], *token;
    260                 const char *secret;
    261                
    262                 oauth_params_parse( &p_in, dec );
    263                 oauth_params_add( &p_out, "nonce", oauth_params_get( &p_in, "nonce" ) );
    264                 oauth_params_add( &p_out, "method", oauth_params_get( &p_in, "method" ) );
    265                 oauth_params_free( &p_in );
    266                
    267                 token = g_strdup( ic->acc->pass );
    268                 oauth_params_parse( &p_in, token );
    269                 g_free( token );
    270                 oauth_params_add( &p_out, "session_key", oauth_params_get( &p_in, "session_key" ) );
    271                
    272                 g_snprintf( time, sizeof( time ), "%lld", (long long) ( gettime() * 1000 ) );
    273                 oauth_params_add( &p_out, "call_id", time );
    274                 oauth_params_add( &p_out, "api_key", oauth2_service_facebook.consumer_key );
    275                 oauth_params_add( &p_out, "v", "1.0" );
    276                 oauth_params_add( &p_out, "format", "XML" );
    277                
    278                 md5_init( &md5 );
    279                 for( p = p_out; p; p = p->next )
    280                         md5_append( &md5, p->data, strlen( p->data ) );
    281                
    282                 secret = oauth_params_get( &p_in, "secret" );
    283                 if( secret )
    284                         md5_append( &md5, (unsigned char*) secret, strlen( secret ) );
    285                 md5_finish_ascii( &md5, time );
    286                 oauth_params_add( &p_out, "sig", time );
    287                
    288                 reply = oauth_params_string( p_out );
    289                 oauth_params_free( &p_out );
    290                 oauth_params_free( &p_in );
    291         }
    292         else if( !( s = sasl_get_part( dec, "rspauth" ) ) )
     212        if( !( s = sasl_get_part( dec, "rspauth" ) ) )
    293213        {
    294214                /* See RFC 2831 for for information. */
     
    351271               
    352272                /* Now build the SASL response string: */
    353                 reply = g_strdup_printf( "username=\"%s\",realm=\"%s\",nonce=\"%s\",cnonce=\"%s\","
    354                                          "nc=%08x,qop=auth,digest-uri=\"%s\",response=%s,charset=%s",
    355                                          jd->username, realm, nonce, cnonce, 1, digest_uri, Hh, "utf-8" );
     273                g_free( dec );
     274                dec = g_strdup_printf( "username=\"%s\",realm=\"%s\",nonce=\"%s\",cnonce=\"%s\","
     275                                       "nc=%08x,qop=auth,digest-uri=\"%s\",response=%s,charset=%s",
     276                                       jd->username, realm, nonce, cnonce, 1, digest_uri, Hh, "utf-8" );
     277                s = tobase64( dec );
    356278        }
    357279        else
    358280        {
    359281                /* We found rspauth, but don't really care... */
    360         }
    361        
    362         s = reply ? tobase64( reply ) : NULL;
    363         reply_pkt = xt_new_node( "response", s, NULL );
    364         xt_add_attr( reply_pkt, "xmlns", XMLNS_SASL );
    365        
    366         if( !jabber_write_packet( ic, reply_pkt ) )
     282                g_free( s );
     283                s = NULL;
     284        }
     285       
     286        reply = xt_new_node( "response", s, NULL );
     287        xt_add_attr( reply, "xmlns", XMLNS_SASL );
     288       
     289        if( !jabber_write_packet( ic, reply ) )
    367290                goto silent_error;
    368291       
     
    378301        g_free( cnonce );
    379302        g_free( nonce );
    380         g_free( reply );
    381303        g_free( realm );
    382304        g_free( dec );
    383305        g_free( s );
    384         xt_free_node( reply_pkt );
     306        xt_free_node( reply );
    385307       
    386308        return ret;
     
    425347        return ( jd->xt && jd->xt->root && xt_find_attr( jd->xt->root, "version" ) ) != 0;
    426348}
    427 
    428 void sasl_oauth2_init( struct im_connection *ic )
    429 {
    430         char *msg, *url;
    431        
    432         imcb_log( ic, "Starting OAuth authentication" );
    433        
    434         /* Temporary contact, just used to receive the OAuth response. */
    435         imcb_add_buddy( ic, "jabber_oauth", NULL );
    436         url = oauth2_url( &oauth2_service_google,
    437                           "https://www.googleapis.com/auth/googletalk" );
    438         msg = g_strdup_printf( "Open this URL in your browser to authenticate: %s", url );
    439         imcb_buddy_msg( ic, "jabber_oauth", msg, 0, 0 );
    440         imcb_buddy_msg( ic, "jabber_oauth", "Respond to this message with the returned "
    441                                             "authorization token.", 0, 0 );
    442        
    443         g_free( msg );
    444         g_free( url );
    445 }
    446 
    447 static gboolean sasl_oauth2_remove_contact( gpointer data, gint fd, b_input_condition cond )
    448 {
    449         struct im_connection *ic = data;
    450         if( g_slist_find( jabber_connections, ic ) )
    451                 imcb_remove_buddy( ic, "jabber_oauth", NULL );
    452         return FALSE;
    453 }
    454 
    455 static void sasl_oauth2_got_token( gpointer data, const char *access_token, const char *refresh_token );
    456 
    457 int sasl_oauth2_get_refresh_token( struct im_connection *ic, const char *msg )
    458 {
    459         char *code;
    460         int ret;
    461        
    462         imcb_log( ic, "Requesting OAuth access token" );
    463        
    464         /* Don't do it here because the caller may get confused if the contact
    465            we're currently sending a message to is deleted. */
    466         b_timeout_add( 1, sasl_oauth2_remove_contact, ic );
    467        
    468         code = g_strdup( msg );
    469         g_strstrip( code );
    470         ret = oauth2_access_token( &oauth2_service_google, OAUTH2_AUTH_CODE,
    471                                    code, sasl_oauth2_got_token, ic );
    472        
    473         g_free( code );
    474         return ret;
    475 }
    476 
    477 int sasl_oauth2_refresh( struct im_connection *ic, const char *refresh_token )
    478 {
    479         return oauth2_access_token( &oauth2_service_google, OAUTH2_AUTH_REFRESH,
    480                                     refresh_token, sasl_oauth2_got_token, ic );
    481 }
    482 
    483 static void sasl_oauth2_got_token( gpointer data, const char *access_token, const char *refresh_token )
    484 {
    485         struct im_connection *ic = data;
    486         struct jabber_data *jd;
    487        
    488         if( g_slist_find( jabber_connections, ic ) == NULL )
    489                 return;
    490        
    491         jd = ic->proto_data;
    492        
    493         if( access_token == NULL )
    494         {
    495                 imcb_error( ic, "OAuth failure (missing access token)" );
    496                 imc_logout( ic, TRUE );
    497                 return;
    498         }
    499         if( refresh_token != NULL )
    500         {
    501                 g_free( ic->acc->pass );
    502                 ic->acc->pass = g_strdup_printf( "refresh_token=%s", refresh_token );
    503         }
    504        
    505         g_free( jd->oauth2_access_token );
    506         jd->oauth2_access_token = g_strdup( access_token );
    507        
    508         jabber_connect( ic );
    509 }
Note: See TracChangeset for help on using the changeset viewer.