Index: lib/oauth2.c
===================================================================
--- lib/oauth2.c	(revision be9f3f1b0e84c358d93291ea5d4f91580cbd1ec5)
+++ lib/oauth2.c	(revision c1538088cc9d9a0bb573fcf4a31248acd26254e2)
@@ -2,7 +2,7 @@
 *                                                                           *
 *  BitlBee - An IRC to IM gateway                                           *
-*  Simple OAuth client (consumer) implementation.                           *
+*  Simple OAuth2 client (consumer) implementation.                          *
 *                                                                           *
-*  Copyright 2010-2012 Wilmer van der Gaast <wilmer@gaast.net>              *
+*  Copyright 2010-2013 Wilmer van der Gaast <wilmer@gaast.net>              *
 *                                                                           *
 *  This program is free software; you can redistribute it and/or modify     *
@@ -22,4 +22,20 @@
 \***************************************************************************/
 
+/* Out of protest, I should rename this file. OAuth2 is a pathetic joke, and
+   of all things, DEFINITELY NOT A STANDARD. The only thing various OAuth2
+   implementations have in common is that name, wrongfully stolen from
+   a pretty nice standard called OAuth 1.0a. That, and the fact that they
+   use JSON. Wait, no, Facebook's version doesn't use JSON. For some of its
+   responses.
+   
+   Apparently too many people were too retarded to comprehend the elementary
+   bits of crypto in OAuth 1.0a (took me one afternoon to implement) so
+   the standard was replaced with what comes down to a complicated scheme
+   around what's really just application-specific passwords.
+   
+   And then a bunch of mostly incompatible implementations. Great work, guys.
+   
+   http://hueniverse.com/2012/07/oauth-2-0-and-the-road-to-hell/ */
+
 #include <glib.h>
 #include "http_client.h"
@@ -27,4 +43,5 @@
 #include "oauth.h"
 #include "json.h"
+#include "json_util.h"
 #include "url.h"
 
@@ -99,8 +116,33 @@
 }
 
+static char* oauth2_parse_error( json_value *e )
+{
+	/* This does a reasonable job with some of the flavours of error
+	   responses I've seen. Because apparently it's not standardised. */
+	
+	if( e->type == json_object )
+	{
+		/* Facebook style */
+		const char *msg = json_o_str( e, "message" );
+		const char *type = json_o_str( e, "type" );
+		json_value *code_o = json_o_get( e, "code" );
+		int code = 0;
+		
+		if( code_o && code_o->type == json_integer )
+			code = code_o->u.integer;
+		
+		return g_strdup_printf( "Error %d: %s", code, msg ? msg : type ? type : "Unknown error" );
+	}
+	else if( e->type == json_string )
+	{
+		return g_strdup( e->u.string.ptr );
+	}
+	return 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;
+	char *atoken = NULL, *rtoken = NULL, *error = NULL;
 	char *content_type;
 	
@@ -110,22 +152,20 @@
 	content_type = get_rfc822_header( req->reply_headers, "Content-Type", 0 );
 	
-	if( req->status_code != 200 )
-	{
-	}
-	else if( content_type && strstr( content_type, "application/json" ) )
+	if( content_type && ( strstr( content_type, "application/json" ) ||
+	                      strstr( content_type, "text/javascript" ) ) )
 	{
 		json_value *js = json_parse( req->reply_body );
 		if( js && js->type == json_object )
 		{
-			int i;
-			
-			for( i = 0; i < js->u.object.length; i ++ )
+			JSON_O_FOREACH( js, k, v )
 			{
-				if( js->u.object.values[i].value->type != json_string )
+				if( strcmp( k, "error" ) == 0 )
+					error = oauth2_parse_error( v );
+				if( v->type != json_string )
 					continue;
-				if( strcmp( js->u.object.values[i].name, "access_token" ) == 0 )
-					atoken = g_strdup( js->u.object.values[i].value->u.string.ptr );
-				if( strcmp( js->u.object.values[i].name, "refresh_token" ) == 0 )
-					rtoken = g_strdup( js->u.object.values[i].value->u.string.ptr );
+				if( strcmp( k, "access_token" ) == 0 )
+					atoken = g_strdup( v->u.string.ptr );
+				if( strcmp( k, "refresh_token" ) == 0 )
+					rtoken = g_strdup( v->u.string.ptr );
 			}
 		}
@@ -144,9 +184,12 @@
 	if( getenv( "BITLBEE_DEBUG" ) )
 		printf( "Extracted atoken=%s rtoken=%s\n", atoken, rtoken );
+	if( !atoken && !rtoken && !error )
+		error = g_strdup( "Unusuable response" );
 	
-	cb_data->func( cb_data->data, atoken, rtoken );
+	cb_data->func( cb_data->data, atoken, rtoken, error );
 	g_free( content_type );
 	g_free( atoken );
 	g_free( rtoken );
+	g_free( error );
 	g_free( cb_data );
 }
Index: lib/oauth2.h
===================================================================
--- lib/oauth2.h	(revision be9f3f1b0e84c358d93291ea5d4f91580cbd1ec5)
+++ lib/oauth2.h	(revision c1538088cc9d9a0bb573fcf4a31248acd26254e2)
@@ -4,5 +4,5 @@
 *  Simple OAuth2 client (consumer) implementation.                          *
 *                                                                           *
-*  Copyright 2010-2011 Wilmer van der Gaast <wilmer@gaast.net>              *
+*  Copyright 2010-2013 Wilmer van der Gaast <wilmer@gaast.net>              *
 *                                                                           *
 *  This program is free software; you can redistribute it and/or modify     *
@@ -25,5 +25,6 @@
    module, and from http://code.google.com/apis/accounts/docs/OAuth2.html . */
 
-typedef void (*oauth2_token_callback)( gpointer data, const char *atoken, const char *rtoken );
+typedef void (*oauth2_token_callback)( gpointer data, const char *atoken,
+                                       const char *rtoken, const char *error );
 
 struct oauth2_service
Index: protocols/jabber/sasl.c
===================================================================
--- protocols/jabber/sasl.c	(revision be9f3f1b0e84c358d93291ea5d4f91580cbd1ec5)
+++ protocols/jabber/sasl.c	(revision c1538088cc9d9a0bb573fcf4a31248acd26254e2)
@@ -483,5 +483,5 @@
 }
 
-static void sasl_oauth2_got_token( gpointer data, const char *access_token, const char *refresh_token );
+static void sasl_oauth2_got_token( gpointer data, const char *access_token, const char *refresh_token, const char *error );
 
 int sasl_oauth2_get_refresh_token( struct im_connection *ic, const char *msg )
@@ -514,5 +514,5 @@
 }
 
-static void sasl_oauth2_got_token( gpointer data, const char *access_token, const char *refresh_token )
+static void sasl_oauth2_got_token( gpointer data, const char *access_token, const char *refresh_token, const char *error )
 {
 	struct im_connection *ic = data;
@@ -527,5 +527,5 @@
 	if( access_token == NULL )
 	{
-		imcb_error( ic, "OAuth failure (missing access token)" );
+		imcb_error( ic, "OAuth failure (%s)", error );
 		imc_logout( ic, TRUE );
 		return;
