source: lib/oauth2.c @ bc676ac

Last change on this file since bc676ac was 7615726, checked in by Wilmer van der Gaast <wilmer@…>, at 2012-01-04T00:01:51Z

Fixing one compiler warning that shouldn't indicate any real problem (only
causes unpredictable behaviour on wrongly formatted JSON input).

  • 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( 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" );
119        }
120        else
121        {
122                /* Facebook use their own odd format here, seems to be URL-encoded. */
123                GSList *p_in = NULL;
124               
125                oauth_params_parse( &p_in, req->reply_body );
126                atoken = g_strdup( oauth_params_get( &p_in, "access_token" ) );
127                rtoken = g_strdup( oauth_params_get( &p_in, "refresh_token" ) );
128                oauth_params_free( &p_in );
129        }
130        if( getenv( "BITLBEE_DEBUG" ) )
131                printf( "Extracted atoken=%s rtoken=%s\n", atoken, rtoken );
132       
133        cb_data->func( cb_data->data, atoken, rtoken );
134        g_free( atoken );
135        g_free( rtoken );
136        g_free( cb_data );
137}
138
139/* Super dumb. I absolutely refuse to use/add a complete json parser library
140   (adding a new dependency to BitlBee for the first time in.. 6 years?) just
141   to parse 100 bytes of data. So I have to do my own parsing because OAuth2
142   dropped support for XML. (GRRR!) This is very dumb and for example won't
143   work for integer values, nor will it strip/handle backslashes. */
144static char *oauth2_json_dumb_get( const char *json, const char *key )
145{
146        int is_key = 0; /* 1 == reading key, 0 == reading value */
147        int found_key = 0;
148               
149        while( json && *json )
150        {
151                /* Grab strings and see if they're what we're looking for. */
152                if( *json == '"' || *json == '\'' )
153                {
154                        char q = *json;
155                        const char *str_start;
156                        json ++;
157                        str_start = json;
158                       
159                        while( *json )
160                        {
161                                /* \' and \" are not string terminators. */
162                                if( *json == '\\' && json[1] == q )
163                                        json ++;
164                                /* But without a \ it is. */
165                                else if( *json == q )
166                                        break;
167                                json ++;
168                        }
169                        if( *json == '\0' )
170                                return NULL;
171                       
172                        if( is_key && strncmp( str_start, key, strlen( key ) ) == 0 )
173                        {
174                                found_key = 1;
175                        }
176                        else if( !is_key && found_key )
177                        {
178                                char *ret = g_memdup( str_start, json - str_start + 1 );
179                                ret[json-str_start] = '\0';
180                                return ret;
181                        }
182                       
183                }
184                else if( *json == '{' || *json == ',' )
185                {
186                        found_key = 0;
187                        is_key = 1;
188                }
189                else if( *json == ':' )
190                        is_key = 0;
191               
192                json ++;
193        }
194       
195        return NULL;
196}
Note: See TracBrowser for help on using the repository browser.