source: lib/oauth2.c @ 3bda2c2

Last change on this file since 3bda2c2 was 3bda2c2, checked in by Wilmer van der Gaast <wilmer@…>, at 2012-12-02T17:39:22Z

Fixed minor memory leak.

  • 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        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( content_type );
135        g_free( atoken );
136        g_free( rtoken );
137        g_free( cb_data );
138}
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. */
145static 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 TracBrowser for help on using the repository browser.