/* * Gophernicus * * Copyright (c) 2009-2018 Kim Holviala * Copyright (c) 2019 Gophernicus Developers * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef _GOPHERNICUS_H #define _GOPHERNICUS_H /* * Ignore useless snprintf() format truncation warnings on GCC 7+ */ #if __GNUC__ >= 7 #pragma GCC diagnostic ignored "-Wformat-truncation" #endif /* * Features */ #undef ENABLE_STRICT_RFC1436 /* Follow RFC1436 to the letter */ #undef ENABLE_AUTOHIDING /* Hide manually listed resources from generated menus */ #define ENABLE_HAPROXY1 /* Autodetect HAproxy/Stunnel proxy protocol v1 */ /* * Platform configuration */ /* Defaults should fit standard POSIX systems */ #define HAVE_IPv4 /* IPv4 should work anywhere */ #define HAVE_IPv6 /* Requires modern POSIX */ #define HAVE_PASSWD /* For systems with passwd-like userdb */ #define PASSWD_MIN_UID 100 /* Minimum allowed UID for ~userdirs */ #define HAVE_LOCALES /* setlocale() and friends */ #define HAVE_SHMEM /* Shared memory support */ #define HAVE_UNAME /* uname() */ #define HAVE_POPEN /* popen() */ #undef HAVE_STRLCPY /* strlcpy() from OpenBSD */ #undef HAVE_SENDFILE /* sendfile() in Linux & others */ /* #undef HAVE_LIBWRAP autodetected, don't enable here */ /* Linux */ #ifdef __linux #undef PASSWD_MIN_UID #define PASSWD_MIN_UID 500 #define _FILE_OFFSET_BITS 64 #endif /* Embedded Linux with uClibc */ #ifdef __UCLIBC__ #undef HAVE_SHMEM #undef HAVE_PASSWD #endif /* Haiku */ #ifdef __HAIKU__ #undef HAVE_SHMEM #undef HAVE_PASSWD #endif /* OpenBSD */ #ifdef __OpenBSD__ #define HAVE_STRLCPY #endif /* MacOS */ #if defined(__APPLE__) #if __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_10 #define HAVE_STRLCPY #endif #endif /* AIX */ #if defined(_AIX) #define _LARGE_FILES 1 #endif /* Add other OS-specific defines here */ /* * Include headers */ #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_SENDFILE #include #include #endif #ifdef HAVE_LOCALES #include #endif #ifdef HAVE_SHMEM #include #include #else #define shm_state void #endif #if defined(HAVE_IPv4) || defined(HAVE_IPv6) #include #include #include #endif #ifdef HAVE_UNAME #include #endif #if !defined(HAVE_STRLCPY) size_t strlcpy(char *dst, const char *src, size_t siz); size_t strlcat(char *dst, const char *src, size_t siz); #endif #ifdef HAVE_LIBWRAP #include #endif /* * Compile-time configuration */ /* Common stuff */ #define CRLF "\r\n" #define EMPTY "" #define PARENT ".." #define ROOT "/" #define FALSE 0 #define TRUE 1 #define QUIT 1 #define OK 0 #define ERROR -1 #define MATCH 0 #define WRAP_DENIED 0 /* Gopher filetypes */ #define TYPE_TEXT '0' #define TYPE_MENU '1' #define TYPE_ERROR '3' #define TYPE_GZIP '5' #define TYPE_QUERY '7' #define TYPE_BINARY '9' #define TYPE_GIF 'g' #define TYPE_HTML 'h' #define TYPE_INFO 'i' #define TYPE_IMAGE 'I' #define TYPE_MIME 'M' #define TYPE_DOC 'd' #define TYPE_TITLE '!' /* Protocols */ #define PROTO_GOPHER 'g' #define PROTO_HTTP 'h' /* Charsets */ #define AUTO 0 #define US_ASCII 1 #define ISO_8859_1 2 #define UTF_8 3 /* HTTP protocol stuff for logging */ #define HTTP_OK 200 #define HTTP_404 404 #define HTTP_DATE "%d/%b/%Y:%T %z" #define HTTP_USERAGENT "Unknown gopher client" /* Defaults for settings */ #define DEFAULT_HOST "localhost" #define DEFAULT_PORT 70 #define DEFAULT_TLS_PORT 0 #define DEFAULT_TYPE TYPE_TEXT #define DEFAULT_MAP "gophermap" #define DEFAULT_TAG "gophertag" #define DEFAULT_CGI "/cgi-bin/" #define DEFAULT_USERDIR "public_gopher" #define DEFAULT_WIDTH 67 #define DEFAULT_CHARSET UTF_8 #define MIN_WIDTH 33 #define MAX_WIDTH 200 #define UNKNOWN_ADDR "unknown" /* Session defaults */ #define DEFAULT_SESSION_TIMEOUT 1800 #define DEFAULT_SESSION_MAX_KBYTES 4194304 #define DEFAULT_SESSION_MAX_HITS 4096 /* Dummy values for gopher protocol */ #define DUMMY_SELECTOR "null" #define DUMMY_HOST "null.host\t1" /* Safe $PATH for exec() */ #ifdef __HAIKU__ #define SAFE_PATH "/boot/common/bin:/bin" #else #define SAFE_PATH "/usr/bin:/bin" #endif /* Special requests */ #define SERVER_STATUS "/server-status" #define CAPS_TXT "/caps.txt" /* Error messages */ #define ERR_ACCESS "Access denied!" #define ERR_NOTFOUND "File or directory not found!" #define ERROR_HOST "error.host\t1" #define ERROR_PREFIX "Error: " /* Strings */ #define SERVER_SOFTWARE "Gophernicus" #define SERVER_SOFTWARE_FULL SERVER_SOFTWARE "/" VERSION " (%s)" #define PROGNAME "gophernicus" #define HEADER_FORMAT "[%s]" #define FOOTER_FORMAT "Gophered by Gophernicus/" VERSION " on %s" #define UNITS "KB", "MB", "GB", "TB", "PB", NULL #define DATE_FORMAT "%Y-%b-%d %H:%M" /* See man 3 strftime */ #define DATE_WIDTH 17 #define DATE_LOCALE "POSIX" #define USERDIR_FORMAT "~%s", users[i].user /* See man 3 getpwent */ #define VHOST_FORMAT "gopher://%s/" /* ISO-8859-1 to US-ASCII look-alike conversion table */ #define ASCII \ "E?,f..++^%S??zY" \ " !c_*Y|$\"C?????" \ "AAAAAAACEEEEIIII" \ "DNOOOOO*OUUUUYTB" \ "aaaaaaaceeeeiiii" \ "dnooooo/ouuuuyty" #define UNKNOWN '?' /* Sizes & maximums */ #define BUFSIZE 1024 /* Default size for string buffers */ #define MAX_HIDDEN 32 /* Maximum number of hidden files */ #define MAX_FILETYPES 1024 /* Maximum number of suffix to filetype mappings */ #define MAX_FILTERS 16 /* Maximum number of file filters */ #define MAX_SDIRENT 1024 /* Maximum number of files per directory to handle */ #define MAX_REWRITE 32 /* Maximum number of selector rewrite options */ #define MAX_USERS 1024 /* Maximum number of users for the ~ option */ /* Struct for file suffix -> gopher filetype mapping */ typedef struct { char suffix[15]; char type; } ftype; /* Struct for selector rewriting */ typedef struct { char match[BUFSIZE]; char replace[BUFSIZE]; } srewrite; /* Struct for keeping the current options & state */ typedef struct { /* Request */ char req_selector[BUFSIZE]; char req_realpath[BUFSIZE]; char req_query_string[BUFSIZE]; char req_search[BUFSIZE]; char req_referrer[BUFSIZE]; char req_local_addr[64]; char req_remote_addr[64]; char req_filetype; char req_protocol; off_t req_filesize; /* Output */ int out_width; int out_charset; /* Settings */ char server_description[64]; char server_location[64]; char server_platform[64]; char server_admin[64]; char server_root[256]; char server_host_default[64]; char server_host[64]; int server_port; int server_tls_port; char default_filetype; char map_file[64]; char tag_file[64]; char cgi_file[64]; char user_dir[64]; char log_file[256]; char hidden[MAX_HIDDEN][256]; int hidden_count; ftype filetype[MAX_FILETYPES]; int filetype_count; char filter_dir[64]; srewrite rewrite[MAX_REWRITE]; int rewrite_count; #ifdef __OpenBSD__ char *extra_unveil_paths; #endif /* Session */ int session_timeout; int session_max_kbytes; int session_max_hits; int session_id; /* Feature options */ char opt_parent; char opt_header; char opt_footer; char opt_date; char opt_syslog; char opt_magic; char opt_iconv; char opt_vhost; char opt_query; char opt_caps; char opt_status; char opt_shm; char opt_root; char opt_proxy; char opt_exec; char opt_personal_spaces; char opt_http_requests; char debug; } state; /* Shared memory for session & accounting data */ #ifdef HAVE_SHMEM #define SHM_KEY 0xbeeb0008 /* Unique identifier + struct version */ #define SHM_MODE 0600 /* Access mode for the shared memory */ #define SHM_SESSIONS 256 /* Max amount of user sessions to track */ typedef struct { long hits; long kbytes; time_t req_atime; char req_selector[128]; char req_remote_addr[64]; char req_filetype; int session_id; char server_host[64]; int server_port; } shm_session; typedef struct { time_t start_time; long hits; long kbytes; char server_platform[64]; char server_description[64]; shm_session session[SHM_SESSIONS]; } shm_state; #endif /* Struct for directory sorting */ typedef struct { char name[128]; /* Should be 256 but we're saving stack space */ mode_t mode; uid_t uid; gid_t gid; off_t size; time_t mtime; } sdirent; /* Struct for the userlist with date */ typedef struct { char user[32]; /* Maximum in most systems */ time_t mtime; } user_date; /* * Useful macros */ #define strclear(str) str[0] = '\0'; #define sstrlcpy(dest, src) strlcpy(dest, src, sizeof(dest)) #define sstrlcat(dest, src) strlcat(dest, src, sizeof(dest)) #define sstrncmp(s1, s2) strncmp(s1, s2, sizeof(s2) - 1) #define sstrncasecmp(s1, s2) strncasecmp(s1, s2, sizeof(s2) - 1) #define sstrniconv(charset, out, in) strniconv(charset, out, in, sizeof(out)) #define max(a,b) (((a) > (b)) ? (a) : (b)) #define min(a,b) (((a) < (b)) ? (a) : (b)) /* * Include generated headers */ #include "files.h" #include "filetypes.h" /* gophernicus.c */ void info(state *st, char *str, char type); void footer(state *st); void die(state *st, const char *message, const char *description); void log_combined(state *st, int status); void html_encode(const char *unsafe, char *dest, int bufsize); /* file.c */ void send_binary_file(state *st); void send_text_file(state *st); void url_redirect(state *st); void server_status(state *st, shm_state *shm, int shmid); void caps_txt(state *st, shm_state *shm); void setenv_cgi(state *st, char *script); void gopher_file(state *st); /* menu.c */ char gopher_filetype(state *st, char *file, char magic); void gopher_menu(state *st); /* string.c */ void strrepeat(char *dest, char c, size_t num); void strreplace(char *str, char from, char to); size_t strcut(char *str, size_t width); char *strkey(char *header, char *key); char strlast(char *str); void chomp(char *str); char *strcharset(int charset); void strniconv(int charset, char *out, char *in, size_t outsize); void strnencode(char *out, const char *in, size_t outsize); void strndecode(char *out, char *in, size_t outsize); void strfsize(char *out, off_t size, size_t outsize); /* platform.c */ void platform(state *st); float loadavg(void); /* session.c */ void get_shm_session(state *st, shm_state *shm); void update_shm_session(state *st, shm_state *shm); /* options.c */ void add_ftype_mapping(state *st, char *suffix); void parse_args(state *st, int argc, char *argv[]); /* log.c */ void log_init(int enable, int debug); void log_fatal(const char *fmt, ...); void log_warning(const char *fmt, ...); void log_info(const char *fmt, ...); void log_debug(const char *fmt, ...); #endif