~callum/gmsfn

a19a2788ef104ad721692ddc8bb0c37db52887b8 — Callum Brown 1 year, 3 months ago 839afb1
Read list of gemlog index pages from config file
1 files changed, 99 insertions(+), 51 deletions(-)

M src/gemlogger.c
M src/gemlogger.c => src/gemlogger.c +99 -51
@@ 7,13 7,13 @@
#include "tofu.h"
#include "url.h"

// From gmni.c
/* From gmni.c */
struct tofu_config {
	struct gemini_tofu tofu;
	enum tofu_action action;
};

// From gmni.c
/* From gmni.c */
static enum tofu_action
tofu_callback(enum tofu_error error, const char *fingerprint,
	struct known_host *host, void *data)


@@ 60,6 60,46 @@ tofu_callback(enum tofu_error error, const char *fingerprint,
}


/* Determine whether the response can be used. */
const bool
bad_response(enum gemini_result r, struct gemini_response *resp)
{
	/* Check response */
	if (r != GEMINI_OK) {
		fprintf(stderr, "Error: %s\n", gemini_strerr(r, resp));
		return true;
	}

	/* Check request was successful */
	if (gemini_response_class(resp->status) != GEMINI_STATUS_CLASS_SUCCESS) {
		fprintf(stderr, "Error: Unsuccessful request. Status: %d %s\n",
			resp->status, resp->meta);
		return true;
	}

	/* Check response is Gemtext */
	if (strncmp(resp->meta, "text/gemini", 11) != 0) {
		fprintf(stderr, "Error: Response is not text/gemini. Meta: %s\n",
			resp->meta);
		return true;
	}

	return false;
}


/* Determine whether the given token is a link and has a url */
const bool
token_is_good_link(struct gemini_token tok)
{
	if (tok.token == GEMINI_LINK) {
		return (strlen(tok.link.url) != 0) && (tok.link.url != NULL);
	} else {
		return false;
	}
}


/* Return url up to and including the last '/' */
char *
get_url_without_filename(const char *url)


@@ 125,72 165,80 @@ get_absolute_url(char *url, char *base_url)
int
main()
{
	/* Setup for using gmni */
	struct addrinfo hints = {0};
	struct gemini_options opts = {
		.hints = &hints,
	};

	struct tofu_config cfg;
	cfg.action = TOFU_ASK;

	SSL_load_error_strings();
	ERR_load_crypto_strings();
	opts.ssl_ctx = SSL_CTX_new(TLS_method());
	gemini_tofu_init(&cfg.tofu, opts.ssl_ctx, &tofu_callback, &cfg);

	int ret = 0;
	// Random note: Does not follow redirects
	const char *url = "gemini://calcuode.com/index.gmi";
	struct gemini_response resp;
	enum gemini_result r = gemini_request(url, &opts, &resp);
	if (r != GEMINI_OK) {
		fprintf(stderr, "Error: %s\n", gemini_strerr(r, &resp));
		ret = (int)r;
		goto next;
	/* Open config file */
	const char *config_path = "/home/callum/gemlogger-test.gmi";
	FILE *config_fp = fopen(config_path, "r");
	if (!config_fp) {
		fprintf(stderr, "Failed to access config file %s.\n", config_path);
		goto finish;
	}

	// Check request was successful
	if (gemini_response_class(resp.status) !=
			GEMINI_STATUS_CLASS_SUCCESS) {
		fprintf(stderr, "Error: Unsuccessful request to %s. Status: %d %s\n",
			url, resp.status, resp.meta);
		goto next;
	}

	// Check response is Gemtext
	if (strncmp(resp.meta, "text/gemini", 11) != 0) {
		fprintf(stderr, "Error: %s is not text/gemini. Meta: %s\n",
			url, resp.meta);
		goto next;
	}

	char *base_url = get_url_without_filename(url);
	char *abs_url;

	struct gemini_parser p;
	gemini_parser_init(&p, resp.bio);
	FILE *out = stdout;
	struct gemini_token tok;
	while (gemini_parser_next(&p, &tok) == 0) {
		if (tok.token == GEMINI_LINK) {
			if (strlen(tok.link.url) == 0 || tok.link.url == NULL) {
				// No url, ignore
	/* Make a gmni response from the config file */
	BIO *file = BIO_new_fp(config_fp, BIO_CLOSE);
	struct gemini_response config_resp;
	config_resp.bio = BIO_new(BIO_f_buffer());
	BIO_push(config_resp.bio, file);
	config_resp.meta = strdup("text/gemini");
	config_resp.status = GEMINI_STATUS_SUCCESS;
	config_resp.fd = -1;
	config_resp.ssl = NULL;
	config_resp.ssl_ctx = NULL;

	/* Parse config file */
	struct gemini_parser config_parser;
	gemini_parser_init(&config_parser, config_resp.bio);
	struct gemini_token config_tok;
	while (gemini_parser_next(&config_parser, &config_tok) == 0) {
		if (token_is_good_link(config_tok)) {
			/* Get gemlog index page */
			struct gemini_response resp;
			enum gemini_result r = gemini_request(config_tok.link.url, &opts, &resp);
			if (bad_response(r, &resp)) {
				fprintf(stderr, "Could not access %s.\n", config_tok.link.url);
				/* Go to next link in config file */
				continue;
			}
			abs_url = get_absolute_url(tok.link.url, base_url);
			fprintf(out, "link text: %s\n", tok.link.text);
			fprintf(out, "link url: %s\n", tok.link.url);
			fprintf(out, "abs  url: %s\n", abs_url);
			fprintf(out, "\n");
			free(abs_url);

			char *base_url = get_url_without_filename(config_tok.link.url);
			char *abs_url;
			FILE *out = stdout;

			/* Parse gemlog index page */
			struct gemini_parser p;
			gemini_parser_init(&p, resp.bio);
			struct gemini_token tok;
			while (gemini_parser_next(&p, &tok) == 0) {
				if (token_is_good_link(tok)) {
					abs_url = get_absolute_url(tok.link.url, base_url);
					fprintf(out, "link text: %s\n", tok.link.text);
					fprintf(out, "link url: %s\n", tok.link.url);
					fprintf(out, "abs  url: %s\n", abs_url);
					fprintf(out, "\n");
					free(abs_url);
				}
			}
			gemini_token_finish(&tok);
			gemini_parser_finish(&p);
			gemini_response_finish(&resp);
		}
	}
	gemini_token_finish(&tok);
	gemini_parser_finish(&p);

next:
	gemini_response_finish(&resp);
	gemini_token_finish(&config_tok);
	gemini_parser_finish(&config_parser);
	gemini_response_finish(&config_resp);
finish:
	gemini_tofu_finish(&cfg.tofu);
	SSL_CTX_free(opts.ssl_ctx);
	return ret;
	return 0;
}