274 lines
6.7 KiB
C
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;
|
|
}
|