split into library and applications

This commit is contained in:
2019-06-25 14:52:38 +02:00
parent 8972538d13
commit 05694d2611
23 changed files with 359 additions and 260 deletions

123
libdc/src/account.c Normal file
View File

@@ -0,0 +1,123 @@
#include <dc/account.h>
#include <dc/refable.h>
#include "internal.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
struct dc_account_
{
dc_refable_t ref; /* do not move anything above ref */
char *email;
char *password;
/* internal ID
*/
char *id;
/* authentication token
*/
char *token;
};
static void dc_account_free(dc_account_t ptr)
{
return_if_true(ptr == NULL,);
free(ptr->email);
free(ptr->password);
free(ptr);
}
dc_account_t dc_account_new(void)
{
dc_account_t ptr = calloc(1, sizeof(struct dc_account_));
ptr->ref.cleanup = (dc_cleanup_t)dc_account_free;
return dc_ref(ptr);
}
dc_account_t dc_account_new2(char const *email, char const *pass)
{
dc_account_t ptr = dc_account_new();
if (ptr != NULL) {
dc_account_set_email(ptr, email);
dc_account_set_password(ptr, pass);
}
return ptr;
}
void dc_account_set_email(dc_account_t a, char const *email)
{
return_if_true(a == NULL,);
return_if_true(email == NULL,);
free(a->email);
a->email = strdup(email);
}
char const *dc_account_email(dc_account_t a)
{
return_if_true(a == NULL, NULL);
return a->email;
}
void dc_account_set_password(dc_account_t a, char const *password)
{
return_if_true(a == NULL,);
return_if_true(password == NULL,);
free(a->password);
a->password = strdup(password);
}
char const *dc_account_password(dc_account_t a)
{
return_if_true(a == NULL, NULL);
return a->password;
}
void dc_account_set_token(dc_account_t a, char const *token)
{
return_if_true(a == NULL,);
free(a->token);
a->token = NULL;
if (token != NULL) {
a->token = strdup(token);
}
}
char const *dc_account_token(dc_account_t a)
{
return_if_true(a == NULL, NULL);
return a->token;
}
bool dc_account_has_token(dc_account_t a)
{
return_if_true(a == NULL, false);
return_if_true(a->token == NULL, false);
return true;
}
void dc_account_set_id(dc_account_t a, char const *id)
{
return_if_true(a == NULL,);
free(a->id);
a->id = strdup(id);
}
char const *dc_account_id(dc_account_t a)
{
return_if_true(a == NULL,NULL);
return a->id;
}

364
libdc/src/api.c Normal file
View File

