diff options
| author | Wilmer van der Gaast <wilmer@google.com> | 2011-07-26 12:58:38 +0100 | 
|---|---|---|
| committer | Wilmer van der Gaast <wilmer@google.com> | 2011-07-26 12:58:38 +0100 | 
| commit | 4a5d88504235e1df5d01a3a5701b83dd82d6695d (patch) | |
| tree | 95eda026f565bc43ad8aea62470d710ba3245369 /lib | |
| parent | 59c9adb4e3e27ded95ec9ccee4f57cf79490bd12 (diff) | |
Working OAuth2 support. Needs some more debugging (error handling is not
great and imc_logout() gets (rightfully) confused when jabber_data is empty).
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/oauth.h | 3 | ||||
| -rw-r--r-- | lib/oauth2.c | 139 | ||||
| -rw-r--r-- | lib/oauth2.h | 50 | 
3 files changed, 158 insertions, 34 deletions
| diff --git a/lib/oauth.h b/lib/oauth.h index 8270a545..5c4ef4b0 100644 --- a/lib/oauth.h +++ b/lib/oauth.h @@ -91,4 +91,7 @@ char *oauth_to_string( struct oauth_info *oi );  struct oauth_info *oauth_from_string( char *in, const struct oauth_service *sp );  /* For reading misc. data. */ +void oauth_params_add( GSList **params, const char *key, const char *value ); +void oauth_params_free( GSList **params ); +char *oauth_params_string( GSList *params );  const char *oauth_params_get( GSList **params, const char *key ); diff --git a/lib/oauth2.c b/lib/oauth2.c index eb923795..d9eefa9f 100644 --- a/lib/oauth2.c +++ b/lib/oauth2.c @@ -22,7 +22,10 @@  \***************************************************************************/  #include <glib.h> +#include "http_client.h"  #include "oauth2.h" +#include "oauth.h" +#include "url.h"  struct oauth2_service oauth2_service_google =  { @@ -40,3 +43,139 @@ char *oauth2_url( const struct oauth2_service *sp, const char *scope )  	                    "&client_id=", sp->consumer_key,  	                    NULL );  } + +struct oauth2_access_token_data +{ +	oauth2_token_callback func; +	gpointer data; +}; + +static char *oauth2_json_dumb_get( const char *json, const char *key ); +static void oauth2_access_token_done( struct http_request *req ); + +int oauth2_access_token( const struct oauth2_service *sp, +                         const char *auth_type, const char *auth, +                         oauth2_token_callback func, gpointer data ) +{ +	GSList *args = NULL; +	char *args_s, *s; +	url_t url_p; +	struct http_request *req; +	struct oauth2_access_token_data *cb_data; +	 +	if( !url_set( &url_p, sp->base_url ) ) +		return 0; +	 +	oauth_params_add( &args, "client_id", sp->consumer_key ); +	oauth_params_add( &args, "client_secret", sp->consumer_secret ); +	oauth_params_add( &args, "grant_type", auth_type ); +	if( strcmp( auth_type, OAUTH2_AUTH_CODE ) == 0 ) +	{ +		oauth_params_add( &args, "redirect_uri", "urn:ietf:wg:oauth:2.0:oob" ); +		oauth_params_add( &args, "code", auth ); +	} +	else +	{ +		oauth_params_add( &args, "refresh_token", auth ); +	} +	args_s = oauth_params_string( args ); +	oauth_params_free( &args ); +	 +	s = g_strdup_printf( "POST %s%s HTTP/1.0\r\n" +	                     "Host: %s\r\n" +	                     "Content-Type: application/x-www-form-urlencoded\r\n" +	                     "Content-Length: %zd\r\n" +	                     "Connection: close\r\n" +	                     "\r\n" +	                     "%s", url_p.file, "token", url_p.host, strlen( args_s ), args_s ); +	g_free( args_s ); +	 +	cb_data = g_new0( struct oauth2_access_token_data, 1 ); +	cb_data->func = func; +	cb_data->data = data; +	 +	req = http_dorequest( url_p.host, url_p.port, url_p.proto == PROTO_HTTPS, +	                      s, oauth2_access_token_done, cb_data ); +	 +	g_free( s ); +	 +	if( req == NULL ) +		g_free( cb_data ); +	 +	return req != NULL; +} + +static void oauth2_access_token_done( struct http_request *req ) +{ +	struct oauth2_access_token_data *cb_data = req->data; +	char *atoken = NULL, *rtoken = NULL; +	 +	if( req->status_code == 200 ) +	{ +		atoken = oauth2_json_dumb_get( req->reply_body, "access_token" ); +		rtoken = oauth2_json_dumb_get( req->reply_body, "refresh_token" ); +	} +	cb_data->func( cb_data->data, atoken, rtoken ); +	g_free( atoken ); +	g_free( rtoken ); +	g_free( cb_data ); +} + +/* Super dumb. I absolutely refuse to use/add a complete json parser library +   (adding a new dependency to BitlBee for the first time in.. 6 years?) just +   to parse 100 bytes of data. So I have to do my own parsing because OAuth2 +   dropped support for XML. (GRRR!) This is very dumb and for example won't +   work for integer values, nor will it strip/handle backslashes. */ +static char *oauth2_json_dumb_get( const char *json, const char *key ) +{ +	int is_key; /* 1 == reading key, 0 == reading value */ +	int found_key = 0; +		 +	while( json && *json ) +	{ +		/* Grab strings and see if they're what we're looking for. */ +		if( *json == '"' || *json == '\'' ) +		{ +			char q = *json; +			const char *str_start; +			json ++; +			str_start = json; +			 +			while( *json ) +			{ +				/* \' and \" are not string terminators. */ +				if( *json == '\\' && json[1] == q ) +					json ++; +				/* But without a \ it is. */ +				else if( *json == q ) +					break; +				json ++; +			} +			if( *json == '\0' ) +				return NULL; +			 +			if( is_key && strncmp( str_start, key, strlen( key ) ) == 0 ) +			{ +				found_key = 1; +			} +			else if( !is_key && found_key ) +			{ +				char *ret = g_memdup( str_start, json - str_start + 1 ); +				ret[json-str_start] = '\0'; +				return ret; +			} +			 +		} +		else if( *json == '{' || *json == ',' ) +		{ +			found_key = 0; +			is_key = 1; +		} +		else if( *json == ':' ) +			is_key = 0; +		 +		json ++; +	} +	 +	return NULL; +} diff --git a/lib/oauth2.h b/lib/oauth2.h index c2985ef6..657a0ab3 100644 --- a/lib/oauth2.h +++ b/lib/oauth2.h @@ -1,7 +1,7 @@  /***************************************************************************\  *                                                                           *  *  BitlBee - An IRC to IM gateway                                           * -*  Simple OAuth client (consumer) implementation.                           * +*  Simple OAuth2 client (consumer) implementation.                          *  *                                                                           *  *  Copyright 2010-2011 Wilmer van der Gaast <wilmer@gaast.net>              *  *                                                                           * @@ -21,28 +21,10 @@  *                                                                           *  \***************************************************************************/ -struct oauth2_info; +/* Implementation mostly based on my experience with writing the previous OAuth +   module, and from http://code.google.com/apis/accounts/docs/OAuth2.html . */ -/* Callback function called twice during the access token request process. -   Return FALSE if something broke and the process must be aborted. */ -typedef gboolean (*oauth_cb)( struct oauth2_info * ); - -struct oauth2_info -{ -	const struct oauth_service *sp; -	 -	oauth_cb func; -	void *data; -	 -	struct http_request *http; -	 -//	char *auth_url; -//	char *request_token; -	 -//	char *token; -//	char *token_secret; -//	GSList *params; -}; +typedef void (*oauth2_token_callback)( gpointer data, const char *atoken, const char *rtoken );  struct oauth2_service  { @@ -51,19 +33,19 @@ struct oauth2_service  	char *consumer_secret;  }; +/* Currently suitable for authenticating to Google Talk only, and only for +   accounts that have 2-factor authorization enabled. */  extern struct oauth2_service oauth2_service_google; -/* http://oauth.net/core/1.0a/#auth_step1 (section 6.1)  -   Request an initial anonymous token which can be used to construct an -   authorization URL for the user. This is passed to the callback function -   in a struct oauth2_info. */ -char *oauth2_url( const struct oauth2_service *sp, const char *scope ); +#define OAUTH2_AUTH_CODE "authorization_code" +#define OAUTH2_AUTH_REFRESH "refresh_token" -/* http://oauth.net/core/1.0a/#auth_step3 (section 6.3) -   The user gets a PIN or so which we now exchange for the final access -   token. This is passed to the callback function in the same -   struct oauth2_info. */ -gboolean oauth2_access_token( const char *pin, struct oauth2_info *st ); +/* Generate a URL the user should open in his/her browser to get an +   authorization code. */ +char *oauth2_url( const struct oauth2_service *sp, const char *scope ); -/* Shouldn't normally be required unless the process is aborted by the user. */ -void oauth2_info_free( struct oauth2_info *info ); +/* Exchanges an auth code or refresh token for an access token. +   auth_type is one of the two OAUTH2_AUTH_.. constants above. */ +int oauth2_access_token( const struct oauth2_service *sp, +                         const char *auth_type, const char *auth, +                         oauth2_token_callback func, gpointer data ); | 
