web sockets are hard

This commit is contained in:
Florian Stinglmayr 2019-07-08 23:29:08 +02:00
parent c3f4f5a180
commit 91bdd23a72
12 changed files with 670 additions and 86 deletions

View File

@ -12,6 +12,7 @@ SET(SOURCES
"include/dc/api.h"
"include/dc/apisync.h"
"include/dc/channel.h"
"include/dc/gateway.h"
"include/dc/guild.h"
"include/dc/loop.h"
"include/dc/message.h"
@ -22,10 +23,13 @@ SET(SOURCES
"src/api-auth.c"
"src/api-channel.c"
"src/api-friends.c"
"src/api-user.c"
"src/apisync.c"
"src/channel.c"
"src/gateway.c"
"src/guild.c"
"src/loop.c"
"src/masking.c"
"src/message.c"
"src/refable.c"
"src/util.c"

View File

@ -5,6 +5,7 @@
#include <dc/account.h>
#include <dc/guild.h>
#include <dc/channel.h>
#include <dc/gateway.h>
#include <stdbool.h>
@ -35,6 +36,11 @@ json_t *dc_api_call_sync(dc_api_t api, char const *token,
char const *verb, char const *method,
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
* email, and password set. If the auth succeeds the account will have
@ -44,6 +50,13 @@ json_t *dc_api_call_sync(dc_api_t api, char const *token,
*/
bool dc_api_authenticate(dc_api_t api, dc_account_t account);
/**
* Log the account in. Will first call dc_api_authenticate(), then
* dc_api_get_userinfo(), then dc_api_get_friends(). If
* any of these steps fail, it returns false.
*/
bool dc_api_login(dc_api_t api, dc_account_t account);
/**
* Inverse of dc_api_authenticate(). Logs the given user out, destroying the
* login token in the process.

View File

@ -0,0 +1,56 @@
#ifndef DC_GATEWAY_H
#define DC_GATEWAY_H
#include <stdint.h>
#include <stdlib.h>
#include <dc/account.h>
#include <curl/curl.h>
struct dc_gateway_;
typedef struct dc_gateway_ *dc_gateway_t;
typedef enum {
GATEWAY_OPCODE_HEARTBEAT = 1,
GATEWAY_OPCODE_IDENTIFY = 2,
GATEWAY_OPCODE_HELLO = 10,
} dc_gateway_opcode_t;
dc_gateway_t dc_gateway_new(void);
void dc_gateway_set_login(dc_gateway_t gw, dc_account_t login);
/**
* 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
*/
struct curl_slist * dc_gateway_slist(dc_gateway_t gw);
/**
* To be used as a WRITEFUNCTION for a curl handle. Don't forget to set the
* 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);
/**
* Process the queue of data that came from the websocket.
*/
void dc_gateway_process(dc_gateway_t gw);
/**
* utility function to make a websocket frame
*/
uint8_t *
dc_gateway_makeframe(uint8_t const *d, size_t data_len,
uint8_t type, size_t *outlen);
#endif

View File

@ -2,6 +2,7 @@
#define DC_LOOP_H
#include <dc/api.h>
#include <dc/gateway.h>
#include <event.h>
#include <curl/curl.h>
@ -39,6 +40,11 @@ struct event_base *dc_loop_event_base(dc_loop_t l);
*/
void dc_loop_add_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);
/**
* Loop once, and process one message in the queues of the event
* base, and one message from the queue of the CURL multi events.

View File

@ -28,6 +28,29 @@ bool dc_api_logout(dc_api_t api, dc_account_t account)
return true;
}
bool dc_api_login(dc_api_t api, dc_account_t account)
{
if (!dc_api_authenticate(api, account)) {
goto cleanup;
}
if (!dc_api_get_userinfo(api, account, account)) {
goto cleanup;
}
if (!dc_api_get_friends(api, account)) {
goto cleanup;
}
return true;
cleanup:
dc_account_set_token(account, NULL);
return false;
}
bool dc_api_authenticate(dc_api_t api, dc_account_t account)
{
json_t *j = json_object(), *reply = NULL, *token = NULL;

93
libdc/src/api-user.c Normal file
View File

@ -0,0 +1,93 @@
#include <dc/api.h>
#include "internal.h"
bool dc_api_get_userinfo(dc_api_t api, dc_account_t login,
dc_account_t user)
{
char *url = NULL;
json_t *reply = NULL, *val = NULL;
bool ret = false;
return_if_true(api == NULL, false);
return_if_true(login == NULL, false);
return_if_true(user == NULL, false);
if (user == login) {
url = strdup("users/@me");
} else {
asprintf(&url, "users/%s", dc_account_id(user));
}
reply = dc_api_call_sync(api, "GET", dc_account_token(login), url, NULL);
goto_if_true(reply == NULL, cleanup);
val = json_object_get(reply, "username");
goto_if_true(val == NULL || !json_is_string(val), cleanup);
dc_account_set_username(user, json_string_value(val));
val = json_object_get(reply, "discriminator");
goto_if_true(val == NULL || !json_is_string(val), cleanup);
dc_account_set_discriminator(user, json_string_value(val));
val = json_object_get(reply, "id");
goto_if_true(val == NULL || !json_is_string(val), cleanup);
dc_account_set_id(user, json_string_value(val));
ret = true;
cleanup:
free(url);
json_decref(reply);
return ret;
}
bool dc_api_get_userguilds(dc_api_t api, dc_account_t login, GPtrArray **out)
{
char const *url = "users/@me/guilds";
json_t *reply = NULL, *c = NULL, *val = NULL;
size_t i = 0;
bool ret = false;
GPtrArray *guilds = g_ptr_array_new_with_free_func(
(GDestroyNotify)dc_unref
);
return_if_true(api == NULL, false);
return_if_true(login == NULL, false);
reply = dc_api_call_sync(api, "GET", dc_account_token(login), url, NULL);
goto_if_true(reply == NULL, cleanup);
goto_if_true(!json_is_array(reply), cleanup);
json_array_foreach(reply, i, c) {
dc_guild_t g = dc_guild_new();
val = json_object_get(c, "id");
goto_if_true(val == NULL || !json_is_string(val), cleanup);
dc_guild_set_id(g, json_string_value(val));
val = json_object_get(c, "name");
goto_if_true(val == NULL || !json_is_string(val), cleanup);
dc_guild_set_name(g, json_string_value(val));
g_ptr_array_add(guilds, g);
}
*out = guilds;
guilds = NULL;
ret = true;
cleanup:
json_decref(reply);
if (guilds) {
g_ptr_array_unref(guilds);
guilds = NULL;
}
return ret;
}

View File

@ -3,6 +3,7 @@
#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"
@ -276,93 +277,88 @@ bool dc_api_error(json_t *j, int *code, char const **message)
return error;
}
bool dc_api_get_userinfo(dc_api_t api, dc_account_t login,
dc_account_t user)
static size_t stall_connection(char *buffer, size_t size, size_t nitems,
void *userdata)
{
char *url = NULL;
json_t *reply = NULL, *val = NULL;
bool ret = false;
CURL *easy = (CURL *)userdata;
return_if_true(api == NULL, false);
return_if_true(login == NULL, false);
return_if_true(user == NULL, false);
if (user == login) {
url = strdup("users/@me");
} else {
asprintf(&url, "users/%s", dc_account_id(user));
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);
}
reply = dc_api_call_sync(api, "GET", dc_account_token(login), url, NULL);
goto_if_true(reply == NULL, cleanup);
return size * nitems;
}
val = json_object_get(reply, "username");
goto_if_true(val == NULL || !json_is_string(val), cleanup);
dc_account_set_username(user, json_string_value(val));
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);
val = json_object_get(reply, "discriminator");
goto_if_true(val == NULL || !json_is_string(val), cleanup);
dc_account_set_discriminator(user, json_string_value(val));
CURL *c = NULL;
struct curl_slist *list = NULL;
/* BE THE BROKEN OR THE BREAKER
*/
dc_gateway_t gw = NULL;
dc_gateway_t ret = NULL;
val = json_object_get(reply, "id");
goto_if_true(val == NULL || !json_is_string(val), cleanup);
dc_account_set_id(user, json_string_value(val));
c = curl_easy_init();
goto_if_true(c == NULL, cleanup);
ret = true;
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:
free(url);
json_decref(reply);
return ret;
}
bool dc_api_get_userguilds(dc_api_t api, dc_account_t login, GPtrArray **out)
{
char const *url = "users/@me/guilds";
json_t *reply = NULL, *c = NULL, *val = NULL;
size_t i = 0;
bool ret = false;
GPtrArray *guilds = g_ptr_array_new_with_free_func(
(GDestroyNotify)dc_unref
);
return_if_true(api == NULL, false);
return_if_true(login == NULL, false);
reply = dc_api_call_sync(api, "GET", dc_account_token(login), url, NULL);
goto_if_true(reply == NULL, cleanup);
goto_if_true(!json_is_array(reply), cleanup);
json_array_foreach(reply, i, c) {
dc_guild_t g = dc_guild_new();
val = json_object_get(c, "id");
goto_if_true(val == NULL || !json_is_string(val), cleanup);
dc_guild_set_id(g, json_string_value(val));
val = json_object_get(c, "name");
goto_if_true(val == NULL || !json_is_string(val), cleanup);
dc_guild_set_name(g, json_string_value(val));
g_ptr_array_add(guilds, g);
}
*out = guilds;
guilds = NULL;
ret = true;
cleanup:
json_decref(reply);
if (guilds) {
g_ptr_array_unref(guilds);
guilds = NULL;
}
if (c != NULL) {
curl_easy_cleanup(c);
}
dc_unref(gw);
return ret;
}

295
libdc/src/gateway.c Normal file
View File

@ -0,0 +1,295 @@
#include <dc/gateway.h>
#include "internal.h"
#include <jansson.h>
struct dc_gateway_
{
dc_refable_t ref;
GPtrArray *ops;
GPtrArray *out;
GByteArray *buffer;
CURLM *multi;
CURL *easy;
struct curl_slist *slist;
dc_account_t login;
uint64_t heartbeat_interval;
time_t last_heartbeat;
};
static void dc_gateway_free(dc_gateway_t g)
{
return_if_true(g == NULL,);
if (g->buffer != NULL) {
g_byte_array_unref(g->buffer);
g->buffer = NULL;
}
if (g->ops != NULL) {
g_ptr_array_unref(g->ops);
g->ops = NULL;
}
if (g->out != NULL) {
g_ptr_array_unref(g->out);
g->out = NULL;
}
if (g->easy != NULL) {
curl_multi_remove_handle(g->multi, g->easy);
curl_easy_cleanup(g->easy);
g->easy = NULL;
}
if (g->slist != NULL) {
curl_slist_free_all(g->slist);
g->slist = NULL;
}
dc_unref(g->login);
free(g);
}
dc_gateway_t dc_gateway_new(void)
{
dc_gateway_t g = calloc(1, sizeof(struct dc_gateway_));
return_if_true(g == NULL, NULL);
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);
goto_if_true(g->ops == NULL, error);
g->out = g_ptr_array_new_with_free_func((GDestroyNotify)json_decref);
goto_if_true(g->out == NULL, error);
g->slist = curl_slist_append(NULL, "");
goto_if_true(g->slist == NULL, error);
return dc_ref(g);
error:
dc_gateway_free(g);
return NULL;
}
void dc_gateway_set_login(dc_gateway_t gw, dc_account_t login)
{
return_if_true(gw == NULL,);
gw->login = dc_ref(login);
}
void dc_gateway_set_curl(dc_gateway_t gw, CURLM *multi, CURL *easy)
{
return_if_true(gw == NULL,);
gw->multi = multi;
gw->easy = easy;
}
CURL *dc_gateway_curl(dc_gateway_t gw)
{
return_if_true(gw == NULL, NULL);
return gw->easy;
}
struct curl_slist * dc_gateway_slist(dc_gateway_t gw)
{
return_if_true(gw == NULL, NULL);
return gw->slist;
}
size_t dc_gateway_writefunc(char *ptr, size_t sz, size_t nmemb, void *data)
{
dc_gateway_t g = (dc_gateway_t)data;
json_t *j = NULL;
size_t i = 0;
FILE *f = fopen("websocket.txt", "a+");
fprintf(f, ">> ");
fwrite(ptr, sz, nmemb, f);
fprintf(f, "\n");
fclose(f);
for (i = 0; *ptr != '{' && i < (sz *nmemb); ptr++, i++)
;
if (i < (sz * nmemb)) {
j = json_loadb(ptr, (sz*nmemb) - i, JSON_DISABLE_EOF_CHECK, NULL);
if (j != NULL) {
g_ptr_array_add(g->ops, j);
}
}
return sz * nmemb;
}
static json_t *dc_gateway_answer(dc_gateway_t gw)
{
json_t *j = NULL;
char const *token = NULL;
j = json_object();
return_if_true(j == NULL, NULL);
token = dc_account_token(gw->login);
if (token != NULL) {
json_object_set_new(j, "token", json_string(token));
}
return j;
}
static void dc_gateway_queue(dc_gateway_t gw, int code, json_t *d)
{
json_t *j = NULL;
j = json_object();
return_if_true(j == NULL,);
if (d == NULL) {
d = dc_gateway_answer(gw);
}
json_object_set_new(j, "t", json_null());
json_object_set_new(j, "s", json_null());
json_object_set_new(j, "d", d);
json_object_set_new(j, "op", json_integer(code));
g_ptr_array_add(gw->out, j);
}
static void dc_gateway_queue_heartbeat(dc_gateway_t gw)
{
dc_gateway_queue(gw, GATEWAY_OPCODE_HEARTBEAT, NULL);
gw->last_heartbeat = time(NULL);
}
static bool dc_gateway_handle_hello(dc_gateway_t gw, json_t *d)
{
json_t *val = NULL;
val = json_object_get(d, "heartbeat_interval");
return_if_true(val == NULL || !json_is_integer(val), false);
/* send an identify first
*/
dc_gateway_queue(gw, GATEWAY_OPCODE_IDENTIFY, NULL);
gw->heartbeat_interval = json_integer_value(val);
dc_gateway_queue_heartbeat(gw);
return true;
}
static bool dc_gateway_handle_op(dc_gateway_t gw, json_t *j)
{
json_t *val = NULL;
dc_gateway_opcode_t op = 0;
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, "d");
return_if_true(val == NULL || !json_is_object(val), false);
switch (op) {
case GATEWAY_OPCODE_HELLO: dc_gateway_handle_hello(gw, val); break;
default: break;
}
return true;
}
#if 0
static void dc_gateway_process_read(dc_gateway_t gw)
{
char buf[100] = {0};
size_t read = 0;
int ret = 0;
FILE *f = NULL;
json_t *j = NULL;
size_t where = 0;
ret = curl_easy_recv(gw->easy, &buf, sizeof(buf), &read);
return_if_true(ret != CURLE_OK,);
g_byte_array_append(gw->buffer, (uint8_t const*)buf, read);
f = fmemopen(gw->buffer->data, gw->buffer->len, "r");
return_if_true(f == NULL,);
j = json_loadf(f, JSON_DISABLE_EOF_CHECK, NULL);
where = ftell(f);
fclose(f);
f = NULL;
if (j != NULL) {
g_ptr_array_add(gw->ops, j);
g_byte_array_remove_range(gw->buffer, 0, where);
}
}
#endif
static void dc_gateway_process_in(dc_gateway_t gw)
{
while (gw->ops->len > 0) {
json_t *j = g_ptr_array_index(gw->ops, 0);
dc_gateway_handle_op(gw, j);
g_ptr_array_remove_index(gw->ops, 0);
}
}
static void dc_gateway_process_out(dc_gateway_t gw)
{
char *str = NULL;
size_t slen = 0, outlen = 0, sent = 0;
uint8_t *mask = NULL;
while (gw->out->len > 0) {
json_t *j = g_ptr_array_index(gw->out, 0);
str = json_dumps(j, JSON_COMPACT);
if (str != NULL) {
slen = strlen(str);
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+");
fprintf(f, "<< %s\n", str);
fclose(f);
}
free(str);
free(mask);
g_ptr_array_remove_index(gw->out, 0);
}
}
void dc_gateway_process(dc_gateway_t gw)
{
time_t diff = 0;
if (gw->heartbeat_interval > 0) {
diff = time(NULL) - gw->last_heartbeat;
if (diff >= (gw->heartbeat_interval / 1000)) {
dc_gateway_queue_heartbeat(gw);
}
}
//dc_gateway_process_read(gw);
dc_gateway_process_in(gw);
dc_gateway_process_out(gw);
}