@@ -0,0 +1,364 @@
#include <dc/api.h>
#include <dc/refable.h>
#include "internal.h"
#define DISCORD_URL "https://discordapp.com/api/v6"
#define DISCORD_USERAGENT "Mozilla/5.0 (X11; Linux x86_64; rv:67.0) Gecko/20100101 Firefox/67.0"
#define DISCORD_API_AUTH "auth/login"
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
static int debug_callback(CURL *handle, curl_infotype type,
char *data, size_t size,
void *userptr
)
{
switch (type) {
case CURLINFO_TEXT: printf("+T: %s", data); break;
case CURLINFO_HEADER_IN: printf(">H: %s", data); break;
case CURLINFO_HEADER_OUT: printf("<H: %s", data); break;
case CURLINFO_DATA_IN: printf(">D: %s\n", data); break;
case CURLINFO_DATA_OUT: printf("<D: %s\n", data); break;
case CURLINFO_SSL_DATA_IN:
case CURLINFO_SSL_DATA_OUT:
default: break;
}
return 0;
}
#endif
static int header_callback(char *data, size_t sz, size_t num, dc_api_t api)
{
char *ptr = NULL;
if ((ptr = strstr(data, "set-cookie")) != NULL) {
free(api->cookie);
api->cookie = NULL;
if ((ptr = strstr(data, ":")) != NULL) {
api->cookie = strdup(ptr+1);
if ((ptr = strstr(api->cookie, ";")) != NULL) {
*ptr = '\0';
}
}
}
return sz * num;
}
static dc_api_sync_t dc_api_post(dc_api_t api,
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);
CURL *c = NULL;
bool ret = false;
dc_api_sync_t sync = NULL;
struct curl_slist *l = NULL;
char *tmp = NULL;
int ptr = 0;
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));
curl_easy_setopt(c, CURLOPT_HEADERFUNCTION, header_callback);
curl_easy_setopt(c, CURLOPT_HEADERDATA, api);
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 (data != NULL) {
curl_easy_setopt(c, CURLOPT_POST, 1UL);
curl_easy_setopt(c, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL);
curl_easy_setopt(c, CURLOPT_COPYPOSTFIELDS, data);
if (len >= 0) {
curl_easy_setopt(c, CURLOPT_POSTFIELDSIZE_LARGE, len);
}
}
if (curl_multi_add_handle(api->curl, c) != CURLM_OK) {
goto cleanup;
}
g_hash_table_insert(api->syncs, c, dc_ref(sync));
curl_multi_socket_action(api->curl, CURL_SOCKET_TIMEOUT, 0, &ptr);
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 *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_post(api, 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 *method, json_t *j)
{
dc_api_sync_t s = NULL;
json_t *reply = NULL;
s = dc_api_call(api, token, method, j);
goto_if_true(s == NULL, cleanup);
if (!dc_api_sync_wait(s)) {
goto cleanup;
}
#ifdef DEBUG
printf("api_call_sync: %d\n", dc_api_sync_code(s));
#endif
reply = json_loadb(dc_api_sync_data(s),
dc_api_sync_datalen(s),
0, NULL
);
cleanup:
dc_unref(s);
s = NULL;
return reply;
}
static 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;
}
bool dc_api_authenticate(dc_api_t api, dc_account_t account)
{
json_t *j = json_object(), *reply = NULL, *token = NULL;
bool ret = false;
json_object_set_new(j, "email",
json_string(dc_account_email(account))
);
json_object_set_new(j, "password",
json_string(dc_account_password(account))
);
reply = dc_api_call_sync(api, NULL, DISCORD_API_AUTH, j);
goto_if_true(reply == NULL, cleanup);
if (dc_api_error(j, NULL, NULL)) {
return false;
}
token = json_object_get(reply, "token");
if (token == NULL || !json_is_string(token)) {
goto cleanup;
}
dc_account_set_token(account, json_string_value(token));
ret = true;
cleanup:
if (j != NULL) {
json_decref(j);
j = NULL;
}
if (reply != NULL) {
json_decref(reply);
reply = NULL;
}
return ret;
}
bool dc_api_userinfo(dc_api_t api, dc_account_t login,
dc_account_t user)
{
char *url = NULL;
json_t *reply = NULL;
bool ret = false;
return_if_true(api == NULL, false);
return_if_true(login == NULL, false);
return_if_true(user == NULL, false);
asprintf(&url, "users/%s", dc_account_id(user));
reply = dc_api_call_sync(api, dc_account_token(login), url, NULL);
goto_if_true(reply == NULL, cleanup);
/* TODO: parse json and store info in user
*/
ret = true;
cleanup:
if (reply != NULL) {
json_decref(reply);
reply = NULL;
}
return ret;
}

130
libdc/src/apisync.c Normal file
View File

