fedi_tools/fedi_notifs.c
2023-06-08 06:40:06 +02:00

274 lines
6.7 KiB
C

/* fedi_tools - GPL - Copyright 2023 - condret */
#include <curl/curl.h>
#include <r_io.h>
#include <r_util.h>
#include <json.h>
#include <stdlib.h>
#include <stdio.h>
__attribute__((constructor)) void foo(void) {
curl_global_init (CURL_GLOBAL_ALL);
}
__attribute__((destructor)) void bla(void) {
curl_global_cleanup ();
}
typedef struct fedi_notifs_config_t {
char *instance;
char *creds;
} FediNotifsCFG;
// workaround for a bug in sdb
static Sdb *load_sdb (char *path) {
Sdb *ret = sdb_new0 ();
Sdb *on_disk = sdb_new0 ();
if (sdb_open (on_disk, path) < 0) {
sdb_free (ret);
sdb_free (on_disk);
return NULL;
}
sdb_copy (on_disk, ret);
sdb_close (on_disk);
sdb_free (on_disk);
return ret;
}
static FediNotifsCFG *get_cfg () {
FediNotifsCFG *fncfg;
if (!(fncfg = R_NEW0 (FediNotifsCFG))) {
return NULL;
}
char *config_path = r_file_home (".fedi_tools.sdb");
if (!config_path) {
free (fncfg);
return NULL;
}
Sdb *db = load_sdb (config_path);
if (!db) {
eprintf ("couldn't open %s :(\n", config_path);
sdb_free (db);
free (config_path);
free (fncfg);
return NULL;
}
bool fail = false;
if (!sdb_exists (db, "instance")) {
eprintf ("'instance' is not set in %s :(\n", config_path);
fail = true;
}
if (!sdb_exists (db, "user")) {
eprintf ("'user' is not set in %s :(\n", config_path);
fail = true;
}
if (!sdb_exists (db, "password")) {
eprintf ("'password' is not set in %s :(\n", config_path);
fail = true;
}
R_FREE (config_path);
if (fail) {
sdb_free (db);
free (fncfg);
return NULL;
}
fncfg->instance = sdb_get (db, "instance", NULL);
fncfg->creds = r_str_newf ("%s:%s", sdb_const_get (db, "user", NULL),
sdb_const_get (db, "password", NULL));
sdb_free (db);
if (!fncfg->instance || !fncfg->creds) {
free (fncfg->instance);
free (fncfg->creds);
free (fncfg);
return NULL;
}
return fncfg;
}
static void cfg_free (FediNotifsCFG *cfg) {
if (cfg) {
free (cfg->instance);
free (cfg->creds);
free (cfg);
}
}
typedef struct fedi_notif_t {
char *acct;
char *display_name;
char *type;
} FediNotif;
typedef struct json_notif_parser_t {
ut32 depth;
RQueue *notif_queue;
RStack *code_stack;
FediNotif *cur;
} JsonNParser;
typedef int (*HandleJson)(JsonNParser *jnp, int type, const char *data, ut32 len);
static int handle_account_acct (JsonNParser *jnp, int type, const char *data, ut32 len) {
jnp->cur->acct = R_NEWS0 (char, len + 1);
memcpy (jnp->cur->acct, data, len);
r_stack_pop (jnp->code_stack);
return 0;
}
static int handle_account_display_name (JsonNParser *jnp, int type, const char *data, ut32 len) {
jnp->cur->display_name = R_NEWS0 (char, len + 1);
memcpy (jnp->cur->display_name, data, len);
r_stack_pop (jnp->code_stack);
return 0;
}
static int handle_account (JsonNParser *jnp, int type, const char *data, ut32 len) {
if (type == JSON_OBJECT_BEGIN) {
jnp->depth++;
return 0;
}
if (type == JSON_KEY && len == 4 && !memcmp (data, "acct", 4)) {
r_stack_push (jnp->code_stack, handle_account_acct);
return 0;
}
if (type == JSON_KEY && len == 12 && !memcmp (data, "display_name", 12)) {
r_stack_push (jnp->code_stack, handle_account_display_name);
return 0;
}
if (type == JSON_OBJECT_END) {
if (2 == jnp->depth--) {
r_stack_pop (jnp->code_stack);
}
return 0;
}
return 0;
}
static int handle_status (JsonNParser *jnp, int type, const char *data, ut32 len) {
if (type == JSON_OBJECT_BEGIN) {
jnp->depth++;
return 0;
}
if (type == JSON_OBJECT_END) {
if (2 == jnp->depth--) {
r_stack_pop (jnp->code_stack);
}
return 0;
}
return 0;
}
static int handle_type (JsonNParser *jnp, int type, const char *data, ut32 len) {
jnp->cur->type = R_NEWS0 (char, len + 1);
memcpy (jnp->cur->type, data, len);
r_stack_pop (jnp->code_stack);
return 0;
}
static int parser_cb (void *user, int type, const char *data, uint32_t length) {
JsonNParser *jnp = (JsonNParser *)user;
HandleJson h = (HandleJson)r_stack_peek (jnp->code_stack);
if (h) {
return h(jnp, type, data, length);
}
if (type == JSON_OBJECT_BEGIN) {
if (jnp->depth == 0) {
jnp->cur = R_NEW0 (FediNotif);
}
jnp->depth++;
}
if (type == JSON_KEY) {
if (length == 7 && !memcmp (data, "account", 7)) {
r_stack_push (jnp->code_stack, handle_account);
return 0;
}
if (length == 6 && !memcmp (data, "status", 6)) {
r_stack_push (jnp->code_stack, handle_status);
return 0;
}
if (length == 4 && !memcmp (data, "type", 4)) {
r_stack_push (jnp->code_stack, handle_type);
return 0;
}
}
if (type == JSON_OBJECT_END) {
if (jnp->depth == 1) {
r_queue_enqueue (jnp->notif_queue, jnp->cur);
jnp->cur = NULL;
}
jnp->depth--;
}
return 0;
}
static size_t write_data(void *ptr, size_t size, size_t nmemb, void *user) {
RIODesc *desc = (RIODesc *)user;
size_t dlen = size * nmemb;
while (dlen > 0x7fffffff) {
r_io_desc_write (desc, (const ut8 *)ptr, 0x7fffffff);
ptr += 0x7fffffff;
dlen -= 0x7fffffff;
}
r_io_desc_write (desc, (const ut8 *)ptr, (int)dlen);
return size * nmemb;
}
int main (int argc, char *argv[]) {
FediNotifsCFG *cfg = get_cfg ();
if (!cfg) {
return -1;
}
char *notifs_url = r_str_newf ("%s/api/v1/notifications", cfg->instance);
if (!notifs_url) {
cfg_free (cfg);
return -1;
}
RIO *io = r_io_new ();
if (!io) {
free (notifs_url);
cfg_free (cfg);
return -1;
}
int tree_fd = r_io_fd_open (io, "treebuf://", R_PERM_RW, 0);
CURL *curl = curl_easy_init();
if(!curl) {
r_io_free (io);
free (notifs_url);
cfg_free (cfg);
return -1;
}
curl_easy_setopt (curl, CURLOPT_HTTPGET, 1L);
curl_easy_setopt (curl, CURLOPT_URL, notifs_url);
curl_easy_setopt (curl, CURLOPT_HTTPAUTH, (long)CURLAUTH_BASIC);
curl_easy_setopt (curl, CURLOPT_USERPWD, cfg->creds);
curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, write_data);
curl_easy_setopt (curl, CURLOPT_WRITEDATA, r_io_desc_get (io, tree_fd));
curl_easy_perform (curl);
ut64 rec_len = r_io_fd_seek (io, tree_fd, 0LL, R_IO_SEEK_CUR);
curl_easy_cleanup (curl);
free (notifs_url);
cfg_free (cfg);
char *data = malloc (rec_len + 1);
r_io_fd_read_at (io, tree_fd, 0ULL, data, (int)rec_len);
data[rec_len] = '\x00';
r_io_free (io);
JsonNParser jnp = {false, r_queue_new (2), r_stack_new (8)};
json_parser parser;
json_parser_init (&parser, NULL, parser_cb, &jnp);
json_parser_string (&parser, data, rec_len, NULL);
free (data);
json_parser_free (&parser);
r_stack_free (jnp.code_stack);
while (!r_queue_is_empty (jnp.notif_queue)) {
FediNotif *notif = (FediNotif *)r_queue_dequeue (jnp.notif_queue);
printf ("%s: @%s\n%s\n", notif->display_name, notif->acct, notif->type);
puts ("==========================================\n");
free (notif->acct);
free (notif->display_name);
free (notif->type);
free (notif);
}
r_queue_free (jnp.notif_queue);
return 0;
}