/* fedi_tools - GPL - Copyright 2023 - condret */ #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_post_config_t { char *instance; char *creds; char *post; char **attachments; ut32 attachment_count; } FediPostCFG; // 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 void halp () { puts ( "fedi_post [postfile] [-i] [-a " " ...\n\n" "postfile contains your post. alternativly you can use -i flag.\n" "if you use -i flag fedi_post opens $EDITOR.\n" "then you write your post, save, quit and fedi_post will do your post\n\n" "you can also attach files to your posts with the -a flag.\n" "if you use -a you need to provide the amount of files you want to attach to your post\n"); } static FediPostCFG *get_cfg (int argc, char *argv[]) { FediPostCFG *fpcfg; if (!argc || !(fpcfg = R_NEW0 (FediPostCFG))) { return NULL; } char *config_path = r_file_home (".fedi_tools.sdb"); if (!config_path) { free (fpcfg); 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 (fpcfg); 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 (fpcfg); return NULL; } fpcfg->instance = sdb_get (db, "instance", NULL); fpcfg->creds = r_str_newf ("%s:%s", sdb_const_get (db, "user", NULL), sdb_const_get (db, "password", NULL)); sdb_free (db); if (!fpcfg->instance || !fpcfg->creds) { free (fpcfg->instance); free (fpcfg->creds); free (fpcfg); return NULL; } int i; for (i = 0; i < argc; i++) { if (!fpcfg->attachment_count && !strcmp (argv[i], "-a")) { if (!(argc - i) > 2) { break; } if (r_num_is_valid_input (NULL, argv[++i])) { int k = 0, j = fpcfg->attachment_count = R_MIN ( r_num_get (NULL, argv[i]), argc - i); if (!(fpcfg->attachments = R_NEWS0 (char *, j))) { fail = true; break; } for (; j;j--) { if (r_file_exists (argv[++i]) && !r_file_is_directory (argv[i])) { fpcfg->attachments[k++] = argv[i]; } else { fpcfg->attachment_count--; } } } continue; } if (!strcmp (argv[i], "-i") && !fpcfg->post) { fpcfg->post = (char *)(size_t)0x1; continue; } if (!fpcfg->post && r_file_exists (argv[i]) && !r_file_is_directory (argv[i])) { fpcfg->post = argv[i]; } if (!strcmp (argv[i], "--help")) { puts ("Who would type --help, if -h exists?"); fail = true; break; } if (!strcmp (argv[i], "-h")) { halp (); fail = true; break; } } if (fail) { free (fpcfg->attachments); free (fpcfg->instance); free (fpcfg->creds); free (fpcfg); return NULL; } return fpcfg; } static void cfg_free (FediPostCFG *cfg) { if (cfg) { free (cfg->instance); free (cfg->creds); free (cfg->attachments); free (cfg); } } static char *cfg_get_post (FediPostCFG *cfg) { if (((size_t)cfg->post) & 0x1) { char *editor = r_sys_getenv ("EDITOR"); if (!editor) { return NULL; } char *temp = r_file_temp (NULL); if (!temp) { free (editor); return NULL; } char *sysstr = r_str_newf ("%s %s", editor, temp); if (!sysstr) { free (temp); free (editor); return NULL; } system (sysstr); free (sysstr); free (editor); char *post_data = r_file_slurp (temp, NULL); r_file_rm (temp); free (temp); return post_data; } return cfg->post? r_file_slurp (cfg->post, NULL): NULL; } 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; } static void upload_media (FediPostCFG *cfg, CURL *curl, char *media, RIODesc *tree_desc, RList *idlist) { char *media_url = r_str_newf ("%s/api/v1/media", cfg->instance); if (!media_url) { return; } curl_easy_setopt (curl, CURLOPT_URL, media_url); curl_mime *mime = curl_mime_init (curl); curl_mimepart *part = curl_mime_addpart (mime); curl_easy_setopt (curl, CURLOPT_MIMEPOST, mime); curl_mime_name (part, "file"); curl_mime_filedata (part, media); 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, tree_desc); curl_easy_perform (curl); curl_mime_free (mime); R_FREE (media_url); ut64 rec_len = r_io_desc_seek (tree_desc, 0LL, R_IO_SEEK_CUR); char *data = malloc (rec_len + 1); r_io_desc_read_at (tree_desc, 0ULL, data, (int)rec_len); data[rec_len] = '\x00'; puts (data); r_io_desc_seek (tree_desc, 0LL, R_IO_SEEK_SET); r_io_use_fd (tree_desc->io, tree_desc->fd); r_io_system (tree_desc->io, "reset"); // resets the treebuf desc; clears the internal rbtree char *end, *id = strstr (data, "\"id\":\""); if (id && (end = strchr (&id[6], '"'))) { *end = '\x00'; r_list_append (idlist, r_str_new (&id[6])); } free (data); } int main (int argc, char *argv[]) { FediPostCFG *cfg = get_cfg (argc - 1, &argv[1]); if (!cfg) { return -1; } char *statuses_url = r_str_newf ("%s/api/v1/statuses", cfg->instance); if (!statuses_url) { cfg_free (cfg); return -1; } char *post_data = cfg_get_post (cfg); RIO *io = r_io_new (); if (!io) { free (post_data); free (statuses_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 (post_data); free (statuses_url); cfg_free (cfg); return -1; } RList *ml = r_list_newf (free); int i; for (i = 0; i < cfg->attachment_count; i++) { upload_media (cfg, curl, cfg->attachments[i], r_io_desc_get (io, tree_fd), ml); } curl_mime *mime = curl_mime_init (curl); curl_mimepart *post = curl_mime_addpart (mime); curl_mime_name (post, "status"); curl_mime_data (post, post_data, CURL_ZERO_TERMINATED); curl_mimepart *visibility = curl_mime_addpart (mime); curl_mime_name (visibility, "visibility"); curl_mime_data (visibility, "public", CURL_ZERO_TERMINATED); RListIter *iter; char *media_idstr; r_list_foreach (ml, iter, media_idstr) { curl_mimepart *media_id = curl_mime_addpart (mime); curl_mime_name (media_id, "media_ids[]"); curl_mime_data (media_id, media_idstr, CURL_ZERO_TERMINATED); } curl_easy_setopt (curl, CURLOPT_URL, statuses_url); curl_easy_setopt (curl, CURLOPT_MIMEPOST, mime); 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_mime_free (mime); curl_easy_cleanup (curl); r_list_free (ml); R_FREE (post_data); free (statuses_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); puts (data); free (data); return 0; }