source: lib/oauth2.c @ a880e34

Last change on this file since a880e34 was 5ebff60, checked in by dequis <dx@…>, at 2015-02-20T22:50:54Z

Reindent everything to K&R style with tabs

Used uncrustify, with the configuration file in ./doc/uncrustify.cfg

Commit author set to "Indent <please@…>" so that it's easier to
skip while doing git blame.

  • Property mode set to 100644
File size: 6.7 KB
Line 
1/***************************************************************************\
2*                                                                           *
3*  BitlBee - An IRC to IM gateway                                           *
4*  Simple OAuth2 client (consumer) implementation.                          *
5*                                                                           *
6*  Copyright 2010-2013 Wilmer van der Gaast <wilmer@gaast.net>              *
7*                                                                           *
8*  This program is free software; you can redistribute it and/or modify     *
9*  it under the terms of the GNU General Public License as published by     *
10*  the Free Software Foundation; either version 2 of the License, or        *
11*  (at your option) any later version.                                      *
12*                                                                           *
13*  This program 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            *
16*  GNU General Public License for more details.                             *
17*                                                                           *
18*  You should have received a copy of the GNU General Public License along  *
19*  with this program; if not, write to the Free Software Foundation, Inc.,  *
20*  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.              *
21*                                                                           *
22\***************************************************************************/
23
24/* Out of protest, I should rename this file. OAuth2 is a pathetic joke, and
25   of all things, DEFINITELY NOT A STANDARD. The only thing various OAuth2
26   implementations have in common is that name, wrongfully stolen from
27   a pretty nice standard called OAuth 1.0a. That, and the fact that they
28   use JSON. Wait, no, Facebook's version doesn't use JSON. For some of its
29   responses.
30
31   Apparently too many people were too retarded to comprehend the elementary
32   bits of crypto in OAuth 1.0a (took me one afternoon to implement) so
33   the standard was replaced with what comes down to a complicated scheme
34   around what's really just application-specific passwords.
35
36   And then a bunch of mostly incompatible implementations. Great work, guys.
37
38   http://hueniverse.com/2012/07/oauth-2-0-and-the-road-to-hell/ */
39
40#include <glib.h>
41#include "http_client.h"
42#include "oauth2.h"
43#include "oauth.h"
44#include "json.h"
45#include "json_util.h"
46#include "url.h"
47
48char *oauth2_url(const struct oauth2_service *sp)
49{
50        return g_strconcat(sp->auth_url,
51                           "?scope=", sp->scope,
52                           "&response_type=code"
53                           "&redirect_uri=", sp->redirect_url,
54                           "&client_id=", sp->consumer_key,
55                           NULL);
56}
57
58struct oauth2_access_token_data {
59        oauth2_token_callback func;
60        gpointer data;
61};
62
63static void oauth2_access_token_done(struct http_request *req);
64
65int oauth2_access_token(const struct oauth2_service *sp,
66                        const char *auth_type, const char *auth,
67                        oauth2_token_callback func, gpointer data)
68{
69        GSList *args = NULL;
70        char *args_s, *s;
71        url_t url_p;
72        struct http_request *req;
73        struct oauth2_access_token_data *cb_data;
74
75        if (!url_set(&url_p, sp->token_url)) {
76                return 0;
77        }
78
79        oauth_params_add(&args, "client_id", sp->consumer_key);
80        oauth_params_add(&args, "client_secret", sp->consumer_secret);
81        oauth_params_add(&args, "grant_type", auth_type);
82        if (strcmp(auth_type, OAUTH2_AUTH_CODE) == 0) {
83                oauth_params_add(&args, "redirect_uri", sp->redirect_url);
84                oauth_params_add(&args, "code", auth);
85        } else {
86                oauth_params_add(&args, "refresh_token", auth);
87        }
88        args_s = oauth_params_string(args);
89        oauth_params_free(&args);
90
91        s = g_strdup_printf("POST %s HTTP/1.0\r\n"
92                            "Host: %s\r\n"
93                            "Content-Type: application/x-www-form-urlencoded\r\n"
94                            "Content-Length: %zd\r\n"
95                            "\r\n"
96                            "%s", url_p.file, url_p.host, strlen(args_s), args_s);
97        g_free(args_s);
98
99        cb_data = g_new0(struct oauth2_access_token_data, 1);
100        cb_data->func = func;
101        cb_data->data = data;
102
103        req = http_dorequest(url_p.host, url_p.port, url_p.proto == PROTO_HTTPS,
104                             s, oauth2_access_token_done, cb_data);
105
106        g_free(s);
107
108        if (req == NULL) {
109                g_free(cb_data);
110        }
111
112        return req != NULL;
113}
114
115static char* oauth2_parse_error(json_value *e)
116{
117        /* This does a reasonable job with some of the flavours of error
118           responses I've seen. Because apparently it's not standardised. */
119
120        if (e->type == json_object) {
121                /* Facebook style */
122                const char *msg = json_o_str(e, "message");
123                const char *type = json_o_str(e, "type");
124                json_value *code_o = json_o_get(e, "code");
125                int code = 0;
126
127                if (code_o && code_o->type == json_integer) {
128                        code = code_o->u.integer;
129                }
130
131                return g_strdup_printf("Error %d: %s", code, msg ? msg : type ? type : "Unknown error");
132        } else if (e->type == json_string) {
133                return g_strdup(e->u.string.ptr);
134        }
135        return NULL;
136}
137
138static void oauth2_access_token_done(struct http_request *req)
139{
140        struct oauth2_access_token_data *cb_data = req->data;
141        char *atoken = NULL, *rtoken = NULL, *error = NULL;
142        char *content_type;
143
144        if (getenv("BITLBEE_DEBUG") && req->reply_body) {
145                printf("%s\n", req->reply_body);
146        }
147
148        content_type = get_rfc822_header(req->reply_headers, "Content-Type", 0);
149
150        if (content_type && (strstr(content_type, "application/json") ||
151                             strstr(content_type, "text/javascript"))) {
152                json_value *js = json_parse(req->reply_body, req->body_size);
153                if (js && js->type == json_object) {
154                        JSON_O_FOREACH(js, k, v){
155                                if (strcmp(k, "error") == 0) {
156                                        error = oauth2_parse_error(v);
157                                }
158                                if (v->type != json_string) {
159                                        continue;
160                                }
161                                if (strcmp(k, "access_token") == 0) {
162                                        atoken = g_strdup(v->u.string.ptr);
163                                }
164                                if (strcmp(k, "refresh_token") == 0) {
165                                        rtoken = g_strdup(v->u.string.ptr);
166                                }
167                        }
168                }
169                json_value_free(js);
170        } else {
171                /* Facebook use their own odd format here, seems to be URL-encoded. */
172                GSList *p_in = NULL;
173
174                oauth_params_parse(&p_in, req->reply_body);
175                atoken = g_strdup(oauth_params_get(&p_in, "access_token"));
176                rtoken = g_strdup(oauth_params_get(&p_in, "refresh_token"));
177                oauth_params_free(&p_in);
178        }
179        if (getenv("BITLBEE_DEBUG")) {
180                printf("Extracted atoken=%s rtoken=%s\n", atoken, rtoken);
181        }
182        if (!atoken && !rtoken && !error) {
183                error = g_strdup("Unusable response");
184        }
185
186        cb_data->func(cb_data->data, atoken, rtoken, error);
187        g_free(content_type);
188        g_free(atoken);
189        g_free(rtoken);
190        g_free(error);
191        g_free(cb_data);
192}
Note: See TracBrowser for help on using the repository browser.