semi working gateway implementation

This commit is contained in:
Florian Stinglmayr 2019-07-09 21:44:23 +02:00
parent 6e3555e0a8
commit b2fc9cbb62
8 changed files with 261 additions and 233 deletions

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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);
}
} }

View File

@ -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

View File

@ -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;

View File

@ -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);