fedi_tools/fedi_post.c
2023-06-07 21:16:53 +02:00

301 lines
7.9 KiB
C

/* fedi_tools - GPL - Copyright 2023 - condret */
#include <curl/curl.h>
#include <r_io.h>
#include <r_util.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_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 <number of attachements>"
" <attachment 1> <attachment 2>...<attachment N>\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;
}