aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlibek Omarov <a1ba.omarov@gmail.com>2021-07-29 17:15:47 +0300
committerAlibek Omarov <a1ba.omarov@gmail.com>2021-07-29 17:15:47 +0300
commitc861857a39898a9e77d782617345cb17ecb4cf10 (patch)
tree0adbb8b7df64353d0076f0f66a9a1fb18c62de0f
parent311bcd01fb23a8858ce1626d473d2d4c7a71cfa2 (diff)
downloaddemiurge-c861857a39898a9e77d782617345cb17ecb4cf10.tar.gz
demiurge-c861857a39898a9e77d782617345cb17ecb4cf10.zip
Overhaul config, allow overriding config with DEMIURGERC environment variable, rewrite login
-rw-r--r--src/follow.c39
-rw-r--r--src/login.c162
-rw-r--r--src/main.c19
-rw-r--r--src/post.c20
-rw-r--r--src/upload_file.c18
-rw-r--r--src/util.c128
-rw-r--r--src/util.h40
7 files changed, 297 insertions, 129 deletions
diff --git a/src/follow.c b/src/follow.c
index b383cdf..d14ea0e 100644
--- a/src/follow.c
+++ b/src/follow.c
@@ -25,13 +25,13 @@
char *
get_account_id(char *name)
{
- char instance[50];
- char client_id[50];
- char client_secret[50];
- char access_token[50];
+ struct config config;
+
+ if(load_config(&config) < 0) {
+ fprintf(stderr, "Error loading config");
+ return NULL;
+ }
- get_tokens_from_file(
- ".demiurgerc", instance, client_id, client_secret, access_token);
CURL *curl = curl_easy_init();
if(curl == NULL) {
fprintf(stderr, "Error creating libcurl thing\n");
@@ -39,7 +39,7 @@ get_account_id(char *name)
}
char *api_url_fmt = "%s/api/v1/accounts/%s/";
char *api_url;
- dm_asprintf(&api_url, api_url_fmt, instance, name);
+ dm_asprintf(&api_url, api_url_fmt, config.instance, name);
struct json_object *parsed_json;
struct json_object *account_id;
@@ -51,7 +51,7 @@ get_account_id(char *name)
char *authorization_header = NULL;
struct curl_slist *header_list = NULL;
- dm_asprintf(&authorization_header, header_fmt, access_token);
+ dm_asprintf(&authorization_header, header_fmt, config.access_token);
header_list = curl_slist_append(header_list, authorization_header);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header_list);
@@ -73,6 +73,7 @@ get_account_id(char *name)
free(api_url);
free(authorization_header);
curl_slist_free_all(header_list);
+
/* parse the thing */
parsed_json = json_tokener_parse(chunk.response);
@@ -83,19 +84,21 @@ get_account_id(char *name)
char *return_str = malloc(strlen(str + 1));
strcpy(return_str, str); // strdup() segfaults for some reason
json_object_put(parsed_json);
- free(chunk.response);
+
+ free_response(&chunk);
return return_str;
}
int
follow_account(char *id, char action)
{
- char instance[50];
- char client_id[50];
- char client_secret[50];
- char access_token[50];
- get_tokens_from_file(
- ".demiurgerc", instance, client_id, client_secret, access_token);
+ struct config config;
+
+ if(load_config(&config) < 0) {
+ fprintf(stderr, "Error loading config");
+ return -1;
+ }
+
CURL *curl = curl_easy_init();
if(curl == NULL) {
fprintf(stderr, "Error creating libcurl thing\n");
@@ -108,13 +111,13 @@ follow_account(char *id, char action)
api_url_fmt = "%s/api/v1/accounts/%s/unfollow";
char *api_url;
- dm_asprintf(&api_url, api_url_fmt, instance, id);
+ dm_asprintf(&api_url, api_url_fmt, config.instance, id);
char *header_fmt = "Authorization: Bearer %s";
char *authorization_header = NULL;
struct curl_slist *header_list = NULL;
- dm_asprintf(&authorization_header, header_fmt, access_token);
+ dm_asprintf(&authorization_header, header_fmt, config.access_token);
header_list = curl_slist_append(header_list, authorization_header);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header_list);
@@ -143,7 +146,7 @@ follow_account(char *id, char action)
json_object_put(parsed_json);
- free(chunk.response);
+ free_response(&chunk);
return 0;
}
diff --git a/src/login.c b/src/login.c
index 9829d75..67b6b22 100644
--- a/src/login.c
+++ b/src/login.c
@@ -25,50 +25,25 @@
#include "asprintf.h"
#include "util.h"
-void
-store_config(const char *instance,
- const char *client_id,
- const char *client_secret,
- const char *access_token)
-{
- FILE *fp = fopen(".demiurgerc", "w+");
-
- fprintf(fp, "%s=%s\n", "instance", instance);
- fprintf(fp, "%s=%s\n", "client_id", client_id);
- fprintf(fp, "%s=%s\n", "client_secret", client_secret);
- fprintf(fp, "%s=%s\n", "access_token", access_token);
- fclose(fp);
-}
-
/* I am confident to present you, without a doubt, the worst code i've
* ever written */
-int
-setup()
+static int
+get_client_id(struct config *config)
{
- char *instance = NULL;
- instance =
- readline("Enter your instance (e.g. https://social.fnord.tld) ");
-
char *api_url = "/api/v1/apps";
CURL *curl = curl_easy_init();
- struct json_object *parsed_json;
- struct json_object *json_client_id;
- struct json_object *json_client_secret;
- struct json_object *json_access_token;
-
if(curl == NULL) {
fprintf(stderr, "Error creating curl (what?)\n");
return -1;
}
-
- char *post_url = NULL;
struct memory chunk = {0};
- dm_asprintf(&post_url, "%s%s", instance, api_url);
+ char *post_url = NULL;
+ dm_asprintf(&post_url, "%s%s", config->instance, api_url);
curl_easy_setopt(curl, CURLOPT_URL, post_url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, cb);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
@@ -78,53 +53,86 @@ setup()
* to do this */
curl_easy_setopt(curl,
- CURLOPT_POSTFIELDS,
- "client_name=demiurge&redirect_uris=urn:ietf:wg:oauth:2."
- "0:oob&scope=read+write+follow");
+ CURLOPT_POSTFIELDS,
+ "client_name=demiurge"
+ "&redirect_uris=urn:ietf:wg:oauth:2.0:oob"
+ "&scope=read+write+follow"
+ "&website=https://example.org");
curl_easy_perform(curl);
curl_easy_cleanup(curl);
+ free(post_url);
+
+ struct json_object *parsed_json;
+ struct json_object *json_client_id;
+ struct json_object *json_client_secret;
parsed_json = json_tokener_parse(chunk.response);
if(parsed_json == NULL) {
fprintf(stderr, "error\n");
+ free_response(&chunk);
return -1;
}
json_object_object_get_ex(parsed_json, "client_id", &json_client_id);
- json_object_object_get_ex(
- parsed_json, "client_secret", &json_client_secret);
+ json_object_object_get_ex(parsed_json, "client_secret", &json_client_secret);
+
const char *client_id = json_object_get_string(json_client_id);
const char *client_secret = json_object_get_string(json_client_secret);
+ int ret;
+
+ if(!client_id || !client_secret) {
+ eputs("Can't get client_id or client_secret");
+ ret = -1;
+ } else {
+ dm_strncpy(config->client_id, client_id, sizeof(config->client_id));
+ dm_strncpy(config->client_secret, client_secret, sizeof(config->client_secret));
+ ret = 0;
+ }
- char *fmt = "%s%sresonse_type=code&client_id=%s&redirect_uri=urn:ietf:wg:"
- "oauth:2.0:oob&force_login&scope=read+write+follow";
- api_url = "/oauth/authorize?";
-
- free(post_url);
-
- post_url = NULL;
+ free_response(&chunk);
+ return ret;
+}
- dm_asprintf(&post_url, fmt, instance, api_url, client_id);
- puts(post_url);
+static char *
+ask_for_token(struct config *config)
+{
+ const char *api_url = "/oauth/authorize?";
+ char *fmt = "%s%s"
+ "client_id=%s"
+ "&redirect_uri=urn:ietf:wg:oauth:2.0:oob"
+ "&response_type=code"
+ "&scope=read+write+follow\n";
- curl = curl_easy_init();
+ printf(fmt, config->instance, api_url, config->client_id);
- char *code = NULL;
- code = readline("Please log in in that url and paste the code here: ");
+ return readline("Please log in in that url and paste the code here: ");
+}
- char *access_token_fmt =
- "client_id=%s&client_secret=%s&redirect_uri=urn:ietf:wg:oauth:2.0:"
- "oob&grant_type=authorization_code&code=%s&scope=read+write+follow";
- api_url = "/oauth/token";
+static int
+get_token(struct config *config, const char *code)
+{
+ char *fmt =
+ "client_id=%s"
+ "&client_secret=%s"
+ "&redirect_uri=urn:ietf:wg:oauth:2.0:oob"
+ "&code=%s"
+ "&grant_type=authorization_code"
+ "&scope=read+write+follow";
+ const char *api_url = "/oauth/token";
- char *post_token_url = NULL;
+ CURL *curl = curl_easy_init();
+ if(curl == NULL) {
+ fprintf(stderr, "Error creating curl (what?)\n");
+ return -1;
+ }
- dm_asprintf(
- &post_token_url, access_token_fmt, client_id, client_secret, code);
+ struct memory chunk = {0};
- post_url = NULL;
+ char *post_token_url;
+ char *post_url;
- dm_asprintf(&post_url, "%s%s", instance, api_url);
+ dm_asprintf(&post_token_url, fmt, config->client_id, config->client_secret, code);
+ dm_asprintf(&post_url, "%s%s", config->instance, api_url);
curl_easy_setopt(curl, CURLOPT_URL, post_url);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_token_url);
@@ -132,15 +140,51 @@ setup()
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
curl_easy_perform(curl);
+ curl_easy_cleanup(curl);
+ free(post_token_url);
+ free(post_url);
+ struct json_object *parsed_json;
+ struct json_object *json_access_token;
+
parsed_json = json_tokener_parse(chunk.response);
json_object_object_get_ex(parsed_json, "access_token", &json_access_token);
const char *access_token = json_object_get_string(json_access_token);
- store_config(instance, client_id, client_secret, access_token);
+ int ret;
+ if(!access_token) {
+ eputs("Can't get access_token");
+ ret = -1;
+ } else {
+ dm_strncpy(config->access_token, access_token, sizeof(config->access_token));
+ ret = 0;
+ }
+
+ free_response(&chunk);
+
+ return ret;
+}
+
+int
+setup()
+{
+ struct config config;
+
+ dm_strncpy(config.instance, readline("Enter your instance (e.g. https://social.fnord.tld) "),
+ sizeof(config.instance));
+
+ if(get_client_id(&config) < 0)
+ return -1;
+
+ char *code = ask_for_token(&config);
+
+ if(get_token(&config, code) < 0)
+ {
+ free(code);
+ return -1;
+ }
+
+ store_config(&config);
free(code);
- free(post_url);
- free(post_token_url);
- free(chunk.response);
return 0;
}
diff --git a/src/main.c b/src/main.c
index bfd3b6a..0d20b1d 100644
--- a/src/main.c
+++ b/src/main.c
@@ -17,11 +17,11 @@
#include <stdio.h>
#include <readline/readline.h>
-#include <unistd.h>
#include <stdlib.h>
#include <getopt.h>
#include <libgen.h>
#include <stdbool.h>
+#include <unistd.h>
#include "login.h"
#include "util.h"
@@ -43,8 +43,9 @@ usage(const char *progname)
int
main(int argc, char **argv)
{
- if(access(".demiurgerc", F_OK))
- setup();
+ if(config_exists() != 0 && setup() != 0) {
+ eputs("Login failed");
+ }
int c;
char *status = NULL;
char *visibility = NULL;
@@ -77,13 +78,17 @@ main(int argc, char **argv)
break;
case 'f':
account_id = get_account_id(optarg);
- follow_account(account_id,'f');
- free(account_id);
+ if(account_id) {
+ follow_account(account_id,'f');
+ free(account_id);
+ }
return 0;
case 'u':
account_id = get_account_id(optarg);
- follow_account(account_id,'u');
- free(account_id);
+ if(account_id) {
+ follow_account(account_id,'u');
+ free(account_id);
+ }
return 0;
}
}
diff --git a/src/post.c b/src/post.c
index 7994493..2d93f37 100644
--- a/src/post.c
+++ b/src/post.c
@@ -29,13 +29,13 @@
int
post_status(const char *status, const char *scope, const char *media_id)
{
- char instance[50];
- char client_id[50];
- char client_secret[50];
- char access_token[50];
+ struct config config;
+
+ if(load_config(&config) < 0) {
+ fprintf(stderr, "Error loading config");
+ return -1;
+ }
- get_tokens_from_file(
- ".demiurgerc", instance, client_id, client_secret, access_token);
CURL *curl = curl_easy_init();
if(curl == NULL) {
fprintf(stderr, "Error creating libcurl thing\n");
@@ -45,12 +45,12 @@ post_status(const char *status, const char *scope, const char *media_id)
char *api_url = "/api/v1/statuses";
char *url = NULL;
- dm_asprintf(&url, "%s%s", instance, api_url);
+ dm_asprintf(&url, "%s%s", config.instance, api_url);
char *header_fmt = "Authorization: Bearer %s";
char *authorization_header = NULL;
struct curl_slist *header_list = NULL;
- dm_asprintf(&authorization_header, header_fmt, access_token);
+ dm_asprintf(&authorization_header, header_fmt, config.access_token);
header_list = curl_slist_append(header_list, authorization_header);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header_list);
@@ -109,7 +109,7 @@ post_status(const char *status, const char *scope, const char *media_id)
parsed_json = json_tokener_parse(chunk.response);
json_object_object_get_ex(parsed_json, "url", &json_url);
json_object_object_get_ex(parsed_json, "error", &server_response);
- char *error = (char *)json_object_get_string(server_response);
+ const char *error = json_object_get_string(server_response);
if(json_url == NULL) {
fprintf(stderr, "Error posting, server said: \"%s\"\n", error);
return -1;
@@ -118,7 +118,7 @@ post_status(const char *status, const char *scope, const char *media_id)
free(url);
free(authorization_header);
- free(chunk.response);
+ free_response(&chunk);
json_object_put(parsed_json);
return 0;
diff --git a/src/upload_file.c b/src/upload_file.c
index c17720c..3a9cf48 100644
--- a/src/upload_file.c
+++ b/src/upload_file.c
@@ -25,16 +25,16 @@
int
upload_file(const char *path, const char *description, char **id_ptr)
{
- char instance[50];
- char client_id[50];
- char client_secret[50];
- char access_token[50];
+ struct config config;
+
+ if(load_config(&config) < 0) {
+ fprintf(stderr, "Error loading config");
+ return -1;
+ }
struct json_object *parsed_json;
struct json_object *json_media_id;
- get_tokens_from_file(
- ".demiurgerc", instance, client_id, client_secret, access_token);
CURL *curl = curl_easy_init();
if(curl == NULL) {
fprintf(stderr, "Error creating libcurl thing\n");
@@ -47,7 +47,7 @@ upload_file(const char *path, const char *description, char **id_ptr)
curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
char *url_to_post = NULL;
- dm_asprintf(&url_to_post, "%s/api/v1/media", instance);
+ dm_asprintf(&url_to_post, "%s/api/v1/media", config.instance);
/* Don't repeat yourself, so they say, it's the root of all evil
* today */
@@ -55,7 +55,7 @@ upload_file(const char *path, const char *description, char **id_ptr)
char *header_fmt = "Authorization: Bearer %s";
struct curl_slist *header_list = NULL;
char *authorization_header = NULL;
- dm_asprintf(&authorization_header, header_fmt, access_token);
+ dm_asprintf(&authorization_header, header_fmt, config.access_token);
header_list = curl_slist_append(header_list, authorization_header);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header_list);
@@ -93,7 +93,7 @@ upload_file(const char *path, const char *description, char **id_ptr)
const char *media_id = json_object_get_string(json_media_id);
*id_ptr = (char *)media_id;
free(parsed_json);
- free(chunk.response);
+ free_response(&chunk);
return 0;
}
diff --git a/src/util.c b/src/util.c
index 04c8d25..15e1440 100644
--- a/src/util.c
+++ b/src/util.c
@@ -18,39 +18,125 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include "util.h"
-struct memory
+static const char *
+get_config_filename(void)
{
- char *response;
- size_t size;
-};
+ static const char *ret = NULL;
-/* This function fucking sucks and should use something else. I'll
- * have to rewrite this shit function some day. But for now, it will
- * do the job.
- * TODO: Check for errors
- */
+ if(ret)
+ return ret;
-/* Be aware with buffer overflows, fscanf() */
+ ret = getenv("DEMIURGERC");
+
+ if(!ret)
+ ret = ".demiurgerc";
+
+ return ret;
+}
+
+int config_exists(void)
+{
+ return access(get_config_filename(), R_OK | W_OK);
+}
int
-get_tokens_from_file(char *filename,
- char *instance,
- char *client_id,
- char *client_secret,
- char *access_token)
+store_config(const struct config *config)
{
- FILE *fp = fopen(filename, "r");
- if(fp == NULL)
+ FILE *fp = fopen(get_config_filename(), "w+");
+
+ if (!fp)
return -1;
- fscanf(fp, "instance=%s\n", instance);
- fscanf(fp, "client_id=%s\n", client_id);
- fscanf(fp, "client_secret=%s\n", client_secret);
- fscanf(fp, "access_token=%s\n", access_token);
+
+ fprintf(fp, "instance=%s\n", config->instance);
+ fprintf(fp, "client_id=%s\n", config->client_id);
+ fprintf(fp, "client_secret=%s\n", config->client_secret);
+ fprintf(fp, "access_token=%s\n", config->access_token);
+ fclose(fp);
return 0;
}
+static int
+validate_config(const struct config *config)
+{
+ int ret = 0;
+
+ if(!config->instance[0]) {
+ fprintf(stderr, "Error: missing instance key\n");
+ ret = -1;
+ }
+
+ if(!config->client_id[0]) {
+ fprintf(stderr, "Error: missing client_id key\n");
+ ret = -1;
+ }
+
+ if(!config->client_secret[0]) {
+ fprintf(stderr, "Error: missing client_secret key\n");
+ ret = -1;
+ }
+
+ if(!config->access_token[0]) {
+ fprintf(stderr, "Error: missing access_token key\n");
+ ret = -1;
+ }
+
+ return ret;
+}
+
+int
+load_config(struct config *config)
+{
+ FILE *fp = fopen(get_config_filename(), "r");
+ int i;
+ char line[256];
+
+ memset(config, 0, sizeof(*config));
+
+ for(i = 1; fgets(line, sizeof(line), fp); i++)
+ {
+ char *key, *value;
+
+ /* TODO: cleanup all whitespaces! */
+
+ value = strchr(line, '=');
+
+ if(!value) {
+ fprintf(stderr, "Parse error at %i\n", i);
+ fclose(fp);
+ return -1;
+ }
+
+ key = line;
+ *value = 0;
+ value++;
+
+ /* remove trailing newline */
+ value[strlen(value) - 1] = 0;
+
+ if(!strcmp(key, "instance")) {
+ dm_strncpy(config->instance, value, sizeof(config->instance));
+ } else if(!strcmp(key, "client_id")) {
+ dm_strncpy(config->client_id, value, sizeof(config->client_id));
+ } else if(!strcmp(key, "client_secret")) {
+ dm_strncpy(config->client_secret, value, sizeof(config->client_secret));
+ } else if(!strcmp(key, "access_token")) {
+ dm_strncpy(config->access_token, value, sizeof(config->access_token));
+ } else {
+ fprintf(stderr, "Unknown key %s at %i\n", key, i);
+ continue;
+ }
+ }
+
+ fclose(fp);
+
+ return validate_config(config);
+}
+
size_t
cb(void *data, size_t size, size_t nmemb, void *userp)
{
diff --git a/src/util.h b/src/util.h
index a015c45..53d82cd 100644
--- a/src/util.h
+++ b/src/util.h
@@ -16,24 +16,54 @@
*/
#include <stdlib.h>
+#include <string.h>
+
+struct config
+{
+ char instance[64];
+ char client_id[64];
+ char client_secret[64];
+ char access_token[64];
+};
+
+int
+load_config(struct config *config);
+
+int
+store_config(const struct config *config);
int
-get_tokens_from_file(char *filename,
- char *instance,
- char *client_id,
- char *client_secret,
- char *access_token);
+config_exists(void);
+
size_t
write_data(void *buffer, size_t size, size_t nmemb, void *userp);
void
eputs(const char *s);
+static inline char *
+dm_strncpy(char *dest, const char *src, size_t size)
+{
+ /* I wish strlcpy was standard C function */
+ char *s = strncpy(dest, src, size);
+ dest[size-1] = 0;
+ return s;
+}
+
struct memory
{
char *response;
size_t size;
};
+static inline void
+free_response(struct memory *memory)
+{
+ if(!memory) return;
+ free(memory->response);
+ memory->response = 0;
+ memory->size = 0;
+}
+
size_t
cb(void *data, size_t size, size_t nmemb, void *userp);