View File

@ -15,6 +15,7 @@ struct dc_loop_
bool multi_owner;
GPtrArray *apis;
GPtrArray *gateways;
};
static void dc_loop_free(dc_loop_t p)
@ -42,6 +43,11 @@ static void dc_loop_free(dc_loop_t p)
p->apis = NULL;
}
if (p->gateways != NULL) {
g_ptr_array_unref(p->gateways);
p->gateways = NULL;
}
free(p);
}
@ -63,6 +69,18 @@ mcurl_handler(CURL *easy, curl_socket_t s, int what, void *userp, void *socketp)
struct event *event = (struct event *)socketp;
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 (event != NULL) {
event_del(event);
@ -148,9 +166,12 @@ dc_loop_t dc_loop_new_full(struct event_base *base, CURLM *multi)
ptr->multi_owner = true;
}
ptr->apis = g_ptr_array_new();
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_unref);
goto_if_true(ptr->gateways == NULL, fail);
ptr->timer = evtimer_new(ptr->base, timer_handler, ptr);
goto_if_true(ptr->timer == NULL, fail);
@ -188,7 +209,14 @@ 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, p);
g_ptr_array_add(l->apis, dc_ref(p));
}
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));
}
void dc_loop_abort(dc_loop_t l)
@ -225,5 +253,10 @@ bool dc_loop_once(dc_loop_t l)
}
}
for (i = 0; i < l->gateways->len; i++) {
dc_gateway_t gw = g_ptr_array_index(l->gateways, i);
dc_gateway_process(gw);
}
return true;
}

