From 47da9bbedc7b30202fda404880a4d8762fcaf8ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anton=20Luka=20=C5=A0ijanec?= Date: Tue, 21 Sep 2021 23:05:01 +0200 Subject: tested json parsers until dm --- Makefile | 2 +- README.md | 2 +- debian/control | 2 + src/api.c | 179 +++++++++++++++++++++++++++--------------------------- src/h.c | 116 +++++++++++++++++++---------------- src/identify.json | 2 +- src/lib.c | 1 + src/main.c | 1 - 8 files changed, 160 insertions(+), 145 deletions(-) diff --git a/Makefile b/Makefile index 167d75a..9eae4b4 100644 --- a/Makefile +++ b/Makefile @@ -28,7 +28,7 @@ clean: rm discord.c tmp -f prepare: - sudo apt install build-essential libwebsockets-dev libcjson-dev libsoundio-dev libgtk-3-dev xxd -y + apt install build-essential libwebsockets-dev libcjson-dev libsoundio-dev libgtk-3-dev xxd -y # developing is to be done on i386. for example the default suppression file is hardcoded for i386 here: # developing is to be done on bullseye. we download gtk.supp even though debian ships it, because that's how we "get rid of" more leaks. diff --git a/README.md b/README.md index b2d21a7..48ab1f6 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ make * ~~`libsoundio-dev` for crossplatform sound io~~ * ~~`libopus-dev` for sound encoding~~ * ~~`libsodium-dev` for encrypting UDP packets to server~~ -* `xxd` for embedding XML GtkBuilder UI definitions directly in the binary +* `xxd` for embedding files inside binary (via C unsigned char arrays without messing with object files) ## automatic building diff --git a/debian/control b/debian/control index 7ecbc03..0360e22 100644 --- a/debian/control +++ b/debian/control @@ -9,6 +9,8 @@ Build-Depends: debhelper (>=11~), libwebsockets-dev, libgtk-3-dev, libsoundio-dev, + libopus-dev, + libsodium-dev, gcc, make Standards-Version: 4.1.4 diff --git a/src/api.c b/src/api.c index 20d7b4f..c8b44bc 100644 --- a/src/api.c +++ b/src/api.c @@ -25,11 +25,12 @@ void dc_api_stack (struct dc_api_io i) { /* stack output struct to be delivered *(i.program->api_ios[i.program->api_ios_length++]) = i; return; } -signed char dc_json_cb (struct lejp_ctx * ctx, char /* enum lejp_callbacks */ reason) { +signed char dc_json_cb (struct lejp_ctx * ctx, char reason) { /* to prevent warnings of incompatible pointer times, we create a new type with enum later on */ enum dc_json_paths path = ctx->path_match-1; /* we assume that the order of incoming data is */ struct dc_lws_pass * pass = ctx->user; /* correct. op and t should come first, etc. */ struct dc_client * client = pass->api_io.client; struct dc_program * program = pass->api_io.program; + struct dc_user * user; /* don't confuse this with client->user, this is just a var for you use */ pass->json_reason = reason; if (reason == LEJPCB_FAILED || reason == LEJPCB_COMPLETE || reason == LEJPCB_START) { if (pass->parsing_status) @@ -39,7 +40,8 @@ signed char dc_json_cb (struct lejp_ctx * ctx, char /* enum lejp_callbacks */ re pass->parsing_status = NULL; return '\0'; } - fprintf(stderr, "JSON: %s %s\n", ctx->path, ctx->buf); + if (getenv("DC_J")) /* print json to standard error */ + fprintf(stderr, "JSON: %s %s\n", ctx->path, ctx->buf); if (ctx->path_match && reason & LEJP_FLAG_CB_IS_VALUE) { switch (path) { case DC_JSON_S: /* packet sequence number */ @@ -69,10 +71,10 @@ signed char dc_json_cb (struct lejp_ctx * ctx, char /* enum lejp_callbacks */ re default: /* to prevent warning: enumeration value DC_JSON_* not handled */ break; } - return '\0'; } - if (startswith(ctx->path, dc_json_paths[DC_JSON_ME]) && reason & LEJP_FLAG_CB_IS_VALUE - && (!client->user || !client->user->username || !client->user->id || client->user->status & DC_INCOMPLETE)) { /* if filled, then it's someone else */ + if (reason & LEJP_FLAG_CB_IS_VALUE && (path == DC_JSON_ME_USERNAME || path == DC_JSON_ME_ID || path == DC_JSON_ME_DISCRIMINATOR) && (!client->user || !client->user->username || !client->user->id || client->user->status & DC_INCOMPLETE)) { /* if filled, then it's someone else */ + if (getenv("DC_R")) /* detect user parser */ + raise(SIGINT); if (!client->user) { /* on first d.user this is our user, subsequent are someone else! */ DC_MR(program->users); /* don't need DC_IN_PROGRESS, we have ref in cl */ program->users[program->users_length++] = client->user = dc_user_init(); @@ -89,6 +91,8 @@ signed char dc_json_cb (struct lejp_ctx * ctx, char /* enum lejp_callbacks */ re if (client->user->status & DC_INCOMPLETE) { /* we can't just check if */ client->user->discriminator = atoi(ctx->buf); /* !discriminat, */ client->user->status &= ~DC_INCOMPLETE; /* because 0 is allowd */ + if (getenv("DC_C")) + dc_interrupted++; } break; default: @@ -96,64 +100,32 @@ signed char dc_json_cb (struct lejp_ctx * ctx, char /* enum lejp_callbacks */ re } return '\0'; /* because we use same checks for parsing other users GUILD_MEMBER_UPDATE */ } - if (startswith(ctx->path, dc_json_paths[DC_JSON_FRIEND]) || startswith(ctx->path, dc_json_paths[DC_JSON_ME])) { - if ((path == DC_JSON_FRIEND || path == DC_JSON_ME) && reason == LEJPCB_OBJECT_START) { - /* dc_user_free(pass->api_io.user); */ /* parser branches MUST ALWAYS set to 0 */ - pass->api_io.user = dc_user_init(); /* and possibly free api_io members after! */ - pass->api_io.user->status |= DC_IN_PROGRESS; /* if we never get here again */ - } - if (path == DC_JSON_FRIEND && reason == LEJPCB_OBJECT_END) { /* if that's a friend */ - pass->api_io.user = dc_addr_user(program, DC_ISAE(program->users), pass->api_io.user, DC_MAY_FREE); - if (!dc_find_user(client->users, client->users_length, pass->api_io.user->id)) { - fprintf(stderr, "got a new friend (: %s#%d %llu\n", pass->api_io.user->username ? pass->api_io.user->username : "NULL", pass->api_io.user->discriminator, pass->api_io.user->id); - pass->api_io.user = dc_add_user(DC_ISAE(client->users), pass->api_io.user, DC_UNSET); /* already reported, already inserted in program, nof */ - pass->api_io.user->status &= ~(DC_IN_PROGRESS); - } - pass->api_io.user = NULL; /* we're done, NULL it or PROBLEMS WILL APPEAR!!! */ - } - if (path == DC_JSON_ME && reason == LEJPCB_OBJECT_END) { /* if it was just a user */ - pass->api_io.user = dc_addr_user(program, DC_ISAE(program->users), pass->api_io.user, DC_MAY_FREE); - pass->api_io.user = NULL; - } - if (reason & LEJP_FLAG_CB_IS_VALUE && pass->api_io.user) - switch(path) { - case DC_JSON_FRIEND_USERNAME: - if (!pass->api_io.user->username) /* yup, we don't trust serv */ - pass->api_io.user->username = strdup(ctx->buf); - break; - case DC_JSON_FRIEND_ID: - pass->api_io.user->id = strtoll(ctx->buf, NULL, 10); - break; - case DC_JSON_FRIEND_DISCRIMINATOR: - pass->api_io.user->discriminator = atoi(ctx->buf); - break; /* yeah, we don't care about nicknames */ - default: - break; - } + if ((path == DC_JSON_ME || path == DC_JSON_USER || path == DC_JSON_MESSAGE_AUTHOR || path == DC_JSON_MESSAGE_REFOBJ_AUTHOR || path == DC_JSON_MESSAGE_MENTION_USER || path == DC_JSON_MESSAGE_REFOBJ_MENTION_USER) && reason == LEJPCB_OBJECT_START) { /* user parsing start */ + /* dc_user_free(pass->api_io.user); */ /* parser branches MUST ALWAYS set to 0 */ + pass->api_io.user = dc_user_init(); /* and possibly free api_io members after! */ + pass->api_io.user->status |= DC_IN_PROGRESS; /* if we never get here again */ } - if (startswith(ctx->path, dc_json_paths[DC_JSON_DM])) { - if (!client->guilds_length) { - DC_MR(program->guilds); - DC_MR(client->guilds); - program->guilds[program->guilds_length++] = client->guilds[0] = dc_guild_init(); - client->guilds_length = 1; /* we don't dc_add_guild because id=0 */ - client->guilds[0]->name = strdup("Direct messages"); /* TODO: gettext for i18n */ - client->guilds[0]->client = client; - } - if (path == DC_JSON_DM && reason == LEJPCB_OBJECT_START) { - fprintf(stderr, "new DM start parsing object\n"); - /* dc_channel_free(pass->api_io.channel); */ /* parser branches must not leave */ - pass->api_io.channel = dc_channel_init(); /* behind any content in api_io */ - pass->api_io.channel->status |= DC_IN_PROGRESS; - pass->api_io.channel->guild = client->guilds[0]; - } - if (path == DC_JSON_DM && reason == LEJPCB_OBJECT_END) { - fprintf(stderr, "new DM (:\n"); - struct dc_channel ** channel = &client->guilds[0]->channel; /* 1. guild = DMs */ - while (*channel) - channel = &(*channel)->next; - pass->api_io.channel->status &= ~(DC_IN_PROGRESS); - free(pass->api_io.channel->name); + if ((path == DC_JSON_ME || path == DC_JSON_USER || path == DC_JSON_MESSAGE_AUTHOR || path == DC_JSON_MESSAGE_REFOBJ_AUTHOR || path == DC_JSON_MESSAGE_MENTION_USER || path == DC_JSON_MESSAGE_REFOBJ_MENTION_USER) && reason == LEJPCB_OBJECT_END) { /* if it was just a user */ + pass->api_io.user->status &= ~DC_IN_PROGRESS; + pass->api_io.user = dc_addr_user(program, DC_ISAE(program->users), pass->api_io.user, DC_MAY_FREE); + if (path != DC_JSON_MESSAGE_AUTHOR && path != DC_JSON_MESSAGE_REFOBJ_AUTHOR && path != DC_JSON_MESSAGE_MENTION_USER && path != DC_JSON_MESSAGE_REFOBJ_MENTION_USER && !getenv("DC_0") /* override if those are disabled */) /* those expect a user parsed */ + pass->api_io.user = NULL; /* when implementing parser: handle OBJECT_END */ + else /* if something goes wrong - if those handlers do not null user, IO_MEMB_GC will */ + pass->api_io.user->status |= DC_EXPLICIT_NULL; /* but will not free. */ + } /* user parsing end */ + if (path == DC_JSON_DM && reason == LEJPCB_OBJECT_START) { /* client->guild[0] always */ + fprintf(stderr, "new DM start parsing object\n"); /* exists */ + /* dc_channel_free(pass->api_io.channel); */ /* parser branches must not leave */ + pass->api_io.channel = dc_channel_init(); /* behind any content in api_io */ + pass->api_io.channel->status |= DC_IN_PROGRESS; + pass->api_io.channel->guild = client->guilds[0]; + } + if (path == DC_JSON_DM && reason == LEJPCB_OBJECT_END) { + struct dc_channel ** channel = &client->guilds[0]->channel; /* 1. guild = DMs */ + while (*channel) + channel = &(*channel)->next; + pass->api_io.channel->status &= ~(DC_IN_PROGRESS); + free(pass->api_io.channel->name); pass->api_io.channel->name = strdup(""); for (size_t i = 0; i < pass->api_io.channel->users_length; i++) { pass->api_io.channel->name = realloc(pass->api_io.channel->name, strlen(pass->api_io.channel->name)+strlen(pass->api_io.channel->users[i]->username)+1+2+1+4); @@ -162,27 +134,19 @@ signed char dc_json_cb (struct lejp_ctx * ctx, char /* enum lejp_callbacks */ re strcat(pass->api_io.channel->name, pass->api_io.channel->users[i]->username); strcat(pass->api_io.channel->name, buf); } - pass->api_io.channel = *channel = dc_addr_channel(program, DC_ISAN, pass->api_io.channel, DC_MAY_FREE | DC_REPLACE); - if ((*channel = dc_find_ll_channel(client->guilds[0]->channel, pass->api_io.channel->id))) - if (*channel != pass->api_io.channel) { - dc_channel_free(pass->api_io.channel, DC_REPLACE); - memmove(*channel, pass->api_io.channel, sizeof(**channel)); - } - pass->api_io.channel->guild = client->guilds[0]; - pass->api_io.channel = NULL; /* we're done, NULL it or PROBLEMS WILL APPEAR!!! */ - } - if (reason & LEJP_FLAG_CB_IS_VALUE && pass->api_io.channel) - switch (path) { - case DC_JSON_DM_TYPE: - pass->api_io.channel->type = atoi(ctx->buf); - break; - case DC_JSON_DM_ID: - pass->api_io.channel->id = strtoull(ctx->buf, NULL, 10); - break; - default: - break; + pass->api_io.channel = dc_addr_channel(program, DC_ISAN, pass->api_io.channel, DC_MAY_FREE | DC_REPLACE); + fprintf(stderr, "new DM id=%llu (:\n", pass->api_io.channel->id); + if ((*channel = dc_find_ll_channel(client->guilds[0]->channel, pass->api_io.channel->id))) { + fprintf(stderr, "DM already in LL, id=%llu\n", pass->api_io.channel->id); + if (*channel != pass->api_io.channel) { + dc_channel_free(*channel, DC_REPLACE); + memmove(*channel, pass->api_io.channel, sizeof(**channel)); } + } + pass->api_io.channel->guild = client->guilds[0]; + pass->api_io.channel = NULL; /* we're done, NULL it or PROBLEMS WILL APPEAR!!! */ } +#if 0 if (startswith(ctx->path, dc_json_paths[DC_JSON_DM_USER])) { /* we don't DC_REPLACE here bcoz */ if (path == DC_JSON_DM_USER && reason == LEJPCB_OBJECT_START) { /* users never update. */ pass->api_io.user = dc_user_init(); @@ -213,14 +177,53 @@ signed char dc_json_cb (struct lejp_ctx * ctx, char /* enum lejp_callbacks */ re } if (startswith(ctx->path, dc_json_paths[DC_JSON_GUILD])) { if (path == DC_JSON_GUILD && reason == LEJPCB_OBJECT_START) { - pass->api_io.guild = dc_guild_init(); - pass->api_io.guild->status |= DC_IN_PROGRESS; + /* pass->api_io.guild = dc_guild_init(); + pass->api_io.guild->status |= DC_IN_PROGRESS; */ } if (path == DC_JSON_GUILD && reason == LEJPCB_OBJECT_END) { fprintf(stderr, "new guild"); - pass->api_io.guild = dc_addr_guild(program, DC_ISAE(program->guilds), pass->api_io.guild, DC_MAY_FREE); + /* pass->api_io.guild = dc_addr_guild(program, DC_ISAE(program->guilds), pass->api_io.guild, DC_MAY_FREE); */ } } +#endif + if (reason & LEJP_FLAG_CB_IS_VALUE) + switch (path) { + case DC_JSON_FRIEND: /* we assume we get users[] before relationships[] */ + user = dc_find_user(program->users, program->users_length, strtoull(ctx->buf, NULL, 10)); + if (user) + dc_add_user(DC_ISAE(client->users), user, DC_UNSET); + break; + case DC_JSON_DM_USER: /* we assume we get users[] before private_channels[] */ + user = dc_find_user(program->users, program->users_length, strtoull(ctx->buf, NULL, 10)); + if (user && pass->api_io.channel) + dc_add_user(DC_ISAE(pass->api_io.channel->users), user,DC_UNSET); + break; + case DC_JSON_ME_USERNAME: /* we are always checking for user because evil srv */ + case DC_JSON_USER_NAME: /* might insert dots in keys and prevent OBJECT_CREATE */ + if (pass->api_io.user && !pass->api_io.user->username) /* no trust srv */ + pass->api_io.user->username = strdup(ctx->buf); + break; + case DC_JSON_ME_ID: + case DC_JSON_USER_ID: + if (pass->api_io.user) + pass->api_io.user->id = strtoll(ctx->buf, NULL, 10); + break; + case DC_JSON_ME_DISCRIMINATOR: + case DC_JSON_USER_DISCRIMINATOR: + if (pass->api_io.user) + pass->api_io.user->discriminator = atoi(ctx->buf); + break; /* yeah, we don't care about nicknames */ + case DC_JSON_DM_TYPE: + if (pass->api_io.channel) + pass->api_io.channel->type = atoi(ctx->buf); + break; + case DC_JSON_DM_ID: + if (pass->api_io.channel) + pass->api_io.channel->id = strtoull(ctx->buf, NULL, 10); + break; + default: + break; + } if (pass->packet == DC_NONE) /* useless to do anything BELOW if we haven't recvd packet type */ return '\0'; switch (pass->packet) { /* we fill in structs, set DC_INCOMPLETE and pass->parsing_status */ @@ -366,7 +369,7 @@ static int dc_lws_cb (struct lws * wsi, enum lws_callback_reasons rs, void * us, pass->api_io.status &= ~DC_FROM_LWS; break; case LWS_CALLBACK_CLIENT_RECEIVE: /* websocket receive, pass to json parser ? */ - if (getenv("DC_N")) + if (getenv("DC_N")) /* output received network to stdout */ fprintf(stdout, "%.*s", len, (const unsigned char *) in); if (pass->json_reason == LEJPCB_COMPLETE || pass->json_reason == LEJPCB_FAILED || !(pass->api_io.status & DC_LEJP_CONSTRUCTED)) { /* we regenerate a new context in case we didn't do that yet or in case the previous one finished parsing */ pass->api_io.status |= DC_LEJP_CONSTRUCTED; @@ -389,8 +392,8 @@ static int dc_lws_cb (struct lws * wsi, enum lws_callback_reasons rs, void * us, return -1; } dc_payload_free(pass->api_io.client->payloads[i], DC_UNSET); - pass->api_io.client->payloads[i] = NULL; - } + pass->api_io.client->payloads[i] = NULL; /* THIS MUST BE DONE, otherws */ + } /* _free function will double free, because it frees till _sizeof */ pass->api_io.client->payloads_length = 0; break; default: @@ -478,7 +481,7 @@ void dc_api_i (struct dc_api_io i) { /* this function does not call attached fun goto ws; /* we could call dc_api_i but that just fills stack */ break; case DC_API_WS: - if (getenv("DC_R")) + if (getenv("DC_R")) /* detect _WS calls */ raise(SIGINT); fprintf(stderr, "DC_API_WS called\n"); ws: diff --git a/src/h.c b/src/h.c index e090c96..1a0988b 100644 --- a/src/h.c +++ b/src/h.c @@ -44,6 +44,7 @@ enum dc_status { /* theese are flags and should be and-checked */ DC_NO_WRITE = 1 << 21, /* signaling dc_ws_stack not to call lws_callback_on_writeable */ DC_SET_WS_ACTIVE = 1 << 22, /* whether _CREATE _cb shall set client->status =| DC_WS_ACTIVE */ DC_REPLACE = 1 << 23, /* dc_add_x replace old with new on found, _free: only free members */ + DC_EXPLICIT_NULL = 1 << 24, /* MEMB_GC will NULL this member (PROGRES?aftr free) (& clear bit) */ DC_INTERNAL = DC_FROM_LWS | DC_FROM_API, /* call originates from an internal function */ }; /* note: when checking status, first check for DC_OK, if it's set then disregard errors! */ enum dc_permissions { /* other permissions exist, but are not implemented/understood */ @@ -131,7 +132,11 @@ struct dc_api_io { /* output struct does NOT contain void * data (user pointer) }; #define DC_API_IO_MEMB_GC(m, t) if (m && m->status & DC_IN_PROGRESS) { \ t##_free(m, DC_UNSET); \ - m = NULL; } + m = NULL; } \ + if (m && m->status & DC_EXPLICIT_NULL) { \ + m->status &= ~DC_EXPLICIT_NULL; \ + m = NULL; \ + } #define DC_API_IO_GC(s) do { /* this is called if: json_cb will start parsing another before callback, */ \ DC_API_IO_MEMB_GC(s.message, dc_message); /* WS connection closed, meaning json_cb */ \ DC_API_IO_MEMB_GC(s.channel, dc_channel); /* will never be called again */ \ @@ -174,17 +179,15 @@ enum dc_json_paths { /* lws reduces the following char array to uint8_t, so we c DC_JSON_ME_USERNAME, /* note: _ME is client->user ONLY for first d.user object. subsequent */ DC_JSON_ME_ID, /* packets may contain user objects as well, they are other users */ DC_JSON_ME_DISCRIMINATOR, - DC_JSON_FRIEND, - DC_JSON_FRIEND_ID, - DC_JSON_FRIEND_USERNAME, - DC_JSON_FRIEND_DISCRIMINATOR, + DC_JSON_FRIEND, /* this is the id */ + DC_JSON_USER, /* list of users at first connect */ + DC_JSON_USER_ID, + DC_JSON_USER_NAME, + DC_JSON_USER_DISCRIMINATOR, DC_JSON_DM, DC_JSON_DM_TYPE, DC_JSON_DM_ID, - DC_JSON_DM_USER, - DC_JSON_DM_USER_NAME, - DC_JSON_DM_USER_ID, - DC_JSON_DM_USER_DISCRIMINATOR, + DC_JSON_DM_USER, /* this is the ID */ DC_JSON_GUILD, DC_JSON_GUILD_NAME, DC_JSON_GUILD_ID, @@ -253,21 +256,19 @@ char * dc_json_paths[] = { /* array of paths we are interested in */ "s", "t", "d.heartbeat_interval", - "d.user", /* NOTE: presence updates have same format, so only set own user once */ + "d.user", "d.user.username", "d.user.id", "d.user.discriminator", - "d.relationships[].user", - "d.relationships[].user.id", - "d.relationships[].user.username", - "d.relationships[].user.discriminator", + "d.relationships[].id", + "d.users[]", + "d.users[].id", + "d.users[].username", + "d.users[].discriminator", "d.private_channels[]", "d.private_channels[].type", "d.private_channels[].id", - "d.private_channels[].recipients[]", - "d.private_channels[].recipients[].username", - "d.private_channels[].recipients[].id", - "d.private_channels[].recipients[].discriminator", + "d.private_channels[].recipients_ids[]", "d.guilds[]", "d.guilds[].name", "d.guilds[].id", @@ -357,7 +358,10 @@ struct dc_payload * dc_payload_init () { return s; } void dc_payload_free (struct dc_payload * s, enum dc_status t) { - free(s->body-LWS_PRE); + if (!s) + return; + if (s->body) + free(s->body-LWS_PRE); if (!(t & DC_REPLACE)) free(s); return; @@ -366,9 +370,9 @@ void dc_payload_free (struct dc_payload * s, enum dc_status t) { #define DC_ISAS_INIT(type/* w/o struct */, name) do { name##_sizeof = DC_ALLOC_CHUNK; /* structs ISA */ \ name = calloc(name##_sizeof, sizeof(struct type *)); } while (0) /* prep arr, NO INIT membrs */ #define DC_ISASIQ(shortname) DC_ISAS_INIT(dc_##shortname, s->shortname##s) /* ISAS init quick */ -#define DC_ISAF(shortname) for (size_t i = 0; i < s->shortname##s_sizeof; i++) /* ISA free */ \ - dc_##shortname##_free(s->shortname##s[i], DC_UNSET); \ - free(s->shortname##s); /* no problem if we free past _lenght, as uninited are NULL */ +#define DC_ISAF(shortname) for (size_t i = 0; i < s->shortname##s_sizeof; i++) /* hmm, I used to fre */ \ + dc_##shortname##_free(s->shortname##s[i], DC_UNSET); /* till _sizeof, but now I fixd */ \ + free(s->shortname##s); /* to only free till _length. is this problematic in any ways? */ struct dc_client { DC_STRUCT_PREFIX char * authorization; /* yesfree - authorization header value */ @@ -385,11 +389,42 @@ struct dc_client { DC_ISASQ(guild); /* yesfree array of pointers only - guilds of this user */ DC_ISASQ(user); /* yesfree array of pointers only - friends of this user */ }; +struct dc_guild { + DC_STRUCT_PREFIX + char * name; /* yesfree */ + unsigned long long int id; /* 0 for virtual DMs guild */ + struct dc_client * client; /* nofree */ + struct dc_channel * channel; /* nofree - first channel */ + struct dc_role * role; /* nofree - first role. NOTE: role->id == guild->id => @everyone role */ + enum dc_permissions permissions; + enum dc_status status; +#ifdef DC_UI_GTK + GtkTreeIter iter; /* NOTE: only works when GtkTreeModel has a flag GTK_TREE_MODEL_ITERS_PERSIST; see paragraph 8 of description of file:///usr/share/doc/libgtk-3-doc/gtk3/GtkTreeModel.html */ + gboolean is_iter; + /* GtkTreeRowReference * row; */ /* yesfree - indicating the row */ /* not used: IRC message: "00:51:29 @Company | also: Don't create too many tree row references, those things are slow" */ +#endif +}; +struct dc_guild * dc_guild_init () { + struct dc_guild * s = calloc(1, sizeof(*s)); + return s; +} +void dc_guild_free (struct dc_guild * s, enum dc_status t) { + if (!s) + return; + free(s->name); + if (!(t & DC_REPLACE)) /* we do this because we want to keep the pointer intact sometimes and */ + free(s); /* reused; for example when replacing/updating structs */ +} struct dc_client * dc_client_init () { struct dc_client * s = calloc(1, sizeof(*s)); DC_ISASIQ(payload); DC_ISASIQ(guild); DC_ISASIQ(user); + DC_MR(s->guilds); /* direct messages virtual guild is not in program, to prevent confusion */ + s->guilds_length = 1; /* because of duplicated 0 id value; freeing is therefore done from */ + s->guilds[0] = dc_guild_init(); /* dc_client_free */ + s->guilds[0]->name = strdup("Direct messages"); /* TODO: use gettext or similar for t9ns */ + s->guilds[0]->client = s; return s; } void dc_client_free (struct dc_client * s, enum dc_status t) { @@ -399,6 +434,7 @@ void dc_client_free (struct dc_client * s, enum dc_status t) { free(s->email); free(s->password); DC_ISAF(payload); + dc_guild_free(s->guilds[0], DC_UNSET); /* see dc_client_init for explanation */ free(s->guilds); free(s->users); if (!(t & DC_REPLACE)) @@ -415,32 +451,6 @@ void dc_ws_stack(struct dc_client * c, struct dc_payload * p, enum dc_status s) lws_callback_on_writable(c->pass->wsi); return; } -struct dc_guild { - DC_STRUCT_PREFIX - char * name; /* yesfree */ - unsigned long long int id; /* 0 for virtual DMs guild */ - struct dc_client * client; /* nofree */ - struct dc_channel * channel; /* nofree - first channel */ - struct dc_role * role; /* nofree - first role. NOTE: role->id == guild->id => @everyone role */ - enum dc_permissions permissions; - enum dc_status status; -#ifdef DC_UI_GTK - GtkTreeIter iter; /* NOTE: only works when GtkTreeModel has a flag GTK_TREE_MODEL_ITERS_PERSIST; see paragraph 8 of description of file:///usr/share/doc/libgtk-3-doc/gtk3/GtkTreeModel.html */ - gboolean is_iter; - /* GtkTreeRowReference * row; */ /* yesfree - indicating the row */ /* not used: IRC message: "00:51:29 @Company | also: Don't create too many tree row references, those things are slow" */ -#endif -}; -struct dc_guild * dc_guild_init () { - struct dc_guild * s = calloc(1, sizeof(*s)); - return s; -} -void dc_guild_free (struct dc_guild * s, enum dc_status t) { - if (!s) - return; - free(s->name); - if (!(t & DC_REPLACE)) /* we do this because we want to keep the pointer intact sometimes and */ - free(s); /* reused; for example when replacing/updating structs */ -} struct dc_channel { DC_STRUCT_PREFIX char * name; /* yesfree - name */ @@ -677,8 +687,9 @@ struct dc_program * dc_program_init () { } void dc_program_free (struct dc_program * s, enum dc_status t) { if (!s) - return; - DC_ISAF(client); + return; /* \/ call first because _cb is called several times during destroy ,,, */ + lws_context_destroy(s->lws_context); /* closes all connections and destroys all wsis */ + DC_ISAF(client); /* /\ ,,, and callback handlers (_CLOSED, _DESTROY) may do stuff with memory. */ DC_ISAF(guild); DC_ISAF(channel); DC_ISAF(message); @@ -688,7 +699,6 @@ void dc_program_free (struct dc_program * s, enum dc_status t) { DC_ISAF(permission); DC_ISAF(attached_function); DC_ISAF(api_io); - lws_context_destroy(s->lws_context); /* closes all connections and destroys all wsis */ if (!(t & DC_REPLACE)) free(s); } @@ -715,6 +725,7 @@ void dc_api_stack (struct dc_api_io); #define DC_ADD_X(x) struct dc_##x * dc_add_##x (struct dc_##x *** p, size_t * so, size_t * l, struct dc_##x * u, enum dc_status s) { \ struct dc_##x * us; \ if ((us = dc_find_##x(*p, *l, u->id))) { \ + fprintf(stderr, "debug: %s found already existing member\n", __func__); \ if (us == u) \ return us; \ if (s & DC_REPLACE) { \ @@ -726,15 +737,14 @@ void dc_api_stack (struct dc_api_io); } \ if (s & DC_MAY_FREE) { \ dc_##x##_free(u, DC_UNSET); \ - fprintf(stderr, "debug: %s freed already existing member\n", __func__); \ } \ return us; \ } \ if (*so <= *l) { \ *so = ceil(*so*DC_REALLOC_K); \ *p = realloc(*p, sizeof(**p) * *so); \ - fprintf(stderr, "debug: %s resized ISA\n", __func__); \ } \ + fprintf(stderr, "debug: %s inserted into ISA\n", __func__); \ (*p)[(*l)++] = u; \ return u; \ } diff --git a/src/identify.json b/src/identify.json index 571abf1..bf20503 100644 --- a/src/identify.json +++ b/src/identify.json @@ -1 +1 @@ -{"op":2,"d":{"token":"%s","capabilities":125,"properties":{"os":"Linux","browser":"Chrome","device":"","system_locale":"en-US","browser_user_agent":"Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36","browser_version":"90.0.4430.212","os_version":"","referrer":"http://of.sijanec.eu:7327/","referring_domain":"of.sijanec.eu:7327","referrer_current":"","referring_domain_current":"","release_channel":"stable","client_build_number":97662,"client_event_source":null},"presence":{"status":"online","since":0,"activities":[{"name":"Custom Status","type":4,"state":"mrt5hg","emoji":null}],"afk":false},"compress":false,"client_state":{"guild_hashes":{},"highest_last_message_id":"0","read_state_version":0,"user_guild_settings_version":-1}}} +{"op":2,"d":{"token":"%s","capabilities":125,"intents":32767,"properties":{"os":"Linux","browser":"Chrome","device":"","system_locale":"en-US","browser_user_agent":"Mozilla/5.0 (X11; Linux i686) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36","browser_version":"90.0.4430.212","os_version":"","referrer":"http://of.sijanec.eu:7327/","referring_domain":"of.sijanec.eu:7327","referrer_current":"","referring_domain_current":"","release_channel":"stable","client_build_number":97662,"client_event_source":null},"presence":{"status":"online","since":0,"activities":[{"name":"Custom Status","type":4,"state":"mrt5hg","emoji":null}],"afk":false},"compress":false,"client_state":{"guild_hashes":{},"highest_last_message_id":"0","read_state_version":0,"user_guild_settings_version":-1}}} diff --git a/src/lib.c b/src/lib.c index 989436c..48f185c 100644 --- a/src/lib.c +++ b/src/lib.c @@ -1,3 +1,4 @@ +int dc_interrupted = 0; #ifndef _GNU_SOURCE /* glibc already provides asprintf, but some cucks tend not to use glibc */ int asprintf (char ** str, const char * format, ...) { /* allocates automaticalls (: */ va_list ap, aq; /* */ diff --git a/src/main.c b/src/main.c index 7288332..b202cdc 100644 --- a/src/main.c +++ b/src/main.c @@ -7,7 +7,6 @@ #include #include #include -int dc_interrupted; void dc_signal (int i) { dc_interrupted++; return; -- cgit v1.2.3