Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • lib/oauth2.c

    r3bda2c2 rca8037e  
    22*                                                                           *
    33*  BitlBee - An IRC to IM gateway                                           *
    4 *  Simple OAuth client (consumer) implementation.                           *
     4*  Simple OAuth2 client (consumer) implementation.                          *
    55*                                                                           *
    6 *  Copyright 2010-2011 Wilmer van der Gaast <wilmer@gaast.net>              *
     6*  Copyright 2010-2013 Wilmer van der Gaast <wilmer@gaast.net>              *
    77*                                                                           *
    88*  This program is free software; you can redistribute it and/or modify     *
     
    2222\***************************************************************************/
    2323
     24/* Out of protest, I should rename this file. OAuth2 is a pathetic joke, and
     25   of all things, DEFINITELY NOT A STANDARD. The only thing various OAuth2
     26   implementations have in common is that name, wrongfully stolen from
     27   a pretty nice standard called OAuth 1.0a. That, and the fact that they
     28   use JSON. Wait, no, Facebook's version doesn't use JSON. For some of its
     29   responses.
     30   
     31   Apparently too many people were too retarded to comprehend the elementary
     32   bits of crypto in OAuth 1.0a (took me one afternoon to implement) so
     33   the standard was replaced with what comes down to a complicated scheme
     34   around what's really just application-specific passwords.
     35   
     36   And then a bunch of mostly incompatible implementations. Great work, guys.
     37   
     38   http://hueniverse.com/2012/07/oauth-2-0-and-the-road-to-hell/ */
     39
    2440#include <glib.h>
    2541#include "http_client.h"
    2642#include "oauth2.h"
    2743#include "oauth.h"
     44#include "json.h"
     45#include "json_util.h"
    2846#include "url.h"
    2947
     
    4462};
    4563
    46 static char *oauth2_json_dumb_get( const char *json, const char *key );
    4764static void oauth2_access_token_done( struct http_request *req );
    4865
     
    7996                             "Content-Type: application/x-www-form-urlencoded\r\n"
    8097                             "Content-Length: %zd\r\n"
    81                              "Connection: close\r\n"
    8298                             "\r\n"
    8399                             "%s", url_p.file, url_p.host, strlen( args_s ), args_s );
     
    99115}
    100116
     117static char* oauth2_parse_error( json_value *e )
     118{
     119        /* This does a reasonable job with some of the flavours of error
     120           responses I've seen. Because apparently it's not standardised. */
     121       
     122        if( e->type == json_object )
     123        {
     124                /* Facebook style */
     125                const char *msg = json_o_str( e, "message" );
     126                const char *type = json_o_str( e, "type" );
     127                json_value *code_o = json_o_get( e, "code" );
     128                int code = 0;
     129               
     130                if( code_o && code_o->type == json_integer )
     131                        code = code_o->u.integer;
     132               
     133                return g_strdup_printf( "Error %d: %s", code, msg ? msg : type ? type : "Unknown error" );
     134        }
     135        else if( e->type == json_string )
     136        {
     137                return g_strdup( e->u.string.ptr );
     138        }
     139        return NULL;
     140}
     141
    101142static void oauth2_access_token_done( struct http_request *req )
    102143{
    103144        struct oauth2_access_token_data *cb_data = req->data;
    104         char *atoken = NULL, *rtoken = NULL;
     145        char *atoken = NULL, *rtoken = NULL, *error = NULL;
    105146        char *content_type;
    106147       
     
    110151        content_type = get_rfc822_header( req->reply_headers, "Content-Type", 0 );
    111152       
    112         if( req->status_code != 200 )
     153        if( content_type && ( strstr( content_type, "application/json" ) ||
     154                              strstr( content_type, "text/javascript" ) ) )
    113155        {
    114         }
    115         else if( content_type && strstr( content_type, "application/json" ) )
    116         {
    117                 atoken = oauth2_json_dumb_get( req->reply_body, "access_token" );
    118                 rtoken = oauth2_json_dumb_get( req->reply_body, "refresh_token" );
     156                json_value *js = json_parse( req->reply_body );
     157                if( js && js->type == json_object )
     158                {
     159                        JSON_O_FOREACH( js, k, v )
     160                        {
     161                                if( strcmp( k, "error" ) == 0 )
     162                                        error = oauth2_parse_error( v );
     163                                if( v->type != json_string )
     164                                        continue;
     165                                if( strcmp( k, "access_token" ) == 0 )
     166                                        atoken = g_strdup( v->u.string.ptr );
     167                                if( strcmp( k, "refresh_token" ) == 0 )
     168                                        rtoken = g_strdup( v->u.string.ptr );
     169                        }
     170                }
     171                json_value_free( js );
    119172        }
    120173        else
     
    130183        if( getenv( "BITLBEE_DEBUG" ) )
    131184                printf( "Extracted atoken=%s rtoken=%s\n", atoken, rtoken );
     185        if( !atoken && !rtoken && !error )
     186                error = g_strdup( "Unusuable response" );
    132187       
    133         cb_data->func( cb_data->data, atoken, rtoken );
     188        cb_data->func( cb_data->data, atoken, rtoken, error );
    134189        g_free( content_type );
    135190        g_free( atoken );
    136191        g_free( rtoken );
     192        g_free( error );
    137193        g_free( cb_data );
    138194}
    139 
    140 /* Super dumb. I absolutely refuse to use/add a complete json parser library
    141    (adding a new dependency to BitlBee for the first time in.. 6 years?) just
    142    to parse 100 bytes of data. So I have to do my own parsing because OAuth2
    143    dropped support for XML. (GRRR!) This is very dumb and for example won't
    144    work for integer values, nor will it strip/handle backslashes. */
    145 static char *oauth2_json_dumb_get( const char *json, const char *key )
    146 {
    147         int is_key = 0; /* 1 == reading key, 0 == reading value */
    148         int found_key = 0;
    149                
    150         while( json && *json )
    151         {
    152                 /* Grab strings and see if they're what we're looking for. */
    153                 if( *json == '"' || *json == '\'' )
    154                 {
    155                         char q = *json;
    156                         const char *str_start;
    157                         json ++;
    158                         str_start = json;
    159                        
    160                         while( *json )
    161                         {
    162                                 /* \' and \" are not string terminators. */
    163                                 if( *json == '\\' && json[1] == q )
    164                                         json ++;
    165                                 /* But without a \ it is. */
    166                                 else if( *json == q )
    167                                         break;
    168                                 json ++;
    169                         }
    170                         if( *json == '\0' )
    171                                 return NULL;
    172                        
    173                         if( is_key && strncmp( str_start, key, strlen( key ) ) == 0 )
    174                         {
    175                                 found_key = 1;
    176                         }
    177                         else if( !is_key && found_key )
    178                         {
    179                                 char *ret = g_memdup( str_start, json - str_start + 1 );
    180                                 ret[json-str_start] = '\0';
    181                                 return ret;
    182                         }
    183                        
    184                 }
    185                 else if( *json == '{' || *json == ',' )
    186                 {
    187                         found_key = 0;
    188                         is_key = 1;
    189                 }
    190                 else if( *json == ':' )
    191                         is_key = 0;
    192                
    193                 json ++;
    194         }
    195        
    196         return NULL;
    197 }
Note: See TracChangeset for help on using the changeset viewer.