From: Marco Emilio "sphakka" Poleggi <7766137+sphakka@users.noreply.github.com> Date: Sat, 24 Aug 2024 15:36:38 +0000 (+0200) Subject: Feature/fix email From: w/ configurable "displayname" (#17) X-Git-Tag: ver3_3_2~5 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2b18ac9cd8647dd32367fdf07eafa24c51cbe410;p=thirdparty%2Ffcron.git Feature/fix email From: w/ configurable "displayname" (#17) * WIP: add displayname option Signed-off-by: Marco Emilio "sphakka" Poleggi <7766137+sphakka@users.noreply.github.com> * Fix configure.in Signed-off-by: Marco Emilio "sphakka" Poleggi <7766137+sphakka@users.noreply.github.com> * Restored orig configure.in + displayname opt Signed-off-by: Marco Emilio "sphakka" Poleggi <7766137+sphakka@users.noreply.github.com> * job.c: create_mail(): Process 'displayname', etc. via new function 'make_mailbox()' as per RFC5322. Signed-off-by: Marco Emilio "sphakka" Poleggi <7766137+sphakka@users.noreply.github.com> * Fixed wrong formatting (tabs => spaces) Signed-off-by: Marco Emilio "sphakka" Poleggi <7766137+sphakka@users.noreply.github.com> * dev(code, doc, test): displayname handling refactored over two functions. * code: config handling of displayname moved to a new function 'format_displayname()' in 'fcronconf.c'; buffer overflow check added. In 'job.c': restored old "From:" mail header behavior if no displayname; buffer overflow check added. * test: added prototype support in 'Makefile' and 'test/' * doc: reviewed and cleaned 'en/fcron.conf.5.sgml' Signed-off-by: Marco Emilio "sphakka" Poleggi <7766137+sphakka@users.noreply.github.com> * dev(code, test): @PR #17, 3rd round. * Makefile(s): fixed alignement with spaces * config.in: moved displayname in "Check for fcron..." section. Removed test install code. * fcronconf.c: changed format_displayname()'s arg to avoid confusion with globals. Use stdbool. Use aux var when assigning from format_displayname(). * crondyn_svr.c, fileconf.c: minimal fixes to avoid conflict with stdbool. * fcron.conf.in: added comment for possible displayname's future default value. * global.h: added stdbool. * job.c: changed make_mailbox_addr()'s arg to avoid confusion with globals. Use stdbool. * test/mailbox_addr.c: rewritten with simpler explicit logic -- valgrinded again ;-) Signed-off-by: Marco Emilio "sphakka" Poleggi <7766137+sphakka@users.noreply.github.com> --------- Signed-off-by: Marco Emilio "sphakka" Poleggi <7766137+sphakka@users.noreply.github.com> --- diff --git a/Makefile.in b/Makefile.in index b0082dc..79b1583 100644 --- a/Makefile.in +++ b/Makefile.in @@ -4,95 +4,96 @@ # @configure_input@ - # The following should not be edited manually (use configure options) # If you must do it, BEWARE : some of the following is also defined # in config.h, so you must modify config.h AND Makefile in order # to set the same values in the two files. -FCRON_ALLOW = fcron.allow -FCRON_DENY = fcron.deny -FCRON_CONF = fcron.conf +# Please, keep assignments aligned with spaces. +FCRON_ALLOW = fcron.allow +FCRON_DENY = fcron.deny +FCRON_CONF = fcron.conf -SRCDIR := @srcdir@ +SRCDIR := @srcdir@ # Useful to build packages # you may want to use this var with a : 'make DESTDIR=dir install' -DESTDIR := +DESTDIR := # Where should we install it ? -prefix = @prefix@ -exec_prefix = @exec_prefix@ -DESTSBIN = @sbindir@ -DESTBIN = @bindir@ -ETC = @sysconfdir@ -FCRONTABS = @FCRONTABS@ -PIDDIR = @PIDDIR@ -FIFODIR = @FIFODIR@ -PIDFILE = @PIDFILE@ -REBOOT_LOCK = @REBOOT_LOCK@ -SUSPEND_FILE = @SUSPEND_FILE@ -FIFOFILE = @FIFOFILE@ -FCRON_SHELL = @FCRON_SHELL@ -SENDMAIL = @SENDMAIL@ -FCRON_EDITOR = @FCRON_EDITOR@ -OPTIM := @CFLAGS@ -LDFLAGS := @LDFLAGS@ -CPPFLAGS := @CPPFLAGS@ -I. -I${SRCDIR} -LIBS := @LIBS@ -LIBOBJS := @LIBOBJS@ -DEFS := @DEFS@ -CC := @CC@ -INSTALL := @INSTALL@ -STRIP := @STRIP@ -ROOTNAME := @ROOTNAME@ -ROOTGROUP := @ROOTGROUP@ -USERNAME := @USERNAME@ -GROUPNAME := @GROUPNAME@ -SYSFCRONTAB := @SYSFCRONTAB@ -DEBUG := @DEBUG@ -BOOTINSTALL := @BOOTINSTALL@ -ANSWERALL := @ANSWERALL@ -USEPAM := @USEPAM@ -FCRONDYN := @FCRONDYN@ -SYSTEMD_DIR := @SYSTEMD_DIR@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ +DESTSBIN = @sbindir@ +DESTBIN = @bindir@ +ETC = @sysconfdir@ +FCRONTABS = @FCRONTABS@ +PIDDIR = @PIDDIR@ +FIFODIR = @FIFODIR@ +PIDFILE = @PIDFILE@ +REBOOT_LOCK = @REBOOT_LOCK@ +SUSPEND_FILE = @SUSPEND_FILE@ +FIFOFILE = @FIFOFILE@ +FCRON_SHELL = @FCRON_SHELL@ +SENDMAIL = @SENDMAIL@ +FCRON_EDITOR = @FCRON_EDITOR@ +OPTIM := @CFLAGS@ +LDFLAGS := @LDFLAGS@ +CPPFLAGS := @CPPFLAGS@ -I. -I${SRCDIR} +LIBS := @LIBS@ +LIBOBJS := @LIBOBJS@ +DEFS := @DEFS@ +CC := @CC@ +INSTALL := @INSTALL@ +STRIP := @STRIP@ +ROOTNAME := @ROOTNAME@ +ROOTGROUP := @ROOTGROUP@ +USERNAME := @USERNAME@ +GROUPNAME := @GROUPNAME@ +SYSFCRONTAB := @SYSFCRONTAB@ +DEBUG := @DEBUG@ +BOOTINSTALL := @BOOTINSTALL@ +ANSWERALL := @ANSWERALL@ +USEPAM := @USEPAM@ +FCRONDYN := @FCRONDYN@ +SYSTEMD_DIR := @SYSTEMD_DIR@ +DISPLAYNAME := @DISPLAYNAME@ # Options -# -DDEBUG even more verbose -# -DCHECKJOBS send a mail containing the exact shell command -# for each execution of each job. -# -DFOREGROUND=[0|1] default run in foreground ? -#OPTION= -DCHECKJOBS -#OPTION= -O3 -mcpu=i686 -OPTION := +# -DDEBUG even more verbose +# -DCHECKJOBS send a mail containing the exact shell command +# for each execution of each job. +# -DFOREGROUND=[0|1] default run in foreground ? +#OPTION = -DCHECKJOBS +#OPTION = -O3 -mcpu=i686 +OPTION := #################################### # Should not be changed under this # #################################### -VERSION := @VERSION@ -CFLAGS += $(OPTIM) $(OPTION) $(DEFS) $(CPPFLAGS) $(LDFLAGS) +VERSION := @VERSION@ +CFLAGS += $(OPTIM) $(OPTION) $(DEFS) $(CPPFLAGS) $(LDFLAGS) ifeq ($(FCRONDYN), 1) -LIBOBJS := $(LIBOBJS) + LIBOBJS := $(LIBOBJS) endif -OBJSD := fcron.o cl.o subs.o mem.o save.o temp_file.o log.o database.o job.o conf.o u_list.o exe_list.o lavg_list.o env_list.o fcronconf.o filesubs.o select.o fcrondyn_svr.o suspend.o $(LIBOBJS) -OBJSTAB := fcrontab.o cl.o subs.o mem.o save.o temp_file.o log.o fileconf.o allow.o read_string.o u_list.o env_list.o fcronconf.o filesubs.o -OBJSDYN := fcrondyn.o subs.o mem.o log.o allow.o read_string.o fcronconf.o filesubs.o -OBJCONV := convert-fcrontab.o cl.o subs.o mem.o save.o log.o u_list.o env_list.o fcronconf.o filesubs.o -OBJSIG := fcronsighup.o subs.o mem.o log.o allow.o fcronconf.o filesubs.o -HEADERSALL := config.h $(SRCDIR)/global.h $(SRCDIR)/cl.h $(SRCDIR)/log.h $(SRCDIR)/subs.h $(SRCDIR)/mem.h $(SRCDIR)/save.h $(SRCDIR)/option.h $(SRCDIR)/dyncom.h +OBJSD := fcron.o cl.o subs.o mem.o save.o temp_file.o log.o database.o job.o conf.o u_list.o exe_list.o lavg_list.o env_list.o fcronconf.o filesubs.o select.o fcrondyn_svr.o suspend.o $(LIBOBJS) +OBJSTAB := fcrontab.o cl.o subs.o mem.o save.o temp_file.o log.o fileconf.o allow.o read_string.o u_list.o env_list.o fcronconf.o filesubs.o +OBJSDYN := fcrondyn.o subs.o mem.o log.o allow.o read_string.o fcronconf.o filesubs.o +OBJCONV := convert-fcrontab.o cl.o subs.o mem.o save.o log.o u_list.o env_list.o fcronconf.o filesubs.o +OBJSIG := fcronsighup.o subs.o mem.o log.o allow.o fcronconf.o filesubs.o +HEADERSALL := config.h $(SRCDIR)/global.h $(SRCDIR)/cl.h $(SRCDIR)/log.h $(SRCDIR)/subs.h $(SRCDIR)/mem.h $(SRCDIR)/save.h $(SRCDIR)/option.h $(SRCDIR)/dyncom.h # this is a regular expression : # do not ci automaticaly generated files and doc (done by doc's Makefile) -RCSNOCI:=.*\(.html\|VERSION\|MANIFEST\|configure\|install.sh\|config.log\|config.status\|config.h\|config.cache\|Makefile\|doc.*\|CVS.*\|.git.*\) +RCSNOCI := .*\(.html\|VERSION\|MANIFEST\|configure\|install.sh\|config.log\|config.status\|config.h\|config.cache\|Makefile\|doc.*\|CVS.*\|.git.*\) RUN_NON_PRIVILEGED := @RUN_NON_PRIVILEGED@ ifeq ($(RUN_NON_PRIVILEGED), 1) - BINMODE:=711 - BINMODESIGHUP:=711 + BINMODE := 711 + BINMODESIGHUP := 711 else - BINMODE:=6711 - BINMODESIGHUP:=4710 + BINMODE := 6711 + BINMODESIGHUP := 4710 endif ifeq ($(FCRONDYN), 1) @@ -126,7 +127,7 @@ exe_list_test: exe_list.o u_list.o exe_list_test.o log.o subs.o -DFCRONTABS="\"${FCRONTABS}\"" \ -DFCRON_ALLOW="\"${FCRON_ALLOW}\"" -DFCRON_DENY="\"${FCRON_DENY}\"" \ -DFCRON_SHELL="\"${FCRON_SHELL}\"" -DSENDMAIL="\"${SENDMAIL}\"" \ - -DFCRON_EDITOR="\"${FCRON_EDITOR}\"" -DBINDIREX="\"${DESTBIN}\"" \ + -DFCRON_EDITOR="\"${FCRON_EDITOR}\"" \-DDISPLAYNAME="\"${DISPLAYNAME}\"" -DBINDIREX="\"${DESTBIN}\"" \ -c $< initscripts: @@ -138,7 +139,12 @@ initscripts: documentation: $(MAKE) -C doc doc-if-none -install: install-staged strip perms +.PHONY: test +test: + $(MAKE) -C test tests + + +install: install-staged strip perms ifeq ($(BOOTINSTALL), 1) $(SRCDIR)/script/boot-install "$(INSTALL) -o $(ROOTNAME)" $(DESTSBIN) $(DEBUG) $(FCRONTABS) $(ANSWERALL) $(SRCDIR) endif @@ -180,16 +186,16 @@ endif perms: install-staged strip # Note : we don't use "chown user:group file" because some systems use ":" # and others "." as separator. - chown $(ROOTNAME) $(DESTDIR)$(DESTSBIN) - chgrp $(ROOTGROUP) $(DESTDIR)$(DESTSBIN) - chown $(ROOTNAME) $(DESTDIR)$(DESTBIN) - chgrp $(ROOTGROUP) $(DESTDIR)$(DESTBIN) - chown $(ROOTNAME) $(DESTDIR)$(ETC) - chgrp $(ROOTGROUP) $(DESTDIR)$(ETC) - chown $(ROOTNAME) $(DESTDIR)$(FIFODIR) - chgrp $(ROOTGROUP) $(DESTDIR)$(FIFODIR) - chown $(ROOTNAME) $(DESTDIR)$(PIDDIR) - chgrp $(ROOTGROUP) $(DESTDIR)$(PIDDIR) + chown $(ROOTNAME) $(DESTDIR)$(DESTSBIN) + chgrp $(ROOTGROUP) $(DESTDIR)$(DESTSBIN) + chown $(ROOTNAME) $(DESTDIR)$(DESTBIN) + chgrp $(ROOTGROUP) $(DESTDIR)$(DESTBIN) + chown $(ROOTNAME) $(DESTDIR)$(ETC) + chgrp $(ROOTGROUP) $(DESTDIR)$(ETC) + chown $(ROOTNAME) $(DESTDIR)$(FIFODIR) + chgrp $(ROOTGROUP) $(DESTDIR)$(FIFODIR) + chown $(ROOTNAME) $(DESTDIR)$(PIDDIR) + chgrp $(ROOTGROUP) $(DESTDIR)$(PIDDIR) # change spool dir mode chown $(USERNAME) $(DESTDIR)$(FCRONTABS) @@ -245,7 +251,7 @@ ifeq ($(FCRONDYN), 1) endif endif -install-boot: install +install-boot: install $(SRCDIR)/script/boot-install "$(INSTALL) -o $(ROOTNAME)" $(DESTSBIN) $(DEBUG) $(FCRONTABS) $(ANSWERALL) $(SRCDIR) install-restart: install @@ -266,6 +272,7 @@ clean: rm -f *.o core rm -f fcron fcrontab fcrondyn fcronsighup convert-fcrontab files/fcron.conf $(MAKE) -C doc clean + $(MAKE) -C test clean ciclean: clean find ./ -name "*~" -exec rm -f {} \; @@ -276,6 +283,7 @@ vclean: ciclean files/fcron.conf script/fcron.init.suse script/fcron.init.systemd script/fcron.init.systemd.reboot \ script/fcron.sh script/sysVinit-launcher $(MAKE) -C doc clean + $(MAKE) -C test clean files/fcron.conf: $(SRCDIR)/files/fcron.conf.in config.h @@ -291,7 +299,7 @@ indent: --dont-cuddle-else *.c *.h configure: configure.in -# update configure script, then Makefile and config.h, and finally +# update configure script, then Makefile and config.h, and finally # run make tar using the new Makefile (needed because the version # is set in the configure.in file) autoconf diff --git a/configure.in b/configure.in index 6fc44e1..7ef4b45 100644 --- a/configure.in +++ b/configure.in @@ -94,10 +94,10 @@ AC_CHECK_LIB(kstat, kstat_open, [kstat=1], [kstat=0]) if test $getloadavg -eq 1; then dnl Nothing to do ... AC_FUNC_GETLOADAVG - AC_MSG_CHECKING(function to use for lavg* options) + AC_MSG_CHECKING(function to use for lavg* options) AC_MSG_RESULT(getloadavg()) elif test $kstat -eq 1; then - AC_MSG_CHECKING(function to use for lavg* options) + AC_MSG_CHECKING(function to use for lavg* options) LIBS="$LIBS -lkstat" AC_LIBOBJ([getloadavg]) AC_DEFINE_UNQUOTED(HAVE_KSTAT, 1) @@ -105,7 +105,7 @@ elif test $kstat -eq 1; then else dnl Try to use the /proc/loadavg file ... AC_FUNC_GETLOADAVG - AC_MSG_CHECKING(function to use for lavg* options) + AC_MSG_CHECKING(function to use for lavg* options) AC_MSG_RESULT(/proc/loadavg) fi AC_CHECK_FUNCS(getcwd gettimeofday mktime putenv strerror setenv unsetenv gethostname) @@ -167,7 +167,7 @@ dnl --------------------------------------------------------------------- fcron_enable_checks=yes AC_ARG_ENABLE(checks, [ --disable-checks Don't verify that programs exist on the host ], dnl ' -[ case "$enableval" in +[ case "$enableval" in no) fcron_enable_checks=no ;; @@ -179,7 +179,11 @@ AC_ARG_ENABLE(checks, ;; esac ]) - + +DISPLAYNAME= +AC_MSG_RESULT([$DISPLAYNAME]) +AC_SUBST([DISPLAYNAME]) + dnl --------------------------------------------------------------------- dnl Programs ... @@ -287,6 +291,7 @@ fi AC_MSG_RESULT([$FCRON_EDITOR]) AC_SUBST([FCRON_EDITOR]) + dnl --------------------------------------------------------------------- dnl Paths ... @@ -364,7 +369,7 @@ AC_ARG_WITH(proc, [ case "$withval" in no) AC_MSG_WARN([ -Without proc, you won't be able to use the lavg* options +Without proc, you won't be able to use the lavg* options ]) AC_DEFINE(NOLOADAVG) ;; @@ -450,7 +455,7 @@ AC_ARG_WITH(run-non-privileged, AC_MSG_WARN([ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -WARNING : +WARNING : This option allows a non privileged user to run fcron. When used, fcron does not change its rights before running a job (i.e., if joe runs fcron, every job will run as joe). @@ -558,7 +563,7 @@ AC_ARG_WITH(rootname, *) rootname=$withval ;; - esac + esac ]) if test $fcron_enable_checks = 'yes'; then @@ -891,13 +896,13 @@ AC_ARG_WITH(pam, AC_MSG_ERROR(Must be set to either "yes" or "no".) ;; esac ], - if test "$usepam" = "1"; then + if test "$usepam" = "1"; then AC_MSG_RESULT(yes) AC_CHECK_LIB(pam, pam_acct_mgmt) else usepam=0 AC_MSG_RESULT(no) - fi + fi ) if test "$usepam" != "0" && echo "$LIBS" | grep -- "-lpam" > /dev/null ; then usepam=1 @@ -926,7 +931,7 @@ AC_ARG_WITH(selinux, else AC_MSG_RESULT(not available) AC_MSG_ERROR([ - You requested the use of SELinux, but SELinux is considered + You requested the use of SELinux, but SELinux is considered as not available by configure script. ]) fi @@ -966,7 +971,7 @@ AC_MSG_CHECKING(Looking for docbook2man converter) AC_ARG_WITH(db2man, [ --with-db2man=PATH(or 'no') set PATH to a docbook2man converter.], [ case "$withval" in - no) + no) DB2MAN="" AC_MSG_RESULT(none) ;; @@ -995,7 +1000,7 @@ AC_ARG_WITH(db2man-spec, [ --with-db2man-spec=PATH set the PATH to docbook2man-spec file (needed if no db2man converter is set).], [ case "$withval" in - no) + no) DB2MAN_SPEC="" AC_MSG_RESULT(none) ;; @@ -1037,7 +1042,7 @@ AC_MSG_CHECKING(Looking for dsssl stylsheets) AC_ARG_WITH(dsssl-dir, [ --with-dsssl-dir=DIR change the default location of DSSSL stylesheets.], [ case "$withval" in - no) + no) DSSSL_DIR="" AC_MSG_RESULT(none) ;; @@ -1069,7 +1074,7 @@ dnl Final settings dnl --------------------------------------------------------------------- -AC_OUTPUT(Makefile doc/Makefile doc/stylesheets/fcron-doc.dsl) +AC_OUTPUT(Makefile doc/Makefile test/Makefile doc/stylesheets/fcron-doc.dsl) dnl --------------------------------------------------------------------- @@ -1169,8 +1174,7 @@ echo "sbin dir: $sbindir" echo "spool dir: $sp" echo "etc dir: $sysconfdir" echo "doc dir: $docdir" -echo "man dir: $mandir " - +echo "man dir: $mandir" echo echo "You can now run '$MAKE' to compile" diff --git a/doc/en/fcron.conf.5.sgml b/doc/en/fcron.conf.5.sgml index 69c25e6..8f1d00d 100644 --- a/doc/en/fcron.conf.5.sgml +++ b/doc/en/fcron.conf.5.sgml @@ -1,4 +1,4 @@ - diff --git a/fcronconf.c b/fcronconf.c index b19dd31..a1c5a44 100644 --- a/fcronconf.c +++ b/fcronconf.c @@ -44,6 +44,69 @@ char *fcrondeny = NULL; char *shell = NULL; char *sendmail = NULL; char *editor = NULL; +char *displayname = NULL; + +char +*format_displayname(char *conf_value) + /* Format the input string `conf_value` according to RFC5322 sec. 3.2.3. + * . + * Returns: either the formatted displayname (possibly unchanged or empty) + * as a new dynamically allocated string (must be properly freed by the + * caller) or NULL on errors like buffer overflow. */ +{ + bool need_quotes = false; + char c = '\0'; + char *bpos = NULL, *dpos = NULL; + char *buf1 = NULL, *buf2 = NULL; + + /* Shorter than max because of the option prefix "displayname = " */ + const uint buf_len = LINE_LEN - 14; + uint cwritten = 0; + + if (strlen(conf_value) == 0) return strdup2(""); + + buf1 = (char *)alloc_safe(buf_len * sizeof(char), "1st buffer"); + buf2 = (char *)alloc_safe(buf_len * sizeof(char), "2nd buffer"); + + /* walk the conf_value and rebuild it in buf1 */ + bpos = buf1; + for (dpos = conf_value; *dpos; *dpos++) { + c = *dpos; + if (strchr(SPECIAL_MBOX_CHARS, c)) { + /* insert escape */ + if (c == DQUOTE) { + *bpos++ = BSLASH; + ++cwritten; + } + need_quotes = true; + } + if (cwritten >= buf_len) { + error("Formatted 'displayname' exceeds %u chars", buf_len); + Free_safe(buf1); + Free_safe(buf2); + return NULL; + } + *bpos++ = c; + ++cwritten; + } + + if (need_quotes) { + if (snprintf(buf2, buf_len, "\"%s\"", buf1) >= buf_len){ + error("Formatted 'displayname' exceeds %u chars", buf_len); + Free_safe(buf1); + Free_safe(buf2); + return NULL; + } + Free_safe(buf1); + + return buf2; + } + + /* unchanged */ + Free_safe(buf2); + + return buf1; +} void init_conf(void) @@ -66,6 +129,7 @@ init_conf(void) sendmail = strdup2(SENDMAIL); #endif editor = strdup2(FCRON_EDITOR); + displayname = strdup2(DISPLAYNAME); } void @@ -82,6 +146,7 @@ free_conf(void) Free_safe(shell); Free_safe(sendmail); Free_safe(editor); + Free_safe(displayname); } void @@ -188,6 +253,11 @@ read_conf(void) else if (strncmp(ptr1, "editor", namesize) == 0) { Set(editor, ptr2); } + else if (strncmp(ptr1, "displayname", namesize) == 0) { + char *output = format_displayname(ptr2); + Set(displayname, output ? output : ""); + Free_safe(output); + } else error("Unknown var name at line %s : line ignored", buf); diff --git a/fcronconf.h b/fcronconf.h index e97694c..d3b03ce 100644 --- a/fcronconf.h +++ b/fcronconf.h @@ -25,6 +25,10 @@ #ifndef __FCRONCONF_H__ #define __FCRONCONF_H__ +/* RFC5322's special mailbox address charcters */ +#define DQUOTE '\"' +#define BSLASH '\\' +#define SPECIAL_MBOX_CHARS "()<>[].,:;@\"\\" /* global variables */ @@ -39,6 +43,7 @@ extern char *fifofile; extern char *editor; extern char *shell; extern char *sendmail; +extern char *displayname; /* end of global variables */ /* functions prototypes */ diff --git a/fcrondyn_svr.c b/fcrondyn_svr.c index 99ca08f..11e7475 100644 --- a/fcrondyn_svr.c +++ b/fcrondyn_svr.c @@ -1,5 +1,5 @@ /* - * FCRON - periodic command scheduler + * FCRON - periodic command scheduler * * Copyright 2000-2021 Thibault Godouet * @@ -12,11 +12,11 @@ * 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * + * * The GNU General Public License can also be found in the file * `LICENSE' that comes with the fcron source distribution. */ @@ -251,7 +251,9 @@ auth_client_so_peercred(struct fcrondyn_cl *client) * Sets client->fcl_user on success, don't do anything on failure * so that the client stays unauthenticated */ { - const int true = 1; + /* [@PR #17] renamed (previously called 'true') to avoid conflict with + stdbool */ + const int value = 1; /* There is no ucred.h (or equivalent) on linux to define struct ucred (!!) * so we do it here */ #if ! ( defined(HAVE_CRED_H) && defined(HAVE_UCRED_H) \ @@ -266,8 +268,8 @@ auth_client_so_peercred(struct fcrondyn_cl *client) socklen_t cred_size = sizeof(cred); struct passwd *p_entry = NULL; - setsockopt(client->fcl_sock_fd, SOL_SOCKET, SO_PASSCRED, &true, - sizeof(true)); + setsockopt(client->fcl_sock_fd, SOL_SOCKET, SO_PASSCRED, &value, + sizeof(value)); if (getsockopt (client->fcl_sock_fd, SOL_SOCKET, SO_PEERCRED, &cred, &cred_size) != 0) { @@ -861,7 +863,7 @@ exe_cmd(struct fcrondyn_cl *client) void remove_connection(struct fcrondyn_cl **client, struct fcrondyn_cl *prev_client, select_instance * si) -/* close the connection, remove it from the list +/* close the connection, remove it from the list and make client points to the next entry */ { debug("closing connection : fd : %d", (*client)->fcl_sock_fd); diff --git a/fileconf.c b/fileconf.c index 99ee08f..99b7923 100644 --- a/fileconf.c +++ b/fileconf.c @@ -1,5 +1,5 @@ /* - * FCRON - periodic command scheduler + * FCRON - periodic command scheduler * * Copyright 2000-2021 Thibault Godouet * @@ -12,11 +12,11 @@ * 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * + * * The GNU General Public License can also be found in the file * `LICENSE' that comes with the fcron source distribution. */ @@ -458,41 +458,43 @@ assign_option_string(char **var, char *value) } +/* [@PR #17] renamed labels 'true' and 'false' to avoid conflict with + stdbool */ char * get_bool(char *ptr, int *i) /* get a bool value : either true (1) or false (0) * return NULL on error */ { if (*ptr == '1') - goto true; + goto conf_true; else if (*ptr == '0') - goto false; + goto conf_false; else if (strncmp(ptr, "true", 4) == 0) { ptr += 3; - goto true; + goto conf_true; } else if (strncmp(ptr, "yes", 3) == 0) { ptr += 2; - goto true; + goto conf_true; } else if (strncmp(ptr, "false", 5) == 0) { ptr += 4; - goto false; + goto conf_false; } else if (strncmp(ptr, "no", 2) == 0) { ptr += 1; - goto false; + goto conf_false; } else return NULL; - true: - *i = 1; + conf_true: + *i = true; ptr++; return ptr; - false: - *i = 0; + conf_false: + *i = false; ptr++; return ptr; @@ -1423,7 +1425,7 @@ read_shortcut(char *ptr, cf_t * cf) } /* The next char must be a space (no other option allowed when using - * a shortcut: if the user wants to use options, they should use the + * a shortcut: if the user wants to use options, they should use the * native fcron lines */ if (!isspace((int)*ptr)) { fprintf(stderr, "%s:%d: No space after shortcut: skipping line.\n", @@ -1674,7 +1676,7 @@ read_period(char *ptr, cf_t * cf) /* skip the % */ ptr++; - /* a runfreq set to 1 means : this is a periodical line + /* a runfreq set to 1 means : this is a periodical line * (runfreq cannot be changed by read_opt() if already set to 1) */ cl->cl_remain = cl->cl_runfreq = 1; @@ -2028,7 +2030,7 @@ read_field(char *ptr, bitstr_t * ary, int max, const char **names) void delete_file(const char *user_name) - /* free a file if user_name is not null + /* free a file if user_name is not null * otherwise free all files */ { cf_t *file = NULL; diff --git a/files/fcron.conf.in b/files/fcron.conf.in index f3bee22..1443613 100644 --- a/files/fcron.conf.in +++ b/files/fcron.conf.in @@ -23,3 +23,13 @@ sendmail = @@SENDMAIL@ # Location of the default editor for "fcrontab -e" editor = @@FCRON_EDITOR@ + +# Display name for the "From: " header of mails sent by us. Default is +# unset/empty, which keeps the "From: " header as it was before this feature +# was added -- not RFC5322-compliant, though. A sane RFC5322-compliant setting, +# could be: +# +# displayname = Fcron Deamon +# +# Please read fcron.conf(5) before setting it! +displayname = @@DISPLAYNAME@ diff --git a/global.h b/global.h index 6409ddc..0a1160e 100644 --- a/global.h +++ b/global.h @@ -1,5 +1,5 @@ /* - * FCRON - periodic command scheduler + * FCRON - periodic command scheduler * * Copyright 2000-2021 Thibault Godouet * @@ -12,18 +12,18 @@ * 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * + * * The GNU General Public License can also be found in the file * `LICENSE' that comes with the fcron source distribution. */ -/* +/* WARNING : this file should not be modified. Compilation's options are in config.h */ @@ -31,7 +31,7 @@ #ifndef __GLOBAL_H__ #define __GLOBAL_H__ -/* config.h must be included before every other includes +/* config.h must be included before every other includes * (contains the compilation options) */ #include "config.h" @@ -69,6 +69,7 @@ #include #endif +#include #include #include #include diff --git a/job.c b/job.c index 584d5f0..71a0ee9 100644 --- a/job.c +++ b/job.c @@ -1,5 +1,5 @@ /* - * FCRON - periodic command scheduler + * FCRON - periodic command scheduler * * Copyright 2000-2021 Thibault Godouet * @@ -12,11 +12,11 @@ * 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * + * * The GNU General Public License can also be found in the file * `LICENSE' that comes with the fcron source distribution. */ @@ -27,6 +27,7 @@ #include "job.h" #include "temp_file.h" + void sig_dfl(void); void end_job(cl_t * line, int status, FILE * mailf, short mailpos, char **sendmailenv); @@ -276,6 +277,42 @@ sig_dfl(void) signal(SIGPIPE, SIG_DFL); } +char * +make_mailbox_addr(char *displayname_conf, char *mail_from, char *hostname) + /* Produce a "mailbox" header as per RFC5322 sec. 3.2.3 + * . + * Returns: either the formatted mailbox header as a new dynamically + * allocated string (must be properly freed by the caller) or NULL on + * errors like buffer overflow. */ +{ + char *buf = NULL; + uint written = 0; + bool need_anglebrackets = false; + + /* Shorter than max because the header prefix "From: " are added + downstream. */ + const uint buf_len = MAIL_LINE_LEN_MAX - 6; + + buf = (char *)alloc_safe(buf_len * sizeof(char), "mailbox addr buffer"); + + /* == strlen(),but faster */ + need_anglebrackets = displayname_conf[0] != '\0'; + + /* no @ here, it's handled upstream */ + if (need_anglebrackets) + written = snprintf(buf, buf_len, "%s %c%s%s%c", + displayname_conf, '<', mail_from, hostname, '>'); + else + written = snprintf(buf, buf_len, "%s%s", mail_from, hostname); + + if (written >= buf_len) { + error("Mailbox addr exceeds %u chars", buf_len); + Free_safe(buf); + return NULL; + } + + return buf; +} FILE * create_mail(cl_t * line, char *subject, char *content_type, char *encoding, @@ -290,6 +327,7 @@ create_mail(cl_t * line, char *subject, char *content_type, char *encoding, /* hostname to add to email addresses (depending on if they have a '@') */ char *hostname_from = ""; char *hostname_to = ""; + char *mailbox_addr = ""; int i = 0; if (mailf == NULL) @@ -318,8 +356,23 @@ create_mail(cl_t * line, char *subject, char *content_type, char *encoding, hostname[0] = '\0'; #endif /* HAVE_GETHOSTNAME */ - /* write mail header */ - fprintf(mailf, "From: %s%s (fcron)\n", mailfrom, hostname_from); + /* write mail header. Global 'displayname' comes from fcronconf.h */ + if (strlen(displayname) > 0){ + /* New behavior -- RFC-compliant */ + mailbox_addr = make_mailbox_addr(displayname, mailfrom, hostname_from); + if (mailbox_addr) { + fprintf(mailf, "From: %s\n", mailbox_addr); + Free_safe(mailbox_addr); + } + else { + error("could not make the mailbox address"); + fprintf(mailf, "From: %s%s\n", mailfrom, hostname_from); + } + } + else + /* Old behavior */ + fprintf(mailf, "From: %s%s (fcron)\n", mailfrom, hostname_from); + fprintf(mailf, "To: %s%s\n", line->cl_mailto, hostname_to); if (subject) @@ -447,7 +500,7 @@ read_write_pipe(int fd, void *buf, size_t size, int action) int read_pipe(int fd, void *buf, size_t size) - /* Read data from pipe. + /* Read data from pipe. * Handles signal interruptions, and read in several passes. * Returns ERR in case of a closed pipe, the errno from read * for other errors, and OK if everything was read successfully */ @@ -457,7 +510,7 @@ read_pipe(int fd, void *buf, size_t size) int write_pipe(int fd, void *buf, size_t size) - /* Read data from pipe. + /* Read data from pipe. * Handles signal interruptions, and read in several passes. * Returns ERR in case of a closed pipe, the errno from write * for other errors, and OK if everything was read successfully */ diff --git a/job.h b/job.h index ec135aa..a83c46b 100644 --- a/job.h +++ b/job.h @@ -1,5 +1,5 @@ /* - * FCRON - periodic command scheduler + * FCRON - periodic command scheduler * * Copyright 2000-2021 Thibault Godouet * @@ -12,11 +12,11 @@ * 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * + * * The GNU General Public License can also be found in the file * `LICENSE' that comes with the fcron source distribution. */ @@ -25,6 +25,8 @@ #ifndef __JOB_H__ #define __JOB_H__ +#define MAIL_LINE_LEN_MAX 998 /* RFC5322's max line length */ + /* functions prototypes */ extern void change_user_setup_env(struct cl_t *cl, char ***sendmailenv, char ***jobenv, char **curshell, diff --git a/test/Makefile.in b/test/Makefile.in new file mode 100644 index 0000000..accc772 --- /dev/null +++ b/test/Makefile.in @@ -0,0 +1,18 @@ +# Prototype makefile, while waiting for integration of a test framework +SRCDIR := .. +LIBS := @LIBS@ +SRCST := mailbox_addr.c +OBJST := $(patsubst %.c,%.o,$(SRCST)) +EXEST := $(patsubst %.c,%,$(SRCST)) +OBJSD := $(addprefix $(SRCDIR)/,log.o job.o mem.o filesubs.o subs.o fcronconf.o temp_file.o env_list.o u_list.o) + +CFLAGS += -I$(SRCDIR) + +.PHONY: clean tests + +tests: $(OBJSD) + $(CC) $(CFLAGS) $(OBJSD) $(LIBS) $(SRCST) -o $(EXEST) + ./$(EXEST) + +clean: + rm -f *.o core $(EXEST) diff --git a/test/mailbox_addr.c b/test/mailbox_addr.c new file mode 100644 index 0000000..cb3cbeb --- /dev/null +++ b/test/mailbox_addr.c @@ -0,0 +1,130 @@ +/* Prototype test harness, while waiting for integration of a test framework */ +#include +#include +#include +#include + +#include "fcron.h" +#include "job.h" +#include "fcronconf.h" + +#define dasserteq(DESCR, RETURNED, EXPECTED) \ +{ \ + printf("%s: %ld ?= %ld\n", (DESCR), (long)(RETURNED), (long)(EXPECTED)); \ + assert((RETURNED) == (EXPECTED)); \ +} + +/* These globals should maybe come from a lib instead of the various exes? */ +char *prog_name = NULL; +pid_t daemon_pid = 0; +char foreground = 1; +char default_mail_charset[TERM_LEN] = ""; +pam_handle_t *pamh = NULL; +const struct pam_conv apamconv = { NULL }; +mode_t saved_umask; +uid_t rootuid = 0; +char *tmp_path = ""; + +char *format_displayname(char *displayname); +char *make_mailbox_addr(char *displayname, char *mail_from, char *hostname); + + +void _test_format_displayname(char *test_desc, char *arg, char *expected) +{ + char *output = NULL; + + output = format_displayname(arg); + printf("%s: format_displayname('%s'): '%s' ?= '%s'\n", + test_desc, arg, output, expected); + assert(strcmp(output, expected) == 0); + Free_safe(output); +} + + +void _test_make_mailbox_addr(char *test_desc, char *displayname, + char *mailfrom, char *hostname, char *expected) +{ + char *output = NULL; + + output = make_mailbox_addr(displayname, mailfrom, hostname); + printf("%s: make_mailbox_addr('%s', '%s', '%s'): '%s' ?= '%s'\n", + test_desc, displayname, mailfrom, hostname, output, expected); + assert(strcmp(output, expected) == 0); + Free_safe(output); +} + +int main(int argc, char* argv[]) +{ + char *displayname = NULL; + char *output = NULL; + + /* Mind that format_displayname() might modify input displayname, thus all + special characters must be tested */ + _test_format_displayname("empty displayname", "", ""); + _test_format_displayname("displayname with no special char", + "Foo Bar", "Foo Bar"); + _test_format_displayname("displayname with no special char", + "Foo'Bar", "Foo'Bar"); + + /* Make sure to extend this series, should SPECIAL_MBOX_CHARS change. All + those require quoting */ + _test_format_displayname("displayname with special char", + "(Foo Bar", "\"(Foo Bar\""); + _test_format_displayname("displayname with special char", + "Foo Bar)", "\"Foo Bar)\""); + _test_format_displayname("displayname with special char", + "Foo Bo Bar", "\"Fo>o Bar\""); + _test_format_displayname("displayname with special char", + "F[oo Bar", "\"F[oo Bar\""); + _test_format_displayname("displayname with special char", + "Foo] Bar", "\"Foo] Bar\""); + _test_format_displayname("displayname with special char", + "Foo . Bar", "\"Foo . Bar\""); + _test_format_displayname("displayname with special char", + "Foo, Bar", "\"Foo, Bar\""); + _test_format_displayname("displayname with special char", + "Foo B:ar", "\"Foo B:ar\""); + _test_format_displayname("displayname with special char", + "Foo ;Bar", "\"Foo ;Bar\""); + _test_format_displayname("displayname with special char", + "Foo@Bar", "\"Foo@Bar\""); + _test_format_displayname("displayname with double quote", + "Foo \" Bar", "\"Foo \\\" Bar\""); + _test_format_displayname("displayname with backslash", + "Foo \\Bar", "\"Foo \\Bar\""); + + /* make_mailbox_addr() only composes the "mailbox" address header: it's + agnostic of any special character */ + _test_make_mailbox_addr("empty displayname => no angle brackets", + "", "baz", "@quux", "baz@quux"); + _test_make_mailbox_addr("displayname => needs angle brackets", + "Foo Bar", "baz", "@quux", "Foo Bar "); + + + /* overflow tests */ + displayname = (char *)realloc_safe(displayname, + LINE_LEN + 1 * sizeof(char), + "displayname buffer"); + memset(displayname, 'a', LINE_LEN); + displayname[LINE_LEN] = '\0'; + displayname[0] = DQUOTE; + + output = format_displayname(displayname); + dasserteq("format_displayname: overflow", output, NULL); + Free_safe(output); + + output = make_mailbox_addr(displayname, "baz", "@quux"); + dasserteq("make_mailbox_addr: overflow", output, NULL); + Free_safe(output); + + Free_safe(displayname); + + return 0; +} + +/* Local Variables: */ +/* mode: c */ +/* indent-tabs-mode: nil */ +/* End: */