1
0
Fork 0
mirror of https://github.com/deltachat/deltachat-core.git synced 2025-10-03 17:59:19 +02:00
deltachat-core/src/dc_oauth2.c
2019-02-16 01:22:48 +01:00

145 lines
4.2 KiB
C

#include "dc_context.h"
#include "dc_oauth2.h"
#include "../libs/jsmn/jsmn.h"
static int jsoneq(const char *json, jsmntok_t *tok, const char *s) {
// from the jsmn parser example
if (tok->type == JSMN_STRING && (int) strlen(s) == tok->end - tok->start &&
strncmp(json + tok->start, s, tok->end - tok->start) == 0) {
return 0;
}
return -1;
}
static char* jsondup(const char *json, jsmntok_t *tok) {
if (tok->type == JSMN_STRING) {
return strndup(json+tok->start, tok->end - tok->start);
}
return strdup("");
}
char* dc_oauth2_get_access_token(dc_context_t* context, const char* code, int flags)
{
char* access_token = NULL;
char* refresh_token = NULL;
char* auth_url = NULL;
char* expires_in_str = NULL;
char* error = NULL;
char* error_description = NULL;
char* json = NULL;
jsmn_parser parser;
jsmntok_t tok[128]; // we do not expect nor read more tokens
int tok_cnt = 0;
int locked = 0;
if (context==NULL || context->magic!=DC_CONTEXT_MAGIC
|| code==NULL || code[0]==0) {
dc_log_warning(context, 0, "Internal OAuth2 error");
goto cleanup;
}
pthread_mutex_lock(&context->oauth2_critical);
locked = 1;
// read generated token
if (!(flags&DC_REGENERATE)) {
access_token = dc_sqlite3_get_config(context->sql, "oauth2_access_token", NULL);
if (access_token!=NULL) {
goto cleanup; // success
}
}
// generate new token: build & call auth url
#define CLIENT_ID "959970109878-t6pl4k9fmsdvfnobae862urapdmhfvbe.apps.googleusercontent.com"
#define CLIENT_SECRET "g2f_Gc1YUJ-fWjnTkdsuk4Xo"
#define REDIRECT_URI "urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob"
refresh_token = dc_sqlite3_get_config(context->sql, "oauth2_refresh_token", NULL);
if (refresh_token==NULL)
{
auth_url = dc_mprintf("https://accounts.google.com/o/oauth2/token"
"?client_id=%s"
"&client_secret=%s"
"&grant_type=authorization_code"
"&code=%s"
"&redirect_uri=%s",
CLIENT_ID, CLIENT_SECRET, code, REDIRECT_URI);
}
else
{
auth_url = dc_mprintf("https://accounts.google.com/o/oauth2/token"
"?client_id=%s"
"&client_secret=%s"
"&grant_type=refresh_token"
"&refresh_token=%s"
"&redirect_uri=%s",
CLIENT_ID, CLIENT_SECRET, refresh_token, REDIRECT_URI);
}
json = (char*)context->cb(context, DC_EVENT_HTTP_POST, (uintptr_t)auth_url, 0);
if (json==NULL) {
dc_log_warning(context, 0, "Error calling OAuth2 url");
goto cleanup;
}
// generate new token: parse returned json
jsmn_init(&parser);
tok_cnt = jsmn_parse(&parser, json, strlen(json), tok, sizeof(tok)/sizeof(tok[0]));
if (tok_cnt<2 || tok[0].type!=JSMN_OBJECT) {
dc_log_warning(context, 0, "Failed to parse OAuth2 json");
goto cleanup;
}
for (int i = 1; i < tok_cnt; i++) {
if (jsoneq(json, &tok[i], "access_token")==0) {
access_token = jsondup(json, &tok[i+1]);
}
else if (jsoneq(json, &tok[i], "refresh_token")==0) {
refresh_token = jsondup(json, &tok[i+1]);
}
else if (jsoneq(json, &tok[i], "expires_in")==0) {
expires_in_str = jsondup(json, &tok[i+1]);
}
else if (jsoneq(json, &tok[i], "error")==0) {
error = jsondup(json, &tok[i+1]);
}
else if (jsoneq(json, &tok[i], "error_description")==0) {
error_description = jsondup(json, &tok[i+1]);
}
}
if (error || error_description) {
dc_log_warning(context, 0, "OAuth error: %s: %s",
error? error : "unknown",
error_description? error_description : "no details");
dc_log_warning(context, 0, "FULL COMMAND WAS: %s", auth_url);
// continue, errors do not imply everything went wrong
}
if (access_token==NULL || access_token[0]==0) {
dc_log_warning(context, 0, "Failed to find OAuth2 access token");
goto cleanup;
}
dc_sqlite3_set_config(context->sql, "oauth2_access_token", access_token);
// update refresh_token if given,
// typically this is on the first round with `grant_type=authorization_code`
// but we update it later, too.
if (refresh_token && refresh_token[0]) {
dc_sqlite3_set_config(context->sql, "oauth2_refresh_token", refresh_token);
}
cleanup:
if (locked) { pthread_mutex_unlock(&context->oauth2_critical); }
free(refresh_token);
free(auth_url);
free(json);
free(expires_in_str);
free(error);
free(error_description);
return access_token? access_token : dc_strdup(NULL);
}