ncdc - discord client

This commit is contained in:
Florian Stinglmayr 2019-06-15 21:33:46 +02:00
commit 8972538d13
11 changed files with 1090 additions and 0 deletions

47
CMakeLists.txt Normal file
View File

@ -0,0 +1,47 @@
CMAKE_MINIMUM_REQUIRED(VERSION 3.0)
FIND_PACKAGE(PkgConfig)
FIND_PACKAGE(Threads)
PKG_CHECK_MODULES(JANSSON REQUIRED jansson)
PKG_CHECK_MODULES(CURL REQUIRED libcurl)
PKG_CHECK_MODULES(EVENT REQUIRED libevent libevent_pthreads)
PKG_CHECK_MODULES(GLIB2 REQUIRED glib-2.0)
SET(TARGET "ncdc")
SET(SOURCES
"include/ncdc/account.h"
"include/ncdc/api.h"
"include/ncdc/apisync.h"
"include/ncdc/ncdc.h"
"include/ncdc/refable.h"
"src/account.c"
"src/api.c"
"src/apisync.c"
"src/ncdc.c"
"src/refable.c"
)
ADD_DEFINITIONS("-Wall -Werror -std=c11 -D_GNU_SOURCE")
INCLUDE_DIRECTORIES("include"
${JANSSON_INCLUDE_DIRS}
${CURL_INCLUDE_DIRS}
${EVENT_INCLUDE_DIRS}
${GLIB2_INCLUDE_DIRS}
)
LINK_DIRECTORIES(${JANSSON_LIBRARY_DIRS}
${CURL_LIBRARY_DIRS}
${EVENT_LIBRARY_DIRS}
${GLIB2_LIBRARY_DIRS}
)
ADD_EXECUTABLE(${TARGET} ${SOURCES})
TARGET_LINK_LIBRARIES(${TARGET}
${JANSSON_LIBRARIES}
${CURL_LIBRARIES}
${EVENT_LIBRARIES}
${GLIB2_LIBRARIES}
${CMAKE_THREAD_LIBS_INIT}
)

25
include/ncdc/account.h Normal file
View File

@ -0,0 +1,25 @@
#ifndef NCDC_ACCOUNT_H
#define NCDC_ACCOUNT_H
#include <ncdc/ncdc.h>
struct ncdc_account_;
typedef struct ncdc_account_ *ncdc_account_t;
ncdc_account_t ncdc_account_new(void);
ncdc_account_t ncdc_account_new2(char const *email, char const *pass);
void ncdc_account_set_email(ncdc_account_t a, char const *email);
char const *ncdc_account_email(ncdc_account_t a);
void ncdc_account_set_password(ncdc_account_t a, char const *password);
char const *ncdc_account_password(ncdc_account_t a);
void ncdc_account_set_id(ncdc_account_t a, char const *id);
char const *ncdc_account_id(ncdc_account_t a);
void ncdc_account_set_token(ncdc_account_t a, char const *token);
char const *ncdc_account_token(ncdc_account_t a);
bool ncdc_account_has_token(ncdc_account_t a);
#endif

34
include/ncdc/api.h Normal file
View File

@ -0,0 +1,34 @@
#ifndef NCDC_API_H
#define NCDC_API_H
#include <ncdc/ncdc.h>
#include <ncdc/apisync.h>
#include <ncdc/account.h>
struct ncdc_api_;
typedef struct ncdc_api_ *ncdc_api_t;
ncdc_api_t ncdc_api_new(void);
void ncdc_api_set_curl_multi(ncdc_api_t api, CURLM *curl);
void ncdc_api_set_event_base(ncdc_api_t api, struct event_base *base);
/* call this function in case the MULTI has told us that some
* transfer has finished.
*/
void ncdc_api_signal(ncdc_api_t api, CURL *easy, int code);
/* internal curl stuff
*/
ncdc_api_sync_t ncdc_api_call(ncdc_api_t api, char const *token,
char const *method, json_t *j);
json_t *ncdc_api_call_sync(ncdc_api_t api, char const *token,
char const *method, json_t *j);
bool ncdc_api_authenticate(ncdc_api_t api, ncdc_account_t account);
bool ncdc_api_userinfo(ncdc_api_t api, ncdc_account_t logion,
ncdc_account_t user);
#endif