63
libdc/src/masking.c Normal file
View File

@ -0,0 +1,63 @@
#include <dc/gateway.h>
#include "internal.h"
static uint8_t *
websocket_mask(uint8_t key[4], uint8_t const *pload, size_t psize)
{
size_t i = 0;
uint8_t *ret = calloc(psize, sizeof(char));
for (i = 0; i < psize; i++) {
ret[i] = pload[i] ^ key[i % 4];
}
return ret;
}
uint8_t *
dc_gateway_makeframe(uint8_t const *d, size_t data_len,
uint8_t type, size_t *outlen)
{
uint8_t *data = NULL;
uint8_t *full_data;
uint32_t len_size = 1;
uint8_t mkey[4] = { 0x12, 0x34, 0x56, 0x78 };
data = websocket_mask(mkey, d, data_len);
if (data_len > 125) {
if (data_len <= UINT16_MAX) {
len_size += 2;
} else {
len_size += 8;
}
}
full_data = calloc(1 + data_len + len_size + 4, sizeof(uint8_t));
if (type == 0) {
type = 129;
}
full_data[0] = type;
if (data_len <= 125) {
full_data[1] = data_len | 0x80;
} else if (data_len <= G_MAXUINT16) {
uint16_t be_len = GUINT16_TO_BE(data_len);
full_data[1] = 126 | 0x80;
memmove(full_data + 2, &be_len, 2);
} else {
guint64 be_len = GUINT64_TO_BE(data_len);
full_data[1] = 127 | 0x80;
memmove(full_data + 2, &be_len, 8);
}
memmove(full_data + (1 + len_size), &mkey, 4);
memmove(full_data + (1 + len_size + 4), data, data_len);
*outlen = 1 + data_len + len_size + 4;
free(data);
return full_data;
}

