-/*
+/* oauth.c -- OAuth 2.0 implementation for XOAUTH2 in SMTP and POP3.
+ *
* This code is Copyright (c) 2014, by the authors of nmh. See the
* COPYRIGHT file in the root directory of the nmh distribution for
* complete copyright information.
#include <h/oauth.h>
#include <h/utils.h>
+#include "lock_file.h"
#define JSON_TYPE "application/json"
FILE *fp;
char *client_res;
- if (!mh_oauth_new (&ctx, svc)) adios(NULL, mh_oauth_get_err_string(ctx));
+ if (!mh_oauth_new (&ctx, svc))
+ adios(NULL, "%s", mh_oauth_get_err_string(ctx));
if (log != NULL) mh_oauth_log_to(stderr, ctx);
- fn = getcpy(mh_oauth_cred_fn(svc));
+ fn = mh_xstrdup(mh_oauth_cred_fn(svc));
fp = lkfopendata(fn, "r+", &failed_to_lock);
if (fp == NULL) {
if (errno == ENOENT) {
}
if ((cred = mh_oauth_cred_load(fp, ctx, user)) == NULL) {
- adios(NULL, mh_oauth_get_err_string(ctx));
+ adios(NULL, "%s", mh_oauth_get_err_string(ctx));
}
if (!mh_oauth_access_token_valid(time(NULL), cred)) {
if (mh_oauth_get_err_code(ctx) == MH_OAUTH_BAD_GRANT) {
adios(NULL, "credentials rejected -- run mhlogin -saslmech xoauth2 -authservice %s", svc);
}
- advise(NULL, "error refreshing OAuth2 token");
- adios(NULL, mh_oauth_get_err_string(ctx));
+ inform("error refreshing OAuth2 token");
+ adios(NULL, "%s", mh_oauth_get_err_string(ctx));
}
fseek(fp, 0, SEEK_SET);
if (!mh_oauth_cred_save(fp, cred, user)) {
- adios(NULL, mh_oauth_get_err_string(ctx));
+ adios(NULL, "%s", mh_oauth_get_err_string(ctx));
}
}
free(fn);
/* XXX writeBase64raw modifies the source buffer! make a copy */
- client_res = getcpy(mh_oauth_sasl_client_response(oauth_res_len, user,
+ client_res = mh_xstrdup(mh_oauth_sasl_client_response(oauth_res_len, user,
cred));
mh_oauth_cred_free(cred);
mh_oauth_free(ctx);
is_json(const char *content_type)
{
return content_type != NULL
- && strncasecmp(content_type, JSON_TYPE, sizeof JSON_TYPE - 1) == 0;
+ && strncasecmp(content_type, JSON_TYPE, LEN(JSON_TYPE)) == 0;
}
static void
}
static char *
-make_user_agent()
+make_user_agent(void)
{
const char *curl = curl_version_info(CURLVERSION_NOW)->version;
- char *s = mh_xmalloc(strlen(user_agent)
- + 1
- + sizeof "libcurl"
- + 1
- + strlen(curl)
- + 1);
- sprintf(s, "%s libcurl/%s", user_agent, curl);
- return s;
+ return concat(user_agent, " libcurl/", curl, NULL);
}
boolean
const char *
mh_oauth_get_err_string(mh_oauth_ctx *ctx)
{
- char *result;
const char *base;
free(ctx->err_formatted);
base = "unknown error";
}
if (ctx->err_details == NULL) {
- return ctx->err_formatted = getcpy(base);
+ return ctx->err_formatted = mh_xstrdup(base);
}
- /* length of the two strings plus ": " and '\0' */
- result = mh_xmalloc(strlen(base) + strlen(ctx->err_details) + 3);
- sprintf(result, "%s: %s", base, ctx->err_details);
- return ctx->err_formatted = result;
+
+ ctx->err_formatted = concat(base, ": ", ctx->err_details, NULL);
+ return ctx->err_formatted;
}
const char *
boolean success = FALSE;
char name[NAMESZ], value_buf[BUFSIZ];
int state;
- m_getfld_state_t getfld_ctx = 0;
+ m_getfld_state_t getfld_ctx;
struct user_creds *user_creds;
NEW(user_creds);
user_creds->len = 0;
user_creds->creds = mh_xmalloc(user_creds->alloc * sizeof *user_creds->creds);
+ getfld_ctx = m_getfld_state_init(fp);
for (;;) {
int size = sizeof value_buf;
- switch (state = m_getfld(&getfld_ctx, name, value_buf, &size, fp)) {
+ switch (state = m_getfld2(&getfld_ctx, name, value_buf, &size)) {
case FLD:
case FLDPLUS: {
char **save, *expire;
time_t *expires_at = NULL;
- if (strncmp(name, "access-", 7) == 0) {
+ if (has_prefix(name, "access-")) {
const char *user = name + 7;
mh_oauth_cred *creds = find_or_alloc_user_creds(user_creds,
user);
save = &creds->access_token;
- } else if (strncmp(name, "refresh-", 8) == 0) {
+ } else if (has_prefix(name, "refresh-")) {
const char *user = name + 8;
mh_oauth_cred *creds = find_or_alloc_user_creds(user_creds,
user);
save = &creds->refresh_token;
- } else if (strncmp(name, "expire-", 7) == 0) {
+ } else if (has_prefix(name, "expire-")) {
const char *user = name + 7;
mh_oauth_cred *creds = find_or_alloc_user_creds(user_creds,
user);
char *tmp = getcpy(value_buf);
while (state == FLDPLUS) {
size = sizeof value_buf;
- state = m_getfld(&getfld_ctx, name, value_buf, &size, fp);
+ state = m_getfld2(&getfld_ctx, name, value_buf, &size);
tmp = add(value_buf, tmp);
}
*save = trimcpy(tmp);
break;
default:
- /* Not adding details for LENERR/FMTERR because m_getfld already
+ /* Not adding details for LENERR/FMTERR because m_getfld2 already
* wrote advise message to stderr. */
set_err(ctx, MH_OAUTH_CRED_FILE);
break;
mh_oauth_sasl_client_response(size_t *res_len,
const char *user, const mh_oauth_cred *cred)
{
- size_t len = sizeof "user=" - 1
- + strlen(user)
- + sizeof "\1auth=Bearer " - 1
- + strlen(cred->access_token)
- + sizeof "\1\1" - 1;
- free(cred->ctx->sasl_client_res);
- cred->ctx->sasl_client_res = mh_xmalloc(len + 1);
- *res_len = len;
- sprintf(cred->ctx->sasl_client_res, "user=%s\1auth=Bearer %s\1\1",
- user, cred->access_token);
- return cred->ctx->sasl_client_res;
+ char **p;
+
+ p = &cred->ctx->sasl_client_res;
+ free(*p);
+ *p = concat("user=", user, "\1auth=Bearer ", cred->access_token, "\1\1", NULL);
+ *res_len = strlen(*p);
+ return *p;
}
/*******************************************************************************
len = 0;
prefix = "";
} else {
- len = sprintf(s, "%s", base_url);
+ len = strlen(base_url);
+ if (len > size - 1) /* Less one for NUL. */
+ return FALSE;
+ strcpy(s, base_url);
prefix = "?";
}
}
static int
-debug_callback(const CURL *handle, curl_infotype type, const char *data,
+debug_callback(CURL *handle, curl_infotype type, char *data,
size_t size, void *userptr)
{
FILE *fp = userptr;
}
fwrite(data, 1, size, fp);
if (data[size - 1] != '\n') {
- fputs("\n", fp);
+ putc('\n', fp);
}
fflush(fp);
return 0;
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, ctx);
- if (strncmp(url, "http://127.0.0.1:", 17) == 0) {
+ if (has_prefix(url, "http://127.0.0.1:")) {
/* Hack: on Cygwin, curl doesn't fail to connect with ECONNREFUSED.
Instead, it waits to timeout. So set a really short timeout, but
just on localhost (for convenience of the user, and the test