20
include/ncdc/apisync.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef NCDC_API_ASYNC_H
#define NCDC_API_ASYNC_H
#include <ncdc/ncdc.h>
struct ncdc_api_sync_;
typedef struct ncdc_api_sync_ *ncdc_api_sync_t;
ncdc_api_sync_t ncdc_api_sync_new(CURLM *curl, CURL *easy);
FILE *ncdc_api_sync_stream(ncdc_api_sync_t sync);
char const *ncdc_api_sync_data(ncdc_api_sync_t sync);
size_t ncdc_api_sync_datalen(ncdc_api_sync_t sync);
int ncdc_api_sync_code(ncdc_api_sync_t sync);
struct curl_slist *ncdc_api_sync_list(ncdc_api_sync_t sync);
bool ncdc_api_sync_wait(ncdc_api_sync_t sync);
void ncdc_api_sync_finish(ncdc_api_sync_t sync, int code);
#endif

31
include/ncdc/ncdc.h Normal file
View File

@ -0,0 +1,31 @@
#ifndef NCDC_H
#define NCDC_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)
extern char *ncdc_private_dir;
#endif

16
include/ncdc/refable.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef NCDC_REFABLE_H
#define NCDC_REFABLE_H
#include <ncdc/ncdc.h>
typedef void (*cleanup_t)(void *);
typedef struct {
int ref;
cleanup_t cleanup;
} ncdc_refable_t;
void *ncdc_ref(void *);
void ncdc_unref(void *);
#endif

121
src/account.c Normal file
View File

