diff options
author | Anton Luka Šijanec <anton@sijanec.eu> | 2022-02-14 23:05:01 +0100 |
---|---|---|
committer | Anton Luka Šijanec <anton@sijanec.eu> | 2022-02-14 23:05:01 +0100 |
commit | b99dcd6c3b1494f44f67229b5435e629fc566b1a (patch) | |
tree | 203d4442a82bd4427a101c986f01043748f20967 | |
parent | a commit just in case. read description. (diff) | |
download | discord.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-- | .gitignore | 1 | ||||
-rw-r--r-- | src/api.c | 564 | ||||
-rw-r--r-- | src/json.c | 80 | ||||
-rw-r--r-- | test/json.c | 15 |
4 files changed, 96 insertions, 564 deletions
@@ -1,5 +1,6 @@ # the following is the binary discord.c +a.out # debugging log files valgrind-out.txt .gdb_history @@ -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); +} |