/* fedi_tools - GPL - Copyright 2023 - condret */ #include #include #include #include #include #include __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; }