@ -0,0 +1,121 @@
#include <ncdc/account.h>
#include <ncdc/refable.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
struct ncdc_account_
{
ncdc_refable_t ref; /* do not move anything above ref */
char *email;
char *password;
/* internal ID
*/
char *id;
/* authentication token
*/
char *token;
};
static void ncdc_account_free(ncdc_account_t ptr)
{
return_if_true(ptr == NULL,);
free(ptr->email);
free(ptr->password);
free(ptr);
}
ncdc_account_t ncdc_account_new(void)
{
ncdc_account_t ptr = calloc(1, sizeof(struct ncdc_account_));
ptr->ref.cleanup = (cleanup_t)ncdc_account_free;
return ncdc_ref(ptr);
}
ncdc_account_t ncdc_account_new2(char const *email, char const *pass)
{
ncdc_account_t ptr = ncdc_account_new();
if (ptr != NULL) {
ncdc_account_set_email(ptr, email);
ncdc_account_set_password(ptr, pass);
}
return ptr;
}
void ncdc_account_set_email(ncdc_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 *ncdc_account_email(ncdc_account_t a)
{
return_if_true(a == NULL, NULL);
return a->email;
}
void ncdc_account_set_password(ncdc_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 *ncdc_account_password(ncdc_account_t a)
{
return_if_true(a == NULL, NULL);
return a->password;
}
void ncdc_account_set_token(ncdc_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 *ncdc_account_token(ncdc_account_t a)
{
return_if_true(a == NULL, NULL);
return a->token;
}
bool ncdc_account_has_token(ncdc_account_t a)
{
return_if_true(a == NULL, false);
return_if_true(a->token == NULL, false);
return true;
}
void ncdc_account_set_id(ncdc_account_t a, char const *id)
{
return_if_true(a == NULL,);
free(a->id);
a->id = strdup(id);
}
char const *ncdc_account_id(ncdc_account_t a)
{
return_if_true(a == NULL,NULL);
return a->id;
}

363
src/api.c Normal file
View File

@ -0,0 +1,363 @@
#include <ncdc/api.h>
#include <ncdc/refable.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 ncdc_api_
{
ncdc_refable_t ref;
struct event_base *base;
CURLM *curl;
GHashTable *syncs;
char *cookie;
};
static void ncdc_api_free(ncdc_api_t ptr)
{
return_if_true(ptr == NULL,);
if (ptr->syncs != NULL) {
g_hash_table_unref(ptr->syncs);
ptr->syncs = NULL;
}
free(ptr);
}
ncdc_api_t ncdc_api_new(void)
{
ncdc_api_t ptr = calloc(1, sizeof(struct ncdc_api_));
return_if_true(ptr == NULL, NULL);
ptr->ref.cleanup = (cleanup_t)ncdc_api_free;
ptr->syncs = g_hash_table_new_full(g_direct_hash, g_direct_equal,
NULL, ncdc_unref
);
if (ptr->syncs == NULL) {
free(ptr);
return NULL;
}
return ncdc_ref(ptr);
}
void ncdc_api_set_curl_multi(ncdc_api_t api, CURLM *curl)
{
return_if_true(api == NULL,);
return_if_true(curl == NULL,);
api->curl = curl;
}
void ncdc_api_set_event_base(ncdc_api_t api, struct event_base *base)
{
return_if_true(api == NULL,);
return_if_true(base == NULL,);
api->base = base;
}
void ncdc_api_signal(ncdc_api_t api, CURL *easy, int code)
{
ncdc_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) {
ncdc_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, ncdc_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 ncdc_api_sync_t ncdc_api_post(ncdc_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;
ncdc_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 = ncdc_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, ncdc_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 = ncdc_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, ncdc_ref(sync));
curl_multi_socket_action(api->curl, CURL_SOCKET_TIMEOUT, 0, &ptr);
ret = true;
cleanup:
if (!ret) {
ncdc_unref(sync);
sync = NULL;
}
return sync;
}
ncdc_api_sync_t ncdc_api_call(ncdc_api_t api, char const *token,
char const *method, json_t *j)
{
char *data = NULL;
char *url = NULL;
ncdc_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 = ncdc_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 *ncdc_api_call_sync(ncdc_api_t api, char const *token,
char const *method, json_t *j)
{
ncdc_api_sync_t s = NULL;
json_t *reply = NULL;
s = ncdc_api_call(api, token, method, j);
goto_if_true(s == NULL, cleanup);
if (!ncdc_api_sync_wait(s)) {
goto cleanup;
}
#ifdef DEBUG
printf("api_call_sync: %d\n", ncdc_api_sync_code(s));
#endif
reply = json_loadb(ncdc_api_sync_data(s),
ncdc_api_sync_datalen(s),
0, NULL
);
cleanup:
ncdc_unref(s);
s = NULL;
return reply;
}
static bool ncdc_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 ncdc_api_authenticate(ncdc_api_t api, ncdc_account_t account)
{
json_t *j = json_object(), *reply = NULL, *token = NULL;
bool ret = false;
json_object_set_new(j, "email",
json_string(ncdc_account_email(account))
);
json_object_set_new(j, "password",
json_string(ncdc_account_password(account))
);
reply = ncdc_api_call_sync(api, NULL, DISCORD_API_AUTH, j);
goto_if_true(reply == NULL, cleanup);
if (ncdc_api_error(j, NULL, NULL)) {
return false;
}
token = json_object_get(reply, "token");
if (token == NULL || !json_is_string(token)) {
goto cleanup;
}
ncdc_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 ncdc_api_userinfo(ncdc_api_t api, ncdc_account_t login,
ncdc_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", ncdc_account_id(user));
reply = ncdc_api_call_sync(api, ncdc_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;
}

128
src/apisync.c Normal file
View File

@ -0,0 +1,128 @@
#include <ncdc/apisync.h>
#include <ncdc/refable.h>
struct ncdc_api_sync_
{
ncdc_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 ncdc_api_sync_free(ncdc_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);
}
ncdc_api_sync_t ncdc_api_sync_new(CURLM *curl, CURL *easy)
{
ncdc_api_sync_t ptr = calloc(1, sizeof(struct ncdc_api_sync_));
return_if_true(ptr == NULL, NULL);
ptr->easy = easy;
ptr->ref.cleanup = (cleanup_t)ncdc_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 ncdc_ref(ptr);
}
struct curl_slist *ncdc_api_sync_list(ncdc_api_sync_t sync)
{
return_if_true(sync == NULL, NULL);
return sync->list;
}
FILE *ncdc_api_sync_stream(ncdc_api_sync_t sync)
{
return_if_true(sync == NULL, NULL);
return sync->stream;
}
char const *ncdc_api_sync_data(ncdc_api_sync_t sync)
{
return_if_true(sync == NULL, NULL);
return sync->buffer;
}
size_t ncdc_api_sync_datalen(ncdc_api_sync_t sync)
{
return_if_true(sync == NULL, 0L);
return sync->bufferlen;
}
int ncdc_api_sync_code(ncdc_api_sync_t sync)
{
return_if_true(sync == NULL, 0L);
return sync->code;
}
bool ncdc_api_sync_wait(ncdc_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 ncdc_api_sync_finish(ncdc_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);
}

280
src/ncdc.c Normal file
View File

@ -0,0 +1,280 @@
#include <ncdc/ncdc.h>
#include <ncdc/api.h>
#include <stdio.h>
#include <unistd.h>
/* event base for libevent
*/
struct event_base *base = NULL;
struct event *stdin_ev = NULL;
struct event *timer = NULL;
/* we loop in a different thread
*/
static bool done = false;
static pthread_t looper;
char *ncdc_private_dir = NULL;
char *ncdc_config_file = NULL;
static GKeyFile *config = NULL;
/* global curl multi for API access
*/
CURLM *curl = NULL;
ncdc_api_t api = NULL;
static void handle_multi_info(void);
static void sighandler(int sig)
{
exit(3);
}
static void cleanup(void)
{
if (stdin_ev != NULL) {
event_del(stdin_ev);
event_free(stdin_ev);
stdin_ev = NULL;
}
if (timer != NULL) {
evtimer_del(timer);
event_free(timer);
timer = NULL;
}
done = true;
pthread_join(looper, NULL);
curl_multi_cleanup(curl);
curl = NULL;
event_base_free(base);
base = NULL;
}
static void stdin_handler(int sock, short what, void *data)
{
}
static void mcurl_socket_handler(int sock, short what, void *data)
{
int unused = 0;
if ((what & EV_READ) == EV_READ) {
curl_multi_socket_action(curl, sock, CURL_CSELECT_IN, &unused);
} else if ((what & EV_WRITE) == EV_WRITE) {
curl_multi_socket_action(curl, sock, CURL_CSELECT_OUT, &unused);
}
}
static void timer_handler(int sock, short what, void *data)
{
int running = 0;
curl_multi_socket_action(curl, CURL_SOCKET_TIMEOUT, 0, &running);
}
static int mcurl_timer(CURLM *curl, long timeout, void *ptr)
{
int running = 0;
struct timeval tm;
if (timeout == -1) {
evtimer_del(timer);
} else if (timeout == 0) {
curl_multi_socket_action(curl, CURL_SOCKET_TIMEOUT, 0, &running);
} else if (timeout > 0) {
tm.tv_sec = timeout / 1000;
tm.tv_usec = (timeout % 1000) * 1000;
evtimer_add(timer, &tm);
}
return 0;
}
static int
mcurl_handler(CURL *easy, curl_socket_t s, int what, void *userp, void *socketp)
{
struct event *event = (struct event *)socketp;
if (what == CURL_POLL_REMOVE) {
if (event != NULL) {
event_del(event);
event_free(event);
curl_multi_assign(curl, s, NULL);
}
} else {
int stat =
((what & CURL_POLL_IN) ? EV_READ : 0) |
((what & CURL_POLL_OUT) ? EV_WRITE : 0) |
EV_PERSIST
;
if (event == NULL) {
event = event_new(base, s, stat, mcurl_socket_handler, NULL);
if (event == NULL) {
return 0;
}
curl_multi_assign(curl, s, event);
} else {
event_del(event);
event_assign(event, base, s, stat, mcurl_socket_handler, NULL);
event_add(event, NULL);
}
}
return 0;
}
static bool init_everything(void)
{
evthread_use_pthreads();
base = event_base_new();
return_if_true(base == NULL, false);
/* add handle for STDIN, this info will then be fed to the UI
*/
stdin_ev = event_new(base, 0 /* stdin */, EV_READ|EV_PERSIST,
stdin_handler, NULL
);
return_if_true(stdin_ev == NULL, false);
event_add(stdin_ev, NULL);
timer = evtimer_new(base, timer_handler, NULL);
return_if_true(timer == NULL, false);
/* create curl multi and feed that to the API too
*/
curl = curl_multi_init();
return_if_true(curl == NULL, false);
curl_multi_setopt(curl, CURLMOPT_SOCKETFUNCTION, mcurl_handler);
curl_multi_setopt(curl, CURLMOPT_TIMERFUNCTION, mcurl_timer);
/* initialise event
*/
api = ncdc_api_new();
return_if_true(api == NULL, false);
ncdc_api_set_event_base(api, base);
ncdc_api_set_curl_multi(api, curl);
config = g_key_file_new();
return_if_true(config == NULL, false);
g_key_file_load_from_file(config, ncdc_config_file, 0, NULL);
return true;
}
static void handle_multi_info(void)
{
struct CURLMsg *msg = NULL;
int remain = 0;
/* check for finished multi curls
*/
msg = curl_multi_info_read(curl, &remain);
if (msg != NULL) {
if (remain <= 0) {
if (evtimer_pending(timer, NULL)) {
evtimer_del(timer);
}
}
if (msg->msg == CURLMSG_DONE) {
ncdc_api_signal(api, msg->easy_handle, msg->data.result);
}
} else {
usleep(10 * 1000);
}
}
static void *loop_thread(void *arg)
{
int ret = 0;
while (!done) {
ret = event_base_loop(base, EVLOOP_ONCE|EVLOOP_NONBLOCK);
if (ret < 0) {
break;
}
handle_multi_info();
}
return NULL;
}
static ncdc_account_t account_from_config(void)
{
char const *email = NULL;
char const *password = NULL;
void *ptr = NULL;
email = g_key_file_get_string(config, "account", "email", NULL);
password = g_key_file_get_string(config, "account", "password", NULL);
return_if_true(email == NULL || password == NULL, NULL);
ptr = ncdc_account_new2(email, password);
ncdc_account_set_id(ptr, "@me");
return ptr;
}
int main(int ac, char **av)
{
bool done = false;
atexit(cleanup);
signal(SIGINT, sighandler);
if (getenv("HOME") == NULL) {
fprintf(stderr, "your environment doesn't contain HOME; pls fix\n");
return 3;
}
asprintf(&ncdc_private_dir, "%s/.ncdc", getenv("HOME"));
if (mkdir(ncdc_private_dir, 0755) < 0) {
if (errno != EEXIST) {
fprintf(stderr, "failed to make %s: %s\n", ncdc_private_dir,
strerror(errno));
return 3;
}
}
asprintf(&ncdc_config_file, "%s/config", ncdc_private_dir);
if (!init_everything()) {
return 3;
}
done = false;
if (pthread_create(&looper, NULL, loop_thread, &done)) {
return 3;
}
ncdc_account_t a = account_from_config();
if (a == NULL) {
fprintf(stderr, "no account specified in config file; sho-sho!\n");
return 3;
}
if (!ncdc_api_authenticate(api, a)) {
fprintf(stderr, "authentication failed, wrong password?\n");
return 3;
}
if (!ncdc_api_userinfo(api, a, a)) {
fprintf(stderr, "failed to get user information\n");
return 3;
}
return 0;
}

25
src/refable.c Normal file
View File

@ -0,0 +1,25 @@
#include <ncdc/refable.h>
void *ncdc_ref(void *arg)
{
ncdc_refable_t *ptr = NULL;
return_if_true(arg == NULL,NULL);
ptr = (ncdc_refable_t *)arg;
++ptr->ref;
return arg;
}
void ncdc_unref(void *arg)
{
ncdc_refable_t *ptr = NULL;
return_if_true(arg == NULL,);
ptr = (ncdc_refable_t *)arg;
if ((--ptr->ref) <= 0 && ptr->cleanup != NULL) {
ptr->cleanup(arg);
}
}