Scan media entities as well, not just url entities. This should expand more
[bitlbee.git] / lib / oauth.c
blob04949e1b891b4be8ae2845033d8d379d0da58342
1 /***************************************************************************\
2 * *
3 * BitlBee - An IRC to IM gateway *
4 * Simple OAuth client (consumer) implementation. *
5 * *
6 * Copyright 2010 Wilmer van der Gaast <wilmer@gaast.net> *
7 * *
8 * This library is free software; you can redistribute it and/or *
9 * modify it under the terms of the GNU Lesser General Public *
10 * License as published by the Free Software Foundation, version *
11 * 2.1. *
12 * *
13 * This library 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 GNU *
16 * Lesser General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU Lesser General Public License *
19 * along with this library; if not, write to the Free Software Foundation, *
20 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *
21 * *
22 \***************************************************************************/
24 #include <glib.h>
25 #include <gmodule.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include "http_client.h"
29 #include "base64.h"
30 #include "misc.h"
31 #include "sha1.h"
32 #include "url.h"
33 #include "oauth.h"
35 #define HMAC_BLOCK_SIZE 64
37 static char *oauth_sign( const char *method, const char *url,
38 const char *params, struct oauth_info *oi )
40 uint8_t hash[sha1_hash_size];
41 GString *payload = g_string_new( "" );
42 char *key;
43 char *s;
45 key = g_strdup_printf( "%s&%s", oi->sp->consumer_secret, oi->token_secret ? oi->token_secret : "" );
47 g_string_append_printf( payload, "%s&", method );
49 s = g_new0( char, strlen( url ) * 3 + 1 );
50 strcpy( s, url );
51 http_encode( s );
52 g_string_append_printf( payload, "%s&", s );
53 g_free( s );
55 s = g_new0( char, strlen( params ) * 3 + 1 );
56 strcpy( s, params );
57 http_encode( s );
58 g_string_append( payload, s );
59 g_free( s );
61 sha1_hmac( key, 0, payload->str, 0, hash );
63 g_free( key );
64 g_string_free( payload, TRUE );
66 /* base64_encode + HTTP escape it (both consumers
67 need it that away) and we're done. */
68 s = base64_encode( hash, sha1_hash_size );
69 s = g_realloc( s, strlen( s ) * 3 + 1 );
70 http_encode( s );
72 return s;
75 static char *oauth_nonce()
77 unsigned char bytes[21];
78 char *ret = g_new0( char, sizeof( bytes) / 3 * 4 + 1 );
80 random_bytes( bytes, sizeof( bytes ) );
81 base64_encode_real( bytes, sizeof( bytes), (unsigned char*) ret, "0123456789"
82 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0A" );
84 return ret;
87 void oauth_params_add( GSList **params, const char *key, const char *value )
89 char *item;
91 if( !key || !value )
92 return;
94 item = g_strdup_printf( "%s=%s", key, value );
95 *params = g_slist_insert_sorted( *params, item, (GCompareFunc) strcmp );
98 void oauth_params_del( GSList **params, const char *key )
100 int key_len = strlen( key );
101 GSList *l, *n;
103 if( params == NULL )
104 return;
106 for( l = *params; l; l = n )
108 n = l->next;
110 if( strncmp( (char*) l->data, key, key_len ) == 0 &&
111 ((char*)l->data)[key_len] == '=' )
113 g_free( l->data );
114 *params = g_slist_remove( *params, l->data );
119 void oauth_params_set( GSList **params, const char *key, const char *value )
121 oauth_params_del( params, key );
122 oauth_params_add( params, key, value );
125 const char *oauth_params_get( GSList **params, const char *key )
127 int key_len = strlen( key );
128 GSList *l;
130 if( params == NULL )
131 return NULL;
133 for( l = *params; l; l = l->next )
135 if( strncmp( (char*) l->data, key, key_len ) == 0 &&
136 ((char*)l->data)[key_len] == '=' )
137 return (const char*) l->data + key_len + 1;
140 return NULL;
143 void oauth_params_parse( GSList **params, char *in )
145 char *amp, *eq, *s;
147 while( in && *in )
149 eq = strchr( in, '=' );
150 if( !eq )
151 break;
153 *eq = '\0';
154 if( ( amp = strchr( eq + 1, '&' ) ) )
155 *amp = '\0';
157 s = g_strdup( eq + 1 );
158 http_decode( s );
159 oauth_params_add( params, in, s );
160 g_free( s );
162 *eq = '=';
163 if( amp == NULL )
164 break;
166 *amp = '&';
167 in = amp + 1;
171 void oauth_params_free( GSList **params )
173 while( params && *params )
175 g_free( (*params)->data );
176 *params = g_slist_remove( *params, (*params)->data );
180 char *oauth_params_string( GSList *params )
182 GSList *l;
183 GString *str = g_string_new( "" );
185 for( l = params; l; l = l->next )
187 char *s, *eq;
189 s = g_malloc( strlen( l->data ) * 3 + 1 );
190 strcpy( s, l->data );
191 if( ( eq = strchr( s, '=' ) ) )
192 http_encode( eq + 1 );
193 g_string_append( str, s );
194 g_free( s );
196 if( l->next )
197 g_string_append_c( str, '&' );
200 return g_string_free( str, FALSE );
203 void oauth_info_free( struct oauth_info *info )
205 if( info )
207 g_free( info->auth_url );
208 g_free( info->request_token );
209 g_free( info->token );
210 g_free( info->token_secret );
211 oauth_params_free( &info->params );
212 g_free( info );
216 static void oauth_add_default_params( GSList **params, const struct oauth_service *sp )
218 char *s;
220 oauth_params_set( params, "oauth_consumer_key", sp->consumer_key );
221 oauth_params_set( params, "oauth_signature_method", "HMAC-SHA1" );
223 s = g_strdup_printf( "%d", (int) time( NULL ) );
224 oauth_params_set( params, "oauth_timestamp", s );
225 g_free( s );
227 s = oauth_nonce();
228 oauth_params_set( params, "oauth_nonce", s );
229 g_free( s );
231 oauth_params_set( params, "oauth_version", "1.0" );
234 static void *oauth_post_request( const char *url, GSList **params_, http_input_function func, struct oauth_info *oi )
236 GSList *params = NULL;
237 char *s, *params_s, *post;
238 void *req;
239 url_t url_p;
241 if( !url_set( &url_p, url ) )
243 oauth_params_free( params_ );
244 return NULL;
247 if( params_ )
248 params = *params_;
250 oauth_add_default_params( &params, oi->sp );
252 params_s = oauth_params_string( params );
253 oauth_params_free( &params );
255 s = oauth_sign( "POST", url, params_s, oi );
256 post = g_strdup_printf( "%s&oauth_signature=%s", params_s, s );
257 g_free( params_s );
258 g_free( s );
260 s = g_strdup_printf( "POST %s HTTP/1.0\r\n"
261 "Host: %s\r\n"
262 "Content-Type: application/x-www-form-urlencoded\r\n"
263 "Content-Length: %zd\r\n"
264 "Connection: close\r\n"
265 "\r\n"
266 "%s", url_p.file, url_p.host, strlen( post ), post );
267 g_free( post );
269 req = http_dorequest( url_p.host, url_p.port, url_p.proto == PROTO_HTTPS,
270 s, func, oi );
271 g_free( s );
273 return req;
276 static void oauth_request_token_done( struct http_request *req );
278 struct oauth_info *oauth_request_token( const struct oauth_service *sp, oauth_cb func, void *data )
280 struct oauth_info *st = g_new0( struct oauth_info, 1 );
281 GSList *params = NULL;
283 st->func = func;
284 st->data = data;
285 st->sp = sp;
287 oauth_params_add( &params, "oauth_callback", "oob" );
289 if( !oauth_post_request( sp->url_request_token, &params, oauth_request_token_done, st ) )
291 oauth_info_free( st );
292 return NULL;
295 return st;
298 static void oauth_request_token_done( struct http_request *req )
300 struct oauth_info *st = req->data;
302 st->http = req;
304 if( req->status_code == 200 )
306 GSList *params = NULL;
308 st->auth_url = g_strdup_printf( "%s?%s", st->sp->url_authorize, req->reply_body );
309 oauth_params_parse( &params, req->reply_body );
310 st->request_token = g_strdup( oauth_params_get( &params, "oauth_token" ) );
311 st->token_secret = g_strdup( oauth_params_get( &params, "oauth_token_secret" ) );
312 oauth_params_free( &params );
315 st->stage = OAUTH_REQUEST_TOKEN;
316 st->func( st );
319 static void oauth_access_token_done( struct http_request *req );
321 gboolean oauth_access_token( const char *pin, struct oauth_info *st )
323 GSList *params = NULL;
325 oauth_params_add( &params, "oauth_token", st->request_token );
326 oauth_params_add( &params, "oauth_verifier", pin );
328 return oauth_post_request( st->sp->url_access_token, &params, oauth_access_token_done, st ) != NULL;
331 static void oauth_access_token_done( struct http_request *req )
333 struct oauth_info *st = req->data;
335 st->http = req;
337 if( req->status_code == 200 )
339 oauth_params_parse( &st->params, req->reply_body );
340 st->token = g_strdup( oauth_params_get( &st->params, "oauth_token" ) );
341 g_free( st->token_secret );
342 st->token_secret = g_strdup( oauth_params_get( &st->params, "oauth_token_secret" ) );
345 st->stage = OAUTH_ACCESS_TOKEN;
346 if( st->func( st ) )
348 /* Don't need these anymore, but keep the rest. */
349 g_free( st->auth_url );
350 st->auth_url = NULL;
351 g_free( st->request_token );
352 st->request_token = NULL;
353 oauth_params_free( &st->params );
357 char *oauth_http_header( struct oauth_info *oi, const char *method, const char *url, char *args )
359 GSList *params = NULL, *l;
360 char *sig = NULL, *params_s, *s;
361 GString *ret = NULL;
363 oauth_params_add( &params, "oauth_token", oi->token );
364 oauth_add_default_params( &params, oi->sp );
366 /* Start building the OAuth header. 'key="value", '... */
367 ret = g_string_new( "OAuth " );
368 for( l = params; l; l = l->next )
370 char *kv = l->data;
371 char *eq = strchr( kv, '=' );
372 char esc[strlen(kv)*3+1];
374 if( eq == NULL )
375 break; /* WTF */
377 strcpy( esc, eq + 1 );
378 http_encode( esc );
380 g_string_append_len( ret, kv, eq - kv + 1 );
381 g_string_append_c( ret, '"' );
382 g_string_append( ret, esc );
383 g_string_append( ret, "\", " );
386 /* Now, before generating the signature, add GET/POST arguments to params
387 since they should be included in the base signature string (but not in
388 the HTTP header). */
389 if( args )
390 oauth_params_parse( &params, args );
391 if( ( s = strchr( url, '?' ) ) )
393 s = g_strdup( s + 1 );
394 oauth_params_parse( &params, s );
395 g_free( s );
398 /* Append the signature and we're done! */
399 params_s = oauth_params_string( params );
400 sig = oauth_sign( method, url, params_s, oi );
401 g_string_append_printf( ret, "oauth_signature=\"%s\"", sig );
402 g_free( params_s );
404 oauth_params_free( &params );
405 g_free( sig );
407 return ret ? g_string_free( ret, FALSE ) : NULL;
410 char *oauth_to_string( struct oauth_info *oi )
412 GSList *params = NULL;
413 char *ret;
415 oauth_params_add( &params, "oauth_token", oi->token );
416 oauth_params_add( &params, "oauth_token_secret", oi->token_secret );
417 ret = oauth_params_string( params );
418 oauth_params_free( &params );
420 return ret;
423 struct oauth_info *oauth_from_string( char *in, const struct oauth_service *sp )
425 struct oauth_info *oi = g_new0( struct oauth_info, 1 );
426 GSList *params = NULL;
428 oauth_params_parse( &params, in );
429 oi->token = g_strdup( oauth_params_get( &params, "oauth_token" ) );
430 oi->token_secret = g_strdup( oauth_params_get( &params, "oauth_token_secret" ) );
431 oauth_params_free( &params );
432 oi->sp = sp;
434 return oi;