summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsijanec <anton@sijanec.eu>2021-03-18 14:18:29 +0100
committersijanec <anton@sijanec.eu>2021-03-18 14:18:29 +0100
commit59004bab38044f694853188d537b2fc545abeb0b (patch)
treea6a63a58c160f0250591694efdd59d939a33ca12
parentveč napisanega, manj delujočega (: (diff)
downloaddiscord.c-59004bab38044f694853188d537b2fc545abeb0b.tar
discord.c-59004bab38044f694853188d537b2fc545abeb0b.tar.gz
discord.c-59004bab38044f694853188d537b2fc545abeb0b.tar.bz2
discord.c-59004bab38044f694853188d537b2fc545abeb0b.tar.lz
discord.c-59004bab38044f694853188d537b2fc545abeb0b.tar.xz
discord.c-59004bab38044f694853188d537b2fc545abeb0b.tar.zst
discord.c-59004bab38044f694853188d537b2fc545abeb0b.zip
-rw-r--r--.gitignore4
-rw-r--r--Makefile2
-rw-r--r--src/api.c189
-rw-r--r--src/i18n.h6
-rw-r--r--src/lib.c4
-rw-r--r--src/main.c2
-rw-r--r--src/ui.c58
7 files changed, 152 insertions, 113 deletions
diff --git a/.gitignore b/.gitignore
index 6097dbc..74a1ce6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,7 @@ lib/cJSON.h
lib/cJSON.c
# the following is the binary
discord.c
+# debugging log files
+netreq.log
+stderr.log
+valgrind-out.txt
diff --git a/Makefile b/Makefile
index 5d5f623..1ea322b 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
default:
- gcc -Wall -pedantic -g -Ilib -Isrc -I. -pthread src/main.c -lcurl -lncursesw -lformw -lm -odiscord.c
+ gcc -Wall -pedantic -g -Ilib -Isrc -I. -pthread $$(ncursesw6-config --cflags --libs) src/main.c -lcurl -lformw -lm -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/src/api.c b/src/api.c
index f798aff..e456e53 100644
--- a/src/api.c
+++ b/src/api.c
@@ -18,9 +18,9 @@
#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, l, m, ...) (dc_push_error(e, s, l, __func__, __FILE__, __LINE__, 0##__VA_OPT__(1), m __VA_OPT__(,) __VA_ARGS__)) */
-#define DC_ERROR(e, s, l, m, ...) (fprintf(stderr, "%s()@" __FILE__ ":%d: " m "\n", __func__, __LINE__ __VA_OPT__(,) __VA_ARGS__))
-#define DC_CLIENT_ERROR(c, m, ...) DC_ERROR(c->errors, &c->errors_sizeof, c->errors_lock, m __VA_OPT__(,) __VA_ARGS__) /* yeah, that m is not a typo */
+#define DC_ERROR(c, m, ...) (dc_push_error(c, __func__, __FILE__, __LINE__, 0##__VA_OPT__(1), m __VA_OPT__(,) __VA_ARGS__))
+/* #define DC_ERROR(c, m, ...) (fprintf(stderr, "%s()@" __FILE__ ":%d: " m "\n", __func__, __LINE__ __VA_OPT__(,) __VA_ARGS__)) */
+#define DC_CLIENT_ERROR(c, m, ...) DC_ERROR(c, m __VA_OPT__(,) __VA_ARGS__) /* yeah, that m is not a typo */
#define DC_CAPI(c, body, endpoint, ...) dc_api(c, 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"
@@ -105,6 +105,7 @@ struct dc_channel {
_Atomic(struct dc_guild *) guild; /* nofree, nouiw */
struct dc_message ** messages; /* yesfree, nouiw */
_Atomic(size_t) messages_sizeof; /* nouiw */
+ int slowmode; /* number of seconds to wait in case of slowmode, HANDLED BY THE USER INTERFACE!! */
};
void dc_channel_free (struct dc_channel * ch) { /* noui, noapi, nolock - only called by dc_guild_free */
free(ch->name); ch->name = NULL;
@@ -153,6 +154,7 @@ struct dc_client {
struct dc_message ** sent_messages; /* yesfree - ui appends, api pops and moves to messages */
_Atomic(size_t) sent_messages_sizeof;
pthread_rwlock_t * sent_messages_lock;
+ _Atomic(time_t) last_sent_message; /* for slowmode implementations */
};
struct dc_client * dc_client_init () { /* gives you a prepared dc_client */
struct dc_client * c = calloc(1, sizeof(struct dc_client));
@@ -196,11 +198,17 @@ void dc_client_free (struct dc_client * c) { /* noui, noapi, nolock - only calle
DC_CFLD(c->sent_messages);
free(c);
}
-int dc_push_error (struct dc_error ** e, _Atomic size_t * s, pthread_rwlock_t * lock, const char * c, char * f, size_t l, unsigned short int isfmt, char * m, ...) {
- if (lock && pthread_rwlock_wrlock(lock))
+int dc_push_error (struct dc_client * c, const char * caller, char * f, size_t l, unsigned short int isfmt, char * m, ...) {
+#define DC_PEE /* dc scalnia oziroma push error error */ c->errors[c->errors_sizeof-1]
+ if (!c)
+ return -2;
+ pthread_rwlock_t * lock = c->errors_lock;
+ if (!lock)
+ return -3;
+ if (pthread_rwlock_wrlock(lock))
return -1; /* does not report an error as that may make things even worse. I could try writing to stderr here but meh */
- e = realloc(e, sizeof(struct dc_error *)*++*s); /* note: format arguments are evaluated twice */
- e[*s-1] = malloc(sizeof(struct dc_error));
+ c->errors = realloc(c->errors, sizeof(struct dc_error *)*++c->errors_sizeof); /* note: format arguments are evaluated twice */
+ DC_PEE = malloc(sizeof(struct dc_error));
size_t strlenm = strlen(m);
size_t va_count = parse_printf_format(m, 0, NULL);
if (isfmt && va_count > 0) {
@@ -208,19 +216,19 @@ int dc_push_error (struct dc_error ** e, _Atomic size_t * s, pthread_rwlock_t *
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);
+ DC_PEE->message = malloc(sizeof(char)*strlenm+1);
+ vsnprintf(DC_PEE->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;
+ DC_PEE->message = malloc(sizeof(char)*strlenm+1);
+ strcpy(DC_PEE->message, m);
+ }
+ DC_PEE->file = f;
+ DC_PEE->line = l;
+ DC_PEE->function = caller /* Caller */;
+ DC_PEE->time = time(NULL);
+ DC_PEE->reported = 0;
if (lock && pthread_rwlock_unlock(lock))
return -2;
return 1;
@@ -240,14 +248,15 @@ cJSON * dc_api (struct dc_client * c, char * body, int isfmt, char * endpoint, .
return NULL;
}
cJSON * json = NULL;
- struct writefunc_string s, h;
+ struct writefunc_string * s = malloc(sizeof(struct writefunc_string));
+ struct writefunc_string * h = malloc(sizeof(struct writefunc_string));
size_t va_count = parse_printf_format(endpoint, 0, NULL);
char * endpoint_formatted = NULL;
int retried = 0;
long response_code = 0;
retry:
- init_writefunc_string(&s);
- init_writefunc_string(&h);
+ init_writefunc_string(s);
+ init_writefunc_string(h);
if (isfmt && va_count > 0 && endpoint_formatted == NULL) {
va_list ap, ap2;
va_start(ap, endpoint);
@@ -259,34 +268,41 @@ cJSON * dc_api (struct dc_client * c, char * body, int isfmt, char * endpoint, .
va_end(ap2);
}
curl_easy_setopt(c->curl, CURLOPT_URL, endpoint_formatted ? endpoint_formatted : endpoint);
- if (!body)
- curl_easy_setopt(c->curl, CURLOPT_HTTPGET, 1L);
curl_easy_setopt(c->curl, CURLOPT_POSTFIELDS, body); /* yes, even null is okay, it's actually a must as it makes sure curl clears old pointers and does not read freed memory. see https://github.com/curl/curl/issues/3214#issuecomment-435335974 */
- curl_easy_setopt(c->curl, CURLOPT_WRITEDATA, &s);
- curl_easy_setopt(c->curl, CURLOPT_HEADERDATA, &h);
+ if (!body)
+ curl_easy_setopt(c->curl, CURLOPT_HTTPGET, 1L); /* this must be done after postfields */
+ curl_easy_setopt(c->curl, CURLOPT_WRITEDATA, s);
+ curl_easy_setopt(c->curl, CURLOPT_NOSIGNAL, 1L);
+ curl_easy_setopt(c->curl, CURLOPT_HEADERDATA, h);
+ curl_easy_setopt(c->curl, CURLOPT_FOLLOWLOCATION, 1L);
+ curl_easy_setopt(c->curl, CURLOPT_HTTPHEADER, c->curl_headers);
+ curl_easy_setopt(c->curl, CURLOPT_TIMEOUT, 20L); /* 20 second timeout */
curl_easy_setopt(c->curl, CURLOPT_HEADERFUNCTION, writefunc);
+ curl_easy_setopt(c->curl, CURLOPT_WRITEFUNCTION, writefunc);
if (curl_easy_perform(c->curl) != CURLE_OK) {
DC_CLIENT_ERROR(c, "curl_easy_perform(curl) != CURLE_OK");
goto rc;
} else {
curl_easy_getinfo(c->curl, CURLINFO_RESPONSE_CODE, &response_code);
}
- fprintf(netreq, "%s\n%s\n%s\n%s====================================\n", endpoint_formatted ? endpoint_formatted : endpoint, body ? body : "GET", h.ptr, s.ptr);
+ fprintf(netreq, "%s\n%s\n%s\n%s====================================\n", endpoint_formatted ? endpoint_formatted : endpoint, body ? body : "GET", h->ptr, s->ptr);
fflush(netreq);
- char * cp = strstr(h.ptr, "\nx-ratelimit-reset-after: ");
+ char * cp = strstr(h->ptr, "\nx-ratelimit-reset-after: ");
if (cp == NULL)
goto norlheaders;
cp += strlen("\nx-ratelimit-reset-after: ");
double retry_after = strtod(cp, NULL);
- cp = strstr(h.ptr, "\nx-ratelimit-remaining: ");
+ cp = strstr(h->ptr, "\nx-ratelimit-remaining: ");
if (cp == NULL)
goto norlheaders;
cp += strlen("\nx-ratelimit-remaining: ");
if (cp[0] >= '0' && cp[0] <= '9' && atoi(cp) == 0) { /* X-RateLimit-Remaining: 0 */
DC_CLIENT_ERROR(c, DC_I18N_HITRL " %lfs. endpoint = %s", retry_after, endpoint_formatted ? endpoint_formatted : endpoint);
sleep(ceil(retry_after)+1); /* TODO: prevent hanging entire thread just for this */
- free(s.ptr); s.ptr = NULL;
- free(h.ptr); h.ptr = NULL;
+ free(s->ptr); s->ptr = NULL;
+ free(h->ptr); h->ptr = NULL;
+ free(s); s = NULL;
+ free(h); h = NULL;
cJSON_Delete(json);
json = NULL;
if (retried++)
@@ -294,26 +310,27 @@ cJSON * dc_api (struct dc_client * c, char * body, int isfmt, char * endpoint, .
goto retry;
}
norlheaders:
- json = cJSON_Parse(s.ptr);
+ 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 ": %.21s s.ptr = %s", error_ptr, s.ptr);
+ DC_CLIENT_ERROR(c, "cJSON_Parse " DC_I18N_FAILED ": " DC_I18N_JSON_ERROR_BEFORE ": %.21s s->ptr = %s", error_ptr, s->ptr);
} else {
- DC_CLIENT_ERROR(c, "cJSON_Parse " DC_I18N_FAILED ". s.ptr = %s", s.ptr ? s.ptr : "NULL");
+ DC_CLIENT_ERROR(c, "cJSON_Parse " DC_I18N_FAILED ". s->ptr = %s", s->ptr ? s->ptr : "NULL");
}
goto rc;
}
rc:
curl_easy_setopt(c->curl, CURLOPT_HEADERFUNCTION, NULL); /* for usages that don't use headerfunction */
free(endpoint_formatted);
- free(s.ptr); s.ptr = NULL;
- free(h.ptr); h.ptr = NULL;
+ free(s->ptr); s->ptr = NULL;
+ free(h->ptr); h->ptr = NULL;
+ free(s); s = NULL;
+ free(h); h = NULL;
return json;
}
int dc_login (struct dc_client * c) { /* noui */
int rs = 1;
- struct writefunc_string s;
char * data = NULL;
cJSON * json = NULL;
if (!c)
@@ -328,34 +345,15 @@ int dc_login (struct dc_client * c) { /* noui */
DC_CLIENT_ERROR(c, "curl_easy_init() " DC_I18N_FAILED);
return -3;
}
- init_writefunc_string(&s);
- data = malloc(snprintf(data, 0, DC_LOGIN_FORMAT, c->email, c->password)+1);
+ data = malloc(snprintf(NULL, 0, DC_LOGIN_FORMAT, c->email, c->password)+1);
sprintf(data, DC_LOGIN_FORMAT, c->email, c->password);
- CURLcode res;
- curl_slist_free_all(c->curl_headers);
+ /* curl_slist_free_all(c->curl_headers); */
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);
+ json = DC_CAPI(c, data, DC_API_PREFIX "auth/login");
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;
+ DC_CLIENT_ERROR(c, "!DC_CAPI auth/login, data = %s", data);
+ rs = -4;
goto rc;
}
cJSON * token = cJSON_GetObjectItem(json, "token");
@@ -371,29 +369,12 @@ int dc_login (struct dc_client * c) { /* noui */
strcpy(data, "Authorization: ");
strcat(data, c->authorization);
if (DC_CUE(c, c->authorization_lock)) {rs = -8; goto rc;}
- 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_POSTFIELDS, NULL);
- 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 = -9;
- goto rc;
- }
cJSON_Delete(json);
- json = cJSON_Parse(s.ptr);
+ json = DC_CAPI(c, NULL, DC_API_PREFIX "users/@me");
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 = -10;
+ DC_CLIENT_ERROR(c, "!DC_CAPI users/@me");
+ rs = -7;
goto rc;
}
token = cJSON_GetObjectItem(json, "username");
@@ -409,7 +390,6 @@ int dc_login (struct dc_client * c) { /* noui */
if (DC_CUE(c, c->username_lock)) {rs = -12; goto rc;}
c->discriminator = strtol(token2->valuestring, NULL, 10);
rc:
- free(s.ptr); s.ptr = NULL;
free(data); data = NULL;
cJSON_Delete(json);
return rs;
@@ -441,6 +421,7 @@ int dc_fetch_guilds (struct dc_client * c) {
if(DC_CWLE(c, c->guilds_lock)) {rs = -5; goto rc;}
cJSON * guild = NULL;
cJSON_ArrayForEach(guild, json) {
+ int skip = 0;
value = cJSON_GetStringValue(cJSON_GetObjectItem(guild, "name"));
value2 = cJSON_GetStringValue(cJSON_GetObjectItem(guild, "id"));
if (!value || !value2) {
@@ -451,8 +432,11 @@ int dc_fetch_guilds (struct dc_client * c) {
}
unsigned long long int idull = strtoull(value2, NULL, 10);
for (int i = 0; i < c->guilds_sizeof; i++)
- if (idull == c->guilds[i]->id)
- continue; /* remove duplicates */
+ if (idull == c->guilds[i]->id) {
+ skip = 1;
+ break; /* remove duplicates */
+ }
+ if (skip) continue;
c->guilds = realloc(c->guilds, sizeof(struct dc_guild *)*++c->guilds_sizeof);
c->guilds[c->guilds_sizeof-1] = malloc(sizeof(struct dc_guild));
c->guilds[c->guilds_sizeof-1]->name = malloc(strlen(value)+1);
@@ -496,13 +480,22 @@ int dc_fetch_channels (struct dc_guild * g) {
goto rc;
}
cJSON * channel = NULL;
+ cJSON * perms = NULL;
+ cJSON * perm = NULL;
/* we lock all client guilds when doing stuff with channels */
if (DC_CWLE(c, c->guilds_lock)) {rs = -7; goto rc;}
cJSON_ArrayForEach(channel, json) {
+ int skip = 0;
+ if (cJSON_IsArray(perms = cJSON_GetObjectItem(channel, "permission_overwrites"))); /* not supported */
+ cJSON_ArrayForEach(perm, perms) {
+ skip++;
+ }
+ if (skip) continue;
char * topic = cJSON_GetStringValue(cJSON_GetObjectItem(channel, "topic"));
char * name = cJSON_GetStringValue(cJSON_GetObjectItem(channel, "name"));
char * id = cJSON_GetStringValue(cJSON_GetObjectItem(channel, "id"));
double type = cJSON_GetNumberValue(cJSON_GetObjectItem(channel, "type"));
+ double slowmode = cJSON_GetNumberValue(cJSON_GetObjectItem(channel, "rate_limit_per_user"));
if (!id || !name || type == NAN) {
DC_CLIENT_ERROR(c, "!id || !name || type == NAN");
continue;
@@ -513,8 +506,11 @@ int dc_fetch_channels (struct dc_guild * g) {
topic = "";
unsigned long long int idull = strtoull(id, NULL, 10);
for (int i = 0; i < g->channels_sizeof; i++)
- if (idull == g->channels[i]->id)
- continue; /* remove duplicates */
+ if (idull == g->channels[i]->id) {
+ skip++;
+ break;
+ }
+ if (skip) continue;
g->channels = realloc(g->channels, sizeof(struct dc_channel *)*++g->channels_sizeof);
g->channels[g->channels_sizeof-1] = malloc(sizeof(struct dc_channel));
g->channels[g->channels_sizeof-1]->name = malloc(strlen(name)+1);
@@ -525,6 +521,7 @@ int dc_fetch_channels (struct dc_guild * g) {
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;
+ g->channels[g->channels_sizeof-1]->slowmode = slowmode != NAN ? slowmode : 0;
}
if (DC_CUE(c, c->guilds_lock)) {rs = -8; goto rc;}
rc:
@@ -583,6 +580,7 @@ int dc_send_message (struct dc_message * m) { /* nolock - once message is append
rc:
free(body); body = NULL;
cJSON_Delete(json); json = NULL;
+ c->last_sent_message = time(NULL);
return rs;
}
int dc_fetch_messages (struct dc_channel * ch) {
@@ -616,6 +614,7 @@ int dc_fetch_messages (struct dc_channel * ch) {
if (DC_CWLE(c, c->guilds_lock)) {rs = -7; goto rc;} /* we lock all guilds of a client when writing messages */
cJSON * message = NULL;
cJSON_ArrayForEach(message, json) {
+ int skip = 0;
char * timestamp = cJSON_GetStringValue(cJSON_GetObjectItem(message, "timestamp"));
char * content = cJSON_GetStringValue(cJSON_GetObjectItem(message, "content"));
char * id = cJSON_GetStringValue(cJSON_GetObjectItem(message, "id"));
@@ -637,11 +636,15 @@ int dc_fetch_messages (struct dc_channel * ch) {
}
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 */
+ if (idull == ch->messages[i]->id) {
+ skip++;
+ break; /* remove duplicates */
+ }
+ if (skip) continue;
ch->messages = realloc(ch->messages, sizeof(struct dc_message *)*++ch->messages_sizeof);
#define DC_FMTM /* fetch messages this message */ ch->messages[ch->messages_sizeof-1]
- DC_FMTM = malloc(sizeof(struct dc_message));
+ /* DC_CLIENT_ERROR(c, "recvd msg %llu", idull); */ /* remember: continue in a nested forloop is not useful in some cases (: */
+ DC_FMTM = calloc(1, sizeof(struct dc_message));
DC_FMTM->time = mktime(&tm);
DC_FMTM->content = malloc(strlen(content)+1);
strcpy(DC_FMTM->content, content);
@@ -651,7 +654,7 @@ int dc_fetch_messages (struct dc_channel * ch) {
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 */
+ 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 */
if (DC_CUE(c, c->guilds_lock)) {rs = -8; goto rc;}
rc:
cJSON_Delete(json); json = NULL;
@@ -670,6 +673,7 @@ struct dc_thread_control {
};
int dc_api_thread (struct dc_thread_control * t) { /* updates messages and sends messages when they are in the outbox */
while (!t->power_api) usleep(250000); /* so as to not make the switcher go bankrupt */
+ curl_global_init(CURL_GLOBAL_ALL);
/* if (pthread_rwlock_wrlock(t->clients_lock))
return -1; */ /* clients are not locked yet */
for (int i = 0; i < t->clients_sizeof && t->power_api != 2; i++)
@@ -683,9 +687,9 @@ int dc_api_thread (struct dc_thread_control * t) { /* updates messages and sends
for (int j = 0; j < t->clients[i]->guilds_sizeof; j++) {
if (!t->clients[i]->guilds[j]->channels_sizeof /*|| !(rand() % 100)*/) /* roughly every 100+inf cycles we'll update channels */
dc_fetch_channels(t->clients[i]->guilds[j]);
- for (int k = 0; k < t->clients[i]->guilds[j]->channels_sizeof; k++)
- if (!(rand() % 10)) /* roughly every 10 cycles we'll update messages */
- dc_fetch_messages(t->clients[i]->guilds[j]->channels[k]);
+ /* for (int k = 0; k < t->clients[i]->guilds[j]->channels_sizeof; k++) */
+ if (!(rand() % 10) && t->clients[i]->joinedchannel) /* roughly every 10 cycles we'll update messages in the joined ch */
+ dc_fetch_messages(t->clients[i]->joinedchannel);
}
if (DC_CWLE(t->clients[i], t->clients[i]->sent_messages_lock)) continue;
if (t->clients[i]->sent_messages_sizeof > 0) {
@@ -695,7 +699,7 @@ int dc_api_thread (struct dc_thread_control * t) { /* updates messages and sends
msg2send->channel->messages = realloc(msg2send->channel->messages, sizeof(struct dc_message *)*++msg2send->channel->messages_sizeof);
msg2send->channel->messages[msg2send->channel->messages_sizeof-1] = msg2send;
DC_CUE(t->clients[i], t->clients[i]->guilds_lock); */ /* we will no longer do this, let the thread fetch the msg */
- for (int j = 0; j <= t->clients[i]->sent_messages_sizeof; j++) /* shift, we removed one from the start */
+ for (int j = 0; j < t->clients[i]->sent_messages_sizeof-1; j++) /* shift, we removed one from the start */
t->clients[i]->sent_messages[j] = t->clients[i]->sent_messages[j+1];
t->clients[i]->sent_messages_sizeof--;
}
@@ -705,6 +709,7 @@ int dc_api_thread (struct dc_thread_control * t) { /* updates messages and sends
}
usleep(250000);
}
+ curl_global_cleanup();
/* if (pthread_rwlock_unlock(t->clients_lock))
return -2; */
return 1;
diff --git a/src/i18n.h b/src/i18n.h
index 7cf244f..955d810 100644
--- a/src/i18n.h
+++ b/src/i18n.h
@@ -13,10 +13,14 @@
#define DC_I18N_LOCKING "zaklepanje"
#define DC_I18N_UNLOCKING "odklepanje"
#define DC_I18N_ERROR "napaka"
-#define DC_I18N_MSGTIMEF "%a, %e. %b %H:%M:%S" /* strftime(3) */
+#define DC_I18N_MSGTIMEF "%e. %b %H:%M:%S" /* strftime(3) */
#define DC_I18N_UI_USAGE "uporaba"
#define DC_I18N_UI_CHANNELS_USAGE "/kanali <številka skupine (0-%lu)>"
#define DC_I18N_UI_JOIN_USAGE "/pridruži <številka skupine (0-%lu)> <številka kanala v tej skupini (0-%lu)>"
#define DC_I18N_UI_LINE_BEFORE_JOIN "discord.c | uporabi ukaz /s za prikaz skupin, /k za prikaz kanalov in /p za priklop v kanal"
#define DC_I18N_HITRL "zmanjkalo dovoljenih zahtev na strežnik. API nit bo čakala " /* and then print seconds */
#define DC_I18N_UI_CNF "nepoznan ukaz"
+#define DC_I18N_UI_NOT_JOINED "niste pridruženi v kanal. uporabite ukaz /pridruži"
+#define DC_I18N_CHANNEL_WILL_BE_REMOVED "kanal bo odstranjen iz lokalnega seznama kanalov"
+#define DC_I18N_UI_EMPTYMSG "ne moreš poslati praznega sporočila."
+#define DC_I18N_UI_SLOWMODE "na tem kanalu po poslanem sporočilu novega ne smeš poslati %ds. počakaj še %ds in poskusi znova."
diff --git a/src/lib.c b/src/lib.c
index b6a5f3a..5a611a3 100644
--- a/src/lib.c
+++ b/src/lib.c
@@ -4,7 +4,7 @@ struct writefunc_string {
};
void init_writefunc_string(struct writefunc_string *s) {
s->len = 0;
- s->ptr = malloc(s->len+1);
+ s->ptr = malloc(s->len+1+250);
if (s->ptr == NULL) {
fprintf(stderr, "malloc() " DC_I18N_FAILED "\n");
exit(EXIT_FAILURE);
@@ -13,7 +13,7 @@ void init_writefunc_string(struct writefunc_string *s) {
}
size_t writefunc(void *ptr, size_t size, size_t nmemb, struct writefunc_string *s) {
size_t new_len = s->len + size*nmemb;
- s->ptr = realloc(s->ptr, new_len+1);
+ s->ptr = realloc(s->ptr, new_len+1+250);
if (s->ptr == NULL) {
fprintf(stderr, "realloc() " DC_I18N_FAILED "\n");
exit(EXIT_FAILURE);
diff --git a/src/main.c b/src/main.c
index d9ec103..340af42 100644
--- a/src/main.c
+++ b/src/main.c
@@ -3,7 +3,6 @@
FILE * netreq;
#include <ui.c>
int main (int argc, char ** argv) {
- curl_global_init(CURL_GLOBAL_ALL);
srand(time(NULL));
netreq = fopen("netreq.log", "w");
int rs = 0;
@@ -71,6 +70,5 @@ int main (int argc, char ** argv) {
fclose(netreq);
dc_client_free(c);
if (api_ret || ui_ret); /* to hide warnings */
- curl_global_cleanup();
return rs;
}
diff --git a/src/ui.c b/src/ui.c
index 2578689..7bcd070 100644
--- a/src/ui.c
+++ b/src/ui.c
@@ -9,6 +9,7 @@
#include <locale.h>
#include <string.h>
#include <assert.h>
+#include <signal.h>
#define DC_SIMPLEPRINT(w, c, f, ...) do { wattron(w, COLOR_PAIR(c)); wprintw(w, f __VA_OPT__(,) __VA_ARGS__); wrefresh(w); } while (0)
/* link with -lncursesw and -lformw */
int dc_ui_print_message (WINDOW * textwin, struct dc_message * msg2do) {
@@ -16,7 +17,8 @@ int dc_ui_print_message (WINDOW * textwin, struct dc_message * msg2do) {
struct tm timestruct;
localtime_r(&msg2do->time, &timestruct);
strftime(timestring, 64, DC_I18N_MSGTIMEF, &timestruct); /* recimo, da je 23 znakov */
- DC_SIMPLEPRINT(textwin, 3, "%023.23s %08.8s: %s\n", timestring, msg2do->username, msg2do->content);
+ DC_SIMPLEPRINT(textwin, 3, "%018.18s %08.8s: %s\n", timestring, msg2do->username, msg2do->content);
+ msg2do->status = 1;
return 1;
}
int dc_ui_processline (struct dc_thread_control * t, char * l, WINDOW * textwin) {
@@ -69,11 +71,10 @@ int dc_ui_processline (struct dc_thread_control * t, char * l, WINDOW * textwin)
DC_CUE(c, c->guilds_lock);
break;
}
- for (i = 0; i < c->guilds[j]->channels[k]->messages_sizeof; i++)
+ for (i = c->guilds[j]->channels[k]->messages_sizeof-1; i >= 0; i--)
dc_ui_print_message(textwin, c->guilds[j]->channels[k]->messages[i]);
c->joinedchannel = c->guilds[j]->channels[k];
DC_CUE(c, c->guilds_lock);
-
break;
case 'q':
case 'Q':
@@ -90,9 +91,35 @@ int dc_ui_processline (struct dc_thread_control * t, char * l, WINDOW * textwin)
}
t->power_api = atoi(strchr(l, ' ')+1);
DC_SIMPLEPRINT(textwin, 4, "t->power_api = %d\n", atoi(strchr(l, ' ')+1));
+ break;
default:
DC_SIMPLEPRINT(textwin, 1, DC_I18N_UI_CNF "\n");
}
+ else { /* send the message, it's not a command */
+ if (!c->joinedchannel) {
+ DC_SIMPLEPRINT(textwin, 1, "!c->joinedchannel - %s\n", DC_I18N_UI_NOT_JOINED);
+ return -1;
+ }
+ if (!strlen(l)) {
+ DC_SIMPLEPRINT(textwin, 1, "!strlen(l) - %s\n", DC_I18N_UI_EMPTYMSG);
+ return -2;
+ }
+ if (time(NULL) - c->last_sent_message <= c->joinedchannel->slowmode) {
+ DC_SIMPLEPRINT(textwin, 1, DC_I18N_UI_SLOWMODE "\n", c->joinedchannel->slowmode, c->joinedchannel->slowmode-(time(NULL)-c->last_sent_message));
+ return -3;
+ }
+ c->last_sent_message = time(NULL); /* because the other thread may not update counter before the next message is sent */
+ /* raise(SIGINT); */ /* To continue from here in GDB: "signal 0". */
+ DC_CWLE(c, c->sent_messages_lock);
+ c->sent_messages = realloc(c->sent_messages, sizeof(struct dc_message *)*++c->sent_messages_sizeof);
+#define DC_UISM c->sent_messages[c->sent_messages_sizeof-1] /* ui send messaeg */
+ DC_UISM = calloc(1, sizeof(struct dc_message));
+ DC_UISM->content = malloc(strlen(l)+1);
+ strcpy(DC_UISM->content, l);
+ DC_UISM->channel = c->joinedchannel;
+ DC_CUE(c, c->sent_messages_lock);
+ /* DO NOT free it */
+ }
wrefresh(textwin);
return 1;
}
@@ -108,12 +135,13 @@ int dc_ui_thread (struct dc_thread_control * t) {
cbreak();
noecho();
nodelay(stdscr, TRUE);
- setlocale(LC_ALL, "sl_SI.UTF-8");
+ setlocale(LC_ALL, "");
start_color();
init_pair(2, COLOR_YELLOW, COLOR_BLACK);
init_pair(1, COLOR_RED, COLOR_BLACK);
init_pair(3, COLOR_WHITE, COLOR_BLACK);
init_pair(4, COLOR_GREEN, COLOR_BLACK);
+ init_pair(5, COLOR_CYAN, COLOR_BLACK);
keypad(stdscr, TRUE);
getmaxyx(stdscr, y, x); /* to je macro, zato y in x nista kazalca (;: */
WINDOW * textwin = subwin(stdscr, y-3, x, 0, 0);
@@ -130,7 +158,7 @@ int dc_ui_thread (struct dc_thread_control * t) {
refresh();
struct dc_channel * prev_joinedchannel = (void *)&i; /* just so we get something that isn't null without warnings (): */
while (t->power_ui != 2) {
- if (!(rand() % 100)) { /* roughly every 100 cycles we get errors and messages */
+ if (!(rand() % 10)) { /* roughly every 10 cycles we get errors and messages */
assert(!DC_CRLE(c, c->errors_lock));
for (int i = 0; i < c->errors_sizeof; i++) {
if (!c->errors[i]->reported) {
@@ -141,25 +169,25 @@ int dc_ui_thread (struct dc_thread_control * t) {
assert(!pthread_rwlock_unlock(c->errors_lock)); /* deadlock if we unlock errors with error reporting, duh */
if (c->joinedchannel) {
DC_CRLE(c, c->guilds_lock);
- for (int i = 0; i < c->joinedchannel->messages_sizeof; i++) {
+ for (int i = c->joinedchannel->messages_sizeof-1; i >= 0; i--) {
struct dc_message * msg2do = c->joinedchannel->messages[i];
- if (!msg2do->status) {
+ if (!msg2do->status)
dc_ui_print_message(textwin, msg2do);
- msg2do->status = 1;
- }
}
DC_CUE(c, c->guilds_lock);
}
if (prev_joinedchannel != c->joinedchannel) {
curs_set(0); /* too flashy */
- attron(COLOR_PAIR(2));
+ attron(COLOR_PAIR(5));
if (c->joinedchannel) {
DC_CRLE(c, c->guilds_lock);
- mvprintw(y-3, 0, "#%s @ %s | %s", c->joinedchannel->name, c->joinedchannel->guild->name, c->joinedchannel->topic);
+ mvprintw(y-3, 0, "#%s @ %s%s%s ", c->joinedchannel->name, c->joinedchannel->guild->name,
+ strlen(c->joinedchannel->topic) ? " - " : "", c->joinedchannel->topic);
DC_CUE(c, c->guilds_lock);
- }
- else
+ } else {
+ attron(COLOR_PAIR(2));
mvprintw(y-3, 0, DC_I18N_UI_LINE_BEFORE_JOIN);
+ }
curs_set(1);
prev_joinedchannel = c->joinedchannel;
}
@@ -205,8 +233,8 @@ int dc_ui_thread (struct dc_thread_control * t) {
case 10:
form_driver(form, REQ_NEXT_FIELD);
form_driver(form, REQ_PREV_FIELD);
- dc_ui_processline(t, field_buffer(field[0], 0), textwin);
- form_driver(form, REQ_CLR_FIELD);
+ if (dc_ui_processline(t, field_buffer(field[0], 0), textwin) > 0)
+ form_driver(form, REQ_CLR_FIELD);
pos_form_cursor(form);
break;
default: