semi working gateway implementation
This commit is contained in:
parent
6e3555e0a8
commit
b2fc9cbb62
@ -29,10 +29,10 @@ SET(SOURCES
|
|||||||
"src/gateway.c"
|
"src/gateway.c"
|
||||||
"src/guild.c"
|
"src/guild.c"
|
||||||
"src/loop.c"
|
"src/loop.c"
|
||||||
"src/masking.c"
|
|
||||||
"src/message.c"
|
"src/message.c"
|
||||||
"src/refable.c"
|
"src/refable.c"
|
||||||
"src/util.c"
|
"src/util.c"
|
||||||
|
"src/ws-frames.c"
|
||||||
)
|
)
|
||||||
|
|
||||||
INCLUDE_DIRECTORIES("include"
|
INCLUDE_DIRECTORIES("include"
|
||||||
|
@ -36,11 +36,6 @@ json_t *dc_api_call_sync(dc_api_t api, char const *token,
|
|||||||
char const *verb, char const *method,
|
char const *verb, char const *method,
|
||||||
json_t *j);
|
json_t *j);
|
||||||
|
|
||||||
/**
|
|
||||||
* Establish a GATEWAY to the discord servers.
|
|
||||||
*/
|
|
||||||
dc_gateway_t dc_api_establish_gateway(dc_api_t api, dc_account_t login);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Authenticate a given user account. The user account should have
|
* Authenticate a given user account. The user account should have
|
||||||
* email, and password set. If the auth succeeds the account will have
|
* email, and password set. If the auth succeeds the account will have
|
||||||
|
@ -12,34 +12,36 @@ struct dc_gateway_;
|
|||||||
typedef struct dc_gateway_ *dc_gateway_t;
|
typedef struct dc_gateway_ *dc_gateway_t;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
GATEWAY_OPCODE_HEARTBEAT = 1,
|
GATEWAY_OPCODE_PING = 1,
|
||||||
GATEWAY_OPCODE_IDENTIFY = 2,
|
GATEWAY_OPCODE_IDENTIFY = 2,
|
||||||
|
GATEWAY_OPCODE_UPDATE = 3,
|
||||||
GATEWAY_OPCODE_HELLO = 10,
|
GATEWAY_OPCODE_HELLO = 10,
|
||||||
|
GATEWAY_OPCODE_PONG = 11,
|
||||||
} dc_gateway_opcode_t;
|
} dc_gateway_opcode_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GATEWAY_FRAME_TEXT_DATA = 129,
|
||||||
|
GATEWAY_FRAME_DISCONNECT = 136,
|
||||||
|
GATEWAY_FRAME_PING = 137,
|
||||||
|
GATEWAY_FRAME_PONG = 138,
|
||||||
|
} dc_gateway_frames_t;
|
||||||
|
|
||||||
dc_gateway_t dc_gateway_new(void);
|
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);
|
||||||
|
|
||||||
/**
|
bool dc_gateway_connect(dc_gateway_t gw);
|
||||||
* Set all required CURL handles. The object will delete the easy handle
|
|
||||||
* and make sure it is removed from the multi handle upon unref. Do not
|
|
||||||
* free the multi handle before you remove the gateway.
|
|
||||||
*/
|
|
||||||
void dc_gateway_set_curl(dc_gateway_t gw, CURLM *multi, CURL *easy);
|
|
||||||
|
|
||||||
CURL *dc_gateway_curl(dc_gateway_t gw);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a CURL slist that lasts as long as the handle itself lasts
|
* Cleans up the easy handle, and thus disconnects from the socket handle
|
||||||
|
* immediately. After this call dc_gateway_connected() will return false.
|
||||||
*/
|
*/
|
||||||
struct curl_slist * dc_gateway_slist(dc_gateway_t gw);
|
void dc_gateway_disconnect(dc_gateway_t gw);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* To be used as a WRITEFUNCTION for a curl handle. Don't forget to set the
|
* Returns true if the gateway is still connected.
|
||||||
* gateway handle as a WRITEDATA too, otherwise this will have no effect.
|
|
||||||
*/
|
*/
|
||||||
size_t dc_gateway_writefunc(char *ptr, size_t sz, size_t nmemb, void *data);
|
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.
|
||||||
@ -53,4 +55,8 @@ uint8_t *
|
|||||||
dc_gateway_makeframe(uint8_t const *d, size_t data_len,
|
dc_gateway_makeframe(uint8_t const *d, size_t data_len,
|
||||||
uint8_t type, size_t *outlen);
|
uint8_t type, size_t *outlen);
|
||||||
|
|
||||||
|
size_t
|
||||||
|
dc_gateway_parseframe(uint8_t const *data, size_t datalen,
|
||||||
|
uint8_t *type, uint8_t **outdata, size_t *outlen);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -2,11 +2,6 @@
|
|||||||
#include <dc/refable.h>
|
#include <dc/refable.h>
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
|
|
||||||
#define DISCORD_URL "https://discordapp.com/api/v6"
|
|
||||||
#define DISCORD_GATEWAY "https://gateway.discord.gg/?encoding=json&v=6"
|
|
||||||
|
|
||||||
#define DISCORD_USERAGENT "Mozilla/5.0 (X11; Linux x86_64; rv:67.0) Gecko/20100101 Firefox/67.0"
|
|
||||||
|
|
||||||
struct dc_api_
|
struct dc_api_
|
||||||
{
|
{
|
||||||
dc_refable_t ref;
|
dc_refable_t ref;
|
||||||
@ -79,6 +74,7 @@ void dc_api_signal(dc_api_t api, CURL *easy, int code)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
int debug_callback(CURL *handle, curl_infotype type,
|
int debug_callback(CURL *handle, curl_infotype type,
|
||||||
char *data, size_t size,
|
char *data, size_t size,
|
||||||
void *userptr)
|
void *userptr)
|
||||||
@ -100,6 +96,7 @@ int debug_callback(CURL *handle, curl_infotype type,
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static dc_api_sync_t
|
static dc_api_sync_t
|
||||||
dc_api_do(dc_api_t api, char const *verb,
|
dc_api_do(dc_api_t api, char const *verb,
|
||||||
@ -235,10 +232,6 @@ json_t *dc_api_call_sync(dc_api_t api, char const *token,
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
printf("api_call_sync: %d\n", dc_api_sync_code(s));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
reply = json_loadb(dc_api_sync_data(s),
|
reply = json_loadb(dc_api_sync_data(s),
|
||||||
dc_api_sync_datalen(s),
|
dc_api_sync_datalen(s),
|
||||||
0, NULL
|
0, NULL
|
||||||
@ -277,89 +270,3 @@ bool dc_api_error(json_t *j, int *code, char const **message)
|
|||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t stall_connection(char *buffer, size_t size, size_t nitems,
|
|
||||||
void *userdata)
|
|
||||||
{
|
|
||||||
CURL *easy = (CURL *)userdata;
|
|
||||||
|
|
||||||
if (strncmp(buffer, "\r\n", size) == 0) {
|
|
||||||
curl_easy_setopt(easy, CURLOPT_CONNECT_ONLY, 1);
|
|
||||||
//curl_easy_pause(easy, CURLPAUSE_ALL);
|
|
||||||
curl_easy_setopt(easy, CURLOPT_FORBID_REUSE, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return size * nitems;
|
|
||||||
}
|
|
||||||
|
|
||||||
dc_gateway_t dc_api_establish_gateway(dc_api_t api, dc_account_t login)
|
|
||||||
{
|
|
||||||
return_if_true(api == NULL, NULL);
|
|
||||||
return_if_true(api->curl == NULL, NULL);
|
|
||||||
return_if_true(login == NULL || !dc_account_has_token(login), NULL);
|
|
||||||
|
|
||||||
CURL *c = NULL;
|
|
||||||
struct curl_slist *list = NULL;
|
|
||||||
/* BE THE BROKEN OR THE BREAKER
|
|
||||||
*/
|
|
||||||
dc_gateway_t gw = NULL;
|
|
||||||
dc_gateway_t ret = NULL;
|
|
||||||
|
|
||||||
c = curl_easy_init();
|
|
||||||
goto_if_true(c == NULL, cleanup);
|
|
||||||
|
|
||||||
gw = dc_gateway_new();
|
|
||||||
goto_if_true(gw == NULL, cleanup);
|
|
||||||
|
|
||||||
curl_easy_setopt(c, CURLOPT_URL, DISCORD_GATEWAY);
|
|
||||||
|
|
||||||
list = dc_gateway_slist(gw);
|
|
||||||
curl_slist_append(list, "Content-Type: application/json");
|
|
||||||
curl_slist_append(list, "Accept: application/json");
|
|
||||||
curl_slist_append(list, "User-Agent: " DISCORD_USERAGENT);
|
|
||||||
curl_slist_append(list, "Pragma: no-cache");
|
|
||||||
curl_slist_append(list, "Cache-Control: no-cache");
|
|
||||||
curl_slist_append(list, "Sec-WebSocket-Key: cbYK1Jm6cpk3Rua");
|
|
||||||
curl_slist_append(list, "Sec-WebSocket-Version: 13");
|
|
||||||
curl_slist_append(list, "Upgrade: websocket");
|
|
||||||
|
|
||||||
curl_easy_setopt(c, CURLOPT_HEADERFUNCTION, stall_connection);
|
|
||||||
curl_easy_setopt(c, CURLOPT_HEADERDATA, c);
|
|
||||||
|
|
||||||
curl_easy_setopt(c, CURLOPT_HTTPHEADER, list);
|
|
||||||
|
|
||||||
curl_easy_setopt(c, CURLOPT_TCP_KEEPALIVE, 1L);
|
|
||||||
curl_easy_setopt(c, CURLOPT_TCP_KEEPIDLE, 120L);
|
|
||||||
curl_easy_setopt(c, CURLOPT_TCP_KEEPINTVL, 60L);
|
|
||||||
|
|
||||||
curl_easy_setopt(c, CURLOPT_FORBID_REUSE, 1L);
|
|
||||||
curl_easy_setopt(c, CURLOPT_FRESH_CONNECT, 1L);
|
|
||||||
|
|
||||||
curl_easy_setopt(c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
|
|
||||||
curl_easy_setopt(c, CURLOPT_FOLLOWLOCATION, 1L);
|
|
||||||
|
|
||||||
curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, dc_gateway_writefunc);
|
|
||||||
curl_easy_setopt(c, CURLOPT_WRITEDATA, gw);
|
|
||||||
|
|
||||||
dc_gateway_set_login(gw, login);
|
|
||||||
dc_gateway_set_curl(gw, api->curl, c);
|
|
||||||
|
|
||||||
if (curl_multi_add_handle(api->curl, c) != CURLM_OK) {
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
c = NULL;
|
|
||||||
|
|
||||||
ret = gw;
|
|
||||||
gw = NULL;
|
|
||||||
|
|
||||||
cleanup:
|
|
||||||
|
|
||||||
if (c != NULL) {
|
|
||||||
curl_easy_cleanup(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
dc_unref(gw);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
@ -10,9 +10,7 @@ struct dc_gateway_
|
|||||||
GPtrArray *out;
|
GPtrArray *out;
|
||||||
GByteArray *buffer;
|
GByteArray *buffer;
|
||||||
|
|
||||||
CURLM *multi;
|
|
||||||
CURL *easy;
|
CURL *easy;
|
||||||
struct curl_slist *slist;
|
|
||||||
|
|
||||||
dc_account_t login;
|
dc_account_t login;
|
||||||
|
|
||||||
@ -24,11 +22,6 @@ static void dc_gateway_free(dc_gateway_t g)
|
|||||||
{
|
{
|
||||||
return_if_true(g == NULL,);
|
return_if_true(g == NULL,);
|
||||||
|
|
||||||
if (g->buffer != NULL) {
|
|
||||||
g_byte_array_unref(g->buffer);
|
|
||||||
g->buffer = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (g->ops != NULL) {
|
if (g->ops != NULL) {
|
||||||
g_ptr_array_unref(g->ops);
|
g_ptr_array_unref(g->ops);
|
||||||
g->ops = NULL;
|
g->ops = NULL;
|
||||||
@ -39,15 +32,9 @@ static void dc_gateway_free(dc_gateway_t g)
|
|||||||
g->out = NULL;
|
g->out = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (g->easy != NULL) {
|
if (g->buffer != NULL) {
|
||||||
curl_multi_remove_handle(g->multi, g->easy);
|
g_byte_array_unref(g->buffer);
|
||||||
curl_easy_cleanup(g->easy);
|
g->buffer = NULL;
|
||||||
g->easy = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (g->slist != NULL) {
|
|
||||||
curl_slist_free_all(g->slist);
|
|
||||||
g->slist = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dc_unref(g->login);
|
dc_unref(g->login);
|
||||||
@ -62,17 +49,14 @@ dc_gateway_t dc_gateway_new(void)
|
|||||||
|
|
||||||
g->ref.cleanup = (dc_cleanup_t)dc_gateway_free;
|
g->ref.cleanup = (dc_cleanup_t)dc_gateway_free;
|
||||||
|
|
||||||
g->buffer = g_byte_array_new();
|
|
||||||
goto_if_true(g->buffer == NULL, error);
|
|
||||||
|
|
||||||
g->ops = g_ptr_array_new_with_free_func((GDestroyNotify)json_decref);
|
g->ops = g_ptr_array_new_with_free_func((GDestroyNotify)json_decref);
|
||||||
goto_if_true(g->ops == NULL, error);
|
goto_if_true(g->ops == NULL, error);
|
||||||
|
|
||||||
g->out = g_ptr_array_new_with_free_func((GDestroyNotify)json_decref);
|
g->out = g_ptr_array_new_with_free_func((GDestroyNotify)json_decref);
|
||||||
goto_if_true(g->out == NULL, error);
|
goto_if_true(g->out == NULL, error);
|
||||||
|
|
||||||
g->slist = curl_slist_append(NULL, "");
|
g->buffer = g_byte_array_new();
|
||||||
goto_if_true(g->slist == NULL, error);
|
goto_if_true(g->buffer == NULL, error);
|
||||||
|
|
||||||
return dc_ref(g);
|
return dc_ref(g);
|
||||||
|
|
||||||
@ -88,48 +72,76 @@ 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_curl(dc_gateway_t gw, CURLM *multi, CURL *easy)
|
bool dc_gateway_connect(dc_gateway_t gw)
|
||||||
{
|
{
|
||||||
return_if_true(gw == NULL,);
|
return_if_true(gw == NULL || gw->easy != NULL, true);
|
||||||
gw->multi = multi;
|
|
||||||
gw->easy = easy;
|
|
||||||
}
|
|
||||||
|
|
||||||
CURL *dc_gateway_curl(dc_gateway_t gw)
|
char header[1000] = {0};
|
||||||
{
|
size_t outlen = 0;
|
||||||
return_if_true(gw == NULL, NULL);
|
int r = 0;
|
||||||
return gw->easy;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct curl_slist * dc_gateway_slist(dc_gateway_t gw)
|
gw->easy = curl_easy_init();
|
||||||
{
|
goto_if_true(gw->easy == NULL, error);
|
||||||
return_if_true(gw == NULL, NULL);
|
|
||||||
return gw->slist;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t dc_gateway_writefunc(char *ptr, size_t sz, size_t nmemb, void *data)
|
curl_easy_setopt(gw->easy, CURLOPT_URL, DISCORD_GATEWAY);
|
||||||
{
|
curl_easy_setopt(gw->easy, CURLOPT_FRESH_CONNECT, 1L);
|
||||||
dc_gateway_t g = (dc_gateway_t)data;
|
curl_easy_setopt(gw->easy, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
|
||||||
json_t *j = NULL;
|
curl_easy_setopt(gw->easy, CURLOPT_CONNECT_ONLY, 1L);
|
||||||
size_t i = 0;
|
|
||||||
|
|
||||||
FILE *f = fopen("websocket.txt", "a+");
|
goto_if_true(curl_easy_perform(gw->easy) != CURLE_OK, error);
|
||||||
fprintf(f, ">> ");
|
|
||||||
fwrite(ptr, sz, nmemb, f);
|
|
||||||
fprintf(f, "\n");
|
|
||||||
fclose(f);
|
|
||||||
|
|
||||||
for (i = 0; *ptr != '{' && i < (sz *nmemb); ptr++, i++)
|
snprintf(header, sizeof(header)-1,
|
||||||
;
|
"GET %s HTTP/1.1\r\n"
|
||||||
|
"Host: %s\r\n"
|
||||||
|
"User-Agent: %s\r\n"
|
||||||
|
"Pragma: no-cache\r\n"
|
||||||
|
"Cache-Control: no-cache\r\n"
|
||||||
|
"Sec-WebSocket-Key: cbYK1Jm6cpk3Rua\r\n"
|
||||||
|
"Sec-WebSocket-Version: 13\r\n"
|
||||||
|
"Upgrade: websocket\r\n"
|
||||||
|
"\r\n",
|
||||||
|
DISCORD_GATEWAY_URL,
|
||||||
|
DISCORD_GATEWAY_HOST,
|
||||||
|
DISCORD_USERAGENT
|
||||||
|
);
|
||||||
|
|
||||||
if (i < (sz * nmemb)) {
|
r = curl_easy_send(gw->easy, header, strlen(header), &outlen);
|
||||||
j = json_loadb(ptr, (sz*nmemb) - i, JSON_DISABLE_EOF_CHECK, NULL);
|
goto_if_true(r != CURLE_OK || outlen != strlen(header), error);
|
||||||
if (j != NULL) {
|
|
||||||
g_ptr_array_add(g->ops, j);
|
do {
|
||||||
|
r = curl_easy_recv(gw->easy, header, sizeof(header), &outlen);
|
||||||
|
if (r == CURLE_OK && outlen > 0) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
if (r != CURLE_AGAIN) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
} while (true);
|
||||||
|
|
||||||
return sz * nmemb;
|
goto_if_true(strstr(header, "HTTP/1.1 101") == NULL, error);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
error:
|
||||||
|
|
||||||
|
curl_easy_cleanup(gw->easy);
|
||||||
|
gw->easy = NULL;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dc_gateway_disconnect(dc_gateway_t gw)
|
||||||
|
{
|
||||||
|
return_if_true(gw == NULL || gw->easy == NULL,);
|
||||||
|
|
||||||
|
curl_easy_cleanup(gw->easy);
|
||||||
|
gw->easy = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool dc_gateway_connected(dc_gateway_t gw)
|
||||||
|
{
|
||||||
|
return_if_true(gw == NULL || gw->easy == NULL, false);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static json_t *dc_gateway_answer(dc_gateway_t gw)
|
static json_t *dc_gateway_answer(dc_gateway_t gw)
|
||||||
@ -169,10 +181,31 @@ static void dc_gateway_queue(dc_gateway_t gw, int code, json_t *d)
|
|||||||
|
|
||||||
static void dc_gateway_queue_heartbeat(dc_gateway_t gw)
|
static void dc_gateway_queue_heartbeat(dc_gateway_t gw)
|
||||||
{
|
{
|
||||||
dc_gateway_queue(gw, GATEWAY_OPCODE_HEARTBEAT, NULL);
|
dc_gateway_queue(gw, GATEWAY_OPCODE_PING, NULL);
|
||||||
gw->last_heartbeat = time(NULL);
|
gw->last_heartbeat = time(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void dc_gateway_queue_identify(dc_gateway_t gw)
|
||||||
|
{
|
||||||
|
json_t *j = json_object(), *dev = json_object();
|
||||||
|
char const *token = dc_account_token(gw->login);
|
||||||
|
|
||||||
|
if (j == NULL || dev == NULL) {
|
||||||
|
json_decref(j);
|
||||||
|
json_decref(dev);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_object_set_new(dev, "$os", json_string("linux"));
|
||||||
|
json_object_set_new(dev, "$browser", json_string("libdc"));
|
||||||
|
json_object_set_new(dev, "$device", json_string("libdc"));
|
||||||
|
|
||||||
|
json_object_set_new(j, "token", json_string(token));
|
||||||
|
json_object_set_new(j, "properties", dev);
|
||||||
|
|
||||||
|
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, json_t *d)
|
||||||
{
|
{
|
||||||
json_t *val = NULL;
|
json_t *val = NULL;
|
||||||
@ -182,7 +215,7 @@ static bool dc_gateway_handle_hello(dc_gateway_t gw, json_t *d)
|
|||||||
|
|
||||||
/* send an identify first
|
/* send an identify first
|
||||||
*/
|
*/
|
||||||
dc_gateway_queue(gw, GATEWAY_OPCODE_IDENTIFY, NULL);
|
dc_gateway_queue_identify(gw);
|
||||||
|
|
||||||
gw->heartbeat_interval = json_integer_value(val);
|
gw->heartbeat_interval = json_integer_value(val);
|
||||||
dc_gateway_queue_heartbeat(gw);
|
dc_gateway_queue_heartbeat(gw);
|
||||||
@ -190,6 +223,11 @@ 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)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static bool dc_gateway_handle_op(dc_gateway_t gw, json_t *j)
|
static bool dc_gateway_handle_op(dc_gateway_t gw, json_t *j)
|
||||||
{
|
{
|
||||||
json_t *val = NULL;
|
json_t *val = NULL;
|
||||||
@ -204,42 +242,71 @@ static bool dc_gateway_handle_op(dc_gateway_t gw, json_t *j)
|
|||||||
|
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case GATEWAY_OPCODE_HELLO: dc_gateway_handle_hello(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_PONG: break;
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
|
||||||
static void dc_gateway_process_read(dc_gateway_t gw)
|
static void dc_gateway_process_read(dc_gateway_t gw)
|
||||||
{
|
{
|
||||||
char buf[100] = {0};
|
|
||||||
size_t read = 0;
|
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
FILE *f = NULL;
|
char buf[100] = {0};
|
||||||
json_t *j = NULL;
|
size_t outlen = 0;
|
||||||
size_t where = 0;
|
|
||||||
|
|
||||||
ret = curl_easy_recv(gw->easy, &buf, sizeof(buf), &read);
|
return_if_true(gw->easy == NULL,);
|
||||||
return_if_true(ret != CURLE_OK,);
|
|
||||||
|
|
||||||
g_byte_array_append(gw->buffer, (uint8_t const*)buf, read);
|
do {
|
||||||
|
ret = curl_easy_recv(gw->easy, buf, sizeof(buf), &outlen);
|
||||||
f = fmemopen(gw->buffer->data, gw->buffer->len, "r");
|
if (ret == CURLE_OK && outlen > 0) {
|
||||||
return_if_true(f == NULL,);
|
FILE *f = fopen("output.txt", "a+");
|
||||||
|
fwrite(buf, outlen, sizeof(char), f);
|
||||||
j = json_loadf(f, JSON_DISABLE_EOF_CHECK, NULL);
|
fputc('\n', f);
|
||||||
where = ftell(f);
|
fclose(f);
|
||||||
|
g_byte_array_append(gw->buffer, (uint8_t const*)buf, outlen);
|
||||||
fclose(f);
|
}
|
||||||
f = NULL;
|
} while (ret == CURLE_OK && outlen > 0);
|
||||||
|
}
|
||||||
if (j != NULL) {
|
|
||||||
g_ptr_array_add(gw->ops, j);
|
static void dc_gateway_process_frame(dc_gateway_t gw)
|
||||||
g_byte_array_remove_range(gw->buffer, 0, where);
|
{
|
||||||
}
|
size_t ret = 0;
|
||||||
|
uint8_t *data = NULL;
|
||||||
|
size_t datalen = 0;
|
||||||
|
uint8_t type = 0;
|
||||||
|
|
||||||
|
ret = dc_gateway_parseframe(gw->buffer->data, gw->buffer->len,
|
||||||
|
&type, &data, &datalen
|
||||||
|
);
|
||||||
|
return_if_true(ret == 0,);
|
||||||
|
|
||||||
|
g_byte_array_remove_range(gw->buffer, 0, ret);
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case GATEWAY_FRAME_TEXT_DATA:
|
||||||
|
{
|
||||||
|
json_t *j = NULL;
|
||||||
|
|
||||||
|
j = json_loadb((char const*)data, datalen,
|
||||||
|
JSON_DISABLE_EOF_CHECK, NULL);
|
||||||
|
if (j != NULL) {
|
||||||
|
g_ptr_array_add(gw->ops, j);
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case GATEWAY_FRAME_DISCONNECT:
|
||||||
|
{
|
||||||
|
dc_gateway_disconnect(gw);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
free(data);
|
||||||
|
data = NULL;
|
||||||
|
datalen = 0;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
static void dc_gateway_process_in(dc_gateway_t gw)
|
static void dc_gateway_process_in(dc_gateway_t gw)
|
||||||
{
|
{
|
||||||
@ -250,38 +317,47 @@ static void dc_gateway_process_in(dc_gateway_t gw)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dc_gateway_process_out(dc_gateway_t gw)
|
static bool dc_gateway_process_out(dc_gateway_t gw, json_t *j)
|
||||||
{
|
{
|
||||||
char *str = NULL;
|
char *str = NULL;
|
||||||
size_t slen = 0, outlen = 0, sent = 0;
|
|
||||||
uint8_t *mask = NULL;
|
uint8_t *mask = NULL;
|
||||||
|
size_t outlen = 0, outlen2 = 0;
|
||||||
|
int ret = 0;
|
||||||
|
bool r = false;
|
||||||
|
|
||||||
while (gw->out->len > 0) {
|
str = json_dumps(j, JSON_COMPACT);
|
||||||
json_t *j = g_ptr_array_index(gw->out, 0);
|
goto_if_true(str == NULL, cleanup);
|
||||||
|
|
||||||
str = json_dumps(j, JSON_COMPACT);
|
mask = dc_gateway_makeframe((uint8_t const *)str, strlen(str),
|
||||||
|
GATEWAY_FRAME_TEXT_DATA,
|
||||||
|
&outlen
|
||||||
|
);
|
||||||
|
goto_if_true(mask == NULL, cleanup);
|
||||||
|
|
||||||
if (str != NULL) {
|
ret = curl_easy_send(gw->easy, mask, outlen, &outlen2);
|
||||||
slen = strlen(str);
|
goto_if_true(ret != CURLE_OK || outlen2 != outlen, cleanup);
|
||||||
mask = dc_gateway_makeframe((uint8_t const *)str, slen, 0, &outlen);
|
|
||||||
curl_easy_send(gw->easy, mask, outlen, &sent);
|
|
||||||
|
|
||||||
FILE *f = fopen("websocket.txt", "a+");
|
r = true;
|
||||||
fprintf(f, "<< %s\n", str);
|
|
||||||
fclose(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(str);
|
cleanup:
|
||||||
free(mask);
|
|
||||||
|
|
||||||
g_ptr_array_remove_index(gw->out, 0);
|
free(str);
|
||||||
}
|
str = NULL;
|
||||||
|
|
||||||
|
free(mask);
|
||||||
|
mask = NULL;
|
||||||
|
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
void dc_gateway_process(dc_gateway_t gw)
|
void dc_gateway_process(dc_gateway_t gw)
|
||||||
{
|
{
|
||||||
time_t diff = 0;
|
time_t diff = 0;
|
||||||
|
|
||||||
|
if (!dc_gateway_connected(gw)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (gw->heartbeat_interval > 0) {
|
if (gw->heartbeat_interval > 0) {
|
||||||
diff = time(NULL) - gw->last_heartbeat;
|
diff = time(NULL) - gw->last_heartbeat;
|
||||||
if (diff >= (gw->heartbeat_interval / 1000)) {
|
if (diff >= (gw->heartbeat_interval / 1000)) {
|
||||||
@ -289,7 +365,22 @@ void dc_gateway_process(dc_gateway_t gw)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//dc_gateway_process_read(gw);
|
dc_gateway_process_read(gw);
|
||||||
dc_gateway_process_in(gw);
|
|
||||||
dc_gateway_process_out(gw);
|
if (gw->buffer->len > 0) {
|
||||||
|
dc_gateway_process_frame(gw);
|
||||||
|
if (!dc_gateway_connected(gw)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gw->ops->len > 0) {
|
||||||
|
dc_gateway_process_in(gw);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (gw->out->len > 0) {
|
||||||
|
json_t *j = g_ptr_array_index(gw->out, 0);
|
||||||
|
dc_gateway_process_out(gw, j);
|
||||||
|
g_ptr_array_remove_index(gw->out, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,11 +25,18 @@
|
|||||||
#include <dc/refable.h>
|
#include <dc/refable.h>
|
||||||
#include <dc/account.h>
|
#include <dc/account.h>
|
||||||
|
|
||||||
//#define DEBUG
|
#define DEBUG
|
||||||
|
|
||||||
#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)
|
||||||
|
|
||||||
#define TOKEN(l) (dc_account_token(l))
|
#define TOKEN(l) (dc_account_token(l))
|
||||||
|
|
||||||
|
#define DISCORD_URL "https://discordapp.com/api/v6"
|
||||||
|
#define DISCORD_GATEWAY_URL "/?encoding=json&v=6"
|
||||||
|
#define DISCORD_GATEWAY_HOST "gateway.discord.gg"
|
||||||
|
#define DISCORD_GATEWAY "https://" DISCORD_GATEWAY_HOST DISCORD_GATEWAY_URL
|
||||||
|
|
||||||
|
#define DISCORD_USERAGENT "Mozilla/5.0 (X11; Linux x86_64; rv:67.0) Gecko/20100101 Firefox/67.0"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -18,6 +18,24 @@ 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,);
|
||||||
@ -69,18 +87,6 @@ mcurl_handler(CURL *easy, curl_socket_t s, int what, void *userp, void *socketp)
|
|||||||
struct event *event = (struct event *)socketp;
|
struct event *event = (struct event *)socketp;
|
||||||
dc_loop_t loop = (dc_loop_t)userp;
|
dc_loop_t loop = (dc_loop_t)userp;
|
||||||
|
|
||||||
for (size_t i = 0; i < loop->gateways->len; i++) {
|
|
||||||
dc_gateway_t gw = g_ptr_array_index(loop->gateways, i);
|
|
||||||
|
|
||||||
if (dc_gateway_curl(gw) == easy) {
|
|
||||||
printf("gateway event: %s: %s%s\n",
|
|
||||||
(what == CURL_POLL_REMOVE ? "remove" : "add"),
|
|
||||||
((what & CURL_POLL_IN) ? "r" : ""),
|
|
||||||
((what & CURL_POLL_OUT) ? "w": "")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (what == CURL_POLL_REMOVE) {
|
if (what == CURL_POLL_REMOVE) {
|
||||||
if (event != NULL) {
|
if (event != NULL) {
|
||||||
event_del(event);
|
event_del(event);
|
||||||
@ -169,7 +175,9 @@ 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((GDestroyNotify)dc_unref);
|
ptr->gateways = g_ptr_array_new_with_free_func(
|
||||||
|
(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);
|
||||||
@ -216,7 +224,13 @@ 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));
|
||||||
|
return_if_true(ptr == NULL,);
|
||||||
|
|
||||||
|
ptr->gateway = dc_ref(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)
|
||||||
@ -254,8 +268,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_gateway_t gw = g_ptr_array_index(l->gateways, i);
|
dc_loop_gateway_t *ptr = g_ptr_array_index(l->gateways, i);
|
||||||
dc_gateway_process(gw);
|
|
||||||
|
if (!dc_gateway_connected(ptr->gateway)) {
|
||||||
|
if (!dc_gateway_connect(ptr->gateway)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dc_gateway_process(ptr->gateway);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -35,12 +35,13 @@ bool ncdc_cmd_login(ncdc_mainwindow_t n, size_t ac, wchar_t **av)
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
gw = dc_api_establish_gateway(api, acc);
|
gw = dc_gateway_new();
|
||||||
if (gw == NULL) {
|
if (gw == NULL) {
|
||||||
LOG(n, L"login: %ls: failed to establish gateway", av[1]);
|
LOG(n, L"login: %ls: failed to establish gateway", av[1]);
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dc_gateway_set_login(gw, acc);
|
||||||
dc_loop_add_gateway(loop, gw);
|
dc_loop_add_gateway(loop, gw);
|
||||||
|
|
||||||
dc_unref(current_account);
|
dc_unref(current_account);
|
||||||
|
Loading…
Reference in New Issue
Block a user