ncdc/libdc/src/api.c

273 lines
6.2 KiB
C

#include <dc/api.h>
#include <dc/refable.h>
#include "internal.h"
struct dc_api_
{
dc_refable_t ref;
struct event_base *base;
CURLM *curl;
GHashTable *syncs;
char *cookie;
};
static void dc_api_free(dc_api_t ptr)
{
return_if_true(ptr == NULL,);
if (ptr->syncs != NULL) {
g_hash_table_unref(ptr->syncs);
ptr->syncs = NULL;
}
free(ptr);
}
dc_api_t dc_api_new(void)
{
dc_api_t ptr = calloc(1, sizeof(struct dc_api_));
return_if_true(ptr == NULL, NULL);
ptr->ref.cleanup = (dc_cleanup_t)dc_api_free;
ptr->syncs = g_hash_table_new_full(g_direct_hash, g_direct_equal,
NULL, dc_unref
);
if (ptr->syncs == NULL) {
free(ptr);
return NULL;
}
return dc_ref(ptr);
}
void dc_api_set_curl_multi(dc_api_t api, CURLM *curl)
{
return_if_true(api == NULL,);
return_if_true(curl == NULL,);
api->curl = curl;
}
void dc_api_set_event_base(dc_api_t api, struct event_base *base)
{
return_if_true(api == NULL,);
return_if_true(base == NULL,);
api->base = base;
}
void dc_api_signal(dc_api_t api, CURL *easy, int code)
{
dc_api_sync_t sync = NULL;
return_if_true(api == NULL,);
return_if_true(easy == NULL,);
sync = g_hash_table_lookup(api->syncs, easy);
if (sync != NULL) {
dc_api_sync_finish(sync, code);
g_hash_table_remove(api->syncs, easy);
}
}
#ifdef DEBUG
int debug_callback(CURL *handle, curl_infotype type,
char *data, size_t size,
void *userptr)
{
FILE *f = fopen("debug.log", "a+");
switch (type) {
case CURLINFO_TEXT: fprintf(f, "+T: %s", data); break;
case CURLINFO_HEADER_IN: fprintf(f, ">H: %s", data); break;
case CURLINFO_HEADER_OUT: fprintf(f, "<H: %s", data); break;
case CURLINFO_DATA_IN: fprintf(f, ">D: %s\n", data); break;
case CURLINFO_DATA_OUT: fprintf(f, "<D: %s\n", data); break;
case CURLINFO_SSL_DATA_IN: fprintf(f, "<S: SSL_DATA_IN\n"); break;
case CURLINFO_SSL_DATA_OUT: fprintf(f, "<S: SSL_DATA_OUT\n"); break;
default: break;
}
fclose(f);
return 0;
}
#endif
static dc_api_sync_t
dc_api_do(dc_api_t api, char const *verb,
char const *url, char const *token,
char const *data, int64_t len)
{
return_if_true(api == NULL, NULL);
return_if_true(api->curl == NULL, NULL);
return_if_true(url == NULL, NULL);
return_if_true(verb == NULL, NULL);
CURL *c = NULL;
bool ret = false;
dc_api_sync_t sync = NULL;
struct curl_slist *l = NULL;
char *tmp = NULL;
c = curl_easy_init();
goto_if_true(c == NULL, cleanup);
sync = dc_api_sync_new(api->curl, c);
goto_if_true(c == NULL, cleanup);
curl_easy_setopt(c, CURLOPT_URL, url);
curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, fwrite);
curl_easy_setopt(c, CURLOPT_WRITEDATA, dc_api_sync_stream(sync));
if (api->cookie != NULL) {
curl_easy_setopt(c, CURLOPT_COOKIE, api->cookie);
}
l = dc_api_sync_list(sync);
if (data != NULL) {
curl_slist_append(l, "Content-Type: application/json");
}
curl_slist_append(l, "Accept: application/json");
curl_slist_append(l, "User-Agent: " DISCORD_USERAGENT);
curl_slist_append(l, "Pragma: no-cache");
curl_slist_append(l, "Cache-Control: no-cache");
if (token != NULL) {
asprintf(&tmp, "Authorization: %s", token);
curl_slist_append(l, tmp);
free(tmp);
tmp = NULL;
}
curl_easy_setopt(c, CURLOPT_HTTPHEADER, l);
curl_easy_setopt(c, CURLOPT_FORBID_REUSE, 1L);
//curl_easy_setopt(c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_easy_setopt(c, CURLOPT_FOLLOWLOCATION, 1L);
#ifdef DEBUG
curl_easy_setopt(c, CURLOPT_VERBOSE, 1L);
curl_easy_setopt(c, CURLOPT_DEBUGFUNCTION, debug_callback);
#endif
if (strcmp(verb, "POST") == 0) {
curl_easy_setopt(c, CURLOPT_POST, 1UL);
curl_easy_setopt(c, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL);
}
if (data != NULL) {
if (len >= 0) {
curl_easy_setopt(c, CURLOPT_POSTFIELDSIZE_LARGE, len);
}
curl_easy_setopt(c, CURLOPT_COPYPOSTFIELDS, data);
}
if (strcmp(verb, "PUT") == 0 ||
strcmp(verb, "DELETE") == 0) {
curl_easy_setopt(c, CURLOPT_CUSTOMREQUEST, verb);
}
if (curl_multi_add_handle(api->curl, c) != CURLM_OK) {
goto cleanup;
}
g_hash_table_insert(api->syncs, c, dc_ref(sync));
ret = true;
cleanup:
if (!ret) {
dc_unref(sync);
sync = NULL;
}
return sync;
}
dc_api_sync_t dc_api_call(dc_api_t api, char const *token,
char const *verb, char const *method,
json_t *j)
{
char *data = NULL;
char *url = NULL;
dc_api_sync_t s = NULL;
asprintf(&url, "%s/%s", DISCORD_URL, method);
goto_if_true(url == NULL, cleanup);
if (j != NULL) {
data = json_dumps(j, JSON_COMPACT);
goto_if_true(data == NULL, cleanup);
}
s = dc_api_do(api, verb, url, token, data, -1);
goto_if_true(s == NULL, cleanup);
cleanup:
free(data);
data = NULL;
free(url);
url = NULL;
return s;
}
json_t *dc_api_call_sync(dc_api_t api, char const *token,
char const *verb, char const *method,
json_t *j)
{
dc_api_sync_t s = NULL;
json_t *reply = NULL;
s = dc_api_call(api, verb, token, method, j);
goto_if_true(s == NULL, cleanup);
if (!dc_api_sync_wait(s)) {
goto cleanup;
}
reply = json_loadb(dc_api_sync_data(s),
dc_api_sync_datalen(s),
0, NULL
);
cleanup:
dc_unref(s);
s = NULL;
return reply;
}
bool dc_api_error(json_t *j, int *code, char const **message)
{
return_if_true(j == NULL, false);
bool error = false;
json_t *c = NULL, *m = NULL;
c = json_object_get(j, "code");
if (c != NULL) {
error = true;
if (code != NULL) {
*code = json_integer_value(c);
}
}
m = json_object_get(j, "message");
if (m != NULL) {
error = true;
if (message != NULL) {
*message = json_string_value(m);
}
}
return error;
}