diff --git a/libdc/CMakeLists.txt b/libdc/CMakeLists.txt index ecee617..5d3f51f 100644 --- a/libdc/CMakeLists.txt +++ b/libdc/CMakeLists.txt @@ -12,11 +12,13 @@ SET(SOURCES "include/dc/api.h" "include/dc/apisync.h" "include/dc/channel.h" + "include/dc/event.h" "include/dc/gateway.h" "include/dc/guild.h" "include/dc/loop.h" "include/dc/message.h" "include/dc/refable.h" + "include/dc/session.h" "include/dc/util.h" "src/account.c" "src/api.c" @@ -26,11 +28,13 @@ SET(SOURCES "src/api-user.c" "src/apisync.c" "src/channel.c" + "src/event.c" "src/gateway.c" "src/guild.c" "src/loop.c" "src/message.c" "src/refable.c" + "src/session.c" "src/util.c" "src/ws-frames.c" ) diff --git a/libdc/include/dc/event.h b/libdc/include/dc/event.h new file mode 100644 index 0000000..4a6e49c --- /dev/null +++ b/libdc/include/dc/event.h @@ -0,0 +1,25 @@ +#ifndef DC_EVENT_H +#define DC_EVENT_H + +#include +#include + +struct dc_event_; +typedef struct dc_event_ *dc_event_t; + +dc_event_t dc_event_new(char const *type, json_t *payload); + +/** + * Returns the event type. This is an upper case string, with + * words separated by underscores. For a list of available event + * types please see the Discord documentation. + */ +char const *dc_event_type(dc_event_t e); + +/** + * The JSON payload associated with the given event type. Note this + * could be json_null() if the event has no associated payload. + */ +json_t *dc_event_payload(dc_event_t e); + +#endif diff --git a/libdc/include/dc/gateway.h b/libdc/include/dc/gateway.h index de1abd8..03f3d0f 100644 --- a/libdc/include/dc/gateway.h +++ b/libdc/include/dc/gateway.h @@ -5,12 +5,22 @@ #include #include - -#include +#include struct dc_gateway_; typedef struct dc_gateway_ *dc_gateway_t; +/** + * The event callback that will be called by the gateway when a new event + * arrives. First parameter is the gateway responsible for sending this + * event, second parameter is the event in question, third parameter is + * user defined callback data. + * + * Note that the event will be allocated, and dc_unref()'d by the gateway, + * so if you need the event around you need to dc_ref() it. + */ +typedef void (*dc_gateway_event_callback_t)(dc_gateway_t, dc_event_t, void*); + typedef enum { GATEWAY_OPCODE_EVENT = 0, GATEWAY_OPCODE_PING = 1, @@ -31,6 +41,13 @@ dc_gateway_t dc_gateway_new(void); void dc_gateway_set_login(dc_gateway_t gw, dc_account_t login); +void dc_gateway_set_callback(dc_gateway_t gw, dc_gateway_event_callback_t c, + void *userdata); + +/** + * Connect the given gateway. Does nothing if the gateway is already + * connected. + */ bool dc_gateway_connect(dc_gateway_t gw); /** @@ -45,7 +62,10 @@ void dc_gateway_disconnect(dc_gateway_t gw); bool dc_gateway_connected(dc_gateway_t gw); /** - * Process the queue of data that came from the websocket. + * Process the queue of data that came from the websocket. Since the + * gateway handle is not part of whole event_base_loop() shebang, this + * must be called individually. dc_loop_once() will do this for you, if + * you opt to use dc_loop() (which you should). */ void dc_gateway_process(dc_gateway_t gw); diff --git a/libdc/include/dc/loop.h b/libdc/include/dc/loop.h index ab4561c..6191326 100644 --- a/libdc/include/dc/loop.h +++ b/libdc/include/dc/loop.h @@ -40,11 +40,21 @@ struct event_base *dc_loop_event_base(dc_loop_t l); */ void dc_loop_add_api(dc_loop_t loop, dc_api_t api); +/** + * Remove the given API handle from the loop. + */ +void dc_loop_remove_api(dc_loop_t loop, dc_api_t api); + /** * Add a gateway to be handled with the rest. */ void dc_loop_add_gateway(dc_loop_t loop, dc_gateway_t gw); +/** + * Remove the given gateway from the loop. + */ +void dc_loop_remove_gateway(dc_loop_t loop, dc_gateway_t gw); + /** * Loop once, and process one message in the queues of the event * base, and one message from the queue of the CURL multi events. diff --git a/libdc/include/dc/session.h b/libdc/include/dc/session.h new file mode 100644 index 0000000..ec41417 --- /dev/null +++ b/libdc/include/dc/session.h @@ -0,0 +1,58 @@ +#ifndef DC_SESSION_H +#define DC_SESSION_H + +#include + +#include +#include +#include +#include +#include + +/** + * A session object will contain all information gathered after a user + * authenticated to the services. It is the "god emperor object" of libdiscord, + * that stores, caches, and provides all information after a log in. From + * available channel objects, account objects of friends, and guild mates, + * to a list of available guilds. + * + * If you consider writing a fully fledged client, or a very sophisticated + * bot, you should consider using this session object in your application. + * + * Please note that the session object attempts to use as little API calls as + * necessary, relying heavily on data provided by the gateway. So it may take + * a bit before appropriate information (i.e. friends) have been loaded. + */ + +struct dc_session_; +typedef struct dc_session_ *dc_session_t; + +/** + * Creates a new session that will attach itself to the given loop. + */ +dc_session_t dc_session_new(dc_loop_t loop); + +/** + * Logs the given user out, and clears all internal data. + */ +bool dc_session_logout(dc_session_t s); + +/** + * Logs the given user into the system, starts a websocket and begins to + * collect information about the given login user (channels, guilds, + * friends). + */ +bool dc_session_login(dc_session_t s, dc_account_t login); + +bool dc_session_has_token(dc_session_t s); + +/** + * Returns the currently logged in user. Which is often called "@me" in + * Discord API. + */ +dc_account_t dc_session_me(dc_session_t s); + +bool dc_session_equal_me(dc_session_t s, dc_account_t a); +bool dc_session_equal_me_fullname(dc_session_t s, char const *a); + +#endif diff --git a/libdc/src/event.c b/libdc/src/event.c new file mode 100644 index 0000000..f9ed42c --- /dev/null +++ b/libdc/src/event.c @@ -0,0 +1,59 @@ +#include +#include "internal.h" + +struct dc_event_ +{ + dc_refable_t ref; + + char *type; + json_t *payload; +}; + +static void dc_event_free(dc_event_t e) +{ + return_if_true(e == NULL,); + + free(e->type); + json_decref(e->payload); + + free(e); +} + +dc_event_t dc_event_new(char const *type, json_t *payload) +{ + return_if_true(type == NULL, NULL); + + dc_event_t e = calloc(1, sizeof(struct dc_event_)); + return_if_true(e == NULL, NULL); + + e->ref.cleanup = (dc_cleanup_t)dc_event_free; + + /* A long, long time ago I had a rather quirky software engineering + * professor. He taught us C, and everytime string handling would be + * the topic we would wait for one scene: He'd put his index finger on + * his chin, barely touching his lower lip, raise both eyebrows in + * astonishment, and wonder, and then he'd say "strdup" in the worst + * German accent. Even after 15 years that scene stuck with me. + */ + e->type = strdup(type); + + if (payload != NULL) { + e->payload = json_incref(payload); + } else { + e->payload = json_null(); + } + + return dc_ref(e); +} + +char const *dc_event_type(dc_event_t e) +{ + return_if_true(e == NULL, NULL); + return e->type; +} + +json_t *dc_event_payload(dc_event_t e) +{ + return_if_true(e == NULL, NULL); + return e->payload; +} diff --git a/libdc/src/gateway.c b/libdc/src/gateway.c index 850a6dd..64a7b79 100644 --- a/libdc/src/gateway.c +++ b/libdc/src/gateway.c @@ -14,6 +14,9 @@ struct dc_gateway_ dc_account_t login; + dc_gateway_event_callback_t callback; + void *callback_data; + uint64_t heartbeat_interval; time_t last_heartbeat; }; @@ -37,6 +40,11 @@ static void dc_gateway_free(dc_gateway_t g) g->buffer = NULL; } + if (g->easy != NULL) { + curl_easy_cleanup(g->easy); + g->easy = NULL; + } + dc_unref(g->login); free(g); @@ -72,6 +80,15 @@ void dc_gateway_set_login(dc_gateway_t gw, dc_account_t login) gw->login = dc_ref(login); } +void dc_gateway_set_callback(dc_gateway_t gw, dc_gateway_event_callback_t c, + void *userdata) +{ + return_if_true(gw == NULL,); + + gw->callback = c; + gw->callback_data = userdata; +} + bool dc_gateway_connect(dc_gateway_t gw) { return_if_true(gw == NULL || gw->easy != NULL, true); @@ -83,6 +100,15 @@ bool dc_gateway_connect(dc_gateway_t gw) gw->easy = curl_easy_init(); goto_if_true(gw->easy == NULL, error); + /* I had already introduced libcurl in a combination with libevent for all + * the low level API stuff (i.e. POST/PUT/DELETE), and at the time of writing + * the websocket code it was too late to rip it out, and replace with something + * else (e.g. libwebsockets). + * + * CURL has no inbuilt way to handle websockets (yet), and thus we have to do + * it ourselves by using CONNECT_ONLY. It works, but it is obviously a crutch. + */ + curl_easy_setopt(gw->easy, CURLOPT_URL, DISCORD_GATEWAY); curl_easy_setopt(gw->easy, CURLOPT_FRESH_CONNECT, 1L); curl_easy_setopt(gw->easy, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); @@ -206,7 +232,7 @@ static void dc_gateway_queue_identify(dc_gateway_t gw) dc_gateway_queue(gw, GATEWAY_OPCODE_IDENTIFY, j); } -static bool dc_gateway_handle_hello(dc_gateway_t gw, json_t *d) +static bool dc_gateway_handle_hello(dc_gateway_t gw, char const *s, json_t *d) { json_t *val = NULL; @@ -223,13 +249,23 @@ static bool dc_gateway_handle_hello(dc_gateway_t gw, json_t *d) return true; } -static bool dc_gateway_handle_update(dc_gateway_t gw, json_t *d) +static bool dc_gateway_handle_update(dc_gateway_t gw, char const *s, json_t *d) { + /* TODO + */ return true; } -static bool dc_gateway_handle_event(dc_gateway_t gw, json_t *d) +static bool dc_gateway_handle_event(dc_gateway_t gw, char const *s, json_t *d) { + dc_event_t e = dc_event_new(s, d); + + if (gw->callback != NULL && e != NULL) { + gw->callback(gw, e, gw->callback_data); + } + + dc_unref(e); + return true; } @@ -237,18 +273,24 @@ static bool dc_gateway_handle_op(dc_gateway_t gw, json_t *j) { json_t *val = NULL; dc_gateway_opcode_t op = 0; + char const *s = NULL; val = json_object_get(j, "op"); return_if_true(val == NULL || !json_is_integer(val), false); op = (dc_gateway_opcode_t)json_integer_value(val); + val = json_object_get(j, "t"); + if (val != NULL && json_is_string(val)) { + s = json_string_value(val); + } + val = json_object_get(j, "d"); return_if_true(val == NULL || !json_is_object(val), false); switch (op) { - case GATEWAY_OPCODE_EVENT: dc_gateway_handle_event(gw, val); break; - case GATEWAY_OPCODE_HELLO: dc_gateway_handle_hello(gw, val); break; - case GATEWAY_OPCODE_UPDATE: dc_gateway_handle_update(gw, val); break; + case GATEWAY_OPCODE_EVENT: dc_gateway_handle_event(gw, s, val); break; + case GATEWAY_OPCODE_HELLO: dc_gateway_handle_hello(gw, s, val); break; + case GATEWAY_OPCODE_UPDATE: dc_gateway_handle_update(gw, s, val); break; case GATEWAY_OPCODE_PONG: break; default: break; } diff --git a/libdc/src/loop.c b/libdc/src/loop.c index 4df2eae..33cd8d7 100644 --- a/libdc/src/loop.c +++ b/libdc/src/loop.c @@ -18,24 +18,6 @@ struct dc_loop_ GPtrArray *gateways; }; -typedef struct { - dc_gateway_t gateway; - struct event *event; -} dc_loop_gateway_t; - -static void dc_loop_gateway_free(dc_loop_gateway_t *p) -{ - return_if_true(p == NULL,); - - if (p->event != NULL) { - event_del(p->event); - event_free(p->event); - } - - dc_unref(p->gateway); - free(p); -} - static void dc_loop_free(dc_loop_t p) { return_if_true(p == NULL,); @@ -175,9 +157,7 @@ dc_loop_t dc_loop_new_full(struct event_base *base, CURLM *multi) ptr->apis = g_ptr_array_new_with_free_func((GDestroyNotify)dc_unref); goto_if_true(ptr->apis == NULL, fail); - ptr->gateways = g_ptr_array_new_with_free_func( - (GDestroyNotify)dc_loop_gateway_free - ); + ptr->gateways = g_ptr_array_new_with_free_func((GDestroyNotify)dc_unref); goto_if_true(ptr->gateways == NULL, fail); ptr->timer = evtimer_new(ptr->base, timer_handler, ptr); @@ -217,20 +197,30 @@ void dc_loop_add_api(dc_loop_t l, dc_api_t a) dc_api_set_event_base(p, l->base); dc_api_set_curl_multi(p, l->multi); - g_ptr_array_add(l->apis, dc_ref(p)); + g_ptr_array_add(l->apis, p); +} + +void dc_loop_remove_api(dc_loop_t loop, dc_api_t api) +{ + return_if_true(loop == NULL || api == NULL,); + + if (g_ptr_array_find(loop->apis, api, NULL)) { + dc_api_set_event_base(api, NULL); + dc_api_set_curl_multi(api, NULL); + g_ptr_array_remove(loop->apis, api); + } } void dc_loop_add_gateway(dc_loop_t l, dc_gateway_t gw) { return_if_true(l == NULL || gw == NULL,); + g_ptr_array_add(l->gateways, dc_ref(gw)); +} - dc_loop_gateway_t *ptr = calloc(1, sizeof(dc_loop_gateway_t)); - return_if_true(ptr == NULL,); - - ptr->gateway = dc_ref(gw); - ptr->event = NULL; - - g_ptr_array_add(l->gateways, ptr); +void dc_loop_remove_gateway(dc_loop_t loop, dc_gateway_t gw) +{ + return_if_true(loop == NULL || gw == NULL,); + g_ptr_array_remove(loop->gateways, gw); } void dc_loop_abort(dc_loop_t l) @@ -268,15 +258,15 @@ bool dc_loop_once(dc_loop_t l) } for (i = 0; i < l->gateways->len; i++) { - dc_loop_gateway_t *ptr = g_ptr_array_index(l->gateways, i); + dc_gateway_t gw = g_ptr_array_index(l->gateways, i); - if (!dc_gateway_connected(ptr->gateway)) { - if (!dc_gateway_connect(ptr->gateway)) { + if (!dc_gateway_connected(gw)) { + if (!dc_gateway_connect(gw)) { continue; } } - dc_gateway_process(ptr->gateway); + dc_gateway_process(gw); } return true; diff --git a/libdc/src/session.c b/libdc/src/session.c new file mode 100644 index 0000000..fef1acb --- /dev/null +++ b/libdc/src/session.c @@ -0,0 +1,122 @@ +#include +#include "internal.h" + +struct dc_session_ +{ + dc_refable_t ref; + + dc_loop_t loop; + dc_api_t api; + dc_account_t login; + dc_gateway_t gateway; +}; + +static void dc_session_free(dc_session_t s) +{ + return_if_true(s == NULL,); + + dc_session_logout(s); + + dc_unref(s->api); + dc_unref(s->loop); + + free(s); +} + +dc_session_t dc_session_new(dc_loop_t loop) +{ + return_if_true(loop == NULL, NULL); + + dc_session_t s = calloc(1, sizeof(struct dc_session_)); + return_if_true(s == NULL, NULL); + + s->ref.cleanup = (dc_cleanup_t)dc_session_free; + + s->loop = dc_ref(loop); + + s->api = dc_api_new(); + if (s->api == NULL) { + dc_session_free(s); + return NULL; + } + + dc_loop_add_api(s->loop, s->api); + + return dc_ref(s); +} + +bool dc_session_logout(dc_session_t s) +{ + return_if_true(s == NULL, false); + + if (s->login != NULL) { + if (dc_account_has_token(s->login)) { + dc_api_logout(s->api, s->login); + } + dc_unref(s->login); + s->login = NULL; + } + + if (s->gateway != NULL) { + dc_loop_remove_gateway(s->loop, s->gateway); + dc_unref(s->gateway); + s->gateway = NULL; + } + + return true; +} + +bool dc_session_login(dc_session_t s, dc_account_t login) +{ + return_if_true(s == NULL || login == NULL, false); + + if (s->login != NULL) { + dc_session_logout(s); + } + + s->login = dc_ref(login); + if (!dc_account_has_token(login)) { + if (!dc_api_authenticate(s->api, s->login)) { + dc_unref(s->login); + s->login = NULL; + return false; + } + + s->gateway = dc_gateway_new(); + if (s->gateway == NULL) { + dc_session_logout(s); + return false; + } + + dc_gateway_set_login(s->gateway, s->login); + dc_loop_add_gateway(s->loop, s->gateway); + } + + return true; +} + +bool dc_session_has_token(dc_session_t s) +{ + return_if_true(s == NULL || s->login == NULL, false); + return dc_account_has_token(s->login); +} + +dc_account_t dc_session_me(dc_session_t s) +{ + return_if_true(s == NULL, NULL); + return s->login; +} + +bool dc_session_equal_me(dc_session_t s, dc_account_t a) +{ + return_if_true(s == NULL || s->login == NULL || a == NULL, false); + return (strcmp(dc_account_fullname(s->login), + dc_account_fullname(a)) == 0 + ); +} + +bool dc_session_equal_me_fullname(dc_session_t s, char const *a) +{ + return_if_true(s == NULL || s->login == NULL || a == NULL, false); + return (strcmp(dc_account_fullname(s->login), a) == 0); +} diff --git a/ncdc/include/ncdc/ncdc.h b/ncdc/include/ncdc/ncdc.h index e27d019..72a9294 100644 --- a/ncdc/include/ncdc/ncdc.h +++ b/ncdc/include/ncdc/ncdc.h @@ -31,20 +31,13 @@ #include #include #include +#include #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) -struct ncdc_account_ { - dc_account_t account; - GPtrArray *friends; - GPtrArray *guilds; -}; - -typedef struct ncdc_account_ *ncdc_account_t; - -extern GHashTable *accounts; -extern dc_account_t current_account; +extern GPtrArray *sessions; +extern dc_session_t current_session; extern dc_api_t api; extern dc_loop_t loop; @@ -55,6 +48,8 @@ extern void *mainwindow; #define KEY_ESCAPE 27 +bool is_logged_in(void); + wchar_t *util_readkey(int esc, WINDOW *win); void exit_main(void); diff --git a/ncdc/src/friends.c b/ncdc/src/friends.c index a02f7a5..a6e25cf 100644 --- a/ncdc/src/friends.c +++ b/ncdc/src/friends.c @@ -4,15 +4,9 @@ static bool ncdc_cmd_friends_list(ncdc_mainwindow_t n, size_t ac, wchar_t **av) { - bool ret = false; size_t i = 0; char c = ' '; - - ret = dc_api_get_friends(api, current_account); - if (!ret) { - LOG(n, L"friends: list: failed to fetch your friends"); - return false; - } + dc_account_t current_account = dc_session_me(current_session); LOG(n, L"/FRIENDS list"); for (i = 0; i < dc_account_friends_size(current_account); i++) { @@ -34,6 +28,7 @@ ncdc_cmd_friends_add(ncdc_mainwindow_t n, size_t ac, wchar_t **av) char *name = NULL; dc_account_t friend = NULL; bool ret = false; + dc_account_t current_account = dc_session_me(current_session); if (ac <= 1) { return false; @@ -71,6 +66,7 @@ ncdc_cmd_friends_remove(ncdc_mainwindow_t n, size_t ac, wchar_t **av) dc_account_t friend = NULL; bool ret = false; size_t i = 0; + dc_account_t current_account = dc_session_me(current_session); if (ac <= 1) { return false; @@ -114,6 +110,7 @@ ncdc_cmd_friends_accept(ncdc_mainwindow_t n, size_t ac, wchar_t **av) dc_account_t friend = NULL; bool ret = false; size_t i = 0; + dc_account_t current_account = dc_session_me(current_session); if (ac <= 1) { return false; @@ -164,8 +161,7 @@ bool ncdc_cmd_friends(ncdc_mainwindow_t n, size_t ac, wchar_t **av) wchar_t *subcmd = NULL; ncdc_commands_t *it = NULL; - if (current_account == NULL || - !dc_account_has_token(current_account)) { + if (!is_logged_in()) { LOG(n, L"friends: not logged in"); return false; } diff --git a/ncdc/src/login.c b/ncdc/src/login.c index a40b999..b3aed99 100644 --- a/ncdc/src/login.c +++ b/ncdc/src/login.c @@ -7,51 +7,54 @@ bool ncdc_cmd_login(ncdc_mainwindow_t n, size_t ac, wchar_t **av) char *arg = NULL; bool ret = false; dc_account_t acc = NULL; - dc_gateway_t gw = NULL; + dc_session_t s = NULL; + uint32_t idx = 0; goto_if_true(ac <= 1, cleanup); arg = w_convert(av[1]); goto_if_true(arg == NULL, cleanup); - acc = g_hash_table_lookup(accounts, arg); + acc = ncdc_config_account(config, arg); if (acc == NULL) { - acc = ncdc_config_account(config, arg); - if (acc == NULL) { - LOG(n, L"login: %ls: no such account in configuration", av[1]); + LOG(n, L"login: %ls: no such account in configuration", av[1]); + goto cleanup; + } + + ret = g_ptr_array_find_with_equal_func( + sessions, arg, + (GEqualFunc)dc_session_equal_me_fullname, + &idx + ); + if (!ret) { + s = dc_session_new(loop); + if (s == NULL) { goto cleanup; } - g_hash_table_insert(accounts, strdup(arg), acc); + g_ptr_array_add(sessions, s); } else { - if (dc_account_has_token(acc)) { + s = g_ptr_array_index(sessions, idx); + if (dc_session_has_token(s)) { LOG(n, L"login: %ls: this account is already logged in", av[1]); goto cleanup; } } - if (!dc_api_login(api, acc)) { + if (!dc_session_login(s, acc)) { LOG(n, L"login: %ls: authentication failed; wrong password?", av[1]); goto cleanup; } - gw = dc_gateway_new(); - if (gw == NULL) { - LOG(n, L"login: %ls: failed to establish gateway", av[1]); - goto cleanup; - } - - dc_gateway_set_login(gw, acc); - dc_loop_add_gateway(loop, gw); - - dc_unref(current_account); - current_account = dc_ref(acc); + dc_unref(current_session); + current_session = dc_ref(s); LOG(n, L"login: %ls: authentication successful", av[1]); ret = true; cleanup: + dc_unref(acc); free(arg); return ret; diff --git a/ncdc/src/logout.c b/ncdc/src/logout.c index 74e3c0d..c3fa440 100644 --- a/ncdc/src/logout.c +++ b/ncdc/src/logout.c @@ -6,19 +6,19 @@ bool ncdc_cmd_logout(ncdc_mainwindow_t n, size_t ac, wchar_t **av) { bool ret = false; - goto_if_true(current_account == NULL || - !dc_account_has_token(current_account), error); + goto_if_true(current_session == NULL || + !dc_session_has_token(current_session), error); - ret = dc_api_logout(api, current_account); + ret = dc_session_logout(current_session); if (!ret) { LOG(n, L"logout: failed to log out the current account"); goto error; } - g_hash_table_remove(accounts, dc_account_fullname(current_account)); + g_ptr_array_remove(sessions, current_session); - dc_unref(current_account); - current_account = NULL; + dc_unref(current_session); + current_session = NULL; LOG(n, L"logout: successfully logged out"); diff --git a/ncdc/src/mainwindow.c b/ncdc/src/mainwindow.c index 25696ca..9ebc702 100644 --- a/ncdc/src/mainwindow.c +++ b/ncdc/src/mainwindow.c @@ -200,9 +200,10 @@ static void ncdc_mainwindow_render_status(ncdc_mainwindow_t n) wcsftime(timestr, 99, L"[%H:%M]", t); fwprintf(f, L"%ls", timestr); - if (current_account == NULL) { + if (!is_logged_in()) { fwprintf(f, L" [not logged in]"); } else { + dc_account_t current_account = dc_session_me(current_session); fwprintf(f, L" [%s]", dc_account_fullname(current_account)); } diff --git a/ncdc/src/msg.c b/ncdc/src/msg.c index c131e7c..62c0616 100644 --- a/ncdc/src/msg.c +++ b/ncdc/src/msg.c @@ -12,9 +12,10 @@ bool ncdc_cmd_msg(ncdc_mainwindow_t n, size_t ac, wchar_t **av) bool ret = false; dc_channel_t c = NULL; ncdc_textview_t v = NULL; + dc_account_t current_account = NULL; size_t i = 0; - if (current_account == NULL || !dc_account_has_token(current_account)) { + if (is_logged_in()) { LOG(n, L"msg: not logged in"); return false; } @@ -22,6 +23,8 @@ bool ncdc_cmd_msg(ncdc_mainwindow_t n, size_t ac, wchar_t **av) target = w_convert(av[1]); goto_if_true(target == NULL, cleanup); + current_account = dc_session_me(current_session); + /* find out if the target is a friend we can contact */ dc_account_t f = dc_account_findfriend(current_account, target); diff --git a/ncdc/src/ncdc.c b/ncdc/src/ncdc.c index 4b1dd56..52557bd 100644 --- a/ncdc/src/ncdc.c +++ b/ncdc/src/ncdc.c @@ -21,10 +21,10 @@ bool thread_done = false; static pthread_t event_thread; static struct event_base *base = NULL; -/* all the accounts we have logged into +/* all the sessions we currently have around */ -GHashTable *accounts = NULL; -dc_account_t current_account = NULL; +GPtrArray *sessions = NULL; +dc_session_t current_session = NULL; char *ncdc_private_dir = NULL; void *config = NULL; @@ -35,26 +35,17 @@ dc_loop_t loop = NULL; */ dc_api_t api = NULL; -static void cleanup_account(dc_account_t a) -{ - if (dc_account_has_token(a)) { - dc_api_logout(api, a); - } - - dc_unref(a); -} - static void cleanup(void) { endwin(); - if (accounts != NULL) { - g_hash_table_unref(accounts); - accounts = NULL; + if (sessions != NULL) { + g_ptr_array_unref(sessions); + sessions = NULL; } - dc_unref(current_account); - current_account = NULL; + dc_unref(current_session); + current_session = NULL; thread_done = true; dc_loop_abort(loop); @@ -138,10 +129,8 @@ static bool init_everything(void) config = ncdc_config_new(); return_if_true(config == NULL, false); - accounts = g_hash_table_new_full(g_str_hash, g_str_equal, - g_free, (GDestroyNotify)cleanup_account - ); - return_if_true(accounts == NULL, false); + sessions = g_ptr_array_new_with_free_func((GDestroyNotify)dc_unref); + return_if_true(sessions == NULL, false); ret = pthread_create(&event_thread, NULL, looper, NULL); return_if_true(ret != 0, false); diff --git a/ncdc/src/util.c b/ncdc/src/util.c index 1e84441..6757d91 100644 --- a/ncdc/src/util.c +++ b/ncdc/src/util.c @@ -1,5 +1,11 @@ #include +bool is_logged_in(void) +{ + return_if_true(current_session == NULL, false); + return dc_session_has_token(current_session); +} + wchar_t *util_readkey(int e, WINDOW *win) { wint_t esc[7] = {0};