1 /***************************************************************************\
3 * BitlBee - An IRC to IM gateway *
4 * Simple OAuth client (consumer) implementation. *
6 * Copyright 2010 Wilmer van der Gaast <wilmer@gaast.net> *
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 *
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. *
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 *
22 \***************************************************************************/
28 #include "http_client.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( "" );
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 );
52 g_string_append_printf( payload
, "%s&", s
);
55 s
= g_new0( char, strlen( params
) * 3 + 1 );
58 g_string_append( payload
, s
);
61 sha1_hmac( key
, 0, payload
->str
, 0, hash
);
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 );
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" );
87 void oauth_params_add( GSList
**params
, const char *key
, const char *value
)
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
);
106 for( l
= *params
; l
; l
= n
)
110 if( strncmp( (char*) l
->data
, key
, key_len
) == 0 &&
111 ((char*)l
->data
)[key_len
] == '=' )
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
);
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;
143 void oauth_params_parse( GSList
**params
, char *in
)
149 eq
= strchr( in
, '=' );
154 if( ( amp
= strchr( eq
+ 1, '&' ) ) )
157 s
= g_strdup( eq
+ 1 );
159 oauth_params_add( params
, in
, s
);
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
)
183 GString
*str
= g_string_new( "" );
185 for( l
= params
; l
; l
= l
->next
)
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
);
197 g_string_append_c( str
, '&' );
200 return g_string_free( str
, FALSE
);
203 void oauth_info_free( struct oauth_info
*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
);
216 static void oauth_add_default_params( GSList
**params
, const struct oauth_service
*sp
)
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
);
228 oauth_params_set( params
, "oauth_nonce", 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
;
241 if( !url_set( &url_p
, url
) )
243 oauth_params_free( params_
);
250 oauth_add_default_params( ¶ms
, oi
->sp
);
252 params_s
= oauth_params_string( params
);
253 oauth_params_free( ¶ms
);
255 s
= oauth_sign( "POST", url
, params_s
, oi
);
256 post
= g_strdup_printf( "%s&oauth_signature=%s", params_s
, s
);
260 s
= g_strdup_printf( "POST %s HTTP/1.0\r\n"
262 "Content-Type: application/x-www-form-urlencoded\r\n"
263 "Content-Length: %zd\r\n"
264 "Connection: close\r\n"
266 "%s", url_p
.file
, url_p
.host
, strlen( post
), post
);
269 req
= http_dorequest( url_p
.host
, url_p
.port
, url_p
.proto
== PROTO_HTTPS
,
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
;
287 oauth_params_add( ¶ms
, "oauth_callback", "oob" );
289 if( !oauth_post_request( sp
->url_request_token
, ¶ms
, oauth_request_token_done
, st
) )
291 oauth_info_free( st
);
298 static void oauth_request_token_done( struct http_request
*req
)
300 struct oauth_info
*st
= req
->data
;
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( ¶ms
, req
->reply_body
);
310 st
->request_token
= g_strdup( oauth_params_get( ¶ms
, "oauth_token" ) );
311 st
->token_secret
= g_strdup( oauth_params_get( ¶ms
, "oauth_token_secret" ) );
312 oauth_params_free( ¶ms
);
315 st
->stage
= OAUTH_REQUEST_TOKEN
;
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( ¶ms
, "oauth_token", st
->request_token
);
326 oauth_params_add( ¶ms
, "oauth_verifier", pin
);
328 return oauth_post_request( st
->sp
->url_access_token
, ¶ms
, oauth_access_token_done
, st
) != NULL
;
331 static void oauth_access_token_done( struct http_request
*req
)
333 struct oauth_info
*st
= req
->data
;
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
;
348 /* Don't need these anymore, but keep the rest. */
349 g_free( st
->auth_url
);
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
;
363 oauth_params_add( ¶ms
, "oauth_token", oi
->token
);
364 oauth_add_default_params( ¶ms
, oi
->sp
);
366 /* Start building the OAuth header. 'key="value", '... */
367 ret
= g_string_new( "OAuth " );
368 for( l
= params
; l
; l
= l
->next
)
371 char *eq
= strchr( kv
, '=' );
372 char esc
[strlen(kv
)*3+1];
377 strcpy( esc
, eq
+ 1 );
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
390 oauth_params_parse( ¶ms
, args
);
391 if( ( s
= strchr( url
, '?' ) ) )
393 s
= g_strdup( s
+ 1 );
394 oauth_params_parse( ¶ms
, 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
);
404 oauth_params_free( ¶ms
);
407 return ret
? g_string_free( ret
, FALSE
) : NULL
;
410 char *oauth_to_string( struct oauth_info
*oi
)
412 GSList
*params
= NULL
;
415 oauth_params_add( ¶ms
, "oauth_token", oi
->token
);
416 oauth_params_add( ¶ms
, "oauth_token_secret", oi
->token_secret
);
417 ret
= oauth_params_string( params
);
418 oauth_params_free( ¶ms
);
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( ¶ms
, in
);
429 oi
->token
= g_strdup( oauth_params_get( ¶ms
, "oauth_token" ) );
430 oi
->token_secret
= g_strdup( oauth_params_get( ¶ms
, "oauth_token_secret" ) );
431 oauth_params_free( ¶ms
);