summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnton Luka Šijanec <anton@sijanec.eu>2022-02-14 23:05:01 +0100
committerAnton Luka Šijanec <anton@sijanec.eu>2022-02-14 23:05:01 +0100
commitb99dcd6c3b1494f44f67229b5435e629fc566b1a (patch)
tree203d4442a82bd4427a101c986f01043748f20967
parenta commit just in case. read description. (diff)
downloaddiscord.c-b99dcd6c3b1494f44f67229b5435e629fc566b1a.tar
discord.c-b99dcd6c3b1494f44f67229b5435e629fc566b1a.tar.gz
discord.c-b99dcd6c3b1494f44f67229b5435e629fc566b1a.tar.bz2
discord.c-b99dcd6c3b1494f44f67229b5435e629fc566b1a.tar.lz
discord.c-b99dcd6c3b1494f44f67229b5435e629fc566b1a.tar.xz
discord.c-b99dcd6c3b1494f44f67229b5435e629fc566b1a.tar.zst
discord.c-b99dcd6c3b1494f44f67229b5435e629fc566b1a.zip
-rw-r--r--.gitignore1
-rw-r--r--src/api.c564
-rw-r--r--src/json.c80
-rw-r--r--test/json.c15
4 files changed, 96 insertions, 564 deletions
diff --git a/.gitignore b/.gitignore
index 5ab9f09..7ef384a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
# the following is the binary
discord.c
+a.out
# debugging log files
valgrind-out.txt
.gdb_history
diff --git a/src/api.c b/src/api.c
index cc91eff..02878bf 100644
--- a/src/api.c
+++ b/src/api.c
@@ -44,562 +44,6 @@ unsigned long long int dc_calculate_permissions (struct dc_user * u, struct dc_c
}
return p;
}
-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;
- pass->json_reason = reason;
- if (reason == LEJPCB_FAILED || reason == LEJPCB_START || (reason == LEJPCB_COMPLETE && pass->packet != DC_MESSAGE_CREATE /* hack for lejp bug with wrong path on OBJECT_END */)) {
- DC_API_IO_GC(pass->api_io);
- pass->packet = DC_NONE;
- return '\0';
- }
- 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 */
- pass->api_io.client->last_packet = strtoull(ctx->buf, NULL, 10);
- break;
- case DC_JSON_OP:
- switch (atoi(ctx->buf)) {
- case DC_PING:
- client->last_ping = 0;
- dc_handle_ping(pass->api_io, NULL);
- break;
- default:
- break;
- }
- break;
- case DC_JSON_PING:
- pass->api_io.client->ping_interval = atoi(ctx->buf)/1000-1;
- pass->api_io.client->last_ping = time(NULL); /* we don't ping b4 IDENT */
- break;
- case DC_JSON_T:
- for (size_t i = 0; i < sizeof(dc_ws_packet)/sizeof(dc_ws_packet[0]); i++)
- if (!strcmp(dc_ws_packet[i], ctx->buf)) {
- pass->packet = DC_STRPKTOFF+i;
- break;
- }
- break;
- default: /* to prevent warning: enumeration value DC_JSON_* not handled */
- break;
- }
- }
- 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 (!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();
- client->user->status |= DC_INCOMPLETE; /* when ->disc is set, it's complete */
- client->user->program = program;
- } /* we do not check on the object end */
- switch (path) { /* email is already set from login */
- case DC_JSON_ME_USERNAME:
- if (!client->user->username) client->user->username = strdup(ctx->buf);
- break;
- case DC_JSON_ME_ID:
- if (!client->user->id) client->user->id = strtoull(ctx->buf, NULL, 10);
- break;
- case DC_JSON_ME_DISCRIMINATOR:
- 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 */
- }
- break;
- default:
- break;
- }
- return '\0'; /* because we use same checks for parsing other users GUILD_MEMBER_UPDATE */
- }
- 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 || path == DC_JSON_MEMBER) && 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->program = program;
- pass->api_io.user->status |= DC_IN_PROGRESS; /* if we never get here again */
- }
- 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 || path == DC_JSON_MEMBER) && reason == LEJPCB_OBJECT_END) {
- if (getenv("DC_R"))
- assert(pass->api_io.user->id);
- if (!pass->api_io.user->id) {
- dc_user_free(pass->api_io.user, DC_UNSET);
- pass->api_io.user = NULL;
- return '\0';
- }
- 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);
- switch (path) {
- case DC_JSON_MESSAGE_AUTHOR:
- if (pass->api_io.message)
- pass->api_io.message->user = pass->api_io.user;
- break;
- case DC_JSON_MESSAGE_REFOBJ_AUTHOR:
- if (pass->api_io.message && pass->api_io.message->reply)
- pass->api_io.message->reply->user = pass->api_io.user;
- break;
- case DC_JSON_MESSAGE_MENTION_USER:
- if (pass->api_io.message)
- dc_add_user(DC_ISAE(pass->api_io.message->users), pass->api_io.user, DC_UNSET);
- break;
- case DC_JSON_MESSAGE_REFOBJ_MENTION_USER:
- if (pass->api_io.message && pass->api_io.message->reply)
- dc_add_user(DC_ISAE(pass->api_io.message->reply->users), pass->api_io.user, DC_UNSET);
- break;
- default:
- break;
- }
- pass->api_io.user = NULL;
- }
- if ((path == DC_JSON_DM || path == DC_JSON_GUILD_CHANNEL || path == DC_JSON_MESSAGE_MENTION_CHANNEL) && reason == LEJPCB_OBJECT_START) {
- pass->api_io.channel = dc_channel_init();
- pass->api_io.channel->status |= DC_IN_PROGRESS;
- pass->api_io.id = 0;
- }
- if (path == DC_JSON_DM && reason == LEJPCB_OBJECT_END) {
- struct dc_channel ** channel = &client->guilds[0]->channel; /* 1. guild = DMs */
- while (*channel) /* a ni to malo kremž? zakaj dodajam na konec, če bi lahko samo */
- channel = &(*channel)->next; /* dodal na začetek v O(1) */
- if (!pass->api_io.channel->id) {
- dc_channel_free(pass->api_io.channel, DC_UNSET);
- pass->api_io.channel = NULL;
- return '\0';
- }
- pass->api_io.channel->next = NULL;
- pass->api_io.channel->guild = client->guilds[0]; /* client->guilds[0] always exists */
- pass->api_io.channel->status &= ~DC_IN_PROGRESS;
- free(pass->api_io.channel->topic);
- pass->api_io.channel->topic = strdup("");
- for (size_t i = 0; i < pass->api_io.channel->users_length; i++) {
- pass->api_io.channel->topic = realloc(pass->api_io.channel->topic, strlen(pass->api_io.channel->topic) + strlen(pass->api_io.channel->users[i]->username) + 1 + 2 + 1 + 4);
- char buf[64];
- snprintf(buf, 63, "#%04d%s", pass->api_io.channel->users[i]->discriminator, i+1 < pass->api_io.channel->users_length ? ", " : "");
- strcat(pass->api_io.channel->topic, pass->api_io.channel->users[i]->username);
- strcat(pass->api_io.channel->topic, buf);
- }
- struct dc_channel * ch;
- if ((ch = dc_find_channel(program->channels, program->channels_length, pass->api_io.channel->id)))
- dc_transfer_channel(pass->api_io.channel, ch);
- pass->api_io.channel = dc_addr_channel(program, DC_ISAE(program->channels), pass->api_io.channel, DC_MAY_FREE | DC_REPLACE);
- if (!dc_find_ll_channel(client->guilds[0]->channel, pass->api_io.channel->id)) {
- fprintf(stderr, "new DM id=%llu (:\n", pass->api_io.channel->id);
- pass->api_io.channel->next = NULL;
- *channel = pass->api_io.channel;
- }
- pass->api_io.channel->guild = client->guilds[0];
- pass->api_io.channel = NULL; /* we're done, NULL it or PROBLEMS WILL APPEAR!!! */
- }
- if ((path == DC_JSON_GUILD_CHANNEL || path == DC_JSON_MESSAGE_MENTION_CHANNEL || path == DC_JSON_MESSAGE_REFOBJ_MENTION_CHANNEL) && reason == LEJPCB_OBJECT_END) {
- struct dc_guild * gu = NULL;
- if (!pass->api_io.channel->id || !DC_CHANNEL_SUPPORTED(pass->api_io.channel->type) || (path == DC_JSON_MESSAGE_MENTION_CHANNEL && (!pass->api_io.id || !(gu = dc_find_guild(program->guilds, program->guilds_length, pass->api_io.id)))) || (path == DC_JSON_MESSAGE_REFOBJ_MENTION_CHANNEL && (!pass->api_io.id || !(gu = dc_find_guild(program->guilds, program->guilds_length, pass->api_io.id))))) {
- dc_channel_free(pass->api_io.channel, DC_UNSET);
- pass->api_io.channel = NULL;
- return '\0';
- }
- if (!gu)
- gu = pass->api_io.guild;
- struct dc_channel ** channel = &gu->channel;
- while (*channel)
- channel = &(*channel)->next;
- struct dc_channel * ch;
- if ((ch = dc_find_channel(program->channels, program->channels_length, pass->api_io.channel->id)))
- dc_transfer_channel(pass->api_io.channel, ch);
- pass->api_io.channel = dc_addr_channel(program, DC_ISAE(program->channels), pass->api_io.channel, DC_MAY_FREE | DC_REPLACE);
- if (!dc_find_ll_channel(gu->channel, pass->api_io.channel->id)) {
- fprintf(stderr, "new channel id=%llu (:\n", pass->api_io.channel->id);
- pass->api_io.channel->next = NULL;
- *channel = pass->api_io.channel;
- }
- for (size_t i = 0; i < pass->api_io.channel->permissions_length; i++)
- pass->api_io.channel->permissions[i]->channel = pass->api_io.channel;
- switch (path) {
- case DC_JSON_MESSAGE_MENTION_CHANNEL:
- if (pass->api_io.message)
- dc_add_channel(DC_ISAE(pass->api_io.message->channels), pass->api_io.channel, DC_UNSET);
- pass->api_io.id = 0;
- break;
- case DC_JSON_MESSAGE_REFOBJ_MENTION_CHANNEL:
- if (pass->api_io.message && pass->api_io.message->reply)
- dc_add_channel(DC_ISAE(pass->api_io.message->reply->channels), pass->api_io.channel, DC_UNSET);
- pass->api_io.id = 0;
- default:
- break;
- }
- pass->api_io.channel = NULL; /* we're done, NULL it or */
- }
- if (path == DC_JSON_GUILD && reason == LEJPCB_OBJECT_START) {
- pass->api_io.guild = dc_guild_init();
- pass->api_io.guild->status |= DC_IN_PROGRESS;
- }
- if (path == DC_JSON_GUILD && reason == LEJPCB_OBJECT_END) {
- if (!pass->api_io.guild->id) {
- dc_guild_free(pass->api_io.guild, DC_UNSET);
- pass->api_io.guild = NULL;
- return '\0';
- }
- pass->api_io.guild->status &= ~DC_IN_PROGRESS;
- pass->api_io.guild = dc_addr_guild(program, DC_ISAE(program->guilds), pass->api_io.guild, DC_MAY_FREE | DC_REPLACE); /* when we replace, new guild's chs' & roles' ptrs */
- struct dc_channel ** channel = &pass->api_io.guild->channel; /* to guild will be inv. */
- struct dc_role ** role = &pass->api_io.guild->role;
- while (*channel) {
- (*channel)->guild = pass->api_io.guild;
- channel = &(*channel)->next;
- }
- while (*role) {
- (*role)->guild = pass->api_io.guild;
- role = &(*role)->next;
- }
- struct dc_guild * gu;
- if ((gu = dc_find_guild(program->guilds, program->guilds_length, pass->api_io.guild->id)))
- DC_TRANSFER_GUILD(pass->api_io.guild, gu);
- dc_add_guild(DC_ISAE(client->guilds), pass->api_io.guild, DC_UNSET);
- pass->api_io.guild = NULL; /* we're done, NULL it or PROBLEMS WILL APPEAR!!! */
- }
- if (path == DC_JSON_GUILD_ROLE && reason == LEJPCB_OBJECT_START) {
- pass->api_io.role = dc_role_init();
- pass->api_io.role->status |= DC_IN_PROGRESS;
- }
- if (path == DC_JSON_GUILD_ROLE && reason == LEJPCB_OBJECT_END) {
- if (!pass->api_io.role->id) {
- dc_role_free(pass->api_io.role, DC_UNSET);
- pass->api_io.role = NULL;
- return '\0';
- }
- pass->api_io.role->status &= ~DC_IN_PROGRESS;
- struct dc_role * ro;
- if ((ro = dc_find_role(program->roles, program->roles_length, pass->api_io.role->id)))
- dc_transfer_role(pass->api_io.role, ro);
- pass->api_io.role = dc_addr_role(program, DC_ISAE(program->roles), pass->api_io.role, DC_MAY_FREE | DC_REPLACE);
- struct dc_role ** role;
- role = &pass->api_io.guild->role;
- while (*role)
- role = &(*role)->next;
- if (!dc_find_ll_role(pass->api_io.guild->role, pass->api_io.role->id)) {
- fprintf(stderr, "new role id=%lld (:\n", pass->api_io.role->id);
- pass->api_io.role->next = NULL;
- *role = pass->api_io.role;
- }
- pass->api_io.role->guild = pass->api_io.guild;
- if (pass->api_io.role->name && !strncmp(pass->api_io.role->name, "@everyone", strlen(pass->api_io.role->name)))
- pass->api_io.role->status |= DC_EVERYONE;
- pass->api_io.role = NULL; /* we're done, NULL it or PROBLEMS WILL APPEAR!!! */
- }
- if (path == DC_JSON_MEMBERSHIP && (reason == LEJPCB_OBJECT_END || reason == LEJPCB_OBJECT_START))
- pass->api_io.id = 0;
- if (pass->packet == DC_MESSAGE_CREATE && path == DC_JSON_MESSAGE && reason == LEJPCB_OBJECT_START) {
- pass->api_io.message = dc_message_init();
- pass->api_io.message->reply = dc_message_init();
- pass->api_io.message->reply->status |= DC_IN_PROGRESS;
- pass->api_io.message->status |= DC_IN_PROGRESS;
- }
- if (pass->packet == DC_MESSAGE_CREATE && pass->api_io.message && reason == LEJPCB_COMPLETE) {
- int ismessage = 0;
- struct dc_message * me;
- if (!pass->api_io.message->id || !pass->api_io.message->channel || !DC_MESSAGE_SUPPORTED(pass->api_io.message->type)) {
- dc_message_free(pass->api_io.message, DC_UNSET);
- pass->api_io.message = NULL;
- return '\0';
- }
- if (pass->api_io.message->reply) {
- if (!pass->api_io.message->reply->id || !pass->api_io.message->reply->channel || !DC_MESSAGE_SUPPORTED(pass->api_io.message->reply->type)) {
- dc_message_free(pass->api_io.message->reply, DC_UNSET);
- pass->api_io.message->reply = NULL;
- goto no_reply;
- }
- pass->api_io.message->reply->status &= ~DC_IN_PROGRESS;
- pass->api_io.message->reply->time = DC_ID2TIME(pass->api_io.message->reply->id);
- if ((me = dc_find_message(program->messages, program->messages_length, pass->api_io.message->reply->id)))
- dc_transfer_message(pass->api_io.message, me);
- pass->api_io.message->reply = dc_addr_message(program, DC_ISAE(program->messages), pass->api_io.message->reply, DC_MAY_FREE | DC_REPLACE);
- struct dc_message ** message = &pass->api_io.message->reply->channel->message;
- while (*message) {
- if ((*message)->time < pass->api_io.message->reply->time && (*message)->next && (*message)->next->time > pass->api_io.message->reply->time)
- break;
- message = &(*message)->next;
- }
- if (!dc_find_ll_message(pass->api_io.message->reply->channel->message, pass->api_io.message->reply->id)) {
- fprintf(stderr, "new message reply id=%llu (:\n", pass->api_io.message->reply->id);
- ismessage++;
- if (*message) {
- pass->api_io.message->reply->next = (*message)->next;
- (*message)->next = pass->api_io.message->reply;
- } else {
- pass->api_io.message->reply->next = NULL;
- *message = pass->api_io.message->reply;
- }
- }
- }
-no_reply:
- pass->api_io.message->status &= ~DC_IN_PROGRESS;
- pass->api_io.message->time = DC_ID2TIME(pass->api_io.message->id);
- if ((me = dc_find_message(program->messages, program->messages_length, pass->api_io.message->id)))
- dc_transfer_message(pass->api_io.message, me);
- pass->api_io.message = dc_addr_message(program, DC_ISAE(program->messages), pass->api_io.message, DC_MAY_FREE | DC_REPLACE);
- struct dc_message ** message = &pass->api_io.message->channel->message;
- while (*message) {
- if ((*message)->time < pass->api_io.message->time && (*message)->next && (*message)->next->time > pass->api_io.message->time) /* if *message is not NULL, */
- break; /* we should insert new message after message into linked list */
- message = &(*message)->next; /* otherwise just insert at the end */
- }
- if (!dc_find_ll_message(pass->api_io.message->channel->message, pass->api_io.message->id)) {
- fprintf(stderr, "new message id=%llu (:\n", pass->api_io.message->id);
- ismessage++;
- if (*message) {
- pass->api_io.message->next = (*message)->next; /* based linked lists */
- (*message)->next = pass->api_io.message;
- } else {
- pass->api_io.message->next = NULL;
- *message = pass->api_io.message;
- }
- }
- struct dc_api_io tostack = {
- .type = DC_API_MESSAGE,
- .status = DC_OK,
- .message = pass->api_io.message,
- .program = program
- };
- if (ismessage)
- dc_api_stack(tostack);
- pass->api_io.message = NULL;
- pass->packet = DC_NONE; /* we do this because this is triggered on LEJPCB_COMPLETE */
- DC_API_IO_GC(pass->api_io); /* only as a hack due to a lejp bug */
- return '\0';
- }
- if (path == DC_JSON_GUILD_CHANNEL_PERMISSION && reason == LEJPCB_OBJECT_START) {
- pass->api_io.permission = dc_permission_init();
- pass->api_io.permission->channel = pass->api_io.channel;
- pass->api_io.permission->status = DC_IN_PROGRESS;
- }
- if (path == DC_JSON_GUILD_CHANNEL_PERMISSION && reason == LEJPCB_OBJECT_END) {
- if (!pass->api_io.permission || (!pass->api_io.permission->user && !pass->api_io.permission->role) || (!pass->api_io.permission->user && !pass->api_io.permission->role)) {
- dc_permission_free(pass->api_io.permission, DC_UNSET);
- pass->api_io.permission = NULL;
- return '\0';
- }
- pass->api_io.permission->status &= ~DC_IN_PROGRESS;
- DC_MR(pass->api_io.channel->permissions);
- pass->api_io.channel->permissions[pass->api_io.channel->permissions_length++] = pass->api_io.permission;
- pass->api_io.permission = NULL;
- }
- if (reason & LEJP_FLAG_CB_IS_VALUE) {
- struct dc_user * user; /* this is just a var for you use */
- struct dc_role * role; /* now we rely on -Wuninitialized to detect weird stuff */
- unsigned long long int id; /* so don't initialize them here, that'd be wrong */
- int št;
- 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 */
- case DC_JSON_MEMBER_USERNAME:
- case DC_JSON_MESSAGE_AUTHOR_USERNAME:
- 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:
- case DC_JSON_MEMBER_ID:
- case DC_JSON_MESSAGE_AUTHOR_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:
- case DC_JSON_MEMBER_DISCRIMINATOR:
- case DC_JSON_MESSAGE_AUTHOR_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:
- case DC_JSON_GUILD_CHANNEL_TYPE:
- case DC_JSON_MESSAGE_MENTION_CHANNEL_TYPE:
- case DC_JSON_MESSAGE_REFOBJ_MENTION_CHANNEL_TYPE:
- if (pass->api_io.channel)
- pass->api_io.channel->type = atoi(ctx->buf);
- break;
- case DC_JSON_DM_ID:
- case DC_JSON_GUILD_CHANNEL_ID:
- case DC_JSON_MESSAGE_MENTION_CHANNEL_ID:
- case DC_JSON_MESSAGE_REFOBJ_MENTION_CHANNEL_ID:
- if (pass->api_io.channel)
- pass->api_io.channel->id = strtoull(ctx->buf, NULL, 10);
- break;
- case DC_JSON_DM_NAME:
- case DC_JSON_GUILD_CHANNEL_NAME:
- case DC_JSON_MESSAGE_MENTION_CHANNEL_NAME:
- case DC_JSON_MESSAGE_REFOBJ_MENTION_CHANNEL_NAME:
- if (pass->api_io.channel && !pass->api_io.channel->name)
- pass->api_io.channel->name = strdup(ctx->buf);
- break;
- case DC_JSON_MESSAGE_MENTION_CHANNEL_GUILD:
- case DC_JSON_MESSAGE_REFOBJ_MENTION_CHANNEL_GUILD:
- if (pass->api_io.channel)
- pass->api_io.id = strtoull(ctx->buf, NULL, 10);
- break;
- case DC_JSON_GUILD_NAME:
- if (pass->api_io.guild && !pass->api_io.guild->name)
- pass->api_io.guild->name = strdup(ctx->buf);
- break;
- case DC_JSON_GUILD_ID:
- if (pass->api_io.guild)
- pass->api_io.guild->id = strtoull(ctx->buf, NULL, 10);
- break;
- case DC_JSON_GUILD_CHANNEL_TOPIC:
- if (pass->api_io.channel && !pass->api_io.channel->topic)
- pass->api_io.channel->topic = strdup(ctx->buf);
- break;
- case DC_JSON_GUILD_ROLE_ID:
- if (pass->api_io.role)
- pass->api_io.role->id = strtoull(ctx->buf, NULL, 10);
- break;
- case DC_JSON_GUILD_ROLE_NAME:
- if (pass->api_io.role && !pass->api_io.role->name)
- pass->api_io.role->name = strdup(ctx->buf);
- break;
- case DC_JSON_GUILD_ROLE_PERMISSION:
- if (pass->api_io.role) {
- if (strtoull(ctx->buf, NULL, 10) & DC_ADMIN)
- pass->api_io.role->permissions = DC_ALL_PERMISSIONS;
- else
- pass->api_io.role->permissions = strtoull(ctx->buf, NULL, 10);
- }
- break;
- case DC_JSON_MEMBERSHIP_USER:
- pass->api_io.id = strtoull(ctx->buf, NULL, 10);
- break;
- case DC_JSON_MEMBERSHIP_ROLE:
- case DC_JSON_MEMBER_ROLE:
- if (!pass->api_io.id) /* we assume we get ID first */
- if (!pass->api_io.user || !(pass->api_io.id = pass->api_io.user->id))
- break;
- if (!(id = strtoull(ctx->buf, NULL, 10)))
- break;
- št = 0;
- if (!(user = dc_find_user(program->users, program->users_length, pass->api_io.id))) {
- št |= 1;
- user = dc_user_init();
- user->program = program;
- user->id = pass->api_io.id;
- }
- if (!(role = dc_find_role(program->roles, program->roles_length, id))) {
- št |= 2;
- role = dc_role_init();
- role->id = id;
- }
- if (!dc_find_user(role->users, role->users_length, user->id))
- fprintf(stderr, "new role membership user %s#%d (usr=%llu role=%llu)\n", user->username ? user->username : "", user->discriminator, user->id, role->id);
- dc_add_user(DC_ISAE(role->users), user, DC_UNSET);
- if (št & 1)
- dc_addr_user(program, DC_ISAE(program->users), user, DC_UNSET);
- if (št & 2)
- dc_addr_role(program, DC_ISAE(program->roles), role, DC_UNSET);
- pass->api_io.id = 0;
- break;
- case DC_JSON_MESSAGE_ID:
- if (pass->api_io.message)
- pass->api_io.message->id = strtoull(ctx->buf, NULL, 10);
- break;
- case DC_JSON_MESSAGE_TYPE:
- if (pass->api_io.message)
- pass->api_io.message->type = atoi(ctx->buf);
- break;
- case DC_JSON_MESSAGE_CHANNEL:
- if (pass->api_io.message)
- pass->api_io.message->channel = dc_find_channel(program->channels, program->channels_length, strtoull(ctx->buf, NULL, 10)); /* if we don't find it, too bad, we won't store msg */
- break;
- case DC_JSON_MESSAGE_CONTENT:
- if (pass->api_io.message) { /* lejp parses in chunks of 256, msg is 2k */
- if (!pass->api_io.message->message) /* so we don't have garbag */
- pass->api_io.message->message = strdup(ctx->buf);
- pass->api_io.message->message = realloc(pass->api_io.message->message, strlen(pass->api_io.message->message)+strlen(ctx->buf)+1);
- strcat(pass->api_io.message->message, ctx->buf);
- }
- break;
- case DC_JSON_MESSAGE_MENTION_ROLE:
- if (pass->api_io.message) {
- struct dc_role * role = dc_find_role(program->roles, program->roles_length, strtoull(ctx->buf, NULL, 10));
- if (role)
- dc_add_role(DC_ISAE(pass->api_io.message->roles), role, DC_UNSET);
- }
- break;
- case DC_JSON_MESSAGE_REFOBJ_MENTION_ROLE:
- if (pass->api_io.message && pass->api_io.message->reply) {
- struct dc_role * role = dc_find_role(program->roles, program->roles_length, strtoull(ctx->buf, NULL, 10));
- if (role)
- dc_add_role(DC_ISAE(pass->api_io.message->reply->roles), role, DC_UNSET);
- }
- break;
- case DC_JSON_MESSAGE_REFOBJ_ID:
- if (pass->api_io.message && pass->api_io.message->reply)
- pass->api_io.message->reply->id = strtoull(ctx->buf, NULL, 10);
- break;
- case DC_JSON_MESSAGE_REFOBJ_TYPE:
- if (pass->api_io.message && pass->api_io.message->reply)
- pass->api_io.message->reply->type = atoi(ctx->buf);
- break;
- case DC_JSON_MESSAGE_REFOBJ_CHANNEL:
- if (pass->api_io.message && pass->api_io.message->reply)
- pass->api_io.message->reply->channel = dc_find_channel(program->channels, program->channels_length, strtoull(ctx->buf, NULL, 10));
- break;
- case DC_JSON_MESSAGE_REFOBJ_CONTENT:
- if (pass->api_io.message && pass->api_io.message->reply) {
- if (!pass->api_io.message->reply->message)
- pass->api_io.message->reply->message = strdup(ctx->buf);
- pass->api_io.message->reply->message = realloc(pass->api_io.message->reply->message, strlen(pass->api_io.message->reply->message)+strlen(ctx->buf)+1);
- strcat(pass->api_io.message->reply->message, ctx->buf);
- }
- break;
- case DC_JSON_GUILD_CHANNEL_PERMISSION_TYPE:
- if (pass->api_io.permission)
- pass->api_io.permission->type = atoi(ctx->buf);
- break;
- case DC_JSON_GUILD_CHANNEL_PERMISSION_ID: /* we assume we get user before perm */
- if (pass->api_io.permission) {
- pass->api_io.permission->user = dc_find_user(program->users, program->users_length, strtoull(ctx->buf, NULL, 10));
- pass->api_io.permission->role = dc_find_role(program->roles, program->roles_length, strtoull(ctx->buf, NULL, 10));
- if (!pass->api_io.permission->role || !pass->api_io.permission->user)
- switch (pass->api_io.permission->type) {
- case DC_USER:
- pass->api_io.permission->user = dc_user_init();
- pass->api_io.permission->user->program = program;
- pass->api_io.permission->user->id = strtoull(ctx->buf, NULL, 10);
- dc_addr_user(program, DC_ISAE(program->users), pass->api_io.permission->user, DC_UNSET);
- break;
- case DC_ROLE:
- pass->api_io.permission->role = dc_role_init();
- pass->api_io.permission->role->id = strtoull(ctx->buf, NULL, 10);
- dc_addr_role(program, DC_ISAE(program->roles), pass->api_io.permission->role, DC_UNSET);
- break;
- }
- }
- break;
- case DC_JSON_GUILD_CHANNEL_PERMISSION_DENY:
- if (pass->api_io.permission)
- pass->api_io.permission->deny = strtoull(ctx->buf, NULL, 10);
- break;
- case DC_JSON_GUILD_CHANNEL_PERMISSION_ALLOW:
- if (pass->api_io.permission)
- pass->api_io.permission->allow = 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) {
- default:
- break;
- }
- return '\0'; /* by the way, what does the return value do here? write in the comments below (; */
-}
static int dc_lws_cb (struct lws * wsi, enum lws_callback_reasons rs, void * us, void * in, size_t len) {
struct dc_lws_pass * pass = (struct dc_lws_pass *) us;
unsigned char buf[LWS_PRE+DC_LWS_BUF+1]; /* whooh, boy, this is more than a meg of stack! */
@@ -739,14 +183,6 @@ static int dc_lws_cb (struct lws * wsi, enum lws_callback_reasons rs, void * us,
case LWS_CALLBACK_CLIENT_RECEIVE: /* websocket receive, pass to json parser ? */
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;
- pass->packet = DC_NONE;
- lejp_construct(&pass->json_ctx, dc_json_cb, us /* == (void *) pass */, (const char * const *) dc_json_paths, LWS_ARRAY_SIZE(dc_json_paths));
- } /* lejp_parse does return number of excess bytes after parsing json. but I */
- pass->len = len; /* assume that server sends in chunks that always terminate */
- pass->cp = in; /* when json object terminates. valve pls fix */
- lejp_parse(&pass->json_ctx, (const unsigned char *) in, len);
break;
case LWS_CALLBACK_CLIENT_WRITEABLE: /* invoke with lws_callback_on_writeable(wsi) 4 ws */
if (!pass->api_io.client) /* we empty all payloads from 0 to finish */
diff --git a/src/json.c b/src/json.c
new file mode 100644
index 0000000..e23d934
--- /dev/null
+++ b/src/json.c
@@ -0,0 +1,80 @@
+#ifndef DC_REALLOC_K
+#define DC_REALLOC_K 1.5
+#endif
+struct dc_json { /* does not care about syntax, only purpose is to detect objects for libcjson */
+ char * buf; /* internal buffer */
+ size_t bufcap; /* internal buffer capacity */
+ size_t nest; /* internal nesting depth of whatevery type: object or array */
+ size_t instr; /* internal if we are currently in a string */
+ size_t start; /* internal starting offset in buffer of first byte of object - { */
+ char backup; /* internal we store byte we overwrote with \0 when we were ready */
+ int ready; /* internal we indicate to the next call that we were ready previous time */
+}; /* note that no memory is transfered. in is copied and return mustn't be freed. */
+char * dc_json (struct dc_json * j, char * in) { /* detects start/end of a cat objects JSON stream */
+ size_t i; /* input a null terminated string - a chunk of the json stream */
+ if (!j->buf)
+ (j->buf = malloc((j->bufcap = 1024) * sizeof(char)))[0] = '\0';
+ if (j->ready) {
+ if (j->ready > 0)
+ memmove(j->buf, j->buf+j->ready, strlen(j->buf+j->ready)+1);
+ j->buf[0] = j->backup;
+ }
+ size_t bufstrlen = strlen(j->buf); /* could optimize and cache it into the struct */
+ size_t instrlen = strlen(in);
+ i = bufstrlen;
+ if (bufstrlen + instrlen > j->bufcap)
+ j->buf = realloc(j->buf, (j->bufcap=(bufstrlen+instrlen)*DC_REALLOC_K)*sizeof(char));
+ strcpy(j->buf+bufstrlen, in);
+ bufstrlen += instrlen;
+ while (i < bufstrlen) {
+ if (j->instr) {
+ if (j->buf[i] == '"') {
+ int escaped = 0;
+ int index = 0;
+ while (j->buf[i-++index] == '\\') {
+ if (escaped)
+ escaped = 0;
+ else
+ escaped++;
+ }
+ if (!escaped) {
+ j->instr = 0;
+ fprintf(stderr, "dc_json: j->instr = 0\n");
+ } else
+ fprintf(stderr, "dc_json: escaped\n");
+ }
+ goto next;
+ }
+ switch (j->buf[i]) {
+ case '{':
+ case '[':
+ if (!j->nest++)
+ j->start = i;
+ break;
+ case '"':
+ j->instr++;
+ fprintf(stderr, "dc_json: j->instr++\n");
+ break;
+ case '}':
+ case ']':
+ if (!--j->nest) {
+ j->backup = j->buf[++i];
+ j->ready = i;
+ if (!j->backup)
+ j->ready = -1;
+ j->buf[i] = '\0';
+ return j->buf+j->start;
+ }
+ break;
+ default:
+ break;
+ }
+next:
+ i++;
+ }
+ return NULL;
+} /* returns pointer to null terminated string when there's an object to be parsed or NULL. */
+void dc_json_free (struct dc_json * s) {
+ free(s->buf);
+ free(s);
+}
diff --git a/test/json.c b/test/json.c
new file mode 100644
index 0000000..6671628
--- /dev/null
+++ b/test/json.c
@@ -0,0 +1,15 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <json.c>
+int main (int argc, char ** argv) {
+ struct dc_json * handler = calloc(1, sizeof(struct dc_json));
+ char * o;
+ for (int i = 1; i < argc; i++)
+ if ((o = dc_json(handler, argv[i]))) {
+ printf("%s\n", o);
+ while ((o = dc_json(handler, "")))
+ printf("%s\n", o);
+ }
+ dc_json_free(handler);
+}