From a330bb3d2947622ece03757cbcf4ecadce5ac261 Mon Sep 17 00:00:00 2001 From: Thibault Godouet Date: Sat, 24 Aug 2024 23:40:44 +0100 Subject: [PATCH] Rename config key displayname to maildisplayname, and refactor. (#33) The main changes are: - rename the fcron.conf config key displayname into maildisplayname, for clarity. - move the underlying code into its own mail.c file, with some refactoring to improve clarity and simplify slightly. - extend the extra overflow tests to test at the limit, and just over the limit (expecting an overflow error) - ensure things work on systems which don't have stdbool.h (which is C99) Full commit list: * fix compilation error (couldn't find selinux.h) * Rename boolean variable names. * Check for stdbool.h's availability. * Refactor maildisplayname. * Move format_displayname() and make_mailbox_addr() into their own file. * Ignore test binary. * Rename option displayname to maildisplayname. --- .gitignore | 1 + Makefile.in | 88 ++++++++++++++-------------- config.h.in | 3 + configure.in | 8 ++- doc/en/fcron.conf.5.sgml | 4 +- doc/fcron-doc.mod.in | 2 +- fcronconf.c | 75 +++--------------------- fcronconf.h | 2 +- fcrondyn_svr.c | 8 +-- fileconf.c | 18 +++--- files/fcron.conf.in | 8 +-- global.h | 6 ++ job.c | 64 +++++---------------- job.h | 1 + mail.c | 120 +++++++++++++++++++++++++++++++++++++++ mail.h | 35 ++++++++++++ script/gen-in.pl | 2 +- test/Makefile.in | 3 +- test/mailbox_addr.c | 66 +++++++++++++++++---- 19 files changed, 312 insertions(+), 202 deletions(-) create mode 100644 mail.c create mode 100644 mail.h diff --git a/.gitignore b/.gitignore index 17c2b5a..73e8181 100644 --- a/.gitignore +++ b/.gitignore @@ -27,6 +27,7 @@ doc/fr/HTML/* doc/fr/man/* doc/fr/txt/* configure +test/mailbox_addr tests/test-open* tests/test-types* tests/test-uidgid* diff --git a/Makefile.in b/Makefile.in index 79b1583..c82582f 100644 --- a/Makefile.in +++ b/Makefile.in @@ -20,42 +20,42 @@ SRCDIR := @srcdir@ 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@ -DISPLAYNAME := @DISPLAYNAME@ +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@ +MAILDISPLAYNAME := @MAILDISPLAYNAME@ # Options # -DDEBUG even more verbose @@ -76,12 +76,12 @@ CFLAGS += $(OPTIM) $(OPTION) $(DEFS) $(CPPFLAGS) $(LDFLAGS) ifeq ($(FCRONDYN), 1) 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 mail.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 mail.o filesubs.o +OBJSDYN := fcrondyn.o subs.o mem.o log.o allow.o read_string.o fcronconf.o mail.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 mail.o filesubs.o +OBJSIG := fcronsighup.o subs.o mem.o log.o allow.o fcronconf.o mail.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 $(SRCDIR)/mail.h # this is a regular expression : # do not ci automaticaly generated files and doc (done by doc's Makefile) @@ -127,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}\"" \-DDISPLAYNAME="\"${DISPLAYNAME}\"" -DBINDIREX="\"${DESTBIN}\"" \ + -DFCRON_EDITOR="\"${FCRON_EDITOR}\"" \-DMAILDISPLAYNAME="\"${MAILDISPLAYNAME}\"" -DBINDIREX="\"${DESTBIN}\"" \ -c $< initscripts: @@ -140,7 +140,7 @@ documentation: $(MAKE) -C doc doc-if-none .PHONY: test -test: +test: fcron $(MAKE) -C test tests diff --git a/config.h.in b/config.h.in index 5d73f2a..7a03ef1 100644 --- a/config.h.in +++ b/config.h.in @@ -387,6 +387,9 @@ /* Define if you have . */ #undef HAVE_SHADOW_H +/* Define if you have . */ +#undef HAVE_STDBOOL_H + /* Define if you have the unsetenv function. */ #undef HAVE_UNSETENV diff --git a/configure.in b/configure.in index 7ef4b45..374f19c 100644 --- a/configure.in +++ b/configure.in @@ -47,6 +47,7 @@ dnl Checks for libraries. dnl Checks for header files. AC_HEADER_DIRENT +AC_HEADER_STDBOOL AC_HEADER_STDC AC_HEADER_SYS_WAIT AC_CHECK_HEADERS(fcntl.h sys/file.h sys/ioctl.h sys/time.h syslog.h unistd.h) @@ -180,9 +181,10 @@ AC_ARG_ENABLE(checks, esac ]) -DISPLAYNAME= -AC_MSG_RESULT([$DISPLAYNAME]) -AC_SUBST([DISPLAYNAME]) +MAILDISPLAYNAME="" +AC_MSG_CHECKING([mail display name]) +AC_MSG_RESULT([$MAILDISPLAYNAME]) +AC_SUBST([MAILDISPLAYNAME]) dnl --------------------------------------------------------------------- diff --git a/doc/en/fcron.conf.5.sgml b/doc/en/fcron.conf.5.sgml index 8f1d00d..afc25fe 100644 --- a/doc/en/fcron.conf.5.sgml +++ b/doc/en/fcron.conf.5.sgml @@ -154,8 +154,8 @@ A copy of the license is included in gfdl.sgml. - displayname=string - () + maildisplayname=string + () diff --git a/doc/fcron-doc.mod.in b/doc/fcron-doc.mod.in index 74344b9..8a5b21a 100644 --- a/doc/fcron-doc.mod.in +++ b/doc/fcron-doc.mod.in @@ -42,7 +42,7 @@ - + diff --git a/fcronconf.c b/fcronconf.c index a1c5a44..bf32711 100644 --- a/fcronconf.c +++ b/fcronconf.c @@ -25,6 +25,7 @@ #include "global.h" #include "mem.h" #include "fcronconf.h" +#include "mail.h" #include "subs.h" void init_conf(void); @@ -44,69 +45,7 @@ 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; -} +char *maildisplayname = NULL; void init_conf(void) @@ -129,7 +68,7 @@ init_conf(void) sendmail = strdup2(SENDMAIL); #endif editor = strdup2(FCRON_EDITOR); - displayname = strdup2(DISPLAYNAME); + maildisplayname = strdup2(MAILDISPLAYNAME); } void @@ -146,7 +85,7 @@ free_conf(void) Free_safe(shell); Free_safe(sendmail); Free_safe(editor); - Free_safe(displayname); + Free_safe(maildisplayname); } void @@ -253,9 +192,9 @@ 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 : ""); + else if (strncmp(ptr1, "maildisplayname", namesize) == 0) { + char *output = format_maildisplayname(ptr2); + Set(maildisplayname, output ? output : ""); Free_safe(output); } else diff --git a/fcronconf.h b/fcronconf.h index d3b03ce..d9a93e6 100644 --- a/fcronconf.h +++ b/fcronconf.h @@ -43,7 +43,7 @@ extern char *fifofile; extern char *editor; extern char *shell; extern char *sendmail; -extern char *displayname; +extern char *maildisplayname; /* end of global variables */ /* functions prototypes */ diff --git a/fcrondyn_svr.c b/fcrondyn_svr.c index 11e7475..d7069e7 100644 --- a/fcrondyn_svr.c +++ b/fcrondyn_svr.c @@ -251,9 +251,7 @@ 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 */ { - /* [@PR #17] renamed (previously called 'true') to avoid conflict with - stdbool */ - const int value = 1; + const int true_val = 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) \ @@ -268,8 +266,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, &value, - sizeof(value)); + setsockopt(client->fcl_sock_fd, SOL_SOCKET, SO_PASSCRED, &true_val, + sizeof(true_val)); if (getsockopt (client->fcl_sock_fd, SOL_SOCKET, SO_PEERCRED, &cred, &cred_size) != 0) { diff --git a/fileconf.c b/fileconf.c index 99b7923..f7c6539 100644 --- a/fileconf.c +++ b/fileconf.c @@ -458,42 +458,40 @@ 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 conf_true; + goto true_val; else if (*ptr == '0') - goto conf_false; + goto false_val; else if (strncmp(ptr, "true", 4) == 0) { ptr += 3; - goto conf_true; + goto true_val; } else if (strncmp(ptr, "yes", 3) == 0) { ptr += 2; - goto conf_true; + goto true_val; } else if (strncmp(ptr, "false", 5) == 0) { ptr += 4; - goto conf_false; + goto false_val; } else if (strncmp(ptr, "no", 2) == 0) { ptr += 1; - goto conf_false; + goto false_val; } else return NULL; - conf_true: + true_val: *i = true; ptr++; return ptr; - conf_false: + false_val: *i = false; ptr++; return ptr; diff --git a/files/fcron.conf.in b/files/fcron.conf.in index 1443613..68b0935 100644 --- a/files/fcron.conf.in +++ b/files/fcron.conf.in @@ -26,10 +26,10 @@ 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: +# was added -- not RFC5322-compliant, though. +# The default will soon be the sane RFC5322-compliant: # -# displayname = Fcron Deamon +# maildisplayname = Fcron Daemon # # Please read fcron.conf(5) before setting it! -displayname = @@DISPLAYNAME@ +maildisplayname = @@MAILDISPLAYNAME@ diff --git a/global.h b/global.h index 0a1160e..d2c0f2c 100644 --- a/global.h +++ b/global.h @@ -69,7 +69,13 @@ #include #endif +#ifdef HAVE_STDBOOL_H #include +#else +#define bool char +#define true 1 +#define false 0 +#endif #include #include #include diff --git a/job.c b/job.c index 71a0ee9..9afddfa 100644 --- a/job.c +++ b/job.c @@ -25,6 +25,7 @@ #include "fcron.h" #include "job.h" +#include "mail.h" #include "temp_file.h" @@ -277,43 +278,6 @@ 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, char **env) @@ -327,7 +291,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 = ""; + char *mailbox_addr = NULL; int i = 0; if (mailf == NULL) @@ -356,22 +320,22 @@ create_mail(cl_t * line, char *subject, char *content_type, char *encoding, hostname[0] = '\0'; #endif /* HAVE_GETHOSTNAME */ - /* write mail header. Global 'displayname' comes from fcronconf.h */ - if (strlen(displayname) > 0){ + /* write mail header. Global 'maildisplayname' comes from fcronconf.h */ + if (maildisplayname[0] != '\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); + mailbox_addr = make_mailbox_addr(maildisplayname, mailfrom, hostname_from); + if (! mailbox_addr) { + warn("could not make the mailbox address"); } } - else + if (mailbox_addr) { + fprintf(mailf, FROM_HEADER_KEY"%s\n", mailbox_addr); + Free_safe(mailbox_addr); + } + else { /* Old behavior */ - fprintf(mailf, "From: %s%s (fcron)\n", mailfrom, hostname_from); + fprintf(mailf, FROM_HEADER_KEY"%s%s (fcron)\n", mailfrom, hostname_from); + } fprintf(mailf, "To: %s%s\n", line->cl_mailto, hostname_to); diff --git a/job.h b/job.h index a83c46b..046e247 100644 --- a/job.h +++ b/job.h @@ -26,6 +26,7 @@ #define __JOB_H__ #define MAIL_LINE_LEN_MAX 998 /* RFC5322's max line length */ +#define FROM_HEADER_KEY "From: " /* functions prototypes */ extern void change_user_setup_env(struct cl_t *cl, char ***sendmailenv, diff --git a/mail.c b/mail.c new file mode 100644 index 0000000..9596861 --- /dev/null +++ b/mail.c @@ -0,0 +1,120 @@ +/* + * FCRON - periodic command scheduler + * + * Copyright 2000-2021 Thibault Godouet + * + * 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 2 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, 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. + */ + + +#include "fcron.h" +#include "mail.h" + +char +*format_maildisplayname(char *displayname_conf) + /* 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 *ipos = NULL; /* Input position */ + char *output = NULL, *quoted_output = NULL; + + const uint buf_len = MAIL_FROM_VALUE_LEN_MAX; + uint cwritten = 0; /* how many chars we have written */ + + if (strlen(displayname_conf) == 0) return strdup2(""); + + output = (char *)alloc_safe(buf_len * sizeof(char), "output buffer"); + + /* walk the conf_value and rebuild it in buf1 */ + for (ipos = displayname_conf; *ipos; ipos++) { + c = *ipos; + if (strchr(SPECIAL_MBOX_CHARS, c)) { + /* insert escape */ + if (c == DQUOTE) { + output[cwritten] = BSLASH; + cwritten++; + } + need_quotes = true; + } + if (cwritten >= buf_len) { + error("Formatted 'displayname' exceeds %u chars", buf_len); + Free_safe(output); + return NULL; + } + output[cwritten] = c; + cwritten++; + } + + if (need_quotes) { + quoted_output = (char *)alloc_safe(buf_len * sizeof(char), "quoted output buffer"); + int needed_len = snprintf(quoted_output, buf_len, "\"%s\"", output); + if (needed_len >= buf_len){ + error("Formatted 'displayname' too long: length:%u > max:%u chars", needed_len, buf_len); + Free_safe(output); + Free_safe(quoted_output); + return NULL; + } + Free_safe(output); + + return quoted_output; + } + + return output; +} + +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; + + const uint buf_len = MAIL_FROM_VALUE_LEN_MAX+1; + + buf = (char *)alloc_safe(buf_len * sizeof(char), "mailbox addr buffer"); + + if (displayname_conf[0] != '\0') { + /* displayname_conf isn't an empty string */ + need_anglebrackets = true; + } + + /* 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; +} diff --git a/mail.h b/mail.h new file mode 100644 index 0000000..d9157a2 --- /dev/null +++ b/mail.h @@ -0,0 +1,35 @@ +/* + * FCRON - periodic command scheduler + * + * Copyright 2000-2021 Thibault Godouet + * + * 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 2 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, 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. + */ + + +#ifndef __MAIL_H__ +#define __MAIL_H__ + +#define MAIL_LINE_LEN_MAX 998 /* RFC5322's max line length */ +#define FROM_HEADER_KEY "From: " +#define MAIL_FROM_VALUE_LEN_MAX (MAIL_LINE_LEN_MAX - sizeof(FROM_HEADER_KEY)) + +extern char *format_maildisplayname(char *displayname_conf); +extern char *make_mailbox_addr(char *displayname_conf, char *mail_from, char *hostname); + +#endif /* __MAIL_H__ */ diff --git a/script/gen-in.pl b/script/gen-in.pl index 5bc4de9..574e7fe 100755 --- a/script/gen-in.pl +++ b/script/gen-in.pl @@ -30,7 +30,7 @@ close(CONFIG); open(MAKEFILE, "$ARGV[2]/Makefile") or print "error while opening Makefile: $!\n" and exit; while ( ) { - if ( /^\s*?(\w+?)\s*?=\s*?([^\s]+)\s/ ) { + if ( /^\s*?(\w+?)\s*?:?=\s*?([^\s].+?)\s*$/ ) { $name = $1; $value = $2; diff --git a/test/Makefile.in b/test/Makefile.in index accc772..38c5476 100644 --- a/test/Makefile.in +++ b/test/Makefile.in @@ -4,9 +4,10 @@ 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) +OBJSD := $(addprefix $(SRCDIR)/,log.o job.o mem.o filesubs.o subs.o fcronconf.o mail.o temp_file.o env_list.o u_list.o) CFLAGS += -I$(SRCDIR) +CFLAGS += @CFLAGS@ .PHONY: clean tests diff --git a/test/mailbox_addr.c b/test/mailbox_addr.c index cb3cbeb..c529b7f 100644 --- a/test/mailbox_addr.c +++ b/test/mailbox_addr.c @@ -7,13 +7,20 @@ #include "fcron.h" #include "job.h" #include "fcronconf.h" +#include "mail.h" -#define dasserteq(DESCR, RETURNED, EXPECTED) \ +#define AssertEq(DESCR, RETURNED, EXPECTED) \ { \ - printf("%s: %ld ?= %ld\n", (DESCR), (long)(RETURNED), (long)(EXPECTED)); \ + printf("%s: check %ld == %ld\n", (DESCR), (long)(RETURNED), (long)(EXPECTED)); \ assert((RETURNED) == (EXPECTED)); \ } +#define AssertNeq(DESCR, RETURNED, EXPECTED) \ +{ \ + printf("%s: check %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; @@ -25,7 +32,7 @@ mode_t saved_umask; uid_t rootuid = 0; char *tmp_path = ""; -char *format_displayname(char *displayname); +char *format_maildisplayname(char *displayname); char *make_mailbox_addr(char *displayname, char *mail_from, char *hostname); @@ -33,7 +40,7 @@ void _test_format_displayname(char *test_desc, char *arg, char *expected) { char *output = NULL; - output = format_displayname(arg); + output = format_maildisplayname(arg); printf("%s: format_displayname('%s'): '%s' ?= '%s'\n", test_desc, arg, output, expected); assert(strcmp(output, expected) == 0); @@ -58,7 +65,7 @@ int main(int argc, char* argv[]) char *displayname = NULL; char *output = NULL; - /* Mind that format_displayname() might modify input displayname, thus all + /* Mind that format_maildisplayname() might modify input displayname, thus all special characters must be tested */ _test_format_displayname("empty displayname", "", ""); _test_format_displayname("displayname with no special char", @@ -103,22 +110,57 @@ int main(int argc, char* argv[]) "Foo Bar", "baz", "@quux", "Foo Bar "); - /* overflow tests */ + /* + * format_displayname() overflow tests + */ + displayname = (char *)realloc_safe(displayname, - LINE_LEN + 1 * sizeof(char), + MAIL_FROM_VALUE_LEN_MAX * 2 * sizeof(char), "displayname buffer"); - memset(displayname, 'a', LINE_LEN); - displayname[LINE_LEN] = '\0'; + + memset(displayname, 'a', MAIL_FROM_VALUE_LEN_MAX*2); + + printf("=== format_maildisplayname: one-char overflow with no expansion...\n"); + displayname[MAIL_FROM_VALUE_LEN_MAX+1] = '\0'; + output = format_maildisplayname(displayname); + AssertEq("format_displayname: overflow with no expansion", output, NULL); + Free_safe(output); + + + printf("=== format_maildisplayname: max size with no special char...\n"); + displayname[MAIL_FROM_VALUE_LEN_MAX] = '\0'; + output = format_maildisplayname(displayname); + _test_format_displayname("format_displayname: max size with no special chars", + output, displayname); + printf("format_displayname: max size with no special char: len(output)=%lu, MAIL_FROM_VALUE_LEN_MAX=%lu\n", strlen(output), MAIL_FROM_VALUE_LEN_MAX); + Free_safe(output); + + printf("=== format_maildisplayname: overflow on max size with special char...\n"); displayname[0] = DQUOTE; + output = format_maildisplayname(displayname); + AssertEq("format_displayname: overflow on max size with special char", output, NULL); + Free_safe(output); - output = format_displayname(displayname); - dasserteq("format_displayname: overflow", output, NULL); + /* + * make_mailbox_addr() overflow tests + */ + + printf("=== make_mailbox_addr: overflow on displayname at max size + 1...\n"); + /* make_mailbox_addr() adds 3 chars: space, <, > */ + displayname[MAIL_FROM_VALUE_LEN_MAX-strlen("baz")-strlen("@quux")-2] = '\0'; + output = make_mailbox_addr(displayname, "baz", "@quux"); + AssertEq("make_mailbox_addr: overflow", output, NULL); Free_safe(output); + printf("=== make_mailbox_addr: displayname at max size...\n"); + /* make_mailbox_addr() adds 3 chars: space, <, > */ + displayname[MAIL_FROM_VALUE_LEN_MAX-strlen("baz")-strlen("@quux")-3] = '\0'; output = make_mailbox_addr(displayname, "baz", "@quux"); - dasserteq("make_mailbox_addr: overflow", output, NULL); + printf("make_mailbox_addr: max size with no special char: len(output)=%lu, MAIL_FROM_VALUE_LEN_MAX=%lu\n", strlen(output), MAIL_FROM_VALUE_LEN_MAX); + AssertNeq("make_mailbox_addr: max size", output, NULL); Free_safe(output); + Free_safe(displayname); return 0; -- 2.47.3