source: lib/oauth2.c @ bf57cd1

Last change on this file since bf57cd1 was bf57cd1, checked in by Wilmer van der Gaast <wilmer@…>, at 2011-12-19T00:17:38Z

Facebook OAuth2 should now be fully usable.

  • Property mode set to 100644
File size: 6.3 KB
Line 
1/***************************************************************************\
2*                                                                           *
3*  BitlBee - An IRC to IM gateway                                           *
4*  Simple OAuth client (consumer) implementation.                           *
5*                                                                           *
6*  Copyright 2010-2011 Wilmer van der Gaast <wilmer@gaast.net>              *
7*                                                                           *
8*  This program is free software; you can redistribute it and/or modify     *
9*  it under the terms of the GNU General Public License as published by     *
10*  the Free Software Foundation; either version 2 of the License, or        *
11*  (at your option) any later version.                                      *
12*                                                                           *
13*  This program is distributed in the hope that it will be useful,          *
14*  but WITHOUT ANY WARRANTY; without even the implied warranty of           *
15*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            *
16*  GNU General Public License for more details.                             *
17*                                                                           *
18*  You should have received a copy of the GNU General Public License along  *
19*  with this program; if not, write to the Free Software Foundation, Inc.,  *
20*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
21*                                                                           *
22\***************************************************************************/
23
24#include <glib.h>
25#include "http_client.h"
26#include "oauth2.h"
27#include "oauth.h"
28#include "url.h"
29
30char *oauth2_url( const struct oauth2_service *sp )
31{
32        return g_strconcat( sp->auth_url,
33                            "?scope=", sp->scope,
34                            "&response_type=code"
35                            "&redirect_uri=", sp->redirect_url, 
36                            "&client_id=", sp->consumer_key,
37                            NULL );
38}
39
40struct oauth2_access_token_data
41{
42        oauth2_token_callback func;
43        gpointer data;
44};
45
46static char *oauth2_json_dumb_get( const char *json, const char *key );
47static void oauth2_access_token_done( struct http_request *req );
48
49int oauth2_access_token( const struct oauth2_service *sp,
50                         const char *auth_type, const char *auth,
51                         oauth2_token_callback func, gpointer data )
52{
53        GSList *args = NULL;
54        char *args_s, *s;
55        url_t url_p;
56        struct http_request *req;
57        struct oauth2_access_token_data *cb_data;
58       
59        if( !url_set( &url_p, sp->token_url ) )
60                return 0;
61       
62        oauth_params_add( &args, "client_id", sp->consumer_key );
63        oauth_params_add( &args, "client_secret", sp->consumer_secret );
64        oauth_params_add( &args, "grant_type", auth_type );
65        if( strcmp( auth_type, OAUTH2_AUTH_CODE ) == 0 )
66        {
67                oauth_params_add( &args, "redirect_uri", sp->redirect_url );
68                oauth_params_add( &args, "code", auth );
69        }
70        else
71        {
72                oauth_params_add( &args, "refresh_token", auth );
73        }
74        args_s = oauth_params_string( args );
75        oauth_params_free( &args );
76       
77        s = g_strdup_printf( "POST %s HTTP/1.0\r\n"
78                             "Host: %s\r\n"
79                             "Content-Type: application/x-www-form-urlencoded\r\n"
80                             "Content-Length: %zd\r\n"
81                             "Connection: close\r\n"
82                             "\r\n"
83                             "%s", url_p.file, url_p.host, strlen( args_s ), args_s );
84        g_free( args_s );
85       
86        cb_data = g_new0( struct oauth2_access_token_data, 1 );
87        cb_data->func = func;
88        cb_data->data = data;
89       
90        req = http_dorequest( url_p.host, url_p.port, url_p.proto == PROTO_HTTPS,
91                              s, oauth2_access_token_done, cb_data );
92       
93        g_free( s );
94       
95        if( req == NULL )
96                g_free( cb_data );
97       
98        return req != NULL;
99}
100
101static void oauth2_access_token_done( struct http_request *req )
102{
103        struct oauth2_access_token_data *cb_data = req->data;
104        char *atoken = NULL, *rtoken = NULL;
105        const char *content_type;
106       
107        if( getenv( "BITLBEE_DEBUG" ) && req->reply_body )
108                printf( "%s\n", req->reply_body );
109       
110        content_type = get_rfc822_header( req->reply_headers, "Content-Type", 0 );
111       
112        if( req->status_code != 200 )
113        {
114        }
115        else if( 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" );
119                if( getenv( "BITLBEE_DEBUG" ) )
120                        printf( "Extracted atoken=%s rtoken=%s\n", atoken, rtoken );
121        }
122        else
123        {
124                /* Facebook use their own odd format here, seems to be URL-encoded. */
125                GSList *p_in = NULL;
126               
127                oauth_params_parse( &p_in, req->reply_body );
128                atoken = g_strdup( oauth_params_get( &p_in, "access_token" ) );
129                rtoken = g_strdup( oauth_params_get( &p_in, "refresh_token" ) );
130                oauth_params_free( &p_in );
131        }
132        cb_data->func( cb_data->data, atoken, rtoken );
133        g_free( atoken );
134        g_free( rtoken );
135        g_free( cb_data );
136}
137
138/* Super dumb. I absolutely refuse to use/add a complete json parser library
139   (adding a new dependency to BitlBee for the first time in.. 6 years?) just
140   to parse 100 bytes of data. So I have to do my own parsing because OAuth2
141   dropped support for XML. (GRRR!) This is very dumb and for example won't
142   work for integer values, nor will it strip/handle backslashes. */
143static char *oauth2_json_dumb_get( const char *json, const char *key )
144{
145        int is_key; /* 1 == reading key, 0 == reading value */
146        int found_key = 0;
147               
148        while( json && *json )
149        {
150                /* Grab strings and see if they're what we're looking for. */
151                if( *json == '"' || *json == '\'' )
152                {
153                        char q = *json;
154                        const char *str_start;
155                        json ++;
156                        str_start = json;
157                       
158                        while( *json )
159                        {
160                                /* \' and \" are not string terminators. */
161                                if( *json == '\\' && json[1] == q )
162                                        json ++;
163                                /* But without a \ it is. */
164                                else if( *json == q )
165                                        break;
166                                json ++;
167                        }
168                        if( *json == '\0' )
169                                return NULL;
170                       
171                        if( is_key && strncmp( str_start, key, strlen( key ) ) == 0 )
172                        {
173                                found_key = 1;
174                        }
175                        else if( !is_key && found_key )
176                        {
177                                char *ret = g_memdup( str_start, json - str_start + 1 );
178                                ret[json-str_start] = '\0';
179                                return ret;
180                        }
181                       
182                }
183                else if( *json == '{' || *json == ',' )
184                {
185                        found_key = 0;
186                        is_key = 1;
187                }
188                else if( *json == ':' )
189                        is_key = 0;
190               
191                json ++;
192        }
193       
194        return NULL;
195}
Note: See TracBrowser for help on using the repository browser.