switch over to use sessions
This commit is contained in:
parent
ffcc9f60f7
commit
30fb4a73df
@ -12,11 +12,13 @@ SET(SOURCES
|
|||||||
"include/dc/api.h"
|
"include/dc/api.h"
|
||||||
"include/dc/apisync.h"
|
"include/dc/apisync.h"
|
||||||
"include/dc/channel.h"
|
"include/dc/channel.h"
|
||||||
|
"include/dc/event.h"
|
||||||
"include/dc/gateway.h"
|
"include/dc/gateway.h"
|
||||||
"include/dc/guild.h"
|
"include/dc/guild.h"
|
||||||
"include/dc/loop.h"
|
"include/dc/loop.h"
|
||||||
"include/dc/message.h"
|
"include/dc/message.h"
|
||||||
"include/dc/refable.h"
|
"include/dc/refable.h"
|
||||||
|
"include/dc/session.h"
|
||||||
"include/dc/util.h"
|
"include/dc/util.h"
|
||||||
"src/account.c"
|
"src/account.c"
|
||||||
"src/api.c"
|
"src/api.c"
|
||||||
@ -26,11 +28,13 @@ SET(SOURCES
|
|||||||
"src/api-user.c"
|
"src/api-user.c"
|
||||||
"src/apisync.c"
|
"src/apisync.c"
|
||||||
"src/channel.c"
|
"src/channel.c"
|
||||||
|
"src/event.c"
|
||||||
"src/gateway.c"
|
"src/gateway.c"
|
||||||
"src/guild.c"
|
"src/guild.c"
|
||||||
"src/loop.c"
|
"src/loop.c"
|
||||||
"src/message.c"
|
"src/message.c"
|
||||||
"src/refable.c"
|
"src/refable.c"
|
||||||
|
"src/session.c"
|
||||||
"src/util.c"
|
"src/util.c"
|
||||||
"src/ws-frames.c"
|
"src/ws-frames.c"
|
||||||
)
|
)
|
||||||
|
25
libdc/include/dc/event.h
Normal file
25
libdc/include/dc/event.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
#ifndef DC_EVENT_H
|
||||||
|
#define DC_EVENT_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <jansson.h>
|
||||||
|
|
||||||
|
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
|
@ -5,12 +5,22 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include <dc/account.h>
|
#include <dc/account.h>
|
||||||
|
#include <dc/event.h>
|
||||||
#include <curl/curl.h>
|
|
||||||
|
|
||||||
struct dc_gateway_;
|
struct dc_gateway_;
|
||||||
typedef struct dc_gateway_ *dc_gateway_t;
|
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 {
|
typedef enum {
|
||||||
GATEWAY_OPCODE_EVENT = 0,
|
GATEWAY_OPCODE_EVENT = 0,
|
||||||
GATEWAY_OPCODE_PING = 1,
|
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_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);
|
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);
|
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);
|
void dc_gateway_process(dc_gateway_t gw);
|
||||||
|
|
||||||
|
@ -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);
|
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.
|
* Add a gateway to be handled with the rest.
|
||||||
*/
|
*/
|
||||||
void dc_loop_add_gateway(dc_loop_t loop, dc_gateway_t gw);
|
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
|
* Loop once, and process one message in the queues of the event
|
||||||
* base, and one message from the queue of the CURL multi events.
|
* base, and one message from the queue of the CURL multi events.
|
||||||
|
58
libdc/include/dc/session.h
Normal file
58
libdc/include/dc/session.h
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
#ifndef DC_SESSION_H
|
||||||
|
#define DC_SESSION_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <dc/api.h>
|
||||||
|
#include <dc/loop.h>
|
||||||
|
#include <dc/account.h>
|
||||||
|
#include <dc/channel.h>
|
||||||
|
#include <dc/gateway.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
59
libdc/src/event.c
Normal file
59
libdc/src/event.c
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
#include <dc/event.h>
|
||||||
|
#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;
|
||||||
|
}
|
@ -14,6 +14,9 @@ struct dc_gateway_
|
|||||||
|
|
||||||
dc_account_t login;
|
dc_account_t login;
|
||||||
|
|
||||||
|
dc_gateway_event_callback_t callback;
|
||||||
|
void *callback_data;
|
||||||
|
|
||||||
uint64_t heartbeat_interval;
|
uint64_t heartbeat_interval;
|
||||||
time_t last_heartbeat;
|
time_t last_heartbeat;
|
||||||
};
|
};
|
||||||
@ -37,6 +40,11 @@ static void dc_gateway_free(dc_gateway_t g)
|
|||||||
g->buffer = NULL;
|
g->buffer = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (g->easy != NULL) {
|
||||||
|
curl_easy_cleanup(g->easy);
|
||||||
|
g->easy = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
dc_unref(g->login);
|
dc_unref(g->login);
|
||||||
|
|
||||||
free(g);
|
free(g);
|
||||||
@ -72,6 +80,15 @@ void dc_gateway_set_login(dc_gateway_t gw, dc_account_t login)
|
|||||||
gw->login = dc_ref(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)
|
bool dc_gateway_connect(dc_gateway_t gw)
|
||||||
{
|
{
|
||||||
return_if_true(gw == NULL || gw->easy != NULL, true);
|
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();
|
gw->easy = curl_easy_init();
|
||||||
goto_if_true(gw->easy == NULL, error);
|
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_URL, DISCORD_GATEWAY);
|
||||||
curl_easy_setopt(gw->easy, CURLOPT_FRESH_CONNECT, 1L);
|
curl_easy_setopt(gw->easy, CURLOPT_FRESH_CONNECT, 1L);
|
||||||
curl_easy_setopt(gw->easy, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
|
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);
|
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;
|
json_t *val = NULL;
|
||||||
|
|
||||||
@ -223,13 +249,23 @@ static bool dc_gateway_handle_hello(dc_gateway_t gw, json_t *d)
|
|||||||
return true;
|
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;
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -237,18 +273,24 @@ static bool dc_gateway_handle_op(dc_gateway_t gw, json_t *j)
|
|||||||
{
|
{
|
||||||
json_t *val = NULL;
|
json_t *val = NULL;
|
||||||
dc_gateway_opcode_t op = 0;
|
dc_gateway_opcode_t op = 0;
|
||||||
|
char const *s = NULL;
|
||||||
|
|
||||||
val = json_object_get(j, "op");
|
val = json_object_get(j, "op");
|
||||||
return_if_true(val == NULL || !json_is_integer(val), false);
|
return_if_true(val == NULL || !json_is_integer(val), false);
|
||||||
op = (dc_gateway_opcode_t)json_integer_value(val);
|
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");
|
val = json_object_get(j, "d");
|
||||||
return_if_true(val == NULL || !json_is_object(val), false);
|
return_if_true(val == NULL || !json_is_object(val), false);
|
||||||
|
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case GATEWAY_OPCODE_EVENT: dc_gateway_handle_event(gw, val); break;
|
case GATEWAY_OPCODE_EVENT: dc_gateway_handle_event(gw, s, val); break;
|
||||||
case GATEWAY_OPCODE_HELLO: dc_gateway_handle_hello(gw, val); break;
|
case GATEWAY_OPCODE_HELLO: dc_gateway_handle_hello(gw, s, val); break;
|
||||||
case GATEWAY_OPCODE_UPDATE: dc_gateway_handle_update(gw, val); break;
|
case GATEWAY_OPCODE_UPDATE: dc_gateway_handle_update(gw, s, val); break;
|
||||||
case GATEWAY_OPCODE_PONG: break;
|
case GATEWAY_OPCODE_PONG: break;
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
|
@ -18,24 +18,6 @@ struct dc_loop_
|
|||||||
GPtrArray *gateways;
|
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)
|
static void dc_loop_free(dc_loop_t p)
|
||||||
{
|
{
|
||||||
return_if_true(p == NULL,);
|
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);
|
ptr->apis = g_ptr_array_new_with_free_func((GDestroyNotify)dc_unref);
|
||||||
goto_if_true(ptr->apis == NULL, fail);
|
goto_if_true(ptr->apis == NULL, fail);
|
||||||
|
|
||||||
ptr->gateways = g_ptr_array_new_with_free_func(
|
ptr->gateways = g_ptr_array_new_with_free_func((GDestroyNotify)dc_unref);
|
||||||
(GDestroyNotify)dc_loop_gateway_free
|
|
||||||
);
|
|
||||||
goto_if_true(ptr->gateways == NULL, fail);
|
goto_if_true(ptr->gateways == NULL, fail);
|
||||||
|
|
||||||
ptr->timer = evtimer_new(ptr->base, timer_handler, ptr);
|
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_event_base(p, l->base);
|
||||||
dc_api_set_curl_multi(p, l->multi);
|
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)
|
void dc_loop_add_gateway(dc_loop_t l, dc_gateway_t gw)
|
||||||
{
|
{
|
||||||
return_if_true(l == NULL || gw == NULL,);
|
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));
|
void dc_loop_remove_gateway(dc_loop_t loop, dc_gateway_t gw)
|
||||||
return_if_true(ptr == NULL,);
|
{
|
||||||
|
return_if_true(loop == NULL || gw == NULL,);
|
||||||
ptr->gateway = dc_ref(gw);
|
g_ptr_array_remove(loop->gateways, gw);
|
||||||
ptr->event = NULL;
|
|
||||||
|
|
||||||
g_ptr_array_add(l->gateways, ptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void dc_loop_abort(dc_loop_t l)
|
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++) {
|
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_connected(gw)) {
|
||||||
if (!dc_gateway_connect(ptr->gateway)) {
|
if (!dc_gateway_connect(gw)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dc_gateway_process(ptr->gateway);
|
dc_gateway_process(gw);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
122
libdc/src/session.c
Normal file
122
libdc/src/session.c
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
#include <dc/session.h>
|
||||||
|
#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);
|
||||||
|
}
|
@ -31,20 +31,13 @@
|
|||||||
#include <dc/api.h>
|
#include <dc/api.h>
|
||||||
#include <dc/loop.h>
|
#include <dc/loop.h>
|
||||||
#include <dc/account.h>
|
#include <dc/account.h>
|
||||||
|
#include <dc/session.h>
|
||||||
|
|
||||||
#define return_if_true(v,r) do { if (v) return r; } while(0)
|
#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)
|
#define goto_if_true(v,l) do { if (v) goto l; } while(0)
|
||||||
|
|
||||||
struct ncdc_account_ {
|
extern GPtrArray *sessions;
|
||||||
dc_account_t account;
|
extern dc_session_t current_session;
|
||||||
GPtrArray *friends;
|
|
||||||
GPtrArray *guilds;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct ncdc_account_ *ncdc_account_t;
|
|
||||||
|
|
||||||
extern GHashTable *accounts;
|
|
||||||
extern dc_account_t current_account;
|
|
||||||
|
|
||||||
extern dc_api_t api;
|
extern dc_api_t api;
|
||||||
extern dc_loop_t loop;
|
extern dc_loop_t loop;
|
||||||
@ -55,6 +48,8 @@ extern void *mainwindow;
|
|||||||
|
|
||||||
#define KEY_ESCAPE 27
|
#define KEY_ESCAPE 27
|
||||||
|
|
||||||
|
bool is_logged_in(void);
|
||||||
|
|
||||||
wchar_t *util_readkey(int esc, WINDOW *win);
|
wchar_t *util_readkey(int esc, WINDOW *win);
|
||||||
|
|
||||||
void exit_main(void);
|
void exit_main(void);
|
||||||
|
@ -4,15 +4,9 @@
|
|||||||
static bool
|
static bool
|
||||||
ncdc_cmd_friends_list(ncdc_mainwindow_t n, size_t ac, wchar_t **av)
|
ncdc_cmd_friends_list(ncdc_mainwindow_t n, size_t ac, wchar_t **av)
|
||||||
{
|
{
|
||||||
bool ret = false;
|
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
char c = ' ';
|
char c = ' ';
|
||||||
|
dc_account_t current_account = dc_session_me(current_session);
|
||||||
ret = dc_api_get_friends(api, current_account);
|
|
||||||
if (!ret) {
|
|
||||||
LOG(n, L"friends: list: failed to fetch your friends");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG(n, L"/FRIENDS list");
|
LOG(n, L"/FRIENDS list");
|
||||||
for (i = 0; i < dc_account_friends_size(current_account); i++) {
|
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;
|
char *name = NULL;
|
||||||
dc_account_t friend = NULL;
|
dc_account_t friend = NULL;
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
|
dc_account_t current_account = dc_session_me(current_session);
|
||||||
|
|
||||||
if (ac <= 1) {
|
if (ac <= 1) {
|
||||||
return false;
|
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;
|
dc_account_t friend = NULL;
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
|
dc_account_t current_account = dc_session_me(current_session);
|
||||||
|
|
||||||
if (ac <= 1) {
|
if (ac <= 1) {
|
||||||
return false;
|
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;
|
dc_account_t friend = NULL;
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
|
dc_account_t current_account = dc_session_me(current_session);
|
||||||
|
|
||||||
if (ac <= 1) {
|
if (ac <= 1) {
|
||||||
return false;
|
return false;
|
||||||
@ -164,8 +161,7 @@ bool ncdc_cmd_friends(ncdc_mainwindow_t n, size_t ac, wchar_t **av)
|
|||||||
wchar_t *subcmd = NULL;
|
wchar_t *subcmd = NULL;
|
||||||
ncdc_commands_t *it = NULL;
|
ncdc_commands_t *it = NULL;
|
||||||
|
|
||||||
if (current_account == NULL ||
|
if (!is_logged_in()) {
|
||||||
!dc_account_has_token(current_account)) {
|
|
||||||
LOG(n, L"friends: not logged in");
|
LOG(n, L"friends: not logged in");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -7,51 +7,54 @@ bool ncdc_cmd_login(ncdc_mainwindow_t n, size_t ac, wchar_t **av)
|
|||||||
char *arg = NULL;
|
char *arg = NULL;
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
dc_account_t acc = NULL;
|
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);
|
goto_if_true(ac <= 1, cleanup);
|
||||||
|
|
||||||
arg = w_convert(av[1]);
|
arg = w_convert(av[1]);
|
||||||
goto_if_true(arg == NULL, cleanup);
|
goto_if_true(arg == NULL, cleanup);
|
||||||
|
|
||||||
acc = g_hash_table_lookup(accounts, arg);
|
acc = ncdc_config_account(config, arg);
|
||||||
if (acc == NULL) {
|
if (acc == NULL) {
|
||||||
acc = ncdc_config_account(config, arg);
|
LOG(n, L"login: %ls: no such account in configuration", av[1]);
|
||||||
if (acc == NULL) {
|
goto cleanup;
|
||||||
LOG(n, L"login: %ls: no such account in configuration", av[1]);
|
}
|
||||||
|
|
||||||
|
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;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_hash_table_insert(accounts, strdup(arg), acc);
|
g_ptr_array_add(sessions, s);
|
||||||
} else {
|
} 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]);
|
LOG(n, L"login: %ls: this account is already logged in", av[1]);
|
||||||
goto cleanup;
|
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]);
|
LOG(n, L"login: %ls: authentication failed; wrong password?", av[1]);
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
gw = dc_gateway_new();
|
dc_unref(current_session);
|
||||||
if (gw == NULL) {
|
current_session = dc_ref(s);
|
||||||
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);
|
|
||||||
|
|
||||||
LOG(n, L"login: %ls: authentication successful", av[1]);
|
LOG(n, L"login: %ls: authentication successful", av[1]);
|
||||||
ret = true;
|
ret = true;
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
|
|
||||||
|
dc_unref(acc);
|
||||||
free(arg);
|
free(arg);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -6,19 +6,19 @@ bool ncdc_cmd_logout(ncdc_mainwindow_t n, size_t ac, wchar_t **av)
|
|||||||
{
|
{
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
|
|
||||||
goto_if_true(current_account == NULL ||
|
goto_if_true(current_session == NULL ||
|
||||||
!dc_account_has_token(current_account), error);
|
!dc_session_has_token(current_session), error);
|
||||||
|
|
||||||
ret = dc_api_logout(api, current_account);
|
ret = dc_session_logout(current_session);
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
LOG(n, L"logout: failed to log out the current account");
|
LOG(n, L"logout: failed to log out the current account");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_hash_table_remove(accounts, dc_account_fullname(current_account));
|
g_ptr_array_remove(sessions, current_session);
|
||||||
|
|
||||||
dc_unref(current_account);
|
dc_unref(current_session);
|
||||||
current_account = NULL;
|
current_session = NULL;
|
||||||
|
|
||||||
LOG(n, L"logout: successfully logged out");
|
LOG(n, L"logout: successfully logged out");
|
||||||
|
|
||||||
|
@ -200,9 +200,10 @@ static void ncdc_mainwindow_render_status(ncdc_mainwindow_t n)
|
|||||||
wcsftime(timestr, 99, L"[%H:%M]", t);
|
wcsftime(timestr, 99, L"[%H:%M]", t);
|
||||||
fwprintf(f, L"%ls", timestr);
|
fwprintf(f, L"%ls", timestr);
|
||||||
|
|
||||||
if (current_account == NULL) {
|
if (!is_logged_in()) {
|
||||||
fwprintf(f, L" [not logged in]");
|
fwprintf(f, L" [not logged in]");
|
||||||
} else {
|
} else {
|
||||||
|
dc_account_t current_account = dc_session_me(current_session);
|
||||||
fwprintf(f, L" [%s]", dc_account_fullname(current_account));
|
fwprintf(f, L" [%s]", dc_account_fullname(current_account));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,9 +12,10 @@ bool ncdc_cmd_msg(ncdc_mainwindow_t n, size_t ac, wchar_t **av)
|
|||||||
bool ret = false;
|
bool ret = false;
|
||||||
dc_channel_t c = NULL;
|
dc_channel_t c = NULL;
|
||||||
ncdc_textview_t v = NULL;
|
ncdc_textview_t v = NULL;
|
||||||
|
dc_account_t current_account = NULL;
|
||||||
size_t i = 0;
|
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");
|
LOG(n, L"msg: not logged in");
|
||||||
return false;
|
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]);
|
target = w_convert(av[1]);
|
||||||
goto_if_true(target == NULL, cleanup);
|
goto_if_true(target == NULL, cleanup);
|
||||||
|
|
||||||
|
current_account = dc_session_me(current_session);
|
||||||
|
|
||||||
/* find out if the target is a friend we can contact
|
/* find out if the target is a friend we can contact
|
||||||
*/
|
*/
|
||||||
dc_account_t f = dc_account_findfriend(current_account, target);
|
dc_account_t f = dc_account_findfriend(current_account, target);
|
||||||
|
@ -21,10 +21,10 @@ bool thread_done = false;
|
|||||||
static pthread_t event_thread;
|
static pthread_t event_thread;
|
||||||
static struct event_base *base = NULL;
|
static struct event_base *base = NULL;
|
||||||
|
|
||||||
/* all the accounts we have logged into
|
/* all the sessions we currently have around
|
||||||
*/
|
*/
|
||||||
GHashTable *accounts = NULL;
|
GPtrArray *sessions = NULL;
|
||||||
dc_account_t current_account = NULL;
|
dc_session_t current_session = NULL;
|
||||||
|
|
||||||
char *ncdc_private_dir = NULL;
|
char *ncdc_private_dir = NULL;
|
||||||
void *config = NULL;
|
void *config = NULL;
|
||||||
@ -35,26 +35,17 @@ dc_loop_t loop = NULL;
|
|||||||
*/
|
*/
|
||||||
dc_api_t api = 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)
|
static void cleanup(void)
|
||||||
{
|
{
|
||||||
endwin();
|
endwin();
|
||||||
|
|
||||||
if (accounts != NULL) {
|
if (sessions != NULL) {
|
||||||
g_hash_table_unref(accounts);
|
g_ptr_array_unref(sessions);
|
||||||
accounts = NULL;
|
sessions = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
dc_unref(current_account);
|
dc_unref(current_session);
|
||||||
current_account = NULL;
|
current_session = NULL;
|
||||||
|
|
||||||
thread_done = true;
|
thread_done = true;
|
||||||
dc_loop_abort(loop);
|
dc_loop_abort(loop);
|
||||||
@ -138,10 +129,8 @@ static bool init_everything(void)
|
|||||||
config = ncdc_config_new();
|
config = ncdc_config_new();
|
||||||
return_if_true(config == NULL, false);
|
return_if_true(config == NULL, false);
|
||||||
|
|
||||||
accounts = g_hash_table_new_full(g_str_hash, g_str_equal,
|
sessions = g_ptr_array_new_with_free_func((GDestroyNotify)dc_unref);
|
||||||
g_free, (GDestroyNotify)cleanup_account
|
return_if_true(sessions == NULL, false);
|
||||||
);
|
|
||||||
return_if_true(accounts == NULL, false);
|
|
||||||
|
|
||||||
ret = pthread_create(&event_thread, NULL, looper, NULL);
|
ret = pthread_create(&event_thread, NULL, looper, NULL);
|
||||||
return_if_true(ret != 0, false);
|
return_if_true(ret != 0, false);
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
#include <ncdc/ncdc.h>
|
#include <ncdc/ncdc.h>
|
||||||
|
|
||||||
|
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)
|
wchar_t *util_readkey(int e, WINDOW *win)
|
||||||
{
|
{
|
||||||
wint_t esc[7] = {0};
|
wint_t esc[7] = {0};
|
||||||
|
Loading…
Reference in New Issue
Block a user