#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include "string-util.h" #include "auth.h" #include "http.h" #define CLIENT_NAME "ap_client" #define PROTOCOL_PREFIX "https://" char *instance_domain; char *client_id; char *client_secret; char *access_token; char *scope; static char *app_register_url = "/api/v1/apps"; static char *login_url = "/oauth/token"; struct register_call_arg { char *url; char *domain; char *post_data; void (*callback)(bool); }; static void *register_call(void *req_arg) { struct register_call_arg *arg; char *resp; json_t *root; arg = (struct register_call_arg *)req_arg; if (!arg || !(arg->url)) { fprintf(stderr, "register_call(): invalid arguments\n"); goto error; } resp = post_request(arg->url, arg->post_data); if (!resp) { fprintf(stderr, "register_call(): failed to send http request\n"); goto error; } if (!resp) { fprintf(stderr, "register_call(): null response\n"); goto error; } root = json_loads(resp, 0, NULL); if (!root) { fprintf(stderr, "register_call(): failed to parse json\n"); goto error; } if (!json_is_object(root)) { fprintf(stderr, "register_call(): json root is not object\n"); goto error; } json_t *cid = json_object_get(root, "client_id"); json_t *csec = json_object_get(root, "client_secret"); if (!json_is_string(cid) || !json_is_string(csec)) { fprintf(stderr, "register_call(): invalid client_id or client_secret\n"); goto error; } client_id = strdup(json_string_value(cid)); client_secret = strdup(json_string_value(csec)); instance_domain = arg->domain; if (strlen(client_id) < 1 || strlen(client_secret) < 1) { fprintf(stderr, "register_call(): invalid client_id or client_secret\n"); goto error; } free(arg->url); if (arg->post_data) free(arg->post_data); free(arg); free(resp); json_decref(root); (*(arg->callback))(true); return NULL; error: if (arg) { if (arg->url) free(arg->url); if (arg->post_data) free(arg->post_data); free(arg); } if (resp) free(resp); if (root) json_decref(root); (*(arg->callback))(false); return NULL; } int register_app(const char *instance, void (*callback)(bool success)) { size_t size; char *instance_prefix; char *url, *req_data; struct register_call_arg *arg; json_t *root; json_error_t error; pthread_t t; if (!instance || strlen(instance) < 1 || !callback) { fprintf(stderr, "register_app(): invalid argument\n"); return -1; } size = sizeof(PROTOCOL_PREFIX) + sizeof(instance) + 1; instance_prefix = malloc(size); if (!instance_prefix) { err(1, "register_app(): malloc failed"); return -1; } strlcpy(instance_prefix, PROTOCOL_PREFIX, size); strlcat(instance_prefix, instance, size); root = json_pack_ex(&error, 1, "{s:s, s:s, s:s}", "client_name", CLIENT_NAME, "redirect_uris", "urn:ietf:wg:oauth:2.0:oob", "scopes", "read write push"); if (!root) { fprintf(stderr, "register_app(): json pack error: line %d: %s\n", error.line, error.text); return -1; } req_data = json_dumps(root, 0); json_decref(root); if (!req_data) { fprintf(stderr, "register_app(): failed to dump json\n"); return -1; } size = strlen(instance_prefix) + strlen(app_register_url) + 1; url = malloc(size); if (!url) { err(1, "register_app(): malloc failed"); return -1; } strlcpy(url, instance_prefix, size); strlcat(url, app_register_url, size); arg = calloc(1, sizeof(struct register_call_arg)); if (!arg) { err(1, "register_app(): calloc failed"); return -1; } arg->url = url; arg->domain = instance_prefix; arg->post_data = req_data; arg->callback = callback; return pthread_create(&t, NULL, ®ister_call, arg); } struct login_call_arg { char *url; char *post_data; void (*callback)(bool); }; static void *login_call(void *req_arg) { struct login_call_arg *arg = NULL; char *resp = NULL; json_t *root = NULL; arg = (struct login_call_arg *)req_arg; if (!arg || !(arg->url)) { fprintf(stderr, "login_call(): invalid arguments\n"); goto error; } resp = post_request(arg->url, arg->post_data); if (!resp) { fprintf(stderr, "login_call(): failed to send http request\n"); goto error; } if (!resp) { fprintf(stderr, "login_call(): null response\n"); goto error; } root = json_loads(resp, 0, NULL); if (!root) { fprintf(stderr, "login_call(): failed to parse json\n"); goto error; } if (!json_is_object(root)) { fprintf(stderr, "login_call(): json root is not object\n"); goto error; } json_t *access_token_j = json_object_get(root, "access_token"); json_t *scope_j = json_object_get(root, "scope"); if (!json_is_string(access_token_j) || !json_is_string(scope_j) || strlen(json_string_value(access_token_j)) < 1 || strlen(json_string_value(scope_j)) < 1) { fprintf(stderr, "login_call(): invalid access_token or scope\n"); goto error; } access_token = strdup(json_string_value(access_token_j)); scope = strdup(json_string_value(scope_j)); free(arg->url); if (arg->post_data) free(arg->post_data); free(arg); free(resp); json_decref(root); (*(arg->callback))(true); printf("access_token: %s\n", access_token); return NULL; error: if (arg) { if (arg->url) free(arg->url); if (arg->post_data) free(arg->post_data); free(arg); } if (resp) free(resp); if (root) json_decref(root); (*(arg->callback))(false); return NULL; } int login( const char *email, const char *password, void (*callback)(bool success)) { json_t *root; json_error_t error; pthread_t t; size_t size; char *url, *req_data; struct login_call_arg *arg; if (!email || strlen(email) < 1 || !password || strlen(password) < 1) { fprintf(stderr, "login(): invalid argument\n"); return -1; } if (!client_id || strlen(client_id) < 1 || !client_secret || strlen(client_secret) < 1 || !instance_domain || strlen(instance_domain) < 1) { fprintf(stderr, "login(): invalid client_id or client_secret\n"); printf("cid: %s\n", client_id); printf("csec: %s\n", client_secret); printf("in: %s\n", instance_domain); return -1; } root = json_pack_ex(&error, 1, "{s:s, s:s, s:s, s:s, s:s}", "client_id", client_id, "client_secret", client_secret, "grant_type", "password", "username", email, "password", password); if (!root) { fprintf(stderr, "login(): json pack error: line %d: %s\n", error.line, error.text); return -1; } req_data = json_dumps(root, 0); json_decref(root); if (!req_data) { fprintf(stderr, "login(): failed to dump json\n"); return -1; } size = strlen(instance_domain) + strlen(login_url) + 1; url = malloc(size); if (!url) { err(1, "login(): malloc failed"); return -1; } strlcpy(url, instance_domain, size); strlcat(url, login_url, size); arg = calloc(1, sizeof(struct login_call_arg)); if (!arg) { err(1, "login(): calloc failed"); return -1; } arg->url = url; arg->post_data = req_data; arg->callback = callback; return pthread_create(&t, NULL, &login_call, arg); return 0; } void auth_cleanup() { if (instance_domain) free(instance_domain); if (access_token) free(access_token); if (client_id) free(client_id); if (client_secret) free(client_secret); }