@@ -0,0 +1,130 @@
#include <dc/apisync.h>
#include <dc/refable.h>
#include "internal.h"
struct dc_api_sync_
{
dc_refable_t ref;
int code;
char *buffer;
size_t bufferlen;
FILE *stream;
pthread_mutex_t mtx;
pthread_cond_t cnd;
CURL *easy;
CURLM *curl;
struct curl_slist *list;
};
static void dc_api_sync_free(dc_api_sync_t s)
{
return_if_true(s == NULL,);
pthread_mutex_lock(&s->mtx);
pthread_cond_destroy(&s->cnd);
pthread_mutex_destroy(&s->mtx);
curl_multi_remove_handle(s->curl, s->easy);
curl_easy_cleanup(s->easy);
if (s->stream != NULL) {
fclose(s->stream);
s->stream = NULL;
}
free(s->buffer);
s->buffer = NULL;
s->bufferlen = 0;
curl_slist_free_all(s->list);
s->list = NULL;
free(s);
}
dc_api_sync_t dc_api_sync_new(CURLM *curl, CURL *easy)
{
dc_api_sync_t ptr = calloc(1, sizeof(struct dc_api_sync_));
return_if_true(ptr == NULL, NULL);
ptr->easy = easy;
ptr->ref.cleanup = (dc_cleanup_t)dc_api_sync_free;
ptr->stream = open_memstream(&ptr->buffer, &ptr->bufferlen);
if (ptr->stream == NULL) {
free(ptr);
return NULL;
}
pthread_mutex_init(&ptr->mtx, NULL);
pthread_cond_init(&ptr->cnd, NULL);
ptr->list = curl_slist_append(NULL, "");
return dc_ref(ptr);
}
struct curl_slist *dc_api_sync_list(dc_api_sync_t sync)
{
return_if_true(sync == NULL, NULL);
return sync->list;
}
FILE *dc_api_sync_stream(dc_api_sync_t sync)
{
return_if_true(sync == NULL, NULL);
return sync->stream;
}
char const *dc_api_sync_data(dc_api_sync_t sync)
{
return_if_true(sync == NULL, NULL);
return sync->buffer;
}
size_t dc_api_sync_datalen(dc_api_sync_t sync)
{
return_if_true(sync == NULL, 0L);
return sync->bufferlen;
}
int dc_api_sync_code(dc_api_sync_t sync)
{
return_if_true(sync == NULL, 0L);
return sync->code;
}
bool dc_api_sync_wait(dc_api_sync_t sync)
{
return_if_true(sync == NULL, false);
if (sync->stream == NULL && sync->buffer != NULL) {
return true;
}
pthread_mutex_lock(&sync->mtx);
pthread_cond_wait(&sync->cnd, &sync->mtx);
pthread_mutex_unlock(&sync->mtx);
return (sync->stream == NULL && sync->buffer != NULL);
}
void dc_api_sync_finish(dc_api_sync_t sync, int code)
{
return_if_true(sync == NULL,);
pthread_mutex_lock(&sync->mtx);
sync->code = code;
if (sync->stream != NULL) {
fclose(sync->stream);
sync->stream = NULL;
}
pthread_cond_broadcast(&sync->cnd);
pthread_mutex_unlock(&sync->mtx);
}

29
libdc/src/internal.h Normal file
View File

@@ -0,0 +1,29 @@
#ifndef DC_INTERNAL_H
#define DC_INTERNAL_H
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <errno.h>
#include <pthread.h>
#include <curl/curl.h>
#include <jansson.h>
#include <glib.h>
#include <event.h>
#include <event2/thread.h>
//#define DEBUG
#define return_if_true(v,r) do { if (v) return r; } while(0)
#define goto_if_true(v,l) do { if (v) goto l; } while(0)
#endif

6
libdc/src/loop.c Normal file
View File

@@ -0,0 +1,6 @@
#include <dc/loop.h>
struct nc_loop_
{
nc_refable_t ref;
};

27
libdc/src/refable.c Normal file
View File

@@ -0,0 +1,27 @@
#include <dc/refable.h>
#include "internal.h"
void *dc_ref(void *arg)
{
dc_refable_t *ptr = NULL;
return_if_true(arg == NULL,NULL);
ptr = (dc_refable_t *)arg;
++ptr->ref;
return arg;
}
void dc_unref(void *arg)
{
dc_refable_t *ptr = NULL;
return_if_true(arg == NULL,);
ptr = (dc_refable_t *)arg;
if ((--ptr->ref) <= 0 && ptr->cleanup != NULL) {
ptr->cleanup(arg);
}
}

12
libdc/src/util.c Normal file
View File

@@ -0,0 +1,12 @@
#include <dc/util.h>
#include "internal.h"
void ncdc_util_dump_json(json_t *j)
{
return_if_true(j == NULL,);
char *str = json_dumps(j, JSON_COMPACT);
printf("%s\n", str);
free(str);
}