summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorꦌꦫꦶꦏ꧀ꦦꦿꦧꦮꦑꦩꦭ꧀ <erik@darapsa.co.id>2022-09-18 21:14:36 +0800
committerꦌꦫꦶꦏ꧀ꦦꦿꦧꦮꦑꦩꦭ꧀ <erik@darapsa.co.id>2022-09-18 21:14:36 +0800
commit7a47bb74f8cf5780200126deb010df77f0090a37 (patch)
treecf86e156f0d5b0fa1742a0e7b443a562f87d9d8a
parent20232131dbb00468fa55df70916db1abc5a720fb (diff)
Move MHD access handler to the library
so the code on the app side is much simpler. Also prepare for accessing the GraphQL Admin API.
-rw-r--r--request.h42
-rw-r--r--session.h4
-rw-r--r--shopify.c291
-rw-r--r--shopify.h29
-rw-r--r--token.h2
5 files changed, 231 insertions, 137 deletions
diff --git a/request.h b/request.h
index 7c8fe99..75bfa65 100644
--- a/request.h
+++ b/request.h
@@ -6,22 +6,28 @@
#define TOKEN_POST "client_id=%s&client_secret=%s&code=%s"
#define TOKEN_POST_LEN strlen(TOKEN_POST) - strlen("%s") * 3
+#define TOKEN_HEADER "X-Shopify-Access-Token: %s"
+#define TOKEN_HEADER_LEN strlen(TOKEN_HEADER) - strlen("%s")
+
+#define GRAPHQL_URL "https://%s/admin/api/2022-07/graphql.json"
+#define GRAPHQL_URL_LEN strlen(GRAPHQL_URL) - strlen("%s")
+
static inline void request_init()
{
curl_global_init(CURL_GLOBAL_DEFAULT);
}
-static size_t append(char *data, size_t size, size_t nmemb, char **tok)
+static size_t append(char *data, size_t size, size_t nmemb, char **json)
{
size_t realsize = size * nmemb;
- size_t tok_len = *tok ? strlen(*tok) : 0;
- *tok = realloc(*tok, tok_len + realsize + 1);
- strlcpy(&(*tok)[tok_len], data, realsize + 1);
+ size_t json_len = *json ? strlen(*json) : 0;
+ *json = realloc(*json, json_len + realsize + 1);
+ strlcpy(&(*json)[json_len], data, realsize + 1);
return realsize;
}
static inline void request_token(const char *host, const char *key,
- const char *secret_key, const char *code, char **tok)
+ const char *secret_key, const char *code, char **json)
{
CURL *curl = curl_easy_init();
char url[TOKEN_URL_LEN + strlen(host) + 1];
@@ -31,12 +37,36 @@ static inline void request_token(const char *host, const char *key,
+ strlen(code) + 1];
sprintf(post, TOKEN_POST, key, secret_key, code);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post);
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, tok);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, append);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, json);
+#ifdef DEBUG
+ curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+#endif
+ curl_easy_perform(curl);
+ curl_easy_cleanup(curl);
+}
+
+static inline void request_graphql(const char *query,
+ const struct shopify_session *session, char **json)
+{
+ CURL *curl = curl_easy_init();
+ char url[GRAPHQL_URL_LEN + strlen(session->shop) + 1];
+ sprintf(url, GRAPHQL_URL, session->shop);
+ curl_easy_setopt(curl, CURLOPT_URL, url);
+ struct curl_slist *list = NULL;
+ curl_slist_append(list, "Content-Type: application/json");
+ char header[TOKEN_HEADER_LEN + strlen(session->token) + 1];
+ sprintf(header, TOKEN_HEADER, session->token);
+ curl_slist_append(list, header);
+ curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
+ curl_easy_setopt(curl, CURLOPT_POSTFIELDS, query);
+ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, append);
+ curl_easy_setopt(curl, CURLOPT_WRITEDATA, json);
#ifdef DEBUG
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
#endif
curl_easy_perform(curl);
+ curl_slist_free_all(list);
curl_easy_cleanup(curl);
}
diff --git a/session.h b/session.h
index 9c850bf..e14f976 100644
--- a/session.h
+++ b/session.h
@@ -1,6 +1,6 @@
-static struct session {
+struct shopify_session {
char *shop;
char *nonce;
char *token;
char *scope;
-} *sessions;
+};
diff --git a/shopify.c b/shopify.c
index e0098d2..17d7fb4 100644
--- a/shopify.c
+++ b/shopify.c
@@ -1,12 +1,14 @@
+#include <stdbool.h>
#include <fcntl.h>
#include <sys/stat.h>
+#include <microhttpd.h>
#include "shopify.h"
#include "crypt.h"
#include "base64.h"
#include "regex.h"
#include "config.h"
-#include "request.h"
#include "session.h"
+#include "request.h"
#include "token.h"
#define AUTH_URL \
@@ -50,26 +52,38 @@ extern inline void config_getscopes(const char *, char **);
extern inline void request_init();
extern inline void request_token(const char *, const char *, const char *,
const char *, char **);
+extern inline void request_graphql(const char *, const struct shopify_session *,
+ char **);
extern inline void request_cleanup();
-extern inline void token_parse(const char *, struct session *);
+extern inline void token_parse(const char *, struct shopify_session *);
-void shopify_init()
-{
- crypt_init();
- request_init();
- sessions = malloc(sizeof(struct session));
- sessions[0].shop = NULL;
-}
+struct parameter {
+ char *key;
+ char *val;
+};
+
+struct container {
+ const char *key;
+ const char *secret;
+ const char *app_url;
+ const char *redir_url;
+ const char *app_id;
+ const char *scope;
+ const char *index;
+ const struct shopify_api *apis;
+};
+
+static struct shopify_session *sessions;
static enum MHD_Result getparam(void *cls, enum MHD_ValueKind kind,
const char *key, const char *val)
{
if (kind == MHD_GET_ARGUMENT_KIND) {
- struct shopify_param **params = cls;
+ struct parameter **params = cls;
int nparams = 0;
while ((*params)[nparams].key)
nparams++;
- *params = realloc(*params, sizeof(struct shopify_param)
+ *params = realloc(*params, sizeof(struct parameter)
* (nparams + 2));
(*params)[nparams].key = malloc(strlen(key) + 1);
strcpy((*params)[nparams].key, key);
@@ -80,7 +94,7 @@ static enum MHD_Result getparam(void *cls, enum MHD_ValueKind kind,
return MHD_YES;
}
-static inline void clear(const struct shopify_param params[])
+static inline void clear(const struct parameter params[])
{
int i = 0;
while (params[i].key) {
@@ -94,33 +108,49 @@ static int keycmp(const void *struct1, const void *struct2)
return strcmp(*(char **)struct1, *(char **)struct2);
}
-bool shopify_valid(struct MHD_Connection *conn, const char *url,
- const char *redir_url, const char *secret_key,
- struct shopify_param *params[])
+static inline int redirect(const char *host, const char *id,
+ struct MHD_Connection *con, struct MHD_Response **res)
+{
+ char url[EMBEDDED_URL_LEN + strlen(host) + strlen(id) + 1];
+ sprintf(url, EMBEDDED_URL, host, id);
+ *res = MHD_create_response_from_buffer(0, "", MHD_RESPMEM_PERSISTENT);
+ MHD_add_response_header(*res, "Location", url);
+ return MHD_queue_response(con, MHD_HTTP_PERMANENT_REDIRECT, *res);
+}
+
+static enum MHD_Result handle_request(void *cls, struct MHD_Connection *con,
+ const char *url, const char *method, const char *version,
+ const char *upload_data, size_t *upload_data_size,
+ void **con_cls)
{
- (*params)[0].key = NULL;
- MHD_get_connection_values(conn, MHD_GET_ARGUMENT_KIND, getparam,
- params);
+ struct parameter *params = *con_cls;
+ if (!params) {
+ params = malloc(sizeof(struct parameter));
+ params[0].key = NULL;
+ *con_cls = params;
+ return MHD_YES;
+ }
+ MHD_get_connection_values(con, MHD_GET_ARGUMENT_KIND, getparam,
+ &params);
int nparams = 0;
- while ((*params)[nparams].key)
+ while (params[nparams].key)
nparams++;
if (!nparams)
- return false;
- qsort(*params, nparams, sizeof(struct shopify_param), keycmp);
- struct shopify_param *param = NULL;
+ return MHD_NO;
+ qsort(params, nparams, sizeof(struct parameter), keycmp);
+ struct parameter *param = NULL;
char *shop = NULL;
- if ((param = bsearch(&(struct shopify_param) { "shop" }, *params,
- nparams, sizeof(struct shopify_param),
- keycmp)))
+ if ((param = bsearch(&(struct parameter) { "shop" }, params, nparams,
+ sizeof(struct parameter), keycmp)))
shop = param->val;
if (!shop || !regex_match(shop)) {
- clear(*params);
- return false;
+ clear(params);
+ return MHD_NO;
}
char *query = NULL;
for (int i = 0; i < nparams; i++) {
- const char *key = (*params)[i].key;
- const char *val = (*params)[i].val;
+ const char *key = params[i].key;
+ const char *val = params[i].val;
if (strcmp(key, "hmac")) {
size_t query_len = query ? strlen(query) : 0;
bool ampersand_len = i != nparams - 1;
@@ -133,112 +163,122 @@ bool shopify_valid(struct MHD_Connection *conn, const char *url,
}
}
char *hmac = NULL;
- if ((param = bsearch(&(struct shopify_param) { "hmac" }, *params,
- nparams, sizeof(struct shopify_param),
- keycmp)))
+ if ((param = bsearch(&(struct parameter) { "hmac" }, params, nparams,
+ sizeof(struct parameter), keycmp)))
hmac = param->val;
+ struct container *container = cls;
+ const char *secret_key = container->secret;
if (!hmac || !crypt_maccmp(secret_key, query, hmac)) {
- clear(*params);
+ clear(params);
free(query);
- return false;
+ return MHD_NO;
}
free(query);
- if (strcmp(url, redir_url))
- return true;
int nsessions = 0;
while (sessions[nsessions].shop)
nsessions++;
- qsort(sessions, nsessions, sizeof(struct session), keycmp);
- if (strcmp(((struct shopify_param *)bsearch(&(struct shopify_param)
- { "state" }, *params, nparams,
- sizeof(struct shopify_param),
+ qsort(sessions, nsessions, sizeof(struct shopify_session), keycmp);
+ const char *redir_url = container->redir_url;
+ if (!strcmp(url, redir_url)
+ && strcmp(((struct parameter *)bsearch(
+ &(struct parameter){ "state" },
+ params, nparams,
+ sizeof(struct parameter),
keycmp))->val,
- ((struct session *)bsearch(&(struct session)
- { shop }, sessions, nsessions,
- sizeof(struct session),
+ ((struct shopify_session *)bsearch(
+ &(struct shopify_session){ shop },
+ sessions, nsessions,
+ sizeof(struct shopify_session),
keycmp))->nonce)) {
- clear(*params);
- return false;
+ clear(params);
+ return MHD_NO;
}
- return true;
-}
-
-static inline int redirect(const char *host, const char *id,
- struct MHD_Connection *conn, struct MHD_Response **resp)
-{
- char url[EMBEDDED_URL_LEN + strlen(host) + strlen(id) + 1];
- sprintf(url, EMBEDDED_URL, host, id);
- *resp = MHD_create_response_from_buffer(0, "", MHD_RESPMEM_PERSISTENT);
- MHD_add_response_header(*resp, "Location", url);
- return MHD_queue_response(conn, MHD_HTTP_PERMANENT_REDIRECT, *resp);
-}
-
-enum MHD_Result shopify_respond(const struct shopify_param params[],
- const char *url, const char *redir_url, const char *app_url,
- const char *app_id, const char *key, const char *secret_key,
- const char *toml_path, const char *html_path,
- struct MHD_Connection *conn, struct MHD_Response **resp)
-{
- int nparams = 0;
- while (params[nparams].key)
- nparams++;
- char *shop = ((struct shopify_param *)bsearch(&(struct shopify_param)
- { "shop" }, params, nparams,
- sizeof(struct shopify_param), keycmp))->val;
const size_t shop_len = strlen(shop);
- char *host = ((struct shopify_param *)bsearch(&(struct shopify_param)
- { "host" }, params, nparams,
- sizeof(struct shopify_param), keycmp))->val;
- struct shopify_param *param = bsearch(&(struct shopify_param)
- { "embedded" }, params, nparams,
- sizeof(struct shopify_param), keycmp);
+ char *host = ((struct parameter *)bsearch(&(struct parameter){ "host" },
+ params, nparams, sizeof(struct parameter),
+ keycmp))->val;
+ param = bsearch(&(struct parameter){ "embedded" }, params, nparams,
+ sizeof(struct parameter), keycmp);
bool embedded = param && !strcmp(param->val, "1");
char *dec_host;
base64_decode(host, &dec_host);
- int nsessions = 0;
- while (sessions[nsessions].shop)
- nsessions++;
- qsort(sessions, nsessions, sizeof(struct session), keycmp);
- struct session *session = bsearch(&(struct session){ shop }, sessions,
- nsessions, sizeof(struct session), keycmp);
+ struct shopify_session *session = bsearch(&(struct shopify_session)
+ { shop }, sessions, nsessions,
+ sizeof(struct shopify_session), keycmp);
+ const char *key = container->key;
const size_t key_len = strlen(key);
+ const char *app_id = container->app_id;
char header[EMBEDDED_HEADER_LEN + shop_len + 1];
sprintf(header, EMBEDDED_HEADER, shop);
- enum MHD_Result ret;
+ struct MHD_Response *res;
+ enum MHD_Result ret = MHD_NO;
if (!strcmp(url, redir_url)) {
- const char *code = ((struct shopify_param *)bsearch(
- &(struct shopify_param){ "code" },
- params, nparams,
- sizeof(struct shopify_param),
+ const char *code = ((struct parameter *)bsearch(
+ &(struct parameter){ "code" }, params,
+ nparams, sizeof(struct parameter),
keycmp))->val;
char *token = NULL;
request_token(dec_host, key, secret_key, code, &token);
token_parse(token, session);
free(token);
- ret = redirect(dec_host, app_id, conn, resp);
+ ret = redirect(dec_host, app_id, con, &res);
} else if (session && session->token) {
if (embedded) {
- int fd = open(html_path, O_RDONLY);
- struct stat sb;
- fstat(fd, &sb);
- char index[sb.st_size + 1];
- read(fd, index, sb.st_size);
- close(fd);
- *resp = MHD_create_response_from_buffer(sb.st_size,
- index, MHD_RESPMEM_MUST_COPY);
- MHD_add_response_header(*resp,
- "Content-Security-Policy", header);
- ret = MHD_queue_response(conn, MHD_HTTP_OK, *resp);
+ if (!strcmp(url, "/") && !strcmp(method, "GET")) {
+ int fd = open(container->index, O_RDONLY);
+ struct stat sb;
+ fstat(fd, &sb);
+ char index[sb.st_size + 1];
+ read(fd, index, sb.st_size);
+ close(fd);
+ res = MHD_create_response_from_buffer(
+ sb.st_size, index,
+ MHD_RESPMEM_MUST_COPY);
+ MHD_add_response_header(res,
+ "Content-Security-Policy",
+ header);
+ ret = MHD_queue_response(con, MHD_HTTP_OK, res);
+ } else {
+ int i = 0;
+ const struct shopify_api *api;
+ while ((api = &(container->apis[i++])))
+ if (!strcmp(url, api->url)
+ && !strcmp(method,
+ api->method)) {
+ char *json = NULL;
+ api->cb(api->arg, session,
+ json);
+ res = MHD_create_response_from_buffer(
+ strlen(json),
+ json,
+ MHD_RESPMEM_MUST_FREE);
+ MHD_add_response_header(res,
+ "Content-"\
+ "Security-"\
+ "Policy",
+ header);
+ MHD_add_response_header(res,
+ "Content-"\
+ "Type",
+ "application/"\
+ "json");
+ ret = MHD_queue_response(con,
+ MHD_HTTP_OK,
+ res);
+ break;
+ }
+ }
} else
- ret = redirect(dec_host, app_id, conn, resp);
+ ret = redirect(dec_host, app_id, con, &res);
} else {
const size_t dec_host_len = strlen(dec_host);
char *scopes = NULL;
- config_getscopes(toml_path, &scopes);
+ config_getscopes(container->scope, &scopes);
const size_t scopes_len = strlen(scopes);
static const size_t nonce_len = 64;
char nonce[nonce_len + 1];
crypt_getnonce(nonce, nonce_len);
+ const char *app_url = container->app_url;
const size_t auth_url_len = AUTH_URL_LEN + dec_host_len
+ key_len + scopes_len + strlen(app_url)
+ strlen(redir_url) + nonce_len;
@@ -246,7 +286,7 @@ enum MHD_Result shopify_respond(const struct shopify_param params[],
sprintf(auth_url, AUTH_URL, dec_host, key, scopes, app_url,
redir_url, nonce);
free(scopes);
- sessions = realloc(sessions, sizeof(struct session)
+ sessions = realloc(sessions, sizeof(struct shopify_session)
* (nsessions + 2));
sessions[nsessions].shop = malloc(shop_len + 1);
strcpy(sessions[nsessions].shop, shop);
@@ -258,17 +298,17 @@ enum MHD_Result shopify_respond(const struct shopify_param params[],
+ strlen(host) + auth_url_len;
char page[page_len + 1];
sprintf(page, REDIR_PAGE, key, host, auth_url);
- *resp = MHD_create_response_from_buffer(page_len,
+ res = MHD_create_response_from_buffer(page_len,
page, MHD_RESPMEM_MUST_COPY);
- MHD_add_response_header(*resp,
- "Content-Security-Policy", header);
- ret = MHD_queue_response(conn, MHD_HTTP_OK, *resp);
+ MHD_add_response_header(res, "Content-Security-Policy",
+ header);
+ ret = MHD_queue_response(con, MHD_HTTP_OK, res);
} else {
- *resp = MHD_create_response_from_buffer(0, "",
+ res = MHD_create_response_from_buffer(0, "",
MHD_RESPMEM_PERSISTENT);
- MHD_add_response_header(*resp, "Location", auth_url);
- ret = MHD_queue_response(conn,
- MHD_HTTP_TEMPORARY_REDIRECT, *resp);
+ MHD_add_response_header(res, "Location", auth_url);
+ ret = MHD_queue_response(con,
+ MHD_HTTP_TEMPORARY_REDIRECT, res);
}
}
free(dec_host);
@@ -276,9 +316,29 @@ enum MHD_Result shopify_respond(const struct shopify_param params[],
return ret;
}
-void shopify_cleanup()
+void shopify_app(const char *api_key, const char *api_secret_key,
+ const char *app_url, const char *redir_url, const char *app_id,
+ const char *scope, const char *index,
+ const struct shopify_api apis[])
{
- request_cleanup();
+ crypt_init();
+ request_init();
+ sessions = malloc(sizeof(struct shopify_session));
+ sessions[0].shop = NULL;
+ struct MHD_Daemon *daemon
+ = MHD_start_daemon(MHD_USE_INTERNAL_POLLING_THREAD, 3000, NULL,
+ NULL, &handle_request, &(struct container){
+ api_key,
+ api_secret_key,
+ app_url,
+ redir_url,
+ app_id,
+ scope,
+ index,
+ apis
+ }, MHD_OPTION_END);
+ getchar();
+ MHD_stop_daemon(daemon);
int i = 0;
while (sessions[i].shop) {
if (sessions[i].scope)
@@ -289,4 +349,11 @@ void shopify_cleanup()
free(sessions[i++].shop);
}
free(sessions);
+ request_cleanup();
+}
+
+void shopify_graphql(const char *query, const struct shopify_session *session,
+ char **json)
+{
+ request_graphql(query, session, json);
}
diff --git a/shopify.h b/shopify.h
index 0c6f4e7..02ac22a 100644
--- a/shopify.h
+++ b/shopify.h
@@ -1,28 +1,25 @@
#ifndef SHOPIFY_H
#define SHOPIFY_H
-#include <stdbool.h>
-#include <microhttpd.h>
-
-struct shopify_param {
- char *key;
- char *val;
+struct shopify_api {
+ char *url;
+ char *method;
+ void (*cb)();
+ void *arg;
};
+struct shopify_session;
+
#ifdef __cplusplus
extern "C" {
#endif
-void shopify_init();
-bool shopify_valid(struct MHD_Connection *conn, const char *url,
- const char *redir_url, const char *key,
- struct shopify_param *params[]);
-enum MHD_Result shopify_respond(const struct shopify_param params[],
- const char *url, const char *redir_url, const char *app_url,
- const char *app_id, const char *key, const char *secret_key,
- const char *toml_path, const char *html_path,
- struct MHD_Connection *conn, struct MHD_Response **resp);
-void shopify_cleanup();
+void shopify_app(const char *api_key, const char *api_secret_key,
+ const char *app_url, const char *redir_url, const char *app_id,
+ const char *scope, const char *index,
+ const struct shopify_api apis[]);
+void shopify_graphql(const char *query, const struct shopify_session *session,
+ char **json);
#ifdef __cplusplus
}
diff --git a/token.h b/token.h
index c0c1f82..40f600e 100644
--- a/token.h
+++ b/token.h
@@ -1,6 +1,6 @@
#include <json.h>
-static inline void token_parse(const char *tok, struct session *session)
+static inline void token_parse(const char *tok, struct shopify_session *session)
{
json_tokener *tokener = json_tokener_new();
json_object *obj = json_tokener_parse_ex(tokener, tok, strlen(tok));