301 lines
7.9 KiB
C
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;
|
|
}
|