source: lib/oauth2.c @ 1174899

Last change on this file since 1174899 was 4a5d885, checked in by Wilmer van der Gaast <wilmer@…>, at 2011-07-26T11:58:38Z

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

  • Property mode set to 100644
File size: 5.9 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
30struct oauth2_service oauth2_service_google =
31{
32        "https://accounts.google.com/o/oauth2/",
33        "783993391592.apps.googleusercontent.com",
34        "k5_EV4EQ7jEVCEk3WBwEFfuW",
35};
36
37char *oauth2_url( const struct oauth2_service *sp, const char *scope )
38{
39        return g_strconcat( sp->base_url, "auth"
40                            "?scope=", scope,
41                            "&response_type=code"
42                            "&redirect_uri=urn:ietf:wg:oauth:2.0:oob",
43                            "&client_id=", sp->consumer_key,
44                            NULL );
45}
46
47struct oauth2_access_token_data
48{
49        oauth2_token_callback func;
50        gpointer data;
51};
52
53static char *oauth2_json_dumb_get( const char *json, const char *key );
54static void oauth2_access_token_done( struct http_request *req );
55
56int oauth2_access_token( const struct oauth2_service *sp,
57                         const char *auth_type, const char *auth,
58                         oauth2_token_callback func, gpointer data )
59{
60        GSList *args = NULL;
61        char *args_s, *s;
62        url_t url_p;
63        struct http_request *req;
64        struct oauth2_access_token_data *cb_data;
65       
66        if( !url_set( &url_p, sp->base_url ) )
67                return 0;
68       
69        oauth_params_add( &args, "client_id", sp->consumer_key );
70        oauth_params_add( &args, "client_secret", sp->consumer_secret );
71        oauth_params_add( &args, "grant_type", auth_type );
72        if( strcmp( auth_type, OAUTH2_AUTH_CODE ) == 0 )
73        {
74                oauth_params_add( &args, "redirect_uri", "urn:ietf:wg:oauth:2.0:oob" );
75                oauth_params_add( &args, "code", auth );
76        }
77        else
78        {
79                oauth_params_add( &args, "refresh_token", auth );
80        }
81        args_s = oauth_params_string( args );
82        oauth_params_free( &args );
83       
84        s = g_strdup_printf( "POST %s%s HTTP/1.0\r\n"
85                             "Host: %s\r\n"
86                             "Content-Type: application/x-www-form-urlencoded\r\n"
87                             "Content-Length: %zd\r\n"
88                             "Connection: close\r\n"
89                             "\r\n"
90                             "%s", url_p.file, "token", url_p.host, strlen( args_s ), args_s );
91        g_free( args_s );
92       
93        cb_data = g_new0( struct oauth2_access_token_data, 1 );
94        cb_data->func = func;
95        cb_data->data = data;
96       
97        req = http_dorequest( url_p.host, url_p.port, url_p.proto == PROTO_HTTPS,
98                              s, oauth2_access_token_done, cb_data );
99       
100        g_free( s );
101       
102        if( req == NULL )
103                g_free( cb_data );
104       
105        return req != NULL;
106}
107
108static void oauth2_access_token_done( struct http_request *req )
109{
110        struct oauth2_access_token_data *cb_data = req->data;
111        char *atoken = NULL, *rtoken = NULL;
112       
113        if( req->status_code == 200 )
114        {
115                atoken = oauth2_json_dumb_get( req->reply_body, "access_token" );
116                rtoken = oauth2_json_dumb_get( req->reply_body, "refresh_token" );
117        }
118        cb_data->func( cb_data->data, atoken, rtoken );
119        g_free( atoken );
120        g_free( rtoken );
121        g_free( cb_data );
122}
123
124/* Super dumb. I absolutely refuse to use/add a complete json parser library
125   (adding a new dependency to BitlBee for the first time in.. 6 years?) just
126   to parse 100 bytes of data. So I have to do my own parsing because OAuth2
127   dropped support for XML. (GRRR!) This is very dumb and for example won't
128   work for integer values, nor will it strip/handle backslashes. */
129static char *oauth2_json_dumb_get( const char *json, const char *key )
130{
131        int is_key; /* 1 == reading key, 0 == reading value */
132        int found_key = 0;
133               
134        while( json && *json )
135        {
136                /* Grab strings and see if they're what we're looking for. */
137                if( *json == '"' || *json == '\'' )
138                {
139                        char q = *json;
140                        const char *str_start;
141                        json ++;
142                        str_start = json;
143                       
144                        while( *json )
145                        {
146                                /* \' and \" are not string terminators. */
147                                if( *json == '\\' && json[1] == q )
148                                        json ++;
149                                /* But without a \ it is. */
150                                else if( *json == q )
151                                        break;
152                                json ++;
153                        }
154                        if( *json == '\0' )
155                                return NULL;
156                       
157                        if( is_key && strncmp( str_start, key, strlen( key ) ) == 0 )
158                        {
159                                found_key = 1;
160                        }
161                        else if( !is_key && found_key )
162                        {
163                                char *ret = g_memdup( str_start, json - str_start + 1 );
164                                ret[json-str_start] = '\0';
165                                return ret;
166                        }
167                       
168                }
169                else if( *json == '{' || *json == ',' )
170                {
171                        found_key = 0;
172                        is_key = 1;
173                }
174                else if( *json == ':' )
175                        is_key = 0;
176               
177                json ++;
178        }
179       
180        return NULL;
181}
Note: See TracBrowser for help on using the repository browser.