motsognir1.0.11_logmore2.0.diff source

Home
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
NotDashEscaped: You need GnuPG to verify this message

========================================================================
The following patch will turn an unchanged Motsognir v1.0.11 source tree
into a Motsognir v1.0.11+logmore2.0 source tree. 

Put this file into the source directory, chdir to the same directory,
then apply the patch with:

  patch -p1 < motsognir1.0.11+logmore2.0.diff

You don't need to trim any text: the patch program knows what to skip.


------------------------------------------------------------------------
Vvv====P:A:T:C:H===F:O:L:L:O:W:S====**====T:H:I:S===S:I:D:E===U:P=======
========================================================================
--- motsognir/NEWS_logmore	        (nonexistent)
+++ motsognir+logmore/NEWS_logmore	2019-03-22 11:32:57.057739008 +0100
@@ -0,0 +1,40 @@
+logmore2.0
+==========
+
+The logmore2.0 patch adds the following features to Motsognir v1.0.11:
+
++ New command line option '--log' allows users to specify either a log
+  file, or a syslog local facility number. This option is also available
+  as new configuration file directive 'log';
+
++ a directory specified on the command line is taken to be the
+  Gopher root;
+
++ standard options '--help', '--version'; plus '--license';
+
++ [NEW in 2.0]  'autoinfo' configuration directive: if set to 1, any
+  gophermap line containing no tabs is considered an "info line"
+  (no need for an initial 'i');
+
+* [NEW in 2.0]  plus sign '+' characters in a URL are no longer
+  translated to spaces by the server;  if you want the old behaviour
+  back, just remove '-DRFC3986' from the Makefile;
+
+! [NEW in 2.0]  stricter validation on port number given in config file;
+
+* [NEW in 2.0]  changes to the build/installation process:
+    * the Makefile now optimizes `motsognir' for size;
+    + new 'install-strip' recipe further reduces the binary's footprint;
+    * everything is installed under the '/usr/local' subtree, unless
+      otherwise directed (by setting DESTDIR and/or PREFIX);
+    ! an existing config file will not be overwritten upon installation;
+    + the paths referenced in the supplied init scripts and man page
+      are filled in at build time to make sure they match actual
+      installation paths;
+    * GNU Make is now required.
+
+
+-- 
+Dario Niedermann <dario@darioniedermann.it>, 2019-03-22
+
+gopher://darioniedermann.it/  <>  https://www.darioniedermann.it/
========================================================================
--- motsognir/cli.c	        (nonexistent)
+++ motsognir+logmore/cli.c	2019-03-20 12:04:49.946323358 +0100
@@ -0,0 +1,195 @@
+/*
+ *  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *  *  Motsognir - The mighty gopher server                               *
+ *  *  Copyright (C) 2008-2019 Mateusz Viste                              *
+ *  *  http://motsognir.sourceforge.net                                   *
+ *  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ *  Some changes and additions: Copyright (C) 2018-2019 Dario Niedermann
+ *  $Id: cli.c 43 2019-03-20 11:04:49Z ndr $
+ * ----------------------------------------------------------------------
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ * ----------------------------------------------------------------------
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <err.h>
+#include <sysexits.h>
+#include <syslog.h>
+#include <libgen.h>
+#include <string.h>
+#include "log.h"
+#include "motsognir.h"
+
+#define FALSE  0
+#define TRUE   1
+
+
+static void print_version(void)
+{
+	printf("Motsognir v%s Copyright (C) Mateusz Viste et al. %s\n",
+					pVer, pDate);
+}
+
+
+static void print_usage(char *argv0)
+{
+	printf("Usage: %s [OPTIONS] [DIRECTORY]", basename(argv0));
+	printf("\n\
+Start the Motsognir Gopher server, with DIRECTORY as the Gopher root\n\n\
+Options:\n\
+  -c, --config=FILE         read server configuration from FILE\n\
+  -l, --log=FILE|0..7       log to FILE or syslog local facility #\n\
+  -V, --version             display version information and exit\n\
+  -L, --licence\n\
+      --license             display license and exit\n\
+  -h, --help                display this help text and exit\n\n\
+Command line arguments override configuration file directives.\n\
+Default Gopher root:        /var/gopher/\n\
+Default configuration file: " CONFIGFILE "\n");
+}
+
+
+static void about(void) {
+	print_version();
+
+	printf("\n\
+This program is free software: you can redistribute it and/or modify it under\n\
+the terms of the GNU General Public License as published by the Free Software\n\
+Foundation, either version 3 of the License, or (at your option) any later\n\
+version.\n");
+
+	printf("\
+This program is distributed in the hope that it will be useful, but WITHOUT ANY\n\
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A\n\
+PARTICULAR PURPOSE. See the GNU General Public License for more details.\n\n\
+Motsognir is a robust and reliable open-source gopher server for POSIX systems.\n\
+Motsognir is entirely written in ISO C99, without any external dependencies.\n\n");
+
+	printf("Homepage: %s\n", HOMEPAGE);
+}
+
+
+static int configLogging(char *dest, struct logParams *logConf)
+{
+	static char timesHere = 0;
+	int status = EXIT_SUCCESS;
+
+	if (++timesHere == 1) {   /* I'll only configure ONCE: from the cmd line
+															 if specified, from config file otherwise. */
+		if (dest[1] == '\0' && dest[0] <= '7' && dest[0] >= '0') {
+				/* destination is a syslog local facility number */
+			logConf->loggingType = TO_SYSLOG;
+				/* reduce the 1st -and last- char in dest to a 0 to 7 value, use
+					 it as an index to look up the facility value in the array */
+			logConf->facility = logConf->localFacility[dest[0]-'0'];
+		}
+		else {  /* option's arg is -arguably- a file name */
+			logConf->loggingType = TO_FILE;
+			logConf->fileName = dest;
+		}
+	}
+	else status = EX_DATAERR;
+
+	return status;
+}
+
+
+int parseCmdLine(char mode, int myArgc, char **myArgv,
+								 struct MotsognirConfig *confStruct)
+{
+	#define DONE_PARSING    -1         /* getopt_long() returns -1 when done */
+	static struct option long_options[] = {
+		{"config",  required_argument, NULL, 'c'},
+		{"help",    no_argument,       NULL, 'h'},
+		{"licence", no_argument,       NULL, 'L'},
+		{"license", no_argument,       NULL, 'L'},
+		{"log",     required_argument, NULL, 'l'},
+		{"version", no_argument,       NULL, 'V'},
+		{0,         0,                 0,     0 }
+	};
+	static struct logParams logConf;
+		/* since LOG_LOCALn symbols might (in theory) have any value, let's put
+			 them in order into an array, where we can refer to them by index   */
+	static int localFacilities[] = {  LOG_LOCAL0, LOG_LOCAL1, LOG_LOCAL2,
+																		LOG_LOCAL3, LOG_LOCAL4, LOG_LOCAL5,
+																		LOG_LOCAL6, LOG_LOCAL7  };
+	int c,  option_index,  this_option = 0,  status = EXIT_SUCCESS;
+
+	if (mode == FIRST_PASS) {   /* Do some one-time setup: */
+		confStruct->configfile  = CONFIGFILE;
+		logConf.loggingType     = TO_SYSLOG;
+		logConf.facility        = LOG_DAEMON;
+		logConf.fileName        = NULL;
+		logConf.localFacility   = localFacilities;
+	}
+
+	optind=1; while (status == EXIT_SUCCESS) {
+		this_option = optind ? optind : 1;
+		option_index = 0;
+
+		c = getopt_long(myArgc, myArgv, ":c:hLl:V", long_options, &option_index);
+		if (c == DONE_PARSING) break;
+
+		switch (c) {
+			case 'c':
+				confStruct->configfile  =  optarg;                              break;
+
+			case 'h':
+				print_usage(myArgv[0]); status = OK_EXIT_NOW;                   break;
+
+			case 'L':
+				about();                status = OK_EXIT_NOW;                   break;
+
+			case 'l':
+				if (configLogging(optarg, &logConf))
+						/* if configLogging is called >1 time, it'll error out */
+					if (mode != LAST_PASS)
+						warnx("further logging destination '%s' ignored", optarg);
+																																				break;
+			case 'V':
+				print_version();        status = OK_EXIT_NOW;                   break;
+
+			case '?':
+				warnx("unrecognized option '%s'", myArgv[this_option]);
+				print_usage(myArgv[0]); status = EX_USAGE;                      break;
+
+			case ':':
+				warnx("option '%s' requires an argument",
+								myArgv[this_option]);
+																status = EX_USAGE;                      break;
+
+			default:                  /* should never happen... */
+				warnx("internal error while parsing command line, char code 0%o", c);
+																status = EX_SOFTWARE;
+		}
+	}
+
+	/* init the logging functions, either with default values,
+		 or values from the command line: */
+	logme(INIT, NO_MSG, &logConf);
+
+	/* non-option args at the end of cmd line? */
+	if (mode == LAST_PASS)    /* only deal with them the last time I'm called */
+		for (c=0; optind < myArgc && c < 256; c++, optind++) {
+			if (!c)       /* the 1st will be our gopher root */
+				confStruct->gopherroot = myArgv[optind];
+			else
+				warnx("ignoring trailing argument: '%s'", myArgv[optind]);
+		}
+
+	return status; 
+}
========================================================================
--- motsognir/initd_motsognir	2019-03-17 19:20:02.567539385 +0100
+++ motsognir+logmore/initd_motsognir	2019-03-20 20:20:15.578049217 +0100
@@ -15,5 +15,5 @@
 
-DAEMON=/usr/sbin/motsognir
+DAEMON="__BINDIR__/motsognir"
 
-test -x $DAEMON || exit 5
+test -x "$DAEMON" || exit 5
 
@@ -23,3 +23,3 @@
                 echo -n "Starting gopher server... "
-                $DAEMON > /dev/null
+                "$DAEMON" > /dev/null
                 status=$?
@@ -35,3 +35,3 @@
                 echo -n "Stopping gopher server... "
-                killall $DAEMON > /dev/null
+                killall "$DAEMON" > /dev/null
                 status=$?
@@ -49,3 +49,3 @@
         status)
-                pidof $DAEMON > /dev/null
+                pidof "$DAEMON" > /dev/null
                 status=$?
@@ -64 +64,2 @@
 esac
+# $Id: initd_motsognir 45 2019-03-20 19:20:15Z ndr $
========================================================================
--- motsognir/log.c	        (nonexistent)
+++ motsognir+logmore/log.c	2019-03-16 15:16:13.279862092 +0100
@@ -0,0 +1,151 @@
+/*
+ *  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *  *  Motsognir - The mighty gopher server                               *
+ *  *  Copyright (C) 2008-2019 Mateusz Viste                              *
+ *  *  http://motsognir.sourceforge.net                                   *
+ *  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ *  Some changes and additions: Copyright (C) 2018-2019 Dario Niedermann
+ *  $Id: log.c 35 2019-03-16 14:16:13Z ndr $
+ * ----------------------------------------------------------------------
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ * ----------------------------------------------------------------------
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <err.h>
+#include <sysexits.h>
+#include <assert.h>
+#include "motsognir.h"
+#include "log.h"
+
+#define MAX_MSGLEN  2048  /* max bytes in a single log entry */
+
+struct logWritersParams {
+	int   level,
+				facility;
+	char  *fileName,
+				*prefix,
+				*msg;
+	FILE  *filePtr;
+};
+
+static int logToFile(struct logWritersParams *params)
+{
+	int status;
+
+	status = EXIT_SUCCESS;
+	if (params->filePtr == NULL)
+		if ((params->filePtr = fopen(params->fileName, "a")) == NULL)
+			return EX_CANTCREAT;
+	/* else... */
+	if (params->prefix != NULL)
+		if (fprintf(params->filePtr, "%s ", params->prefix) < 1)
+			status = EX_IOERR;
+
+	if ((fprintf(params->filePtr, "%s\n", params->msg) < 1)
+			 || fflush(params->filePtr))
+					status = EX_IOERR;
+
+	if (status == EX_IOERR) fclose(params->filePtr);
+	return status;
+}
+
+
+static int logToSyslog(struct logWritersParams *params)
+{ 
+	assert(params->level >= LOG_EMERG && params->level <= LOG_DEBUG);
+	assert((params->facility >= LOG_LOCAL0 && params->facility <= LOG_LOCAL7)
+																				 || params->facility == LOG_DAEMON);
+	syslog(params->level | params->facility,  "%s", params->msg);
+	return 0;
+}
+
+
+void logme(int level, const char *fmt, ...)
+{
+	va_list argPtr;           /* points to the list of unnamed args */
+	static  struct logWritersParams logWritersSettings;
+	static  char msg[MAX_MSGLEN],  whereToLog;
+	static  int (*writerFunc)() =  NULL;
+	struct  logParams             *logSettings;
+	int     msgLen;
+	char   *clientIP;
+
+	va_start(argPtr, fmt);    /* make argPtr point to 1st unnamed arg */
+
+	switch (level) {
+		case INIT:
+		/* If I'm called with pseudo-level INIT, I won't log a message: instead
+			 I'll regard 1st unnamed passed-in arg as a pointer to a logParams struct.
+			 Spares the need of being passed a config struct by every func calling me
+		*/
+			assert(fmt==NO_MSG);  /* or there must be collision with INIT's value */
+			logWritersSettings.msg  = msg;        /* never to be changed */
+			logSettings = va_arg(argPtr, struct logParams *);
+				/* mk our static copys of settings (passed-in struc may be destroyd) */
+			whereToLog = logSettings->loggingType;
+			assert(whereToLog==TO_FILE||whereToLog==TO_SYSLOG);
+			logWritersSettings.facility = logSettings->facility;
+				/* no need to alloc -- points to somewhere in argv[] if it came from
+					 a command line opt, or to a strdup'ed string if from config file: */
+			logWritersSettings.fileName = logSettings->fileName;
+			logWritersSettings.filePtr  = NULL;
+
+			/* pick function according to message destination -- file or syslog */
+			writerFunc = (whereToLog == TO_FILE) ? logToFile : logToSyslog;
+			break;
+
+		case SET_PREFIX:
+		/* This pseudo-level means I'll set the common prefix to log entries.
+			 The 1st unnamed passed-in arg must now be a connected client's IP,
+			 because I only get called this way when a new client connects.
+			 The 2nd unnamed arg points to a char array that will contain the prefix.
+		*/
+			assert(writerFunc != NULL);  /* I must've been previously INIT'ed */
+			clientIP = va_arg(argPtr, char *);
+			logWritersSettings.prefix = va_arg(argPtr, char *);
+			snprintf(logWritersSettings.prefix, LOGPREFIX_MAXLEN, fmt, clientIP);
+			if (whereToLog == TO_SYSLOG) {
+				/* set up the logging to log with PID and peer's IP address */
+				openlog(logWritersSettings.prefix, LOG_PID,
+								logWritersSettings.facility);
+			}
+			else
+				assert(whereToLog == TO_FILE);
+			break;
+
+		default:                       /* an actual syslog level */
+			assert(level >= LOG_EMERG && level <= LOG_DEBUG);
+			logWritersSettings.level = level;
+					/* turn fmt and any following args into a fixed string in msg: */
+			msgLen = vsnprintf(msg, MAX_MSGLEN, fmt, argPtr); 
+					/* write msg to log: */
+			while ((*writerFunc)(&logWritersSettings)) {
+					/* got error. From logToFile() 'cause logToSyslog() only returns 0 */
+				whereToLog = TO_SYSLOG;
+					/* facility must still be as initially,since we were loggin'to file*/
+				assert(logWritersSettings.facility == LOG_DAEMON);
+				writerFunc = logToSyslog;
+				LOG(ERR,"Could not write to log file '%s' -- reverting to syslog",
+									logWritersSettings.fileName);
+			}
+			if (msgLen >= MAX_MSGLEN)
+				LOG(WARNING,"previous log message was truncated");
+	}
+	va_end(argPtr);                  /* clean up va_ stuff */
+}
========================================================================
--- motsognir/log.h	        (nonexistent)
+++ motsognir+logmore/log.h	2019-03-16 15:16:13.279862092 +0100
@@ -0,0 +1,30 @@
+/*
+ *  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *  *  Motsognir - The mighty gopher server                               *
+ *  *  Copyright (C) 2008-2019 Mateusz Viste                              *
+ *  *  http://motsognir.sourceforge.net                                   *
+ *  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ *  Some changes and additions: Copyright (C) 2018-2019 Dario Niedermann
+ *  $Id: log.h 35 2019-03-16 14:16:13Z ndr $
+ * ----------------------------------------------------------------------
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ * ----------------------------------------------------------------------
+ */
+
+#define LOG(level, ...) 	logme(LOG_##level, __VA_ARGS__)
+#define NO_MSG          	NULL
+#define LOGPREFIX_MAXLEN	128
+
+void logme(int level, const char *fmt, ...);
========================================================================
--- motsognir/Makefile	2019-03-17 19:20:02.443536867 +0100
+++ motsognir+logmore/Makefile	2019-03-21 18:08:04.522915664 +0100
@@ -1,3 +1,20 @@
+# $Id: Makefile 48 2019-03-21 17:08:04Z ndr $
+
 CC ?= gcc
-CFLAGS += -Wall -Wextra -O3 -std=gnu89 -pedantic -Wformat-security
+DESTDIR ?= /usr/local
+
+ifeq ($(DESTDIR),/usr)
+	SYSCONFDIR ?= /etc
+else
+	SYSCONFDIR ?= $(DESTDIR)/etc
+endif
+CONFIGFILE   ?= $(SYSCONFDIR)/motsognir.conf
+
+CFLAGS += -Wall -Wextra -Os -std=c99   -pedantic -Wformat-security  \
+					-D_POSIX_C_SOURCE=200809L -D_DEFAULT_SOURCE -D_BSD_SOURCE \
+					-DCONFIGFILE='"$(abspath $(CONFIGFILE))"'  -DRFC3986
+
+SED=sed 's@__BINDIR__@$(abspath $(DESTDIR))/sbin@g;s@__SYSCONFDIR__@$(abspath $(SYSCONFDIR))@g'
+
+.PHONY:	all clean install install-strip
 
@@ -5,9 +22,9 @@
 
-motsognir: motsognir.o extmap.o
-	$(CC) motsognir.o extmap.o -o motsognir $(CFLAGS)
+motsognir: motsognir.o extmap.o cli.o log.o
+	$(CC) motsognir.o extmap.o cli.o log.o -o motsognir $(CFLAGS)
 
 motsognir.8.gz: motsognir.8
-	cat motsognir.8 | gzip > motsognir.8.gz
+	$(SED) motsognir.8 | gzip > motsognir.8.gz
 
-motsognir.o: motsognir.c
+motsognir.o: motsognir.c motsognir.h extmap.h binary.h log.h
 	$(CC) -c motsognir.c -o motsognir.o $(CFLAGS)
@@ -17,2 +34,6 @@
 
+cli.o:	cli.c log.h motsognir.h
+
+log.o:	log.c log.h motsognir.h
+
 extmaptest: extmaptest.c extmap.o
@@ -23,12 +44,17 @@
 
-install:
-	mkdir -p $(PREFIX)/$(DESTDIR)/usr/sbin/
-	mkdir -p $(PREFIX)/$(DESTDIR)/etc/init.d/
-	mkdir -p $(PREFIX)/$(DESTDIR)/usr/share/doc/motsognir/
-	mkdir -p $(PREFIX)/$(DESTDIR)/usr/share/man/man8/
-	cp motsognir $(PREFIX)/$(DESTDIR)/usr/sbin/
-	cp motsognir.conf $(PREFIX)/$(DESTDIR)/etc/
-	cp motsognir.8.gz $(PREFIX)/$(DESTDIR)/usr/share/man/man8/
-	@if [ -d $(PREFIX)/$(DESTDIR)/etc/init.d ] ; then cp initd_motsognir $(PREFIX)/$(DESTDIR)/etc/init.d/motsognir ; fi
-	@if [ -d $(PREFIX)/$(DESTDIR)/etc/rc.d ] ; then cp rc_d_motsognir $(PREFIX)/$(DESTDIR)/etc/rc.d/motsognir ; fi
-	cp changes.txt manual.pdf $(PREFIX)/$(DESTDIR)/usr/share/doc/motsognir/
+install:  all
+	mkdir -p "$(PREFIX)/$(DESTDIR)/sbin"
+	mkdir -p "$(PREFIX)/$(SYSCONFDIR)/init.d" "$(PREFIX)/$(SYSCONFDIR)/rc.d"
+	mkdir -p "$(PREFIX)/$(DESTDIR)/share/doc/motsognir"
+	mkdir -p "$(PREFIX)/$(DESTDIR)/share/man/man8/"
+	cp     motsognir          "$(PREFIX)/$(DESTDIR)/sbin/"
+	cp -i  motsognir.conf     "$(PREFIX)/$(CONFIGFILE)".new
+	-[ ! -e "$(PREFIX)/$(CONFIGFILE)" ] \
+		&& mv "$(PREFIX)/$(CONFIGFILE)".new  "$(PREFIX)/$(CONFIGFILE)"
+	cp     motsognir.8.gz     "$(PREFIX)/$(DESTDIR)/share/man/man8/"
+	$(SED) initd_motsognir  > "$(PREFIX)/$(SYSCONFDIR)/init.d/motsognir"
+	$(SED) rc_d_motsognir   > "$(PREFIX)/$(SYSCONFDIR)/rc.d/motsognir"
+	cp changes.txt manual.pdf "$(PREFIX)/$(DESTDIR)/share/doc/motsognir/"
+
+install-strip:  install
+	strip -s "$(PREFIX)/$(DESTDIR)/sbin/motsognir"
========================================================================
--- motsognir/motsognir.8	2019-03-17 19:20:02.575539547 +0100
+++ motsognir+logmore/motsognir.8	2019-03-20 17:26:50.336509636 +0100
@@ -34,3 +34,3 @@
 .fi
-Now, all you have to do is edit your /etc/motsognir.conf file to make it suit your needs, and then launch motsognir:
+Now, all you have to do is edit your __SYSCONFDIR__/motsognir.conf file to make it suit your needs, and then launch motsognir:
 .PP
@@ -38,3 +38,3 @@
 .fam C
-  # /usr/sbin/motsognir
+  # __BINDIR__/motsognir
 
@@ -42,3 +42,3 @@
 .fi
-Last note: If you wish to store the motsognir.conf file outside of /etc/, this is possible as well, but you will have to instruct motsognir about its location every time your run it:
+Last note: If you wish to store the motsognir.conf file outside of __SYSCONFDIR__/, this is possible as well, but you will have to instruct motsognir about its location every time your run it:
 .PP
@@ -46,3 +46,3 @@
 .fam C
-  # /usr/sbin/motsognir --config /my/custom/location/motsognir.conf
+  # __BINDIR__/motsognir --config /my/custom/location/motsognir.conf
 
@@ -53,3 +53,3 @@
 
-The Motsognir's configuration file is located by default in /etc/motsognir.conf, and should be readable by the user which will run the motsognir service. The configuration file is a plain-text file, containing some tokens with values. All lines beginning with the "#" character are ignored (and can be used to put some comments in the configuration file).
+The Motsognir's configuration file is located by default in __SYSCONFDIR__/motsognir.conf, and should be readable by the user which will run the motsognir service. The configuration file is a plain-text file, containing some tokens with values. All lines beginning with the "#" character are ignored (and can be used to put some comments in the configuration file).
 If any of the parameter is missing from the configuration file, or have an empty value, Motsognir will load a default value instead. Here is an example of a self-explanatory configuration file:
@@ -158,3 +158,3 @@
 # need to restart the Motsognir process for the file to be reloaded.
-# Example: HttpErrFile=/etc/motsognir-httperr.html
+# Example: HttpErrFile=__SYSCONFDIR__/motsognir-httperr.html
 HttpErrFile=
========================================================================
--- motsognir/motsognir.c	2019-03-17 19:20:02.575539547 +0100
+++ motsognir+logmore/motsognir.c	2019-03-21 18:08:04.522915664 +0100
@@ -7,2 +7,4 @@
  *
+ *  Some changes and additions: Copyright (C) 2018-2019 Dario Niedermann
+ *  $Id: motsognir.c 48 2019-03-21 17:08:04Z ndr $
  * ----------------------------------------------------------------------
@@ -29,3 +31,2 @@
 #include <pwd.h>
-#include <regex.h>   /* regcomp(), regexec()... */
 #include <signal.h>
@@ -48,48 +49,8 @@
 #include "extmap.h"
+#include "motsognir.h"
+#include "log.h"
 
-/* Constants */
-#define pVer "1.0.11"
-#define pDate "2008-2019"
-#define HOMEPAGE "http://motsognir.sourceforge.net"
-
-/* declare the default config file location, if not already declared from CLI
- * at compile-time - esp. useful for systems that store config files in other
- * locations, like /usr/local/etc/ for FreeBSD... */
-#ifndef CONFIGFILE
-  #define CONFIGFILE "/etc/motsognir.conf"
-#endif
-
-
-struct MotsognirConfig {
-  char *gopherroot;
-  char *userdir;
-  char **pubdirlist;
-  int gopherport;
-  char *gopherhostname;
-  char *defaultgophermap;
-  int verbosemode;
-  int capssupport;
-  char *capsservergeolocationstring;
-  char *capsserverarchitecture;
-  char *capsserverdescription;
-  char *capsserverdefaultencoding;
-  int cgisupport;
-  int phpsupport;
-  int subgophermaps;
-  int paranoidmode;
-  char *plugin;
-  regex_t *pluginfilter;
-  char *runasuser;
-  uid_t runasuser_uid;
-  gid_t runasuser_gid;
-  char *runasuser_home;
-  char *chroot;
-  char *httperrfile;
-  char *bind;
-  int disableipv6;
-  char *extmapfile;
-  struct extmap_t *extmap;
-  char securldelim;
-};
-
+/* implementation in 'cli.c': */
+int parseCmdLine(char mode, int argc, char **argv,
+								 struct MotsognirConfig *confStruct);
 
@@ -158,3 +119,3 @@
   if (initgroups(config->runasuser, config->runasuser_gid) != 0 || setgid(config->runasuser_gid) != 0 || setuid(config->runasuser_uid) != 0) {
-    syslog(LOG_WARNING, "ERROR: Couldn't change to '%.32s' uid=%lu gid=%lu: %s", config->runasuser, (unsigned long)config->runasuser_uid, (unsigned long)config->runasuser_gid, strerror(errno));
+		LOG(WARNING,"ERROR: Couldn't change to '%.32s' uid=%lu gid=%lu: %s", config->runasuser, (unsigned long)config->runasuser_uid, (unsigned long)config->runasuser_gid, strerror(errno));
     return(-1);
@@ -163,3 +124,3 @@
   if (getuid() != config->runasuser_uid) {
-    syslog(LOG_WARNING, "ERROR: For some mysterious reasons Motsognir was unable to switch to user '%s'.", config->runasuser);
+		LOG(WARNING,"ERROR: For some mysterious reasons Motsognir was unable to switch to user '%s'.", config->runasuser);
     return(-1);
@@ -370,3 +331,3 @@
     if (dstlen + 4 >= dstmaxlen) {
-      syslog(LOG_WARNING, "WARNING: reached percent encoding length limit - aborting");
+			LOG(WARNING,"WARNING: reached percent encoding length limit - aborting");
       break; /* stop the work if we reached our limit */
@@ -441,2 +402,3 @@
     if (string[x] != '%') {
+#ifndef RFC3986
       if (string[x] == '+') {
@@ -446,2 +408,5 @@
       }
+#else
+			string[y++] = string[x];
+#endif
       continue;
@@ -451,3 +416,3 @@
       string[x] = 0;
-      syslog(LOG_WARNING, "ERROR: detected invalid percent encoding");
+			LOG(WARNING,"ERROR: detected invalid percent encoding");
       return(-1);
@@ -457,3 +422,3 @@
       string[x] = 0;
-      syslog(LOG_WARNING, "ERROR: detected a dangerous percent encoding (%%00)");
+			LOG(WARNING,"ERROR: detected a dangerous percent encoding (%%00)");
       return(-1);
@@ -465,3 +430,3 @@
       string[x - 2] = 0;
-      syslog(LOG_WARNING, "ERROR: detected an invalid percent encoding");
+			LOG(WARNING,"ERROR: detected an invalid percent encoding");
       return(-1);
@@ -517,23 +482,5 @@
 
-static void about(const char *version, const char *datestring, const char *homepage) {
-  printf("Motsognir v%s Copyright (C) Mateusz Viste %s\n\n", version, datestring);
-  printf("This program is free software: you can redistribute it and/or modify it under\n"
-         "the terms of the GNU General Public License as published by the Free Software\n"
-         "Foundation, either version 3 of the License, or (at your option) any later\n"
-         "version.\n");
-  printf("This program is distributed in the hope that it will be useful, but WITHOUT ANY\n"
-         "WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A\n"
-         "PARTICULAR PURPOSE. See the GNU General Public License for more details.\n\n");
-  printf("Motsognir is a robust and reliable open-source gopher server for POSIX systems.\n"
-         "Motsognir is entirely written in ANSI C, without any external dependencies.\n\n");
-  printf("Available command-line parameters:\n"
-         "  --config file.conf       use a configuration file in a custom location\n"
-         "\n");
-  printf("homepage: %s\n\n", homepage);
-}
-
-
 static void sendbackhttperror(int sock, const struct MotsognirConfig *config) {
   char txtline[1024], portstr[16];
-  syslog(LOG_INFO, "HTTP request detected - a HTTP error message is returned");
+	LOG(INFO,"HTTP request detected - a HTTP error message is returned");
   sendline(sock, "HTTP/1.1 400 Bad request");
@@ -575,3 +522,3 @@
   char txtline[1024];
-  syslog(LOG_INFO, "GOPHER+ request detected - a gopher+ fake redirector is returned");
+	LOG(INFO,"GOPHER+ request detected - a gopher+ fake redirector is returned");
   sendline(sock, "+-1");
@@ -596,3 +543,3 @@
     if (res == NULL) {
-      syslog(LOG_ERR, "ERROR: OUT OF MEMORY ON LINE #%d", __LINE__);
+			LOG(ERR,"ERROR: OUT OF MEMORY ON LINE #%d", __LINE__);
       return(res);
@@ -619,3 +566,3 @@
 
-static int loadconfig(struct MotsognirConfig *config, const char *configfile) {
+static int loadconfig(struct MotsognirConfig *config) {
   FILE *fd;
@@ -624,8 +571,6 @@
   int valuebuffpos = 0;
-  int bytebuff;
+	int bytebuff, priv_argc;
   int state = 0; /* 0=reading token, 1=reading value, 2=reading comment */
   struct passwd *pw;
-
-  /* zero out the config structure, just in case */
-  memset(config, 0, sizeof(*config));
+	char *priv_argv[3];
 
@@ -659,6 +604,7 @@
   config->securldelim = 0;
+	config->autoinfo = 0;
 
-  fd = fopen(configfile, "r");
+	fd = fopen(config->configfile, "r");
   if (fd == NULL) {
-    syslog(LOG_WARNING, "WARNING: Failed to open the configuration file at '%s'", configfile);
+		LOG(WARNING,"WARNING: Failed to open the configuration file at '%s'", config->configfile);
     return(-1);
@@ -715,3 +661,4 @@
         } else if (strcasecmp(tokenbuff, "GopherPort") == 0) {
-          config->gopherport = atoi(valuebuff);
+					/* prevent wrap-arounds: error out on a port value >5 chars */
+					config->gopherport = (strlen(valuebuff) > 5) ? 0 : atoi(valuebuff);
         } else if (strcasecmp(tokenbuff, "GopherHostname") == 0) {
@@ -732,5 +679,5 @@
           if (config->pluginfilter == NULL) {
-            syslog(LOG_ERR, "ERROR: Out of memory while trying to allocate regex space!");
+						LOG(ERR,"ERROR: Out of memory while trying to allocate regex space!");
           } else if (regcomp(config->pluginfilter, valuebuff, REG_EXTENDED | REG_NOSUB) != 0) {
-            syslog(LOG_ERR, "ERROR: Invalid PluginFilter regex!");
+						LOG(ERR,"ERROR: Invalid PluginFilter regex!");
             free(config->pluginfilter);
@@ -746,3 +693,3 @@
           config->httperrfile = readfiletomem(valuebuff);
-          if (config->httperrfile == NULL) syslog(LOG_WARNING, "WARNING: Failed to load custom http error file '%s'. Default content will be used instead.", valuebuff);
+					if (config->httperrfile == NULL) LOG(WARNING,"WARNING: Failed to load custom http error file '%s'. Default content will be used instead.", valuebuff);
         } else if (strcasecmp(tokenbuff, "ExtMapFile") == 0) {
@@ -751,2 +698,8 @@
           config->securldelim = atoi(valuebuff);
+				} else if (strcasecmp(tokenbuff, "autoinfo") == 0) {
+					config->autoinfo = (atoi(valuebuff) == 1);
+				} else if (strcasecmp(tokenbuff, "log") == 0) {
+							/* simulate a '-l ARG' given on command line */
+						priv_argc=3; priv_argv[1]="-l"; priv_argv[2]=strdup(valuebuff);
+						parseCmdLine(FROM_FILE, priv_argc, priv_argv, config);
         }
@@ -768,3 +721,3 @@
   if (config->verbosemode < 0) {
-    syslog(LOG_ERR, "ERROR: Invalid verbose level found in the configuration file (%d)", config->verbosemode);
+		LOG(ERR,"ERROR: Invalid verbose level found in the configuration file (%d)", config->verbosemode);
     return(-1);
@@ -772,4 +725,4 @@
 
-  if (config->gopherport < 1) {
-    syslog(LOG_ERR, "ERROR: Invalid gopher port found in the configuration file (%d)", config->gopherport);
+	if (config->gopherport < 1 || config->gopherport > 65535) {
+		LOG(ERR,"ERROR: Invalid gopher port found in the configuration file (%d)", config->gopherport);
     return(-1);
@@ -778,3 +731,3 @@
   if (config->gopherroot[0] == 0) {
-    syslog(LOG_ERR, "ERROR: Missing gopher root path in the configuration file. Please add a valid 'GopherRoot=' directive");
+		LOG(ERR,"ERROR: Missing gopher root path in the configuration file. Please add a valid 'GopherRoot=' directive");
     return(-1);
@@ -785,3 +738,3 @@
     if ((config->userdir[0] != '/') || (strstr(config->userdir, "%s") == NULL)) {
-      syslog(LOG_ERR, "ERROR: The UserDir configuration is invalid. It shall be an absolute path (start by '/') and contain the '%%s' placeholder.");
+			LOG(ERR,"ERROR: The UserDir configuration is invalid. It must be an absolute path (start by '/') and contain the '%%s' placeholder.");
       return(-1);
@@ -791,3 +744,3 @@
   if (config->gopherhostname == NULL) {
-    syslog(LOG_WARNING, "WARNING: Missing gopher hostname in the configuration file. The local IP address will be used instead. Please add a valid 'GopherHostname=' directive.");
+		LOG(WARNING,"WARNING: Missing gopher hostname in the configuration file. The local IP address will be used instead. Please add a valid 'GopherHostname=' directive.");
   }
@@ -797,3 +750,3 @@
   if (config->extmap == NULL) {
-    syslog(LOG_ERR, "ERROR: failed to load the extension mapping file '%s'", config->extmapfile);
+		LOG(ERR,"ERROR: failed to load the extension mapping file '%s'", config->extmapfile);
     return(-1);
@@ -805,3 +758,3 @@
     if (pw == NULL) {
-      syslog(LOG_ERR, "ERROR: Could not map the username '%s' to a valid uid", config->runasuser);
+			LOG(ERR,"ERROR: Could not map the username '%s' to a valid uid", config->runasuser);
       return(-1);
@@ -852,3 +805,3 @@
   /* Retrieve server-side parameters */
-  syslog(LOG_INFO, "Got following server-side parameters: %s | %s", res[0], res[1]);
+	LOG(INFO,"Got following server-side parameters: %s | %s", res[0], res[1]);
   return(res); /* return the array with params */
@@ -896,3 +849,3 @@
     if ((timeoutStartTime != NULL) && (time(NULL) - *timeoutStartTime >= 10)) {
-      syslog(LOG_INFO, "Request takes too long to come. Connection aborted.");
+			LOG(INFO,"Request takes too long to come. Connection aborted.");
       return(-1);
@@ -934,3 +887,3 @@
   char linebuff[1024];
-  syslog(LOG_INFO, "The request is asking for a URL redirection - returned a html document redirecting to '%s'", rawurl);
+	LOG(INFO,"The request is asking for a URL redirection - returned a html document redirecting to '%s'", rawurl);
   sendline(sock, "<!DOCTYPE html>");
@@ -983,3 +936,3 @@
   if (dirptr == NULL) {
-    syslog(LOG_WARNING, "ERROR: Could not access directory '%s' (%s)", localfile, strerror(errno));
+		LOG(WARNING,"ERROR: Could not access directory '%s' (%s)", localfile, strerror(errno));
     sendline(sock, "3Error: could not access directory\tfake\tfake\t0");
@@ -991,3 +944,3 @@
   if (direntriescount < 0) {
-    syslog(LOG_WARNING, "ERROR: Failed to scan the directory '%s': %s", localfile, strerror(errno));
+		LOG(WARNING,"ERROR: Failed to scan the directory '%s': %s", localfile, strerror(errno));
     return;
@@ -995,3 +948,3 @@
 
-  syslog(LOG_INFO, "Found %d items in '%s'", direntriescount, localfile);
+	LOG(INFO,"Found %d items in '%s'", direntriescount, localfile);
 
@@ -1028,3 +981,3 @@
 /* cuts a gophermap line into chunks and fills values. returns 0 on success, non-zero on error. */
-static int explodegophermapline(const char *linebuff, char *itemtype, char *itemdesc, char *itemselector, char *itemserver, long *itemport) {
+static int explodegophermapline(const char *linebuff, char *itemtype, char *itemdesc, char *itemselector, char *itemserver, long *itemport, const struct MotsognirConfig *config) {
   int x;
@@ -1032,3 +985,3 @@
   /* first make sure to clear out all variables */
-  *itemtype = 0;
+	*itemtype = 'i';
   itemdesc[0] = 0;
@@ -1038,9 +991,11 @@
   /* if the line is empty, stop right now */
-  if (*linebuff == 0) {
-    *itemtype = 'i';
-    return(0);
-  }
-  /* read the itemtype */
-  *itemtype = *linebuff;
+	if (*linebuff == 0) return(0);
+	/* else... */
+	if (config->autoinfo
+	&& CONTAINS_NO_TABS(linebuff) && !IS_A_DIRECTIVE(linebuff))
+		;		/* then it's an 'info' line: emit as-is */
+	else {
+		*itemtype = *linebuff;  /* read the itemtype */
   linebuff += 1;
+	}
   /* read the item's description */
@@ -1102,5 +1057,5 @@
   if ((srvsideparams[0] != NULL) || (srvsideparams[1] != NULL)) {
-    syslog(LOG_INFO, "running server-side app '%s' with queries '%s' + '%s'", localfile, srvsideparams[0], srvsideparams[1]);
+		LOG(INFO,"running server-side app '%s' with queries '%s' + '%s'", localfile, srvsideparams[0], srvsideparams[1]);
   } else {
-    syslog(LOG_INFO, "running server-side app '%s'", localfile);
+		LOG(INFO,"running server-side app '%s'", localfile);
   }
@@ -1134,3 +1089,3 @@
   if (cgifd == NULL) {
-    syslog(LOG_WARNING, "ERROR: failed to run the server-side app '%s'", localfile);
+		LOG(WARNING,"ERROR: failed to run the server-side app '%s'", localfile);
     return(0);
@@ -1154,4 +1109,4 @@
       /* */
-      if (explodegophermapline(tmpstring, &itemtype, itemdesc, itemselector, itemserver, &itemport) != 0) {
-        syslog(LOG_WARNING, "ERROR: dynamic gophermap processing aborted due to failure to interpret its output as being a gophermap line (%s)", localfile);
+			if (explodegophermapline(tmpstring, &itemtype, itemdesc, itemselector, itemserver, &itemport, config) != 0) {
+				LOG(WARNING,"ERROR: dynamic gophermap processing aborted due to failure to interpret its output as being a gophermap line (%s)", localfile);
         break;
@@ -1174,5 +1129,5 @@
   if (res == -1) {
-    syslog(LOG_WARNING, "WARNING: call to server-side app '%s' failed (%s)", localfile, strerror(errno));
+		LOG(WARNING,"WARNING: call to server-side app '%s' failed (%s)", localfile, strerror(errno));
   } else if (WEXITSTATUS(res) != 0) {
-    syslog(LOG_WARNING, "WARNING: server-side app '%s' terminated with a non-zero exit code (%d)", localfile, WEXITSTATUS(res));
+		LOG(WARNING,"WARNING: server-side app '%s' terminated with a non-zero exit code (%d)", localfile, WEXITSTATUS(res));
   }
@@ -1202,6 +1157,6 @@
   if (gophermapfd == NULL) {
-    syslog(LOG_WARNING, "ERROR: Failed to open the gophermap at '%s' (%s)", gophermapfile, strerror(errno));
+		LOG(WARNING,"ERROR: Failed to open the gophermap at '%s' (%s)", gophermapfile, strerror(errno));
     return;
   }
-  syslog(LOG_INFO, "Response=\"Return gophermap. (%s)", gophermapfile);
+	LOG(INFO,"Response=\"Return gophermap. (%s)", gophermapfile);
 
@@ -1220,3 +1175,3 @@
     /* explode the gophermap line into separate items */
-    if (explodegophermapline(linebuff, &itemtype, itemdesc, itemselector, itemserver, &itemport) != 0) {
+		if (explodegophermapline(linebuff, &itemtype, itemdesc, itemselector, itemserver, &itemport, config) != 0) {
       sendline(sock, "3Parsing error\tfake\tfake\t0");
@@ -1230,3 +1185,3 @@
         if (realscriptname == NULL) {
-          syslog(LOG_WARNING, "WARNING: Failed to resolve the path to '%s'", itemdesc);
+					LOG(WARNING,"WARNING: Failed to resolve the path to '%s'", itemdesc);
         } else {
@@ -1253,3 +1208,3 @@
   char gophermapfile[5120];
-  syslog(LOG_INFO, "The resource is a directory");
+	LOG(INFO,"The resource is a directory");
   if (lastcharofstring(localfile) != '/') strcat(localfile, "/");
@@ -1287,3 +1242,3 @@
     /* no gophermap found, simply list files & directories */
-    syslog(LOG_INFO, "No gophermap found. Listing directory content");
+		LOG(INFO,"No gophermap found. Listing directory content");
     outputdircontent(sock, config, localfile, directorytolist, 0);
@@ -1314,3 +1269,3 @@
   if (sockmaster < 0) {
-    syslog(LOG_WARNING, "FATAL ERROR: socket could not be open (%s)", strerror(errno));
+		LOG(WARNING,"FATAL ERROR: socket could not be open (%s)", strerror(errno));
     return(-2);
@@ -1319,3 +1274,3 @@
   /* I set the socket to be reusable, to avoid having to wait for a longish time when the server is restarted */
-  if (setsockopt(sockmaster, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one)) < 0) syslog(LOG_WARNING, "WARNING: failed to set REUSEADDR on main socket");
+	if (setsockopt(sockmaster, SOL_SOCKET, SO_REUSEADDR, (char *)&one, sizeof(one)) < 0) LOG(WARNING,"WARNING: failed to set REUSEADDR on main socket");
 
@@ -1331,3 +1286,3 @@
     if (inet_pton(AF_INET, config->bind, &(serv_addr.sin_addr)) != 1) {
-      syslog(LOG_WARNING, "FATAL ERROR: failed to parse the IPv4 address bind value. Please check your 'bind' configuration.");
+			LOG(WARNING,"FATAL ERROR: failed to parse the IPv4 address bind value. Please check your 'bind' configuration.");
       return(-2);
@@ -1336,3 +1291,3 @@
     if (inet_pton(AF_INET6, config->bind, &(serv_addr6.sin6_addr)) != 1) {
-      syslog(LOG_WARNING, "FATAL ERROR: failed to parse the IP address bind value. Please check your 'bind' configuration.");
+      LOG(WARNING,"FATAL ERROR: failed to parse the IP address bind value. Please check your 'bind' configuration.");
       return(-2);
@@ -1354,3 +1309,3 @@
     if (bind(sockmaster, (struct sockaddr *) &serv_addr6, sizeof(serv_addr6)) < 0) {
-      syslog(LOG_WARNING, "FATAL ERROR: binding failed (%s)", strerror(errno));
+      LOG(WARNING,"FATAL ERROR: binding failed (%s)", strerror(errno));
       return(-2);
@@ -1359,3 +1314,3 @@
     if (bind(sockmaster, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
-      syslog(LOG_WARNING, "FATAL ERROR: binding failed (%s)", strerror(errno));
+		LOG(WARNING,"FATAL ERROR: binding failed (%s)", strerror(errno));
       return(-2);
@@ -1373,3 +1328,3 @@
 
-  syslog(LOG_INFO, "motsognir v" pVer " process started");
+	LOG(INFO,"motsognir v" pVer " process started");
 
@@ -1384,3 +1339,3 @@
     close(sockmaster);
-    syslog(LOG_WARNING, "Failed to dameonize the motsognir process (%s)", strerror(errno));
+		LOG(WARNING,"Failed to dameonize the motsognir process (%s)", strerror(errno));
     return(-2);
@@ -1397,3 +1352,3 @@
   /* I want to be the pack master now (aka session leader) */
-  if (setsid() == -1) syslog(LOG_WARNING, "WARNING: setsid() failed (%s)", strerror(errno));
+	if (setsid() == -1) LOG(WARNING,"WARNING: setsid() failed (%s)", strerror(errno));
 
@@ -1403,3 +1358,3 @@
     if (chroot(config->chroot) != 0) {
-      syslog(LOG_WARNING, "Failed to chroot(): %s", strerror(errno));
+			LOG(WARNING,"Failed to chroot(): %s", strerror(errno));
       return(-2);
@@ -1409,3 +1364,3 @@
   /* set the working directory to the root directory */
-  if (chdir ("/") == -1) syslog(LOG_WARNING, "WARNING: failed to switch to / directory (%s)", strerror(errno));
+	if (chdir ("/") == -1) LOG(WARNING,"WARNING: failed to switch to / directory (%s)", strerror(errno));
 
@@ -1417,3 +1372,3 @@
     if (getuid() != 0) {
-      syslog(LOG_WARNING, "A 'RunAsUser' directive has been configured, but the process has not been launched under root account. The 'RunAsUser' directive is therefore ignored.");
+			LOG(WARNING,"A 'RunAsUser' directive has been configured, but the process has not been launched under root account. The 'RunAsUser' directive is therefore ignored.");
     } else { /* if I'm root, drop off privileges */
@@ -1422,3 +1377,3 @@
       } else {
-        syslog(LOG_WARNING, "Successfully dropped root privileges. Motsognir runs as user '%s' now.", config->runasuser);
+				LOG(WARNING,"Successfully dropped root privileges. Motsognir runs as user '%s' now.", config->runasuser);
       }
@@ -1437,3 +1392,3 @@
     if (sockslave < 0) {
-      syslog(LOG_WARNING, "FATAL ERROR: accepting connection failed (%s)", strerror(errno));
+			LOG(WARNING,"FATAL ERROR: accepting connection failed (%s)", strerror(errno));
       close(sockmaster);
@@ -1445,3 +1400,3 @@
     if (mypid == 0) { /* I'm the child */
-      static char logprefix[128];
+			static char logprefix[LOGPREFIX_MAXLEN];
       close(sockmaster);
@@ -1450,3 +1405,3 @@
         if (inet_ntop(cli_addr6.sin6_family, &cli_addr6.sin6_addr, clientipaddrstr, clientipaddrstr_maxlen) == NULL) {
-          syslog(LOG_WARNING, "Failed to fetch client's IP address: %s", strerror(errno));
+				LOG(WARNING,"Failed to fetch client's IP address: %s", strerror(errno));
           snprintf(clientipaddrstr, clientipaddrstr_maxlen, "UNKNOWN");
@@ -1455,3 +1410,3 @@
         if (inet_ntop(cli_addr.sin_family, &cli_addr.sin_addr, clientipaddrstr, clientipaddrstr_maxlen) == NULL) {
-          syslog(LOG_WARNING, "Failed to fetch client's IP address: %s", strerror(errno));
+          LOG(WARNING,"Failed to fetch client's IP address: %s", strerror(errno));
           snprintf(clientipaddrstr, clientipaddrstr_maxlen, "UNKNOWN");
@@ -1463,3 +1418,3 @@
         if ((getsockname(sockslave, (struct sockaddr *) &serv_addr6, &clilen) < 0) || (inet_ntop(serv_addr6.sin6_family, &serv_addr6.sin6_addr, serveripaddrstr, serveripaddrstr_maxlen) == NULL)) {
-          syslog(LOG_WARNING, "Failed to fetch server's IP address: %s", strerror(errno));
+          LOG(WARNING,"Failed to fetch server's IP address: %s", strerror(errno));
           snprintf(serveripaddrstr, serveripaddrstr_maxlen, "UNKNOWN");
@@ -1469,3 +1424,3 @@
         if ((getsockname(sockslave, (struct sockaddr *) &serv_addr, &clilen) < 0) || (inet_ntop(serv_addr.sin_family, &serv_addr.sin_addr, serveripaddrstr, serveripaddrstr_maxlen) == NULL)) {
-          syslog(LOG_WARNING, "Failed to fetch server's IPv4 address: %s", strerror(errno));
+          LOG(WARNING,"Failed to fetch server's IPv4 address: %s", strerror(errno));
           snprintf(serveripaddrstr, serveripaddrstr_maxlen, "UNKNOWN");
@@ -1477,5 +1432,4 @@
       /* set logprefix to contain the client's address */
-      snprintf(logprefix, sizeof(logprefix), "motsognir [%s]", clientipaddrstr);
-      openlog(logprefix, LOG_PID, LOG_DAEMON); /* set up the logging to log with PID and peer's IP address */
-      syslog(LOG_INFO, "new connection to %s", serveripaddrstr);
+			logme(SET_PREFIX, "motsognir [%s]", clientipaddrstr, logprefix);
+			LOG(INFO,"new connection to %s", serveripaddrstr);
       /* if no gopher hostname was set, use the server's address */
@@ -1489,3 +1443,3 @@
     } else { /* error condition */
-      syslog(LOG_WARNING, "FATAL ERROR: fork() failed!");
+			LOG(WARNING,"FATAL ERROR: fork() failed!");
       close(sockslave);
@@ -1506,3 +1460,3 @@
   if (linebuff == NULL) {
-    syslog(LOG_WARNING, "ERROR: Out of memory while trying to allocate buffer for file");
+		LOG(WARNING,"ERROR: Out of memory while trying to allocate buffer for file");
     return;
@@ -1511,3 +1465,3 @@
   if (fd == NULL) { /* file could not be opened */
-    syslog(LOG_WARNING, "ERROR: File '%s' could not be opened", filename);
+		LOG(WARNING,"ERROR: File '%s' could not be opened", filename);
     free(linebuff);
@@ -1532,3 +1486,3 @@
   if (buff == NULL) {
-    syslog(LOG_WARNING, "ERROR: Out of memory while trying to allocate buffer for file");
+		LOG(WARNING,"ERROR: Out of memory while trying to allocate buffer for file");
     return;
@@ -1537,3 +1491,3 @@
   if (fd == NULL) { /* file could not be opened */
-    syslog(LOG_WARNING, "ERROR: File '%s' could not be opened", filename);
+		LOG(WARNING,"ERROR: File '%s' could not be opened", filename);
     free(buff);
@@ -1580,3 +1534,3 @@
   }
-  syslog(LOG_WARNING, "Evasion check: path '%s' (%s) seem to belong to neither '%s' nor any entry of the pubdir list", localfile, resolvedpath, gopherroot);
+	LOG(WARNING,"Evasion check: path '%s' (%s) seems to belong to neither '%s' nor any entry of the pubdir list", localfile, resolvedpath, gopherroot);
   return(1);
@@ -1672,3 +1626,3 @@
   if ((curdir == NULL) || (chdir(curdir) == -1)) {
-    syslog(LOG_WARNING, "WARNING: failed to switch current directory to %s (%s), original resource: %s", curdir, strerror(errno), s);
+		LOG(WARNING,"WARNING: failed to switch current directory to %s (%s), original resource: %s", curdir, strerror(errno), s);
     res = -1;
@@ -1696,4 +1650,3 @@
   char gophertype;
-  char *configfile = CONFIGFILE;
-  int sock;
+	int sock, status;
   struct MotsognirConfig config;
@@ -1701,17 +1654,11 @@
 
-  if (argc > 1) {
-    int x;
-    for (x = 1; x < argc; x++) {
-      if (strcmp(argv[x], "--config") == 0) {
-        x++;
-        if (x < argc) configfile = argv[x];
-      } else { /* unknown command line */
-        about(pVer, pDate, HOMEPAGE);
-        return(1);
-      }
-    }
-  }
+	/* zero out the config structure, just in case */
+	memset(&config, 0, sizeof(config));
+
+	if ((status = parseCmdLine(FIRST_PASS, argc, argv, &config)) != EXIT_SUCCESS)
+		return (status==OK_EXIT_NOW) ? EXIT_SUCCESS : status;
 
+	/* else... */
   /* load motsognir's configuration from file */
-  if (loadconfig(&config, configfile) != 0) {
+	if (loadconfig(&config) != 0) {
     puts("ERROR: A configuration error has been detected. Check the logs for details.");
@@ -1719,2 +1666,7 @@
   }
+		/* Again?! Yep... Command line args must override config file
+			 directives. But we also needed a 1st pass to possibly learn
+			 where the config file was... */
+	if ((status = parseCmdLine(LAST_PASS, argc, argv, &config)) != EXIT_SUCCESS)
+		return (status==OK_EXIT_NOW) ? EXIT_SUCCESS : status;
 
@@ -1730,3 +1682,3 @@
   if (sockreadline(sock, directorytolist, sizeof(directorytolist), &StartTime) < 0) {
-    syslog(LOG_WARNING, "Error during selector receiving phase. Connection aborted.");
+		LOG(WARNING,"Error during selector receiving phase. Connection aborted.");
     close(sock);
@@ -1734,3 +1686,3 @@
   }
-  syslog(LOG_INFO, "Query='%s'", directorytolist);
+	LOG(INFO,"Query='%s'", directorytolist);
   if (directorytolist[0] == 0) {   /* Empty request means "gimme the root listing" */
@@ -1752,3 +1704,3 @@
     if (res > 0) {
-      syslog(LOG_INFO, "Query handled by plugin (%s)", config.plugin);
+			LOG(INFO,"Query handled by plugin (%s)", config.plugin);
       drainsock(sock);  /* read whatever request the peer sent us, to drain the socket before closing it (otherwise the tcp stack would trigger a ugly RST) */
@@ -1796,3 +1748,3 @@
   if (percdecode(directorytolist) != 0) {
-    syslog(LOG_WARNING, "Percent decoding on request failed. Query aborted.");
+		LOG(WARNING,"Percent decoding on request failed. Query aborted.");
     return(0);
@@ -1803,3 +1755,3 @@
   if (securitycheckresult != NULL) {
-    syslog(LOG_INFO, "The gopher security module has detected a suspect condition. The query won't be processed. Reason: %s", securitycheckresult);
+		LOG(INFO,"The gopher security module has detected a suspect condition. The query won't be processed. Reason: %s", securitycheckresult);
     close(sock);
@@ -1815,6 +1767,6 @@
 
-  syslog(LOG_INFO, "Requested resource: %s / Local resource: %s", directorytolist, localfile);
+	LOG(INFO,"Requested resource: %s / Local resource: %s", directorytolist, localfile);
 
   if (checkforevasion(rootdir, config.pubdirlist, localfile) != 0) {
-    syslog(LOG_INFO, "Evasion attempt. Forbidden!");
+		LOG(INFO,"Evasion attempt. Forbidden!");
     sendline(sock, "iForbidden!\tfake\tfake\t0");
@@ -1826,3 +1778,3 @@
   if (is_it_a_directory(localfile) != 0) {
-    if (chdir(localfile) != 0) syslog(LOG_WARNING, "WARNING: failed to switch to directory '%s'", localfile);
+		if (chdir(localfile) != 0) LOG(WARNING,"WARNING: failed to switch to directory '%s'", localfile);
     outputdir(sock, &config, localfile, directorytolist, remoteclientaddr, srvsideparams);
@@ -1836,3 +1788,3 @@
   if (changedir(localfile) != 0) {
-    syslog(LOG_INFO, "ERROR: changedir() failure for '%s'", localfile);
+		LOG(INFO,"ERROR: changedir() failure for '%s'", localfile);
     sendline(sock, "iForbidden!\tfake\tfake\t0");
@@ -1844,3 +1796,3 @@
   if ((strcmp(directorytolist, "/caps.txt") == 0) && (config.capssupport != 0)) {  /* If asking for /caps.txt, return it. */
-    syslog(LOG_INFO, "Returned caps.txt data");
+		LOG(INFO,"Returned caps.txt data");
     printcapstxt(sock, &config, pVer);
@@ -1854,3 +1806,3 @@
   if ((fexist(localfile) == 0) || (islocalfileagophermap(localfile) != 0)) {
-    syslog(LOG_INFO, "FileExists check: the file doesn't exists");
+		LOG(INFO,"FileExists check: the file doesn't exist");
     sendline(sock, "3The selected resource doesn't exist!\tfake\tfake\t0");
@@ -1867,3 +1819,3 @@
       /* error while reading attributes */
-      syslog(LOG_INFO, "stat() failed: %s", strerror(errno));
+			LOG(INFO,"stat() failed: %s", strerror(errno));
       sendline(sock, "3Internal error\tfake\tfake\t0");
@@ -1875,3 +1827,3 @@
       /* not world-readable */
-      syslog(LOG_INFO, "Paranoid mode check failed: file is not world-readable");
+			LOG(INFO,"Paranoid mode check failed: file is not world-readable");
       sendline(sock, "3Permission denied\tfake\tfake\t0");
@@ -1899,3 +1851,3 @@
   /* we want a normal file's content */
-  syslog(LOG_INFO, "Returning file '%s'", localfile);
+	LOG(INFO,"Returning file '%s'", localfile);
   gophertype = DetectGopherType(localfile, config.extmap);
@@ -1914,3 +1866,3 @@
   close(sock);
-  syslog(LOG_INFO, "connection closed. duration: %lus", (unsigned long)(time(NULL) - StartTime));
+	LOG(INFO,"connection closed. duration: %lus", (unsigned long)(time(NULL) - StartTime));
   return(0);
========================================================================
--- motsognir/motsognir.h	        (nonexistent)
+++ motsognir+logmore/motsognir.h	2019-03-19 18:53:18.201861453 +0100
@@ -0,0 +1,89 @@
+/*
+ *  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *  *  Motsognir - The mighty gopher server                               *
+ *  *  Copyright (C) 2008-2019 Mateusz Viste                              *
+ *  *  http://motsognir.sourceforge.net                                   *
+ *  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
+ *
+ *  Some changes and additions: Copyright (C) 2018-2019 Dario Niedermann
+ *  $Id: motsognir.h 40 2019-03-19 17:53:18Z ndr $
+ * ----------------------------------------------------------------------
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ * ----------------------------------------------------------------------
+ */
+
+#include <regex.h>   /* regcomp(), regexec()... */
+
+/* Constants */
+#define pVer         "1.0.11+logmore2.0"
+#define pDate        "2008-2019"
+#define HOMEPAGE     "http://motsognir.sourceforge.net"
+#define OK_EXIT_NOW  -1
+
+	/* logging: */
+#define TO_SYSLOG     0
+#define TO_FILE       1
+#define INIT          65536  /* just needs to be a # not used in syslog.h */
+#define SET_PREFIX    65537  /* just needs to be a # not used in syslog.h */
+
+	/* parseCmdLine() operating modes: */
+#define FIRST_PASS    0
+#define FROM_FILE     1
+#define LAST_PASS     2
+
+struct MotsognirConfig {
+	char            *gopherroot;
+	char            *userdir;
+	char           **pubdirlist;
+	int              gopherport;
+	char            *gopherhostname;
+	char            *defaultgophermap;
+	int              verbosemode;
+	int              capssupport;
+	char            *capsservergeolocationstring;
+	char            *capsserverarchitecture;
+	char            *capsserverdescription;
+	char            *capsserverdefaultencoding;
+	int              cgisupport;
+	int              phpsupport;
+	int              subgophermaps;
+	int              paranoidmode;
+	char            *plugin;
+	regex_t         *pluginfilter;
+	char            *runasuser;
+	uid_t            runasuser_uid;
+	gid_t            runasuser_gid;
+	char            *runasuser_home;
+	char            *chroot;
+	char            *httperrfile;
+	char            *bind;
+  int              disableipv6;
+	char            *extmapfile;
+	struct extmap_t *extmap;
+	char            *configfile;
+	char             securldelim;
+	int		           autoinfo;
+};
+
+struct   logParams {
+	char   loggingType;
+	int    facility;
+	char  *fileName;
+	int   *localFacility; /* upon struct creation, point me to an array of
+													 syslog facility values. Like in parseCmdLine() */
+};
+
+#define CONTAINS_NO_TABS(str)	(strchr(str, '\t') == NULL)
+#define IS_A_DIRECTIVE(str)		(strcmp(str, "%FILES%") == 0 ||		\
+															 strcmp(str, "%DIRS%")  == 0)
========================================================================
--- motsognir/rc_d_motsognir	2019-03-17 19:20:02.575539547 +0100
+++ motsognir+logmore/rc_d_motsognir	2019-03-20 20:20:15.578049217 +0100
@@ -3,3 +3,3 @@
 # motsognir.sh for rc.d usage (c) 2013 Mateusz Viste.
-# $Id$
+# $Id: rc_d_motsognir 45 2019-03-20 19:20:15Z ndr $
 
@@ -14,3 +14,3 @@
 #  # optional
-#  motsognir_configfile="/usr/local/etc/motsognir.conf"
+#  motsognir_configfile="__SYSCONFDIR__/motsognir.conf"
 #
@@ -21,3 +21,3 @@
 motsognir_enable=${motsognir_enable:-"NO"}
-motsognir_configfile=${motsognir_configfile:-"/usr/local/etc/motsognir.conf"}
+motsognir_configfile=${motsognir_configfile:-"__SYSCONFDIR__/motsognir.conf"}
 
@@ -26,3 +26,3 @@
 load_rc_config $name
-command=/usr/local/bin/motsognir
+command="__BINDIR__/motsognir"
 command_args="--config ${motsognir_configfile}"
==================================================END==OF==PATCH=*=*=***
-----BEGIN PGP SIGNATURE-----

iQIcBAEBCAAGBQJclN7MAAoJEPJFaEEGrTgG4qcP/jYhsCGV7+CTO8Eakal4EKlp
Y+dwUY0+lvG9AwTCfoFPUdf8QAuxEFPD3REvmE00WovbktjZmPQ1Qtg/ccqHcH8M
45QbX14JaDcU92S2+cHAx3PUqfDb9YAHTnay+wEGP6ZhyO8u8ry00cHbkNfRl0jj
QOOG26aU2+9yhOsUHmcTtMfQw/YGGxXm49nn0DNFS+BMo8cMyJkCa49RkuT42X+C
0EK/gRepav51j0claDKGz4ndUelV7ntUyn+GUB3DPKNlC5pHsjR/tcQ0TEVCKN9a
IvCauPMA2+v2ZNovD0p+3SfynyoyWq5XrAHj1OB906Li6M92bff5fUxsvrqDbZ8P
7gOr2QIe9SJyB+F7sx0sebW9kBlA92CbmrmNwvlxwFJN7uIi0+Ut4eiZpGRwT+NV
/eNMpSbAl149eNfkw41um4LyTE5rpE/792q2gb+c990XzXck7qgepVvp2UsqZdYF
tqp+4x2ySw89MwfwZ26LUusFhmWh9iyAbi2fXYkgufOCdcaf6ZJAm5tjwV2pZe5g
ROYfr4ryMKEABVQRtafNCd8B3Eg9OL/iRcSqyEsqq2E8wAXa1xZXKJr6Btca1uCI
X+Tik8Mu5xrEaMAj+lCzcr8DVwGymxwDO/rm5hKNt7sEr6qV3I14v/M2TmZtE/5c
PE7qaQiQke1hC7pIvG7Y
=LfjR
-----END PGP SIGNATURE-----
“Logmore patch for Motsognir v1.0.11” is Copyright © Dario Niedermann — Released with no warranty under the terms of the GPLv3 license. Written and tested on Linux using GNU tools.
HomeNo Javascript iconValid HTML 4.01 StrictValid CSS!