summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/api.c89
-rw-r--r--src/main.c63
-rw-r--r--src/ui.c84
3 files changed, 215 insertions, 21 deletions
diff --git a/src/api.c b/src/api.c
index 4cb7174..a90b8ac 100644
--- a/src/api.c
+++ b/src/api.c
@@ -1,3 +1,6 @@
+#if __INCLUDE_LEVEL__ != 0
+#pragma once
+#endif
#define _XOPEN_SOURCE 600
#define _POSIX_C_SOURCE 200809L
#include <sys/types.h>
@@ -63,14 +66,14 @@ struct dc_error {
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) { /* noui, noapi, nolock - only called by dc_client_free */
+void dc_error_free(struct dc_error * e) { /* noui, noapi, nolock - only called by dc_client_free */
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; /* yesfree */
int discriminator;
@@ -81,7 +84,7 @@ struct dc_message {
unsigned long long int id; /* nouiw */
unsigned short int status; /* noapiw - 0 if it was not yet printed on the display, 1 if it was */
};
-int dc_message_free (struct dc_message * m) { /* noui, noapi, nolock - only called by dc_channel_free */
+void dc_message_free (struct dc_message * m) { /* noui, noapi, nolock - only called by dc_channel_free */
free(m->username); m->username = NULL;
free(m->content); m->content = NULL;
m->channel = NULL;
@@ -105,7 +108,7 @@ struct dc_channel {
struct dc_message ** messages; /* yesfree, nouiw */
_Atomic(size_t) messages_sizeof; /* nouiw */
};
-int dc_channel_free (struct dc_channel * ch) { /* noui, noapi, nolock - only called by dc_guild_free */
+void dc_channel_free (struct dc_channel * ch) { /* noui, noapi, nolock - only called by dc_guild_free */
free(ch->name); ch->name = NULL;
free(ch->topic); ch->topic = NULL;
ch->guild = NULL;
@@ -123,7 +126,7 @@ struct dc_guild {
struct dc_channel ** channels; /* yesfree, nouiw */
_Atomic(struct dc_client *) client; /* nofree - obviously */
};
-int dc_guild_free (struct dc_guild * g) { /* noui, noapi, nolock - only called by dc_client_free */
+void dc_guild_free (struct dc_guild * g) { /* noui, noapi, nolock - only called by dc_client_free */
free(g->name); g->name = NULL;
g->id = 0;
for (int i = 0; i < g->channels_sizeof; i++)
@@ -141,7 +144,7 @@ struct dc_client {
char * password; /* yesfree, noapiw, nolock */
char * username; /* yesfree, nouiw */
pthread_rwlock_t * username_lock;
- _Atomic(int) discriminator; /* nouiw - < 0 indicates that the user is not logged in - set last in login procedure */
+ _Atomic(int) discriminator; /* nouiw - -1: the user is not logged in, -2: user login failed and will not be retried, < 0: the user is not logged in */
struct dc_guild ** guilds; /* yesfree, nouiw */
_Atomic(size_t) guilds_sizeof; /* nouiw */
pthread_rwlock_t * guilds_lock;
@@ -173,7 +176,7 @@ struct dc_client * dc_client_init () { /* gives you a prepared dc_client */
free(c);
return NULL; /* don't even check for this, if pthread_rwlock_init fails we'll segfault */
}
-int dc_client_free (struct dc_client * c) { /* noui, noapi, nolock - only called by main on exit */
+void dc_client_free (struct dc_client * c) { /* noui, noapi, nolock - only called by main on exit */
curl_easy_cleanup(c->curl);
curl_slist_free_all(c->curl_headers);
free(c->authorization); c->authorization = NULL;
@@ -241,7 +244,7 @@ cJSON * dc_api (CURL * curl, char * body, int isfmt, char * 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 */
+ vsnprintf(endpoint_formatted, strlenm, endpoint, 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);
}
@@ -280,6 +283,7 @@ int dc_login (struct dc_client * c) { /* noui */
data = malloc(strlen(DC_LOGIN_FORMAT)+strlen(c->email)+strlen(c->password)+1);
CURLcode res;
sprintf(data, DC_LOGIN_FORMAT, c->email, c->password);
+ 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");
@@ -369,11 +373,12 @@ int dc_fetch_guilds (struct dc_client * c) {
char * value2 = NULL;
CURLcode res;
cJSON * json = NULL;
- if (c->discriminator < 0)
+ if (c->discriminator < 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);
@@ -413,7 +418,7 @@ int dc_fetch_guilds (struct dc_client * c) {
for (int i = 0; i < c->guilds_sizeof; i++)
if (idull == c->guilds[i]->id)
continue; /* remove duplicates */
- c->guilds = realloc(c->guilds, ++c->guilds_sizeof);
+ c->guilds = realloc(c->guilds, sizeof(struct dc_guild *)*++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 = idull;
@@ -435,11 +440,12 @@ int dc_fetch_channels (struct dc_guild * g) {
struct dc_client * c = g->client;
if (!c)
return -2;
- if (c->discriminator < 0)
+ if (c->discriminator < 0) {
if ((rs = dc_login(c)) < 0) {
DC_CLIENT_ERROR(c, "dc_login(c) " DC_I18N_FAILED " (%d)", rs);
return -3;
} else rs = 1;
+ }
CURLcode res;
struct writefunc_string s;
init_writefunc_string(&s);
@@ -491,7 +497,7 @@ int dc_fetch_channels (struct dc_guild * g) {
for (int i = 0; i < g->channels_sizeof; i++)
if (idull == g->channels[i]->id)
continue; /* remove duplicates */
- g->channels = realloc(g->channels, ++g->channels_sizeof);
+ g->channels = realloc(g->channels, sizeof(struct dc_channel *)*++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);
@@ -515,11 +521,12 @@ int dc_send_message (struct dc_message * m) { /* nolock - once message is append
struct dc_client * c = m->channel->guild->client; /* segfault senpai */
if (!c)
return -2;
- if (!c->username || !c->username[0])
+ if (c->discriminator < 0) {
if ((rs = dc_login(c)) < 0) {
DC_CLIENT_ERROR(c, "dc_login(c) " DC_I18N_FAILED " (%d)", rs);
return -3;
} else rs = 1;
+ }
cJSON * json = cJSON_CreateObject();
cJSON * nons = cJSON_CreateNumber(rand());
cJSON_AddItemToObject(json, "nonce", nons);
@@ -577,11 +584,12 @@ int dc_fetch_messages (struct dc_channel * ch) {
struct dc_client * c = g->client;
if (!c)
return -3;
- if (c->discriminator < 0)
+ if (c->discriminator < 0) {
if ((rs = dc_login(c)) < 0) {
DC_CLIENT_ERROR(c, "dc_login(c) " DC_I18N_FAILED " (%d)", rs);
return -4;
} else rs = 1;
+ }
cJSON * json = DC_API(c->curl, NULL, DC_API_PREFIX "channels/%llu/messages?limit=100&_=%d", ch->id, rand());
if (!json) {
const char *error_ptr = cJSON_GetErrorPtr();
@@ -615,7 +623,7 @@ int dc_fetch_messages (struct dc_channel * ch) {
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 */
+ 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;
@@ -624,7 +632,7 @@ int dc_fetch_messages (struct dc_channel * ch) {
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);
+ 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->time = mktime(&tm);
DC_FMTM->content = malloc(strlen(content)+1);
@@ -641,12 +649,51 @@ int dc_fetch_messages (struct dc_channel * ch) {
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_thread_control {
+ unsigned short int power_api; /* 1 if the thread should run, set it to 0 for the thread to return at the end of the loop */
+ unsigned short int power_ui; /* so same struct can be used for both api and ui thread, they have individual power switches */
struct dc_client ** clients; /* "array" of pointers to clients the thread should manage, ONLY ONE dc_api_thread PER PROCESS! */
- _Atomic(size_t) dc_clients_sizeof;
- pthread_rwlock_t * clients_lock;
+ _Atomic(size_t) clients_sizeof; /* noapiw */
+ pthread_rwlock_t * clients_lock; /* do not lock yet. you can use safe-appending from the ui thread by never reallocing the pointers and only incrementing _sizeof - don't even init&destroy*/
+ FILE * cout; /* file descriptor of the terminal for the ui thread to write to */
+ FILE * cin; /* file descriptor of the terminal for the ui thread to read from */
+ FILE * cerr; /* file descriptor of the terminal for the ui thread to write error messages to */
};
-int dc_api_thread (struct dc_api_thread_control * t) { /* updates messages and sends messages when they are in the outbox */
+int dc_api_thread (struct dc_thread_control * t) { /* updates messages and sends messages when they are in the outbox */
+ /* if (pthread_rwlock_wrlock(t->clients_lock))
+ return -1; */ /* clients are not locked yet */
+ for (int i = 0; i < t->clients_sizeof; i++)
+ dc_login(t->clients[i]);
+ while (t->power_api == 1) { /* as there's only one api thread and only it modifies things that need a guilds_lock, it's okay to */
+ for (int i = 0; i < t->clients_sizeof; i++) { /* perform such unsafe loops without read-locking. note that you will deadlock */
+ if (t->clients[i]->discriminator < -1) /* should you attempt to read-lock guilds_lock. */
+ continue; /* the only exception is sent_messages that is write-locked */
+ if (!t->clients[i]->guilds_sizeof || !(rand() % 1000)) /* roughly every 1000 cycles we'll update guilds */
+ dc_fetch_guilds(t->clients[i]);
+ for (int j = 0; j < t->clients[i]->guilds_sizeof; j++) {
+ if (!t->clients[i]->guilds[j]->channels_sizeof || !(rand() % 100)) /* roughly every 100 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]);
+ }
+ if (DC_CWLE(t->clients[i], t->clients[i]->sent_messages_lock)) continue;
+ if (t->clients[i]->sent_messages_sizeof > 0) {/* no need to lock as only one thread does this */
+ struct dc_message * msg2send = t->clients[i]->sent_messages[0];
+ if (dc_send_message(msg2send) > 0) {
+ DC_CWLE(t->clients[i], t->clients[i]->guilds_lock);
+ 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);
+ for (int j = 0; j <= t->clients[i]->sent_messages_sizeof; 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--;
+ }
+ }
+ while (DC_CUE(t->clients[i], t->clients[i]->sent_messages_lock));
+ }
+ }
+ /* if (pthread_rwlock_unlock(t->clients_lock))
+ return -2; */
return 1;
} /* the thread shall use mutexes when doing things with shared memory - client structs */
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..28b1449
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,63 @@
+#include <ui.h>
+int main (int argc, char ** argv) {
+ srand(time(NULL));
+ int rs = 0;
+ int opt;
+ pthread_t api_thread, ui_thread;
+ int api_ret ui_ret;
+ struct dc_client * c = dc_client_init();
+ struct dc_thread_control = {
+ .power_api = 1;
+ .power_ui = 1;
+ .clients = &c;
+ .clients_sizeof = 1;
+ .cout = stdout;
+ .cin = stdin;
+ .cerr = stderr;
+ };
+ while ((opt = getopt(argc, argv, "e:p:h")) != -1) {
+ switch (opt) {
+ case 'h':
+ fprintf(stdout, DC_I18N_USAGE);
+ dc_client_free(c);
+ return 0;
+ break;
+ case 'e':
+ c->email = malloc(strlen(optarg)+1);
+ strcpy(c->email, optarg);
+ break;
+ case 'p':
+ c->password = malloc(strlen(optarg)+1);
+ strcpy(c->password, optarg);
+ break;
+ default:
+ fprintf(stderr, DC_I18N_UNREC_ARG "\n", opt);
+ dc_client_free(c);
+ return 1;
+ }
+ }
+ if (!c->email) {
+ if (!getenv("DC_E")) {
+ fprintf(stderr, DC_I18N_MISSING_EP "\n");
+ dc_client_free(c);
+ return 2;
+ }
+ c->email = malloc(strlen(getenv("DC_E"))+1);
+ strcpy(c->email, getenv("DC_E"));
+ }
+ if (!c->password) {
+ if (!getenv("DC_P")) {
+ fprintf(stderr, DC_I18N_MISSING_EP "\n");
+ dc_client_free(c);
+ return 3;
+ }
+ c->password = malloc(strlen(getenv("DC_P"))+1);
+ strcpy(c->password, getenv("DC_P"));
+ }
+ api_ret = pthread_create(&api_thread NULL, dc_api_thread, dc_thread_control);
+ ui_ret = pthread_create(&ui_thread, NULL, dc_ui_thread, dc_thread_control);
+ pthread_join(api_thread, NULL);
+ pthread_join(ui_thread, NULL);
+ rc:
+ return rs;
+}
diff --git a/src/ui.c b/src/ui.c
new file mode 100644
index 0000000..3f8dbaf
--- /dev/null
+++ b/src/ui.c
@@ -0,0 +1,84 @@
+#define _XOPEN_SOURCE 600
+#define _POSIX_C_SOURCE 200809L
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+/* #include <api.c> */
+#include <ncursesw/ncurses.h>
+#include <ncursesw/form.h>
+#include <unistd.h>
+#include <locale.h>
+/* link with -lncursesw and -lformw */
+/* int dc_ui_thread (struct * dc_thread_control t) { */
+int main () {
+ FIELD * field[2]; /* field[0] je polje z besedilom */
+ field[1] = NULL;
+ FORM * form;
+ int ret, x, y;
+ wint_t ch;
+ initscr();
+ cbreak();
+ noecho();
+ nodelay(stdscr, TRUE);
+ setlocale(LC_ALL, "sl_SI.UTF-8");
+ start_color();
+ init_pair(0, COLOR_WHITE, COLOR_BLACK);
+ init_pair(1, COLOR_RED, COLOR_BLACK);
+ attron(COLOR_PAIR(1));
+ keypad(stdscr, TRUE);
+ getmaxyx(stdscr, y, x); /* to je macro, zato y in x nista kazalca (;: */
+ WINDOW * textwin = newwin(y-3, x, 0, 0);
+ WINDOW * formwin = newwin(2, x, y-2, 0);
+ scrollok(textwin, TRUE);
+ field[0] = new_field(2, x, y-2, 0, 5 /* offscreen rows */, 0);
+ set_field_back(field[0], A_UNDERLINE);
+ field_opts_off(field[0], O_AUTOSKIP);
+ form = new_form(field);
+ set_form_win(form, formwin);
+ post_form(form);
+ int i = 0;
+ wmove(textwin, 0, 0);
+ time_t last_time = 0;
+ refresh();
+ while (1) {
+ if (last_time < time(NULL)) {
+ last_time = time(NULL);
+ attron(COLOR_PAIR(0));
+ wprintw(textwin, "\nvrstica stevilka %d", i);
+ attron(COLOR_PAIR(1));
+ mvprintw(y-3, 0, "#piš-ti-kurc-2 | discord.c | šijanec 2021");
+ }
+ ret = get_wch(&ch);
+ if (ret != ERR) {
+ switch (ch) {
+ case KEY_LEFT:
+ form_driver(form, REQ_PREV_CHAR);
+ break;
+ case KEY_RIGHT:
+ form_driver(form, REQ_NEXT_CHAR);
+ break;
+ case KEY_BACKSPACE:
+ form_driver(form, REQ_DEL_PREV);
+ break;
+ case KEY_DOWN:
+ form_driver(form, REQ_SCR_FLINE);
+ break;
+ case KEY_UP:
+ form_driver(form, REQ_SCR_BLINE);
+ break;
+ default:
+ form_driver_w(form, ret, ch);
+ break;
+ }
+ }
+ wnoutrefresh(stdscr);
+ wnoutrefresh(textwin);
+ doupdate();
+ i++;
+ }
+ unpost_form(form);
+ free_form(form);
+ free_field(field[0]);
+ endwin();
+ return 1;
+}