diff options
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | README.md | 18 | ||||
-rw-r--r-- | src/api.c | 574 | ||||
-rw-r--r-- | src/i18n.h | 5 | ||||
-rw-r--r-- | src/main.c | 2 |
5 files changed, 597 insertions, 4 deletions
@@ -1,5 +1,5 @@ default: - gcc -g -Ilib -Isrc -I. src/main.c -lcurl -odiscord.c + gcc -g -Ilib -Isrc -I. src/api.c -lcurl -odiscord.c prepare: wget https://raw.githubusercontent.com/DaveGamble/cJSON/master/cJSON.c -O lib/cJSON.c wget https://raw.githubusercontent.com/DaveGamble/cJSON/master/cJSON.h -O lib/cJSON.h diff --git a/README.md b/README.md new file mode 100644 index 0000000..f6c9ba6 --- /dev/null +++ b/README.md @@ -0,0 +1,18 @@ +# discord.c + +an alternative client for the discord messaging platform, written in the C programming language. + +## requirements + +* a POSIX system +* GNU C library +* GNU compiler collection +* GNU Make +* libcurl 7.17.0 or newer with HTTPS support + +## instructions + +``` +make +./discord.c -e email@address.example -p password +``` diff --git a/src/api.c b/src/api.c new file mode 100644 index 0000000..adcdf3c --- /dev/null +++ b/src/api.c @@ -0,0 +1,574 @@ +#pragma once +#define _XOPEN_SOURCE +#include <stdlib.h> +#include <stdio.h> +#include <curl/curl.h> +#include <unistd.h> +#include <i18n.h> +#include <string.h> +#include <lib.c> +#include <cJSON.h> +#include <cJSON.c> +#include <time.h> +#include <stdarg.h> +#include <printf.h> +#define DC_API_PREFIX "https://discord.com/api/v8/" /* this can be a format string, DO NOT use format characters inside */ +#define DC_LOGIN_FORMAT "{\"login\":\"%s\",\"password\":\"%s\",\"undelete\":false,\"captcha_key\":null,\"login_source\":null,\"gift_code_sku_id\":null}" +#define DC_USER_AGENT "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36" +#define DC_ERROR(e, s, m, ...) dc_push_error(e, s, __func__, __FILE__, __LINE__, 0##__VA_OPT__(1), m __VA_OPT__(,) __VA_ARGS__) +#define DC_CLIENT_ERROR(c, m, ...) DC_ERROR(c->errors, &c->errors_sizeof, m __VA_OPT__(,) __VA_ARGS__) /* yeah, that m is not a typo */ +#define DC_API(curl, body, endpoint, ...) dc_api(curl, body, 0##__VA_OPT__(1), endpoint __VA_OPT__(,) __VA_ARGS__) +#define cJSON_GetObjectItem2(root, name1, name2) (cJSON_GetObjectItem(root, name1) ? cJSON_GetObjectItem(cJSON_GetObjectItem(root, name1), name2) : NULL) +#define DC_TIMESTAMP_FORMAT "%Y-%m-%dT%H:%M:%S.XXXXXX%z" +struct dc_error { + size_t line; + const char * function /* this is a static string, so it is not copied, do not free it */; + char * file /* this is a static string, so it is not copied, do not free it */; + char * message /* this is copied, caller can free it if it was dynamically created by it, it must be freed before destroying struct */; + time_t time; + short unsigned int reported /* 0: error was not yet shown to the user, 1: error was already shown to the user */; +}; +int dc_error_free(struct dc_error * e) { + free(e->message); e->message = NULL; /* other strings are static */ + e->line = 0; + e->function = NULL; + e->file = NULL; + e->reported = 0; + free(e); +}; +struct dc_message { + char * username; + int discriminator; + char * content; + time_t time; + struct dc_channel * channel; + unsigned short int tts; + unsigned long long int id; +}; +int dc_message_free (struct dc_message * m) { + free(m->username); m->username = NULL; + free(m->content); m->content = NULL; + m->channel = NULL; + m->tts = 0; + m->discriminator = -1; + m->id = 0; + /* i'm not sure how to "unset" time_t ... */ + free(m); +} +int dc_message_compare (const void * a, const void * b) { + struct dc_message * c = (struct dc_message *) a; + struct dc_message * d = (struct dc_message *) b; + /* strežnik v arrayu najprej pošlje sporočila iz sedanjosti, nato tista iz preteklosti, zato bo tudi sortiran array takšen */ + return c->time > d->time ? -1 : c->time < d->time ? 1 : 0; +} +struct dc_channel { + char * name; + char * topic; + unsigned long long int id; + struct dc_guild * guild; + struct dc_message * messages; + size_t messages_sizeof; + unsigned short int sent /* describes status, was message already sent (1) or is it to-be sent (0)? */; +}; +int dc_channel_free (struct dc_channel * ch) { + free(ch->name); ch->name = NULL; + free(ch->topic); ch->topic = NULL; + ch->guild = NULL; + ch->id = 0; + for (int i = 0; i < ch->messages_sizeof; i++) + dc_message_free(&ch->messages[i]); + ch->messages_sizeof = 0; + free(ch); +} +struct dc_guild { + char * name; + unsigned long long id; + size_t channels_sizeof; + struct dc_channel * channels; +}; +int dc_guild_free (struct dc_guild * g) { + free(g->name); g->name = NULL; + g->id = 0; + for (int i = 0; i < g->channels_sizeof; i++) + dc_channel_free(&g->channels[i]); + g->channels_sizeof = 0; + free(g); +} +struct dc_client { + CURL * curl; + struct curl_slist * curl_headers; + char * authorization; + char * email; /* email and password are copied, even though they are sometimes static ENV vars - they are not always! */ + char * password; + char * username; + int discriminator; + struct dc_guild * guilds; + size_t guilds_sizeof; + struct dc_channel * joinedchannel; + struct dc_error * errors; + size_t errors_sizeof; + struct dc_message * sent_messages; + size_t sent_messages_sizeof; +}; +int dc_client_free (struct dc_client * c) { + curl_easy_cleanup(c->curl); + curl_slist_free_all(c->curl_headers); + free(c->authorization); c->authorization = NULL; + free(c->email); c->email = NULL; + free(c->password); c->password = NULL; + free(c->username); c->username = NULL; + c->discriminator = -1; + c->joinedchannel = NULL; + for (int i = 0; i < c->guilds_sizeof; i++) + dc_guild_free(&c->guilds[i]); + c->guilds_sizeof = 0; + for (int i = 0; i < c->errors_sizeof; i++) + dc_error_free(&c->errors[i]); + c->errors_sizeof = 0; + for (int i = 0; i < c->sent_messages_sizeof; c++) + dc_message_free(&c->sent_messages[i]); + c->sent_messages_sizeof = 0; + free(c); +} +int dc_push_error (struct dc_error * e, size_t * s, const char * c, char * f, size_t l, unsigned short int isfmt, char * m, ...) { + e = realloc(e, sizeof(struct dc_error)*++*s); /* note: format arguments are evaluated twice */ + size_t strlenm = strlen(m); + size_t va_count = parse_printf_format(m, 0, NULL); + if (isfmt && va_count > 0) { + va_list ap, ap2; + va_start(ap, m); + va_copy(ap2, ap); + strlenm = vsnprintf(NULL, 0, m, ap); + e[*s-1].message = malloc(sizeof(char)*strlenm+1); + vsnprintf(e[*s-1].message, strlenm+1, m, ap2); + va_end(ap); + va_end(ap2); + } else { + e[*s-1].message = malloc(sizeof(char)*strlenm+1); + strcpy(e[*s-1].message, m); + } + e[*s-1].file = f; + e[*s-1].line = l; + e[*s-1].function = c /* Caller */; + e[*s-1].time = time(NULL); + e[*s-1].reported = 0; + return 1; +} +cJSON * dc_api (CURL * curl, char * body, int isfmt, char * endpoint, ...) { /* note: format arguments are evaluated twice */ + if (!curl) + return NULL; + if (!endpoint) + return NULL; + cJSON * json = NULL; + struct writefunc_string s; + init_writefunc_string(&s); + size_t va_count = parse_printf_format(endpoint, 0, NULL); + char * endpoint_formatted = NULL; + if (isfmt && va_count > 0) { + va_list ap, ap2; + va_start(ap, endpoint); + va_copy(ap2, ap); + size_t strlenm = vsnprintf(NULL, 0, endpoint, ap); + endpoint_formatted = malloc(sizeof(char)*strlenm+1); + vsnprintf(endpoint_formatted, strlenm, endpoint_formatted, ap2); /* sn instead of s because double evaulation may produce */ + va_end(ap); /* larger output the next time and lead to overflow */ + va_end(ap2); + } + curl_easy_setopt(curl, CURLOPT_URL, endpoint_formatted ? endpoint_formatted : endpoint); + if (!body) + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + else + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + if (curl_easy_perform(curl) != CURLE_OK) + goto rc; + json = cJSON_Parse(s.ptr); + rc: + free(endpoint_formatted); + free(s.ptr); s.ptr = NULL; + return json; +} +int dc_login (struct dc_client * c) { + int rs = 1; + struct writefunc_string s; + char * data = NULL; + cJSON * json = NULL; + if (!c) + return -1; + if (!c->email || !c->password) { + DC_CLIENT_ERROR(c, DC_I18N_MISSING_EP); + return -2; + } + if (!c->curl) + c->curl = curl_easy_init(); + if (!c->curl) { + DC_CLIENT_ERROR(c, "curl_easy_init() " DC_I18N_FAILED); + return -3; + } + init_writefunc_string(&s); + data = malloc(strlen(DC_LOGIN_FORMAT)+strlen(c->email)+strlen(c->password)+1); + CURLcode res; + sprintf(data, DC_LOGIN_FORMAT, c->email, c->password); + c->curl_headers = curl_slist_append(c->curl_headers, "Content-Type: application/json"); + c->curl_headers = curl_slist_append(c->curl_headers, "User-Agent: " DC_USER_AGENT); + curl_easy_setopt(c->curl, CURLOPT_URL, DC_API_PREFIX "auth/login"); + curl_easy_setopt(c->curl, CURLOPT_FOLLOWLOCATION, 1L); + curl_easy_setopt(c->curl, CURLOPT_POSTFIELDS, data); + curl_easy_setopt(c->curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(c->curl, CURLOPT_HTTPHEADER, c->curl_headers); + curl_easy_setopt(c->curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(c->curl); + if (res != CURLE_OK) { + DC_CLIENT_ERROR(c, "curl_easy_perform() " DC_I18N_FAILED ": %s", curl_easy_strerror(res)); /* yeah, format strings are supported */ + rs = -4; + goto rc; + } + json = cJSON_Parse(s.ptr); + if (!json) { + const char *error_ptr = cJSON_GetErrorPtr(); + if (!error_ptr) { + DC_CLIENT_ERROR(c, "cJSON_Parse " DC_I18N_FAILED ": " DC_I18N_JSON_ERROR_BEFORE ": %s", error_ptr); + } else { + DC_CLIENT_ERROR(c, "cJSON_Parse " DC_I18N_FAILED); + } + rs = -5; + goto rc; + } + cJSON * token = cJSON_GetObjectItem(json, "token"); + if (!cJSON_IsString(token)) { + DC_CLIENT_ERROR(c, "!cJSON_IsString(token)"); + rs = -6; + goto rc; + } + c->authorization = realloc(c->authorization, strlen(token->valuestring)+1); + strcpy(c->authorization, token->valuestring); + data = realloc(data, strlen(c->authorization)+strlen("Authorization: ")+1); + strcpy(data, "Authorization: "); + strcat(data, c->authorization); + free(s.ptr); s.ptr = NULL; + init_writefunc_string(&s); + curl_easy_setopt(c->curl, CURLOPT_URL, DC_API_PREFIX "users/@me"); + curl_easy_setopt(c->curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(c->curl, CURLOPT_WRITEDATA, &s); + c->curl_headers = curl_slist_append(c->curl_headers, data); + res = curl_easy_perform(c->curl); + if (res != CURLE_OK) { + DC_CLIENT_ERROR(c, "curl_easy_perform() " DC_I18N_FAILED ": %s", curl_easy_strerror(res)); + rs = -7; + goto rc; + } + cJSON_Delete(json); + json = cJSON_Parse(s.ptr); + if (!json) { + const char *error_ptr = cJSON_GetErrorPtr(); + if (!error_ptr) { + DC_CLIENT_ERROR(c, "cJSON_Parse " DC_I18N_FAILED ": " DC_I18N_JSON_ERROR_BEFORE ": %s", error_ptr); + } else { + DC_CLIENT_ERROR(c, "cJSON_Parse " DC_I18N_FAILED); + } + rs = -8; + goto rc; + } + token = cJSON_GetObjectItem(json, "username"); + cJSON * token2 = cJSON_GetObjectItem(json, "discriminator"); + if (!cJSON_IsString(token) || !cJSON_IsString(token2)) { + DC_CLIENT_ERROR(c, "!cJSON_IsString(token) || !cJSON_IsString(token2)"); + rs = -9; + goto rc; + } + c->username = realloc(c->username, strlen(token->valuestring)+1); + strcpy(c->username, token->valuestring); + c->discriminator = strtol(token2->valuestring, NULL, 10); + rc: + free(s.ptr); s.ptr = NULL; + free(data); data = NULL; + cJSON_Delete(json); + return rs; +} +int dc_fetch_guilds (struct dc_client * c) { + if (!c) + return -5; + int rs = 1; + struct writefunc_string s; + char * value = NULL; + char * value2 = NULL; + CURLcode res; + cJSON * json = NULL; + if (!c->username || !c->username[0]) + if ((rs = dc_login(c)) < 0) { + DC_CLIENT_ERROR(c, "dc_login(c) " DC_I18N_FAILED " (%d)", rs); + return -1; + } else rs = 1; + curl_easy_setopt(c->curl, CURLOPT_URL, DC_API_PREFIX "users/@me/guilds"); + curl_easy_setopt(c->curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(c->curl, CURLOPT_WRITEDATA, &s); + init_writefunc_string(&s); + res = curl_easy_perform(c->curl); + if (res != CURLE_OK) { + DC_CLIENT_ERROR(c, "curl_easy_perform(c->curl) " DC_I18N_FAILED ": %s", curl_easy_strerror(res)); + rs = -2; + goto rc; + } + json = cJSON_Parse(s.ptr); + if (!json) { + const char *error_ptr = cJSON_GetErrorPtr(); + if (!error_ptr) { + DC_CLIENT_ERROR(c, "cJSON_Parse " DC_I18N_FAILED ": " DC_I18N_JSON_ERROR_BEFORE ": %s", error_ptr); + } else { + DC_CLIENT_ERROR(c, "cJSON_Parse " DC_I18N_FAILED); + } + rs = -3; + goto rc; + } + if (!cJSON_IsArray(json)) { + DC_CLIENT_ERROR(c, "!cJSON_IsArray(json)"); + rs = -4; + goto rc; + } + for (int i = 0; i < c->guilds_sizeof; i++) + dc_guild_free(&c->guilds[i]); + c->guilds = NULL; + c->guilds_sizeof = 0; + cJSON * guild = NULL; + cJSON_ArrayForEach(guild, json) { + value = cJSON_GetStringValue(cJSON_GetObjectItem(json, "id")); + value2= cJSON_GetStringValue(cJSON_GetObjectItem(json, "name")); + if (!value || !value2) { + DC_CLIENT_ERROR(c, "!cJSON_GetStringValue(cJSON_GetObjectItem(json, \"id\" || \"name\"))"); + continue; + } + c->guilds = realloc(c->guilds, ++c->guilds_sizeof); + c->guilds[c->guilds_sizeof-1].name = malloc(strlen(value)+1); + strcpy(c->guilds[c->guilds_sizeof-1].name, value); + c->guilds[c->guilds_sizeof-1].id = strtoull(value2, NULL, 10); + c->guilds[c->guilds_sizeof-1].channels_sizeof = 0; + c->guilds[c->guilds_sizeof-1].channels = NULL; + } + rc: + free(s.ptr); s.ptr = NULL; + cJSON_Delete(json); json = NULL; + return rs; +} +int dc_fetch_channels (struct dc_client * c, struct dc_guild * g) { + int rs = 1; + if (!c) + return -1; + if (!c->username || !c->username[0]) + if ((rs = dc_login(c)) < 0) { + DC_CLIENT_ERROR(c, "dc_login(c) " DC_I18N_FAILED " (%d)", rs); + return -2; + } else rs = 1; + if (!g || !g->id) { + DC_CLIENT_ERROR(c, "!g || !g->id"); + return -3; + } + CURLcode res; + struct writefunc_string s; + init_writefunc_string(&s); + cJSON * json = NULL; + char * url = malloc(strlen(DC_API_PREFIX "guilds/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/channels")+1); + sprintf(url, DC_API_PREFIX "guilds/%llu/channels", g->id); + curl_easy_setopt(c->curl, CURLOPT_URL, url); + curl_easy_setopt(c->curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(c->curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(c->curl); + if (res != CURLE_OK) { + DC_CLIENT_ERROR(c, "curl_easy_perform(c->curl) " DC_I18N_FAILED ": %s", curl_easy_strerror(res)); + rs = -4; + goto rc; + } + json = cJSON_Parse(s.ptr); + if (!json) { + const char *error_ptr = cJSON_GetErrorPtr(); + if (!error_ptr) { + DC_CLIENT_ERROR(c, "cJSON_Parse " DC_I18N_FAILED ": " DC_I18N_JSON_ERROR_BEFORE ": %s", error_ptr); + } else { + DC_CLIENT_ERROR(c, "cJSON_Parse " DC_I18N_FAILED); + } + rs = -5; + goto rc; + } + if (!cJSON_IsArray(json)) { + DC_CLIENT_ERROR(c, "!cJSON_IsArray(json), s.ptr = %s", s.ptr); + rs = -6; + goto rc; + } + cJSON * channel = NULL; + for (int i = 0; i < g->channels_sizeof; i++) + dc_channel_free(&g->channels[i]); + g->channels_sizeof = 0; + cJSON_ArrayForEach(channel, json) { + char * topic = cJSON_GetStringValue(cJSON_GetObjectItem(json, "topic")); + char * name = cJSON_GetStringValue(cJSON_GetObjectItem(json, "name")); + char * id = cJSON_GetStringValue(cJSON_GetObjectItem(json, "id")); + double type = cJSON_GetNumberValue(cJSON_GetObjectItem(json, "type")); + if (!id || !name || type == NAN) { + DC_CLIENT_ERROR(c, "!id || !name || type == NAN"); + continue; + } + if (type != 0) /* if it's not a text channel (z. B. voice channel, category, ...) */ + continue; + if (!topic) + topic = ""; + g->channels = realloc(g->channels, ++g->channels_sizeof); + g->channels[g->channels_sizeof-1].name = malloc(strlen(name)+1); + strcpy(g->channels[g->channels_sizeof-1].name, name); + g->channels[g->channels_sizeof-1].topic = malloc(strlen(topic)+1); + strcpy(g->channels[g->channels_sizeof-1].topic, topic); + g->channels[g->channels_sizeof-1].id = strtoull(id, NULL, 10); + g->channels[g->channels_sizeof-1].guild = g; + g->channels[g->channels_sizeof-1].messages = NULL; + g->channels[g->channels_sizeof-1].messages_sizeof = 0; + } + rc: + free(s.ptr); s.ptr = NULL; + free(url); url = NULL; + cJSON_Delete(json); json = NULL; + return rs; +} +int dc_send_message (struct dc_client * c, struct dc_message * m) { + int rs = 1; + struct tm tm; /* not used at all, you can delete it, I am autistic xD */ + if (!c) + return -1; + if (!c->username || !c->username[0]) + if ((rs = dc_login(c)) < 0) { + DC_CLIENT_ERROR(c, "dc_login(c) " DC_I18N_FAILED " (%d)", rs); + return -2; + } else rs = 1; + /* it'd be bad to assume joinedchannel if the following is false though */ + if (!m) { + DC_CLIENT_ERROR(c, "!m"); + return -3; + } + cJSON * json = cJSON_CreateObject(); + cJSON * nons = cJSON_CreateNumber(rand()); + cJSON_AddItemToObject(json, "nonce", nons); + cJSON * content = cJSON_CreateString(m->content); + cJSON_AddItemToObject(json, "content", content); + cJSON * tts = m->tts ? cJSON_CreateTrue() : cJSON_CreateFalse(); + cJSON_AddItemToObject(json, "tts", tts); + char * body = cJSON_Print(json); + if (!body) { + DC_CLIENT_ERROR(c, "cJSON_Print " DC_I18N_FAILED); + rs = -4; + goto rc; + } + cJSON_Delete(json); json = NULL; + /* {content: "yeet", nonce: "820762917392613376", tts: false} */ + json = DC_API(c->curl, body, DC_API_PREFIX "channels/%llu/messages", m->channel->id); + if (!json) { + const char *error_ptr = cJSON_GetErrorPtr(); + if (!error_ptr) { + DC_CLIENT_ERROR(c, "cJSON_Parse " DC_I18N_FAILED ": " DC_I18N_JSON_ERROR_BEFORE ": %s", error_ptr); + } else { + DC_CLIENT_ERROR(c, "cJSON_Parse " DC_I18N_FAILED); + } + rs = -5; + goto rc; + } + char * discriminator = cJSON_GetStringValue(cJSON_GetObjectItem2(json, "author", "discriminator")); + if (!discriminator) { + DC_CLIENT_ERROR(c, "!discriminator"); + rs = -6; + goto rc; + } + m->discriminator = strtol(discriminator, NULL, 10); + char * username = cJSON_GetStringValue(cJSON_GetObjectItem2(json, "author", "username")); + if (!username) { + DC_CLIENT_ERROR(c, "!username"); + rs = -6; + goto rc; + } + m->username = malloc(strlen(username)+1); + strcpy(m->username, username); /* we don't directly point as that changes when we delete */ + rc: + free(body); body = NULL; + cJSON_Delete(json); json = NULL; + return rs; +} +int dc_fetch_messages (struct dc_client * c, struct dc_channel * ch) { + int rs = 1; + struct tm tm; + if (!c) + return -1; + if (!c->username || !c->username[0]) + if ((rs = dc_login(c)) < 0) { + DC_CLIENT_ERROR(c, "dc_login(c) " DC_I18N_FAILED " (%d)", rs); + return -2; + } else rs = 1; + /* it'd be bad to assume joinedchannel if the following is false though */ + if (!ch || !ch->id) { + DC_CLIENT_ERROR(c, "!ch || !ch->id"); + return -3; + } + cJSON * json = DC_API(c->curl, NULL, DC_API_PREFIX "channels/%llu/messages?limit=100", ch->id); + if (!json) { + const char *error_ptr = cJSON_GetErrorPtr(); + if (!error_ptr) { + DC_CLIENT_ERROR(c, "cJSON_Parse " DC_I18N_FAILED ": " DC_I18N_JSON_ERROR_BEFORE ": %s", error_ptr); + } else { + DC_CLIENT_ERROR(c, "cJSON_Parse " DC_I18N_FAILED); + } + rs = -5; + goto rc; + } + if (!cJSON_IsArray(json)) { + DC_CLIENT_ERROR(c, "!cJSON_IsArray(json)"); + rs = -6; + goto rc; + } + cJSON * message = NULL; + /* + for (int i = 0; i < ch->messages_sizeof; i++) + dc_message_free(&ch->messages[i]); + ch->messages_sizeof = 0; + */ /* we'll rather add messages to existing ones */ + cJSON_ArrayForEach(message, json) { + char * timestamp = cJSON_GetStringValue(cJSON_GetObjectItem(json, "timestamp")); + char * content = cJSON_GetStringValue(cJSON_GetObjectItem(json, "content")); + char * id = cJSON_GetStringValue(cJSON_GetObjectItem(json, "id")); + char * discriminator = cJSON_GetStringValue(cJSON_GetObjectItem2(json, "author", "discriminator")); + char * username = cJSON_GetStringValue(cJSON_GetObjectItem2(json, "author", "username")); + if (!id || !timestamp || !content || !username || !discriminator) { + DC_CLIENT_ERROR(c, "!id || !timestamp || !content || !username || discriminator < 0"); + continue; + } + if (strlen(timestamp) < 26) { + DC_CLIENT_ERROR(c, "strlen(timestamp) < 26"); + continue; + } + for (int i = 20; i <= 25; i++) + timestamp[i] == 'X'; /* because strptime does not have wildcard support and those numbers are sub-second fractions */ + if (!strptime(timestamp, DC_TIMESTAMP_FORMAT, &tm)) { + DC_CLIENT_ERROR(c, "strptime(timestamp, DC_TIMESTAMP_FORMAT, &tm) " DC_I18N_FAILED); + continue; + } + unsigned long long int idull = strtoull(id, NULL, 10); + for (int i = 0; i < ch->messages_sizeof; i++) + if (idull == ch->messages[i].id) + continue; /* remove duplicates */ + ch->messages = realloc(ch->messages, ++ch->messages_sizeof); +#define DC_FMTM /* fetch messages this message */ ch->messages[ch->messages_sizeof-1] + DC_FMTM.time = mktime(&tm); + DC_FMTM.content = malloc(strlen(content)+1); + strcpy(DC_FMTM.content, content); + DC_FMTM.username = malloc(strlen(username)+1); + strcpy(DC_FMTM.username, username); + DC_FMTM.id = idull; + DC_FMTM.discriminator = strtol(discriminator, NULL, 10); + DC_FMTM.channel = ch; + } + qsort(ch->messages, ch->messages_sizeof, sizeof(struct dc_message), dc_message_compare); /* we sort so that present messages are in the start of the array and old messages are to the end of the array */ + rc: + cJSON_Delete(json); json = NULL; + return rs; +} +struct dc_api_thread_control { + unsigned short int power; /* 1 if the thread should run, set it to 0 for the thread to return at the end of the loop */ + struct dc_client * clients; /* "array" of clients the thread should manage, ONLY ONE dc_api_thread PER PROCESS! */ + size_t dc_clients_sizeof; +}; +int dc_api_thread (struct dc_api_thread_control * t) { /* updates messages and sends messages when they are in the outbox */ + return -1; +} /* the thread shall use mutexes when doing things with shared memory - client structs */ @@ -1,11 +1,12 @@ -#define DC_I18N_FAILED "neuspel" +#define DC_I18N_FAILED "neuspel" /**/ #define DC_I18N_UNREC_ARG "neprepoznan argument %c, poskusi -h" #define DC_I18N_USAGE "uporaba: %s -e naslov@example -p geslo" #define DC_I18N_ARG_ALREADY_SET "argument %c že ima nastavljeno vrednost (mogoče okoljske spremenljivke)" -#define DC_I18N_MISSING_EP "manjka poštni naslov in/ali geslo" +#define DC_I18N_MISSING_EP "manjka poštni naslov in/ali geslo" /**/ #define DC_I18N_LOGGED_IN "prijavljeni ste kot %s" #define DC_I18N_LOGIN_FAILED "prijava neuspela. prijavite se v brskalniku. strežnik je odgovoril: %s" #define DC_I18N_NOT_JOINED "preden lahko pišeš, se moraš pridružiti kanalu" #define DC_I18N_GUILD_NOT_SET "skupina ni izbrana!" #define DC_I18N_CHANNEL_NOT_SET "kanal ni izbran!" #define DC_I18N_MESSAGES_GET_FAIL "pri pridobivanju sporočil je prišlo do napake" +#define DC_I18N_JSON_ERROR_BEFORE "JSON napaka pred" @@ -60,7 +60,7 @@ int main(int argc, char ** argv) { struct writefunc_string s; init_writefunc_string(&s); - char * data = malloc(sizeof(DC_LOGIN_FORMAT)+strlen(email)+strlen(password)); + char * data = malloc(strlen(DC_LOGIN_FORMAT)+strlen(email)+strlen(password)); sprintf(data, DC_LOGIN_FORMAT, email, password); struct curl_slist *headers = NULL; headers = curl_slist_append(headers, "Content-Type: application/json"); |