View File

@ -47,6 +47,7 @@ extern GHashTable *accounts;
extern dc_account_t current_account;
extern dc_api_t api;
extern dc_loop_t loop;
extern char *ncdc_private_dir;
extern void *config;

View File

@ -7,6 +7,7 @@ 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;
goto_if_true(ac <= 1, cleanup);
@ -29,22 +30,22 @@ bool ncdc_cmd_login(ncdc_mainwindow_t n, size_t ac, wchar_t **av)
}
}
if (!dc_api_authenticate(api, acc)) {
if (!dc_api_login(api, acc)) {
LOG(n, L"login: %ls: authentication failed; wrong password?", av[1]);
goto cleanup;
}
if (!dc_api_get_userinfo(api, acc, acc)) {
LOG(n, L"login: %ls: failed to get basic user information", av[1]);
gw = dc_api_establish_gateway(api, acc);
if (gw == NULL) {
LOG(n, L"login: %ls: failed to establish gateway", av[1]);
goto cleanup;
}
if (!dc_api_get_friends(api, acc)) {
LOG(n, L"login: %ls: failed to load friends", av[1]);
goto cleanup;
}
dc_loop_add_gateway(loop, gw);
dc_unref(current_account);
current_account = dc_ref(acc);
LOG(n, L"login: %ls: authentication successful", av[1]);
ret = true;