source: lib/oauth.c @ 346dfd9

Last change on this file since 346dfd9 was 346dfd9, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-04-25T19:22:02Z

oauth_access_token() added. I managed to increase the counter on
http://twitter.com/oauth_clients/details/127170 . \o/

  • Property mode set to 100644
File size: 8.7 KB
Line 
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\***************************************************************************/
23
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
34#define CONSUMER_KEY "xsDNKJuNZYkZyMcu914uEA"
35#define CONSUMER_SECRET "FCxqcr0pXKzsF9ajmP57S3VQ8V6Drk4o2QYtqMcOszo"
36/* How can it be a secret if it's right here in the source code? No clue... */
37
38#define HMAC_BLOCK_SIZE 64
39
40struct oauth_info;
41typedef void (*oauth_cb)( struct oauth_info * );
42
43struct oauth_info
44{
45        oauth_cb func;
46        void *data;
47       
48        struct http_request *http;
49       
50        char *auth_params;
51        char *token;
52};
53
54static char *oauth_sign( const char *method, const char *url,
55                         const char *params, const char *token_secret )
56{
57        sha1_state_t sha1;
58        uint8_t hash[sha1_hash_size];
59        uint8_t key[HMAC_BLOCK_SIZE+1];
60        char *s;
61        int i;
62       
63        /* Create K. If our current key is >64 chars we have to hash it,
64           otherwise just pad. */
65        memset( key, 0, HMAC_BLOCK_SIZE );
66        i = strlen( CONSUMER_SECRET ) + 1 + ( token_secret ? strlen( token_secret ) : 0 );
67        if( i > HMAC_BLOCK_SIZE )
68        {
69                sha1_init( &sha1 );
70                sha1_append( &sha1, (uint8_t*) CONSUMER_SECRET, strlen( CONSUMER_SECRET ) );
71                sha1_append( &sha1, (uint8_t*) "&", 1 );
72                if( token_secret )
73                        sha1_append( &sha1, (uint8_t*) token_secret, strlen( token_secret ) );
74                sha1_finish( &sha1, key );
75        }
76        else
77        {
78                g_snprintf( (gchar*) key, HMAC_BLOCK_SIZE + 1, "%s&%s",
79                            CONSUMER_SECRET, token_secret ? : "" );
80        }
81       
82        /* Inner part: H(K XOR 0x36, text) */
83        sha1_init( &sha1 );
84       
85        for( i = 0; i < HMAC_BLOCK_SIZE; i ++ )
86                key[i] ^= 0x36;
87        sha1_append( &sha1, key, HMAC_BLOCK_SIZE );
88       
89        /* OAuth: text = method&url&params, all http_encoded. */
90        sha1_append( &sha1, (const uint8_t*) method, strlen( method ) );
91        sha1_append( &sha1, (const uint8_t*) "&", 1 );
92       
93        s = g_new0( char, strlen( url ) * 3 + 1 );
94        strcpy( s, url );
95        http_encode( s );
96        sha1_append( &sha1, (const uint8_t*) s, strlen( s ) );
97        sha1_append( &sha1, (const uint8_t*) "&", 1 );
98        g_free( s );
99       
100        s = g_new0( char, strlen( params ) * 3 + 1 );
101        strcpy( s, params );
102        http_encode( s );
103        sha1_append( &sha1, (const uint8_t*) s, strlen( s ) );
104        g_free( s );
105       
106        sha1_finish( &sha1, hash );
107       
108        /* Final result: H(K XOR 0x5C, inner stuff) */
109        sha1_init( &sha1 );
110        for( i = 0; i < HMAC_BLOCK_SIZE; i ++ )
111                key[i] ^= 0x36 ^ 0x5c;
112        sha1_append( &sha1, key, HMAC_BLOCK_SIZE );
113        sha1_append( &sha1, hash, sha1_hash_size );
114        sha1_finish( &sha1, hash );
115       
116        /* base64_encode it and we're done. */
117        return base64_encode( hash, sha1_hash_size );
118}
119
120static char *oauth_nonce()
121{
122        unsigned char bytes[9];
123       
124        random_bytes( bytes, sizeof( bytes ) );
125        return base64_encode( bytes, sizeof( bytes ) );
126}
127
128void oauth_params_add( GSList **params, const char *key, const char *value )
129{
130        char *item;
131       
132        item = g_strdup_printf( "%s=%s", key, value );
133        *params = g_slist_insert_sorted( *params, item, (GCompareFunc) strcmp );
134}
135
136void oauth_params_del( GSList **params, const char *key )
137{
138        int key_len = strlen( key );
139        GSList *l, *n;
140       
141        for( l = *params; l; l = n )
142        {
143                n = l->next;
144               
145                if( strncmp( (char*) l->data, key, key_len ) == 0 &&
146                    ((char*)l->data)[key_len] == '=' )
147                {
148                        g_free( l->data );
149                        *params = g_slist_remove( *params, l );
150                }
151        }
152}
153
154void oauth_params_set( GSList **params, const char *key, const char *value )
155{
156        oauth_params_del( params, key );
157        oauth_params_add( params, key, value );
158}
159
160const char *oauth_params_get( GSList **params, const char *key )
161{
162        int key_len = strlen( key );
163        GSList *l;
164       
165        for( l = *params; l; l = l->next )
166        {
167                if( strncmp( (char*) l->data, key, key_len ) == 0 &&
168                    ((char*)l->data)[key_len] == '=' )
169                        return (const char*) l->data + key_len + 1;
170        }
171       
172        return NULL;
173}
174
175GSList *oauth_params_parse( char *in )
176{
177        GSList *ret = NULL;
178        char *amp, *eq;
179       
180        while( in && *in )
181        {
182                eq = strchr( in, '=' );
183                if( !eq )
184                        break;
185               
186                *eq = '\0';
187                if( ( amp = strchr( eq + 1, '&' ) ) )
188                        *amp = '\0';
189               
190                oauth_params_add( &ret, in, eq + 1 );
191               
192                *eq = '=';
193                if( amp == NULL )
194                        break;
195               
196                *amp = '&';
197                in = amp + 1;
198        }
199       
200        return ret;
201}
202
203void oauth_params_free( GSList **params )
204{
205        while( params && *params )
206        {
207                g_free( (*params)->data );
208                *params = g_slist_remove( *params, (*params)->data );
209        }
210}
211
212char *oauth_params_string( GSList *params )
213{
214        GSList *l;
215        GString *str = g_string_new( "" );
216       
217        for( l = params; l; l = l->next )
218        {
219                g_string_append( str, l->data );
220                if( l->next )
221                        g_string_append_c( str, '&' );
222        }
223       
224        return g_string_free( str, FALSE );
225}
226
227static void *oauth_post_request( const char *url, GSList **params_, http_input_function func, void *data )
228{
229        GSList *params = NULL;
230        char *s, *params_s, *post;
231        void *req;
232        url_t url_p;
233       
234        if( !url_set( &url_p, url ) )
235        {
236                oauth_params_free( params_ );
237                return NULL;
238        }
239       
240        if( params_ )
241                params = *params_;
242       
243        oauth_params_set( &params, "oauth_consumer_key", CONSUMER_KEY );
244        oauth_params_set( &params, "oauth_signature_method", "HMAC-SHA1" );
245       
246        s = g_strdup_printf( "%d", (int) time( NULL ) );
247        oauth_params_set( &params, "oauth_timestamp", s );
248        g_free( s );
249       
250        s = oauth_nonce();
251        oauth_params_set( &params, "oauth_nonce", s );
252        g_free( s );
253       
254        oauth_params_set( &params, "oauth_version", "1.0" );
255       
256        params_s = oauth_params_string( params );
257        oauth_params_free( params_ );
258       
259        s = oauth_sign( "POST", url, params_s, NULL );
260        s = g_realloc( s, strlen( s ) * 3 + 1 );
261        http_encode( s );
262       
263        post = g_strdup_printf( "%s&oauth_signature=%s", params_s, s );
264        g_free( params_s );
265        g_free( s );
266       
267        s = g_strdup_printf( "POST %s HTTP/1.0\r\n"
268                             "Host: %s\r\n"
269                             "Content-Type: application/x-www-form-urlencoded\r\n"
270                             "Content-Length: %zd\r\n"
271                             "\r\n"
272                             "%s", url_p.file, url_p.host, strlen( post ), post );
273        g_free( post );
274       
275        req = http_dorequest( url_p.host, url_p.port, url_p.proto == PROTO_HTTPS,
276                              s, func, data );
277        g_free( s );
278       
279        return req;
280}
281
282static void oauth_request_token_done( struct http_request *req );
283
284void *oauth_request_token( const char *url, oauth_cb func, void *data )
285{
286        struct oauth_info *st = g_new0( struct oauth_info, 1 );
287        GSList *params = NULL;
288       
289        st->func = func;
290        st->data = data;
291       
292        oauth_params_add( &params, "oauth_callback", "oob" );
293       
294        return oauth_post_request( url, NULL, oauth_request_token_done, st );
295}
296
297static void oauth_request_token_done( struct http_request *req )
298{
299        struct oauth_info *st = req->data;
300       
301        st->http = req;
302       
303        if( req->status_code == 200 )
304        {
305                GSList *params;
306               
307                st->auth_params = g_strdup( req->reply_body );
308                params = oauth_params_parse( st->auth_params );
309                st->token = g_strdup( oauth_params_get( &params, "oauth_token" ) );
310                oauth_params_free( &params );
311        }
312       
313        //st->func( st );
314}
315
316static void oauth_access_token_done( struct http_request *req );
317
318void *oauth_access_token( const char *url, const char *pin, struct oauth_info *st )
319{
320        GSList *params = NULL;
321       
322        oauth_params_add( &params, "oauth_token", st->token );
323        oauth_params_add( &params, "oauth_verifier", pin );
324       
325        return oauth_post_request( url, &params, oauth_access_token_done, st );
326}
327
328static void oauth_access_token_done( struct http_request *req )
329{
330}
Note: See TracBrowser for help on using the repository browser.