#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 */