diff --git a/.gitignore b/.gitignore index 6ec8d51..4a40b62 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ gmni gmnlm *.1 *.o +*.a +*.pc diff --git a/Makefile b/Makefile index 5ac44db..22fed5d 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,7 @@ .POSIX: .SUFFIXES: OUTDIR=.build +VERSION=0.0.0 include $(OUTDIR)/config.mk include $(OUTDIR)/cppcache @@ -12,9 +13,26 @@ gmnlm: $(gmnlm_objects) @printf 'CCLD\t$@\n' @$(CC) $(LDFLAGS) -o $@ $(gmnlm_objects) $(LIBS) +libgmni.a: $(libgmni.a_objects) + @printf 'AR\t$@\n' + @$(AR) -rcs $@ $(libgmni.a_objects) + doc/gmni.1: doc/gmni.scd doc/gmnlm.1: doc/gmnlm.scd +libgmni.pc: + @printf 'GEN\t$@\n' + @printf 'prefix=%s\n' "$(PREFIX)" > $@ + @printf 'exec_prefix=$${prefix}\n' >> $@ + @printf 'includedir=$${prefix}/include\n' >> $@ + @printf 'libdir=$${prefix}/lib\n' >> $@ + @printf 'Name: libgmni\n' >> $@ + @printf 'Version: %s\n' "$(VERSION)" >> $@ + @printf 'Description: The gmni client library\n' >> $@ + @printf 'Requires: libssl libcrypto\n' >> $@ + @printf 'Cflags: -I$${includedir}/gmni\n' >> $@ + @printf 'Libs: -L$${libdir} -lgmni\n' >> $@ + .SUFFIXES: .c .o .scd .1 .c.o: @@ -22,7 +40,7 @@ doc/gmnlm.1: doc/gmnlm.scd @touch $(OUTDIR)/cppcache @grep $< $(OUTDIR)/cppcache >/dev/null || \ $(CPP) $(CFLAGS) -MM -MT $@ $< >> $(OUTDIR)/cppcache - @$(CC) -c $(CFLAGS) -o $@ $< + @$(CC) -c -fPIC $(CFLAGS) -o $@ $< .scd.1: @printf 'SCDOC\t$@\n' @@ -31,7 +49,7 @@ doc/gmnlm.1: doc/gmnlm.scd docs: doc/gmni.1 doc/gmnlm.1 clean: - @rm -f gmni gmnlm doc/gmni.1 doc/gmnlm.1 $(gmnlm_objects) $(gmni_objects) + @rm -f gmni gmnlm libgmni.a libgmni.pc doc/gmni.1 doc/gmnlm.1 $(gmnlm_objects) $(gmni_objects) distclean: clean @rm -rf "$(OUTDIR)" @@ -41,6 +59,11 @@ install: all mkdir -p $(MANDIR)/man1 install -Dm755 gmni $(BINDIR)/gmni install -Dm755 gmnlm $(BINDIR)/gmnlm + install -Dm755 libgmni.a $(LIBDIR)/libgmni.a + install -Dm644 include/gmni/gmni.h $(INCLUDEDIR)/gmni/gmni.h + install -Dm644 include/gmni/tofu.h $(INCLUDEDIR)/gmni/tofu.h + install -Dm644 include/gmni/url.h $(INCLUDEDIR)/gmni/url.h + install -Dm644 libgmni.pc $(LIBDIR)/pkgconfig install -Dm644 doc/gmni.1 $(MANDIR)/man1/gmni.1 install -Dm644 doc/gmnlm.1 $(MANDIR)/man1/gmnlm.1 diff --git a/config.sh b/config.sh index b03dfff..a696362 100644 --- a/config.sh +++ b/config.sh @@ -134,6 +134,7 @@ run_configure() { _INSTDIR=\$(DESTDIR)\$(PREFIX) BINDIR?=${BINDIR:-\$(_INSTDIR)/bin} LIBDIR?=${LIBDIR:-\$(_INSTDIR)/lib} + INCLUDEDIR?=${INCLUDEDIR:-\$(_INSTDIR)/include} MANDIR?=${MANDIR:-\$(_INSTDIR)/share/man} CACHE=\$(OUTDIR)/cache CFLAGS=${CFLAGS} @@ -146,7 +147,7 @@ run_configure() { for target in $all do - $target >>"$outdir"/config.mk + ${target//./_} >>"$outdir"/config.mk done echo done diff --git a/configure b/configure index 44db11c..e82a0e2 100755 --- a/configure +++ b/configure @@ -23,6 +23,21 @@ gmnlm() { src/util.c } -all="gmni gmnlm" +libgmni_a() { + genrules libgmni.a \ + src/client.c \ + src/escape.c \ + src/tofu.c \ + src/url.c \ + src/util.c \ + src/parser.c +} + +libgmni_pc() { + : +} + +all="gmni gmnlm libgmni.a libgmni.pc" + run_configure diff --git a/include/gmni.h b/include/gmni.h deleted file mode 100644 index 7e27b48..0000000 --- a/include/gmni.h +++ /dev/null @@ -1,164 +0,0 @@ -#ifndef GEMINI_CLIENT_H -#define GEMINI_CLIENT_H -#include -#include -#include -#include - -enum gemini_result { - GEMINI_OK, - GEMINI_ERR_OOM, - GEMINI_ERR_INVALID_URL, - GEMINI_ERR_NOT_GEMINI, - GEMINI_ERR_RESOLVE, - GEMINI_ERR_CONNECT, - GEMINI_ERR_SSL, - GEMINI_ERR_SSL_VERIFY, - GEMINI_ERR_IO, - GEMINI_ERR_PROTOCOL, -}; - -enum gemini_status { - GEMINI_STATUS_INPUT = 10, - GEMINI_STATUS_SENSITIVE_INPUT = 11, - GEMINI_STATUS_SUCCESS = 20, - GEMINI_STATUS_REDIRECT_TEMPORARY = 30, - GEMINI_STATUS_REDIRECT_PERMANENT = 31, - GEMINI_STATUS_TEMPORARY_FAILURE = 40, - GEMINI_STATUS_SERVER_UNAVAILABLE = 41, - GEMINI_STATUS_CGI_ERROR = 42, - GEMINI_STATUS_PROXY_ERROR = 43, - GEMINI_STATUS_SLOW_DOWN = 44, - GEMINI_STATUS_PERMANENT_FAILURE = 50, - GEMINI_STATUS_NOT_FOUND = 51, - GEMINI_STATUS_GONE = 52, - GEMINI_STATUS_PROXY_REQUEST_REFUSED = 53, - GEMINI_STATUS_BAD_REQUEST = 59, - GEMINI_STATUS_CLIENT_CERTIFICATE_REQUIRED = 60, - GEMINI_STATUS_CERTIFICATE_NOT_AUTHORIZED = 61, - GEMINI_STATUS_CERTIFICATE_NOT_VALID = 62, -}; - -enum gemini_status_class { - GEMINI_STATUS_CLASS_INPUT = 10, - GEMINI_STATUS_CLASS_SUCCESS = 20, - GEMINI_STATUS_CLASS_REDIRECT = 30, - GEMINI_STATUS_CLASS_TEMPORARY_FAILURE = 40, - GEMINI_STATUS_CLASS_PERMANENT_FAILURE = 50, - GEMINI_STATUS_CLASS_CLIENT_CERTIFICATE_REQUIRED = 60, -}; - -struct gemini_response { - enum gemini_status status; - char *meta; - - // Response body may be read from here if appropriate: - BIO *bio; - - // Connection state - SSL_CTX *ssl_ctx; - SSL *ssl; - int fd; -}; - -struct gemini_options { - // If NULL, an SSL context will be created. If unset, the ssl field - // must also be NULL. - SSL_CTX *ssl_ctx; - - // If ai_family != AF_UNSPEC (the default value on most systems), the - // client will connect to this address and skip name resolution. - struct addrinfo *addr; - - // If non-NULL, these hints are provided to getaddrinfo. Useful, for - // example, to force IPv4/IPv6. - struct addrinfo *hints; -}; - -// Requests the specified URL via the gemini protocol. If options is non-NULL, -// it may specify some additional configuration to adjust client behavior. -// -// Returns a value indicating the success of the request. -// -// Caller must call gemini_response_finish afterwards to clean up resources -// before exiting or re-using it for another request. -enum gemini_result gemini_request(const char *url, - struct gemini_options *options, - struct gemini_response *resp); - -// Must be called after gemini_request in order to free up the resources -// allocated during the request. -void gemini_response_finish(struct gemini_response *resp); - -// Returns a user-friendly string describing an error. -const char *gemini_strerr(enum gemini_result r, struct gemini_response *resp); - -// Returns the given URL with the input response set to the specified value. -// The caller must free the string. -char *gemini_input_url(const char *url, const char *input); - -// Returns the general response class (i.e. with the second digit set to zero) -// of the given Gemini status code. -enum gemini_status_class gemini_response_class(enum gemini_status status); - -enum gemini_tok { - GEMINI_TEXT, - GEMINI_LINK, - GEMINI_PREFORMATTED_BEGIN, - GEMINI_PREFORMATTED_END, - GEMINI_PREFORMATTED_TEXT, - GEMINI_HEADING, - GEMINI_LIST_ITEM, - GEMINI_QUOTE, -}; - -struct gemini_token { - enum gemini_tok token; - - // The token field determines which of the union members is valid. - union { - char *text; - - struct { - char *text; - char *url; // May be NULL - } link; - - char *preformatted; - - struct { - char *title; - int level; // 1, 2, or 3 - } heading; - - char *list_item; - char *quote_text; - }; -}; - -struct gemini_parser { - BIO *f; - char *buf; - size_t bufsz; - size_t bufln; - bool preformatted; -}; - -// Initializes a text/gemini parser which reads from the specified BIO. -void gemini_parser_init(struct gemini_parser *p, BIO *f); - -// Finishes this text/gemini parser and frees up its resources. -void gemini_parser_finish(struct gemini_parser *p); - -// Reads the next token from a text/gemini file. -// -// Returns 0 on success, 1 on EOF, and -1 on failure. -// -// Caller must call gemini_token_finish before exiting or re-using the token -// parameter. -int gemini_parser_next(struct gemini_parser *p, struct gemini_token *token); - -// Must be called after gemini_next to free up resources for the next token. -void gemini_token_finish(struct gemini_token *token); - -#endif diff --git a/include/tofu.h b/include/tofu.h deleted file mode 100644 index a88167b..0000000 --- a/include/tofu.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef GEMINI_TOFU_H -#define GEMINI_TOFU_H -#include -#include -#include -#include - -enum tofu_error { - TOFU_VALID, - // Expired, wrong CN, etc. - TOFU_INVALID_CERT, - // Cert is valid but we haven't seen it before - TOFU_UNTRUSTED_CERT, - // Cert is valid but we already trust another cert for this host - TOFU_FINGERPRINT_MISMATCH, -}; - -enum tofu_action { - TOFU_ASK, - TOFU_FAIL, - TOFU_TRUST_ONCE, - TOFU_TRUST_ALWAYS, -}; - -struct known_host { - char *host, *fingerprint; - time_t expires; - int lineno; - struct known_host *next; -}; - -// Called when the user needs to be prompted to agree to trust an unknown -// certificate. Return true to trust this certificate. -typedef enum tofu_action (tofu_callback_t)(enum tofu_error error, - const char *fingerprint, struct known_host *host, void *data); - -struct gemini_tofu { - char known_hosts_path[PATH_MAX+1]; - struct known_host *known_hosts; - int lineno; - tofu_callback_t *callback; - void *cb_data; -}; - -void gemini_tofu_init(struct gemini_tofu *tofu, - SSL_CTX *ssl_ctx, tofu_callback_t *cb, void *data); -void gemini_tofu_finish(struct gemini_tofu *tofu); - -#endif diff --git a/include/url.h b/include/url.h deleted file mode 100644 index 155fd55..0000000 --- a/include/url.h +++ /dev/null @@ -1,103 +0,0 @@ -#ifndef URLAPI_H -#define URLAPI_H -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2018, Daniel Stenberg, , et al. - * - * This software is licensed as described in the file COPYING, which - * you should have received as part of this distribution. The terms - * are also available at https://curl.haxx.se/docs/copyright.html. - * - * You may opt to use, copy, modify, merge, publish, distribute and/or sell - * copies of the Software, and permit persons to whom the Software is - * furnished to do so, under the terms of the COPYING file. - * - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY - * KIND, either express or implied. - * - ***************************************************************************/ - -/* the error codes for the URL API */ -typedef enum { - CURLUE_OK, - CURLUE_BAD_HANDLE, /* 1 */ - CURLUE_BAD_PARTPOINTER, /* 2 */ - CURLUE_MALFORMED_INPUT, /* 3 */ - CURLUE_BAD_PORT_NUMBER, /* 4 */ - CURLUE_UNSUPPORTED_SCHEME, /* 5 */ - CURLUE_URLDECODE, /* 6 */ - CURLUE_OUT_OF_MEMORY, /* 7 */ - CURLUE_USER_NOT_ALLOWED, /* 8 */ - CURLUE_UNKNOWN_PART, /* 9 */ - CURLUE_NO_SCHEME, /* 10 */ - CURLUE_NO_USER, /* 11 */ - CURLUE_NO_PASSWORD, /* 12 */ - CURLUE_NO_OPTIONS, /* 13 */ - CURLUE_NO_HOST, /* 14 */ - CURLUE_NO_PORT, /* 15 */ - CURLUE_NO_QUERY, /* 16 */ - CURLUE_NO_FRAGMENT /* 17 */ -} CURLUcode; - -typedef enum { - CURLUPART_URL, - CURLUPART_SCHEME, - CURLUPART_USER, - CURLUPART_PASSWORD, - CURLUPART_OPTIONS, - CURLUPART_HOST, - CURLUPART_PORT, - CURLUPART_PATH, - CURLUPART_QUERY, - CURLUPART_FRAGMENT -} CURLUPart; - -#define CURLU_PATH_AS_IS (1<<4) /* leave dot sequences */ -#define CURLU_DISALLOW_USER (1<<5) /* no user+password allowed */ -#define CURLU_URLDECODE (1<<6) /* URL decode on get */ -#define CURLU_URLENCODE (1<<7) /* URL encode on set */ -#define CURLU_APPENDQUERY (1<<8) /* append a form style part */ - -typedef struct Curl_URL CURLU; - -/* - * curl_url() creates a new CURLU handle and returns a pointer to it. - * Must be freed with curl_url_cleanup(). - */ -struct Curl_URL *curl_url(void); - -/* - * curl_url_cleanup() frees the CURLU handle and related resources used for - * the URL parsing. It will not free strings previously returned with the URL - * API. - */ -void curl_url_cleanup(struct Curl_URL *handle); - -/* - * curl_url_dup() duplicates a CURLU handle and returns a new copy. The new - * handle must also be freed with curl_url_cleanup(). - */ -struct Curl_URL *curl_url_dup(struct Curl_URL *in); - -/* - * curl_url_get() extracts a specific part of the URL from a CURLU - * handle. Returns error code. The returned pointer MUST be freed with - * free() afterwards. - */ -CURLUcode curl_url_get(struct Curl_URL *handle, CURLUPart what, - char **part, unsigned int flags); - -/* - * curl_url_set() sets a specific part of the URL in a CURLU handle. Returns - * error code. The passed in string will be copied. Passing a NULL instead of - * a part string, clears that part. - */ -CURLUcode curl_url_set(struct Curl_URL *handle, CURLUPart what, - const char *part, unsigned int flags); - -#endif diff --git a/src/client.c b/src/client.c index 398e133..8b6b9e7 100644 --- a/src/client.c +++ b/src/client.c @@ -9,8 +9,8 @@ #include #include #include -#include "gmni.h" -#include "url.h" +#include +#include static enum gemini_result gemini_get_addrinfo(struct Curl_URL *uri, struct gemini_options *options, diff --git a/src/gmni.c b/src/gmni.c index 61f41e2..e8b25f9 100644 --- a/src/gmni.c +++ b/src/gmni.c @@ -12,8 +12,8 @@ #include #include #include -#include "gmni.h" -#include "tofu.h" +#include +#include #include "util.h" static void diff --git a/src/gmnlm.c b/src/gmnlm.c index 245ec85..2fba84e 100644 --- a/src/gmnlm.c +++ b/src/gmnlm.c @@ -14,9 +14,9 @@ #include #include #include -#include "gmni.h" -#include "tofu.h" -#include "url.h" +#include +#include +#include #include "util.h" struct link { diff --git a/src/parser.c b/src/parser.c index 5794151..ad2c0e6 100644 --- a/src/parser.c +++ b/src/parser.c @@ -5,7 +5,7 @@ #include #include #include -#include "gmni.h" +#include void gemini_parser_init(struct gemini_parser *p, BIO *f) diff --git a/src/tofu.c b/src/tofu.c index 48a627f..863efc6 100644 --- a/src/tofu.c +++ b/src/tofu.c @@ -10,8 +10,8 @@ #include #include #include -#include "gmni.h" -#include "tofu.h" +#include +#include #include "util.h" static int diff --git a/src/url.c b/src/url.c index dabf45f..47741e4 100644 --- a/src/url.c +++ b/src/url.c @@ -31,7 +31,7 @@ #include #include #include "escape.h" -#include "url.h" +#include /* Provided by gmni */ static char * diff --git a/src/util.c b/src/util.c index 360c99a..0a479af 100644 --- a/src/util.c +++ b/src/util.c @@ -7,7 +7,7 @@ #include #include #include -#include "gmni.h" +#include #include "util.h" static void