Changeset 4a5d885 for protocols/jabber


Ignore:
Timestamp:
2011-07-26T11:58:38Z (13 years ago)
Author:
Wilmer van der Gaast <wilmer@…>
Branches:
master
Children:
1174899
Parents:
59c9adb4
Message:

Working OAuth2 support. Needs some more debugging (error handling is not
great and imc_logout() gets (rightfully) confused when jabber_data is empty).

Location:
protocols/jabber
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • protocols/jabber/jabber.c

    r59c9adb4 r4a5d885  
    9898        struct im_connection *ic = imcb_new( acc );
    9999        struct jabber_data *jd = g_new0( struct jabber_data, 1 );
    100         struct ns_srv_reply **srvl = NULL, *srv = NULL;
    101         char *connect_to, *s;
    102         int i;
     100        char *s;
    103101       
    104102        /* For now this is needed in the _connected() handlers if using
     
    139137        jd->node_cache = g_hash_table_new_full( g_str_hash, g_str_equal, NULL, jabber_cache_entry_free );
    140138        jd->buddies = g_hash_table_new( g_str_hash, g_str_equal );
     139       
     140        if( set_getbool( &acc->set, "oauth" ) )
     141        {
     142                /* For the first login with OAuth, we have to authenticate via the browser.
     143                   For subsequent logins, exchange the refresh token for a valid access
     144                   token (even though the last one maybe didn't expire yet). */
     145                if( strncmp( acc->pass, "refresh_token=", 14 ) != 0 )
     146                        sasl_oauth2_init( ic );
     147                else
     148                        sasl_oauth2_refresh( ic, acc->pass + 14 );
     149        }
     150        else
     151                jabber_connect( ic );
     152}
     153
     154/* Separate this from jabber_login() so we can do OAuth first if necessary.
     155   Putting this in io.c would probably be more correct. */
     156void jabber_connect( struct im_connection *ic )
     157{
     158        account_t *acc = ic->acc;
     159        struct jabber_data *jd = ic->proto_data;
     160        int i;
     161        char *connect_to;
     162        struct ns_srv_reply **srvl = NULL, *srv = NULL;
    141163       
    142164        /* Figure out the hostname to connect to. */
     
    280302        if( g_strcasecmp( who, JABBER_XMLCONSOLE_HANDLE ) == 0 )
    281303                return jabber_write( ic, message, strlen( message ) );
     304       
     305        if( g_strcasecmp( who, "jabber_oauth" ) == 0 )
     306        {
     307                if( sasl_oauth2_get_refresh_token( ic, message ) )
     308                {
     309                        return 1;
     310                }
     311                else
     312                {
     313                        imcb_error( ic, "OAuth failure" );
     314                        imc_logout( ic, TRUE );
     315                }
     316        }
    282317       
    283318        if( ( s = strchr( who, '=' ) ) && jabber_chat_by_jid( ic, s + 1 ) )
  • protocols/jabber/jabber.h

    r59c9adb4 r4a5d885  
    9292        char *username;         /* USERNAME@server */
    9393        char *server;           /* username@SERVER -=> server/domain, not hostname */
     94       
     95        char *oauth2_access_token;
    9496       
    9597        /* After changing one of these two (or the priority setting), call
     
    232234#define XMLNS_IBB          "http://jabber.org/protocol/ibb"                      /* XEP-0047 */
    233235
     236/* jabber.c */
     237void jabber_connect( struct im_connection *ic );
     238
    234239/* iq.c */
    235240xt_status jabber_pkt_iq( struct xt_node *node, gpointer data );
     
    316321xt_status sasl_pkt_result( struct xt_node *node, gpointer data );
    317322gboolean sasl_supported( struct im_connection *ic );
     323void sasl_oauth2_init( struct im_connection *ic );
     324int sasl_oauth2_get_refresh_token( struct im_connection *ic, const char *msg );
     325int sasl_oauth2_refresh( struct im_connection *ic, const char *refresh_token );
    318326
    319327/* conference.c */
  • protocols/jabber/sasl.c

    r59c9adb4 r4a5d885  
    7676        xt_add_attr( reply, "xmlns", XMLNS_SASL );
    7777       
    78         if( sup_oauth2 && set_getbool( &ic->acc->set, "oauth" ) )
    79         {
    80                 imcb_log( ic, "Open this URL in your browser to authenticate: %s",
    81                           oauth2_url( &oauth2_service_google,
    82                                       "https://www.googleapis.com/auth/googletalk" ) );
    83                 xt_free_node( reply );
    84                 reply = NULL;
     78        if( set_getbool( &ic->acc->set, "oauth" ) )
     79        {
     80                int len;
     81               
     82                if( !sup_oauth2 )
     83                {
     84                        imcb_error( ic, "OAuth requested, but not supported by server" );
     85                        imc_logout( ic, FALSE );
     86                        xt_free_node( reply );
     87                        return XT_ABORT;
     88                }
     89               
     90                /* X-OAUTH2 is, not *the* standard OAuth2 SASL/XMPP implementation.
     91                   It's currently used by GTalk and vaguely documented on
     92                   http://code.google.com/apis/cloudprint/docs/rawxmpp.html . */
     93                xt_add_attr( reply, "mechanism", "X-OAUTH2" );
     94               
     95                len = strlen( jd->username ) + strlen( jd->oauth2_access_token ) + 2;
     96                s = g_malloc( len + 1 );
     97                s[0] = 0;
     98                strcpy( s + 1, jd->username );
     99                strcpy( s + 2 + strlen( jd->username ), jd->oauth2_access_token );
     100                reply->text = base64_encode( (unsigned char *)s, len );
     101                reply->text_len = strlen( reply->text );
     102                g_free( s );
    85103        }
    86104        else if( sup_digest )
     
    358376        return ( jd->xt && jd->xt->root && xt_find_attr( jd->xt->root, "version" ) ) != 0;
    359377}
     378
     379void sasl_oauth2_init( struct im_connection *ic )
     380{
     381        char *msg, *url;
     382       
     383        imcb_log( ic, "Starting OAuth authentication" );
     384       
     385        /* Temporary contact, just used to receive the OAuth response. */
     386        imcb_add_buddy( ic, "jabber_oauth", NULL );
     387        url = oauth2_url( &oauth2_service_google,
     388                          "https://www.googleapis.com/auth/googletalk" );
     389        msg = g_strdup_printf( "Open this URL in your browser to authenticate: %s", url );
     390        imcb_buddy_msg( ic, "jabber_oauth", msg, 0, 0 );
     391        imcb_buddy_msg( ic, "jabber_oauth", "Respond to this message with the returned "
     392                                            "authorization token.", 0, 0 );
     393       
     394        g_free( msg );
     395        g_free( url );
     396}
     397
     398static gboolean sasl_oauth2_remove_contact( gpointer data, gint fd, b_input_condition cond )
     399{
     400        struct im_connection *ic = data;
     401        imcb_remove_buddy( ic, "jabber_oauth", NULL );
     402        return FALSE;
     403}
     404
     405static void sasl_oauth2_got_token( gpointer data, const char *access_token, const char *refresh_token );
     406
     407int sasl_oauth2_get_refresh_token( struct im_connection *ic, const char *msg )
     408{
     409        char *code;
     410        int ret;
     411       
     412        imcb_log( ic, "Requesting OAuth access token" );
     413       
     414        /* Don't do it here because the caller may get confused if the contact
     415           we're currently sending a message to is deleted. */
     416        b_timeout_add( 1, sasl_oauth2_remove_contact, ic );
     417       
     418        code = g_strdup( msg );
     419        g_strstrip( code );
     420        ret = oauth2_access_token( &oauth2_service_google, OAUTH2_AUTH_CODE,
     421                                   code, sasl_oauth2_got_token, ic );
     422       
     423        g_free( code );
     424        return ret;
     425}
     426
     427int sasl_oauth2_refresh( struct im_connection *ic, const char *refresh_token )
     428{
     429        return oauth2_access_token( &oauth2_service_google, OAUTH2_AUTH_REFRESH,
     430                                    refresh_token, sasl_oauth2_got_token, ic );
     431}
     432
     433static void sasl_oauth2_got_token( gpointer data, const char *access_token, const char *refresh_token )
     434{
     435        struct im_connection *ic = data;
     436        struct jabber_data *jd;
     437       
     438        if( g_slist_find( jabber_connections, ic ) == NULL )
     439                return;
     440       
     441        jd = ic->proto_data;
     442       
     443        if( access_token == NULL )
     444        {
     445                imcb_error( ic, "OAuth failure (missing access token)" );
     446                imc_logout( ic, TRUE );
     447        }
     448        if( refresh_token != NULL )
     449        {
     450                g_free( ic->acc->pass );
     451                ic->acc->pass = g_strdup_printf( "refresh_token=%s", refresh_token );
     452        }
     453       
     454        g_free( jd->oauth2_access_token );
     455        jd->oauth2_access_token = g_strdup( access_token );
     456       
     457        jabber_connect( ic );
     458}
Note: See TracChangeset for help on using the changeset viewer.