#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; // TODO: Make these private // Response body may be read from here if appropriate: br_sslio_context body; // Connection state br_ssl_client_context *sc; int fd; }; struct gmni_client_certificate; struct gemini_options { // 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; // If non-NULL, this will be used as the client certificate for the // request. The other fields must be set as well. struct gmni_client_certificate *client_cert; }; struct gemini_tofu; // 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_tofu *tofu, 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 { int (*read)(void *state, void *buf, size_t nbyte); void *state; char *buf; size_t bufsz; size_t bufln; bool preformatted; }; // Initializes a text/gemini parser. The provided "read" function will be called // with the provided "state" value in order to obtain more gemtext data. The // read function should behave like read(3). void gemini_parser_init(struct gemini_parser *p, int (*read)(void *state, void *buf, size_t nbyte), void *state); // 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