source: lib/oauth2.c @ 9b0ad7e

Last change on this file since 9b0ad7e was 18c6d36, checked in by Wilmer van der Gaast <wilmer@…>, at 2011-12-18T19:25:44Z

More generic OAuth support now. Should work well for all GTalk accounts now
and somewhat for MS Messenger. The fb part needs different parsing of the
authorize request, and possibly some other work.

  • Property mode set to 100644
File size: 5.8 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       
106        if( getenv( "BITLBEE_DEBUG" ) && req->reply_body )
107                printf( "%s\n", req->reply_body );
108       
109        if( req->status_code == 200 )
110        {
111                atoken = oauth2_json_dumb_get( req->reply_body, "access_token" );
112                rtoken = oauth2_json_dumb_get( req->reply_body, "refresh_token" );
113                if( getenv( "BITLBEE_DEBUG" ) )
114                        printf( "Extracted atoken=%s rtoken=%s\n", atoken, rtoken );
115        }
116        cb_data->func( cb_data->data, atoken, rtoken );
117        g_free( atoken );
118        g_free( rtoken );
119        g_free( cb_data );
120}
121
122/* Super dumb. I absolutely refuse to use/add a complete json parser library
123   (adding a new dependency to BitlBee for the first time in.. 6 years?) just
124   to parse 100 bytes of data. So I have to do my own parsing because OAuth2
125   dropped support for XML. (GRRR!) This is very dumb and for example won't
126   work for integer values, nor will it strip/handle backslashes. */
127static char *oauth2_json_dumb_get( const char *json, const char *key )
128{
129        int is_key; /* 1 == reading key, 0 == reading value */
130        int found_key = 0;
131               
132        while( json && *json )
133        {
134                /* Grab strings and see if they're what we're looking for. */
135                if( *json == '"' || *json == '\'' )
136                {
137                        char q = *json;
138                        const char *str_start;
139                        json ++;
140                        str_start = json;
141                       
142                        while( *json )
143                        {
144                                /* \' and \" are not string terminators. */
145                                if( *json == '\\' && json[1] == q )
146                                        json ++;
147                                /* But without a \ it is. */
148                                else if( *json == q )
149                                        break;
150                                json ++;
151                        }
152                        if( *json == '\0' )
153                                return NULL;
154                       
155                        if( is_key && strncmp( str_start, key, strlen( key ) ) == 0 )
156                        {
157                                found_key = 1;
158                        }
159                        else if( !is_key && found_key )
160                        {
161                                char *ret = g_memdup( str_start, json - str_start + 1 );
162                                ret[json-str_start] = '\0';
163                                return ret;
164                        }
165                       
166                }
167                else if( *json == '{' || *json == ',' )
168                {
169                        found_key = 0;
170                        is_key = 1;
171                }
172                else if( *json == ':' )
173                        is_key = 0;
174               
175                json ++;
176        }
177       
178        return NULL;
179}
Note: See TracBrowser for help on using the repository browser.