diff --git a/libdc/include/dc/account.h b/libdc/include/dc/account.h index 1428a9e..dab9024 100644 --- a/libdc/include/dc/account.h +++ b/libdc/include/dc/account.h @@ -46,7 +46,9 @@ dc_account_t dc_account_new2(char const *email, char const *pass); dc_account_t dc_account_from_fullname(char const *fullid); dc_account_t dc_account_from_json(json_t *j); +dc_account_t dc_account_from_relationship(json_t *j); json_t *dc_account_to_json(dc_account_t a); +bool dc_account_load(dc_account_t a, json_t *j); void dc_account_set_email(dc_account_t a, char const *email); char const *dc_account_email(dc_account_t a); @@ -76,6 +78,7 @@ bool dc_account_equal(dc_account_t a, dc_account_t b); /* relationships */ void dc_account_set_friends(dc_account_t a, dc_account_t *ptr, size_t len); +void dc_account_add_friend(dc_account_t a, dc_account_t friend); dc_account_t dc_account_nthfriend(dc_account_t a, size_t i); size_t dc_account_friends_size(dc_account_t a); dc_account_t dc_account_findfriend(dc_account_t a, char const *fullname); diff --git a/libdc/include/dc/channel.h b/libdc/include/dc/channel.h index ce0b1f5..31a229a 100644 --- a/libdc/include/dc/channel.h +++ b/libdc/include/dc/channel.h @@ -56,6 +56,7 @@ dc_channel_type_t dc_channel_type(dc_channel_t c); void dc_channel_set_type(dc_channel_t c, dc_channel_type_t t); size_t dc_channel_recipients(dc_channel_t c); +void dc_channel_addrecipient(dc_channel_t c, dc_account_t a); dc_account_t dc_channel_nthrecipient(dc_channel_t c, size_t i); size_t dc_channel_messages(dc_channel_t c); diff --git a/libdc/include/dc/event.h b/libdc/include/dc/event.h index 4a6e49c..1a5bdd6 100644 --- a/libdc/include/dc/event.h +++ b/libdc/include/dc/event.h @@ -7,6 +7,13 @@ struct dc_event_; typedef struct dc_event_ *dc_event_t; +typedef enum { + DC_EVENT_TYPE_UNKNOWN = 0, + DC_EVENT_TYPE_READY, + + DC_EVENT_TYPE_LAST, +} dc_event_type_t; + dc_event_t dc_event_new(char const *type, json_t *payload); /** @@ -22,4 +29,9 @@ char const *dc_event_type(dc_event_t e); */ json_t *dc_event_payload(dc_event_t e); +/** + * Returns an integer code representing the given type string. + */ +dc_event_type_t dc_event_type_code(dc_event_t e); + #endif diff --git a/libdc/include/dc/session.h b/libdc/include/dc/session.h index ec41417..fd914b3 100644 --- a/libdc/include/dc/session.h +++ b/libdc/include/dc/session.h @@ -52,6 +52,19 @@ bool dc_session_has_token(dc_session_t s); */ dc_account_t dc_session_me(dc_session_t s); +/** + * access to the internal account cache + */ +void dc_session_add_account(dc_session_t s, dc_account_t u); + +/** + * access to the internal channel cache + */ +void dc_session_add_channel(dc_session_t s, dc_channel_t u); + +/** + * comparision functions for sorting, and finding + */ 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); diff --git a/libdc/src/account.c b/libdc/src/account.c index b1205a1..93da004 100644 --- a/libdc/src/account.c +++ b/libdc/src/account.c @@ -116,24 +116,43 @@ dc_account_t dc_account_from_fullname(char const *fullid) return acc; } -dc_account_t dc_account_from_json(json_t *j) +bool dc_account_load(dc_account_t user, json_t *j) +{ + json_t *val = NULL; + + return_if_true(!json_is_object(j), false); + + val = json_object_get(j, "id"); + return_if_true(val == NULL || !json_is_string(val), false); + dc_account_set_id(user, json_string_value(val)); + + val = json_object_get(j, "username"); + return_if_true(val == NULL || !json_is_string(val), false); + dc_account_set_username(user, json_string_value(val)); + + val = json_object_get(j, "discriminator"); + return_if_true(val == NULL || !json_is_string(val), false); + dc_account_set_discriminator(user, json_string_value(val)); + + return true; +} + +dc_account_t dc_account_from_relationship(json_t *j) { dc_account_t user = dc_account_new(); json_t *val = NULL; - goto_if_true(!json_is_object(j), error); + val = json_object_get(j, "user"); + goto_if_true(val == NULL || !json_is_object(val), error); - val = json_object_get(j, "username"); - goto_if_true(val == NULL || !json_is_string(val), error); - dc_account_set_username(user, json_string_value(val)); + if (!dc_account_load(user, val)) { + goto error; + } - val = json_object_get(j, "discriminator"); - goto_if_true(val == NULL || !json_is_string(val), error); - dc_account_set_discriminator(user, json_string_value(val)); - - val = json_object_get(j, "id"); - goto_if_true(val == NULL || !json_is_string(val), error); - dc_account_set_id(user, json_string_value(val)); + val = json_object_get(j, "type"); + if (val != NULL && json_is_integer(val)) { + dc_account_set_friend_state(user, json_integer_value(val)); + } return user; @@ -143,6 +162,18 @@ error: return NULL; } +dc_account_t dc_account_from_json(json_t *j) +{ + dc_account_t user = dc_account_new(); + + if (!dc_account_load(user, j)) { + dc_unref(user); + return NULL; + } + + return user; +} + json_t *dc_account_to_json(dc_account_t a) { json_t *j = NULL; @@ -315,6 +346,12 @@ dc_account_t dc_account_findfriend(dc_account_t a, char const *fullname) return NULL; } +void dc_account_add_friend(dc_account_t a, dc_account_t friend) +{ + return_if_true(a == NULL || friend == NULL,); + g_ptr_array_add(a->friends, dc_ref(friend)); +} + dc_account_t dc_account_nthfriend(dc_account_t a, size_t i) { return_if_true(a == NULL || a->friends == NULL, NULL); diff --git a/libdc/src/channel.c b/libdc/src/channel.c index 8f370d0..3a72801 100644 --- a/libdc/src/channel.c +++ b/libdc/src/channel.c @@ -256,6 +256,12 @@ dc_account_t dc_channel_nthrecipient(dc_channel_t c, size_t i) return g_ptr_array_index(c->recipients, i); } +void dc_channel_addrecipient(dc_channel_t c, dc_account_t a) +{ + return_if_true(c == NULL || a == NULL,); + g_ptr_array_add(c->recipients, dc_ref(a)); +} + size_t dc_channel_messages(dc_channel_t c) { return_if_true(c == NULL || c->messages == NULL, 0); diff --git a/libdc/src/event.c b/libdc/src/event.c index f9ed42c..7328b0e 100644 --- a/libdc/src/event.c +++ b/libdc/src/event.c @@ -57,3 +57,22 @@ json_t *dc_event_payload(dc_event_t e) return_if_true(e == NULL, NULL); return e->payload; } + +dc_event_type_t dc_event_type_code(dc_event_t e) +{ + static char const *types[] = { + "UNKNOWN", + "READY", + NULL + }; + + int i = 0; + + for (i = 0; types[i] != NULL; i++) { + if (strcmp(types[i], e->type) == 0) { + return (dc_event_type_t)i; + } + } + + return DC_EVENT_TYPE_UNKNOWN; +} diff --git a/libdc/src/gateway.c b/libdc/src/gateway.c index 64a7b79..ec2b6cf 100644 --- a/libdc/src/gateway.c +++ b/libdc/src/gateway.c @@ -309,10 +309,6 @@ static void dc_gateway_process_read(dc_gateway_t gw) do { ret = curl_easy_recv(gw->easy, buf, sizeof(buf), &outlen); if (ret == CURLE_OK && outlen > 0) { - FILE *f = fopen("output.txt", "a+"); - fwrite(buf, outlen, sizeof(char), f); - fputc('\n', f); - fclose(f); g_byte_array_append(gw->buffer, (uint8_t const*)buf, outlen); } } while (ret == CURLE_OK && outlen > 0); diff --git a/libdc/src/session.c b/libdc/src/session.c index 1ad012b..8373d8e 100644 --- a/libdc/src/session.c +++ b/libdc/src/session.c @@ -9,12 +9,35 @@ struct dc_session_ dc_api_t api; dc_account_t login; dc_gateway_t gateway; + + GHashTable *accounts; + GHashTable *channels; +}; + +/* event handlers + */ +typedef void (*dc_session_handler_t)(dc_session_t s, dc_event_t e); +static void dc_session_handle_ready(dc_session_t s, dc_event_t e); + +static dc_session_handler_t handlers[DC_EVENT_TYPE_LAST] = { + [DC_EVENT_TYPE_UNKNOWN] = NULL, + [DC_EVENT_TYPE_READY] = dc_session_handle_ready, }; static void dc_session_free(dc_session_t s) { return_if_true(s == NULL,); + if (s->accounts != NULL) { + g_hash_table_unref(s->accounts); + s->accounts = NULL; + } + + if (s->channels != NULL) { + g_hash_table_unref(s->channels); + s->channels = NULL; + } + dc_session_logout(s); dc_unref(s->api); @@ -23,14 +46,68 @@ static void dc_session_free(dc_session_t s) free(s); } +static void dc_session_handle_ready(dc_session_t s, dc_event_t e) +{ + json_t *r = dc_event_payload(e); + json_t *user = NULL; + json_t *relationships = NULL; + size_t idx = 0; + json_t *c = NULL; + json_t *channels = NULL; + + /* retrieve user information about ourselves, including snowflake, + * discriminator, and other things + */ + user = json_object_get(r, "user"); + if (user != NULL && json_is_object(user)) { + dc_account_load(s->login, user); + dc_session_add_account(s, s->login); + } + + /* load relationships, aka friends + */ + relationships = json_object_get(r, "relationships"); + if (relationships != NULL && json_is_array(relationships)) { + json_array_foreach(relationships, idx, c) { + dc_account_t u = dc_account_from_relationship(user); + + if (u == NULL) { + continue; + } + + dc_account_add_friend(s->login, u); + dc_session_add_account(s, u); + } + } + + /* load channels + */ + channels = json_object_get(r, "private_channels"); + if (channels != NULL && json_is_array(channels)) { + json_array_foreach(channels, idx, c) { + dc_channel_t chan = dc_channel_from_json(c); + if (chan == NULL) { + continue; + } + + dc_session_add_channel(s, chan); + } + } +} + static void dc_session_handler(dc_gateway_t gw, dc_event_t e, void *p) { dc_session_t s = (dc_session_t)p; + dc_session_handler_t h = handlers[dc_event_type_code(e)]; + + if (h != NULL) { + h(s, e); + } char *str = NULL; str = json_dumps(dc_event_payload(e), 0); FILE *f = fopen("events.txt", "a+"); - fprintf(f, "%p: %s: %s\n", s, dc_event_type(e), str); + fprintf(f, "%s: %s\n", dc_event_type(e), str); free(str); fclose(f); } @@ -44,17 +121,29 @@ dc_session_t dc_session_new(dc_loop_t loop) s->ref.cleanup = (dc_cleanup_t)dc_session_free; + s->accounts = g_hash_table_new_full(g_str_hash, g_str_equal, + free, dc_unref + ); + goto_if_true(s->accounts == NULL, error); + + s->channels = g_hash_table_new_full(g_str_hash, g_str_equal, + free, dc_unref + ); + goto_if_true(s->channels == NULL, error); + s->loop = dc_ref(loop); s->api = dc_api_new(); - if (s->api == NULL) { - dc_session_free(s); - return NULL; - } + goto_if_true(s->api == NULL, error); dc_loop_add_api(s->loop, s->api); return dc_ref(s); + +error: + + dc_session_free(s); + return NULL; } bool dc_session_logout(dc_session_t s) @@ -133,3 +222,29 @@ 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); } + +void dc_session_add_account(dc_session_t s, dc_account_t u) +{ + return_if_true(s == NULL || u == NULL,); + return_if_true(dc_account_id(u) == NULL,); + + char const *id = dc_account_id(u); + + if (!g_hash_table_contains(s->accounts, id)) { + g_hash_table_insert(s->accounts, strdup(id), dc_ref(u)); + } +} + +void dc_session_add_channel(dc_session_t s, dc_channel_t u) +{ + return_if_true(s == NULL || u == NULL,); + return_if_true(dc_channel_id(u) == NULL,); + + char const *id = dc_channel_id(u); + + if (!g_hash_table_contains(s->channels, id)) { + g_hash_table_insert(s->channels, strdup(id), dc_ref(u)); + /* TODO: dedup for saving storage + */ + } +} diff --git a/libdc/src/ws-frames.c b/libdc/src/ws-frames.c index 091205e..2e36902 100644 --- a/libdc/src/ws-frames.c +++ b/libdc/src/ws-frames.c @@ -102,7 +102,7 @@ dc_gateway_parseframe(uint8_t const *data, size_t datalen, goto_if_true(datalen <= idx, cleanup); memcpy(&len, data+idx, sizeof(len)); - data_len = len; + data_len = GUINT16_TO_BE(len); idx += sizeof(len); } else if (l == 127) { /* read an uint16_t from the data @@ -113,7 +113,7 @@ dc_gateway_parseframe(uint8_t const *data, size_t datalen, goto_if_true(datalen <= idx, cleanup); memcpy(&len, data+idx, sizeof(len)); - data_len = len; + data_len = GUINT64_TO_BE(len); idx += sizeof(len); }