Add the fedi_post tool
This commit is contained in:
commit
f738e36e1c
280
fedi_post.c
Normal file
280
fedi_post.c
Normal file
|
@ -0,0 +1,280 @@
|
|||
/* 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 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;
|
||||
}
|
||||
puts (sdb_const_get (db, "instance", 0));
|
||||
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 (fail) {
|
||||
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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user