diff --git a/Changelog b/Changelog
index ebe5e735..25fb4c22 100644
--- a/Changelog
+++ b/Changelog
@@ -1,5 +1,48 @@
Version Changes for Hypermail
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+============================
+HYPERMAIL VERSION 2.4.1:
+============================
+
+2022-11-04 Jose Kahan
+ * src/Makefile.in
+ Some C linkers require libraries to be declared after functions in that
+ library are called, and not before. This was notably with -lm and
+ -ltrio, where -lm had to be declared after -ltrio.
+ (closes # 84)
+
+2021-11-17 David Hughes
+ * docs/hmrc.html src/parse.c
+ Added support for strftime (3) formatting in the append_filename configuration
+ option to allow for archived messages to be split over multiple directories
+ (monthly or yearly for example)
+
+2021-06-04 Baptiste Daroussin
+ * src/uudecode.c src/parse.c
+ Fixes for memory issues detected by libasan
+
+2020-06-19 Jose Kahan
+ * src/domains.c
+ valid_root_domains() was validating a domain name against the
+ historical DNS domains such as .org, .com. DNS has evolved and allows a
+ bigger variety of domains than those this function verified. That part
+ of the function has been invalidated and will be either eventually
+ removed or evolve to a user-configurable option.
+
+2020-06-12 Jose Kahan
+ * configure.ac
+ configure didn't signal an error if yacc was not installed
+
+ * src/string.c
+ parseurl(): URLs were not detected and converted when they were
+ preceeded by a : that was not attached to a valid URL like
+ foo: https://example.com or : https://example.com
+
+2020-02-23 @cacsar
+ * Makefile.in
+ make install wasn't working
+
============================
HYPERMAIL VERSION 2.4.0:
============================
diff --git a/Makefile.in b/Makefile.in
index caeb383a..c679d37d 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -47,14 +47,14 @@ support:
@cd archive; $(MAKE) all CC="$(CC)" CFLAGS="$(CFLAGS)" CPPFLAGS="$(CPPFLAGS)"
install:
- @cd src; $(MAKE) install CC="$(CC)" CFLAGS="$(CFLAGS) \
- "CPPFLAGS="$(CPPFLAGS)" \
+ @cd src; $(MAKE) install CC="$(CC)" CFLAGS="$(CFLAGS)" \
+ CPPFLAGS="$(CPPFLAGS)" \
cgidir="$(cgidir)" bindir="$(bindir)" LIBS="$(LIBS)"
@cd docs; $(MAKE) install CC="$(CC)" CFLAGS="$(CFLAGS)" \
- "CPPFLAGS="$(CPPFLAGS)" \
+ CPPFLAGS="$(CPPFLAGS)" \
$(MAKEFLAGS) mandir="$(mandir)" htmldir="$(htmldir)"
@cd archive; $(MAKE) install CC="$(CC)" CFLAGS="$(CFLAGS)" \
- "CPPFLAGS="$(CPPFLAGS)" \
+ CPPFLAGS="$(CPPFLAGS)" \
bindir="$(bindir)"
uninstall:
diff --git a/configure b/configure
index a9feaaad..7858d04e 100755
--- a/configure
+++ b/configure
@@ -709,6 +709,7 @@ LN_S
INSTALL_DATA
INSTALL_SCRIPT
INSTALL_PROGRAM
+YACC_CHECK
YFLAGS
YACC
CPP
@@ -3444,6 +3445,46 @@ fi
done
test -n "$YACC" || YACC="yacc"
+# Extract the first word of "$YACC", so it can be a program name with args.
+set dummy $YACC; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_YACC_CHECK+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ if test -n "$YACC_CHECK"; then
+ ac_cv_prog_YACC_CHECK="$YACC_CHECK" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ test -z "$as_dir" && as_dir=.
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ ac_cv_prog_YACC_CHECK="yes"
+ $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+YACC_CHECK=$ac_cv_prog_YACC_CHECK
+if test -n "$YACC_CHECK"; then
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $YACC_CHECK" >&5
+$as_echo "$YACC_CHECK" >&6; }
+else
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+if test x"$YACC_CHECK" != x"yes"; then :
+ as_fn_error $? "Please install either bison or yacc and run configure again" "$LINENO" 5
+fi
# Find a good install program. We prefer a C program (faster),
# so one script is as good as another. But avoid the broken or
# incompatible versions:
diff --git a/configure.ac b/configure.ac
index 2a3b7fa9..79714bfd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -46,6 +46,8 @@ dnl ===========================================================================
AC_PROG_CC
AC_PROG_CPP
AC_PROG_YACC
+AC_CHECK_PROG(YACC_CHECK,$YACC,yes)
+AS_IF([test x"$YACC_CHECK" != x"yes"], [AC_MSG_ERROR([Please install either bison or yacc and run configure again])])
AC_PROG_INSTALL
AC_PROG_LN_S
AC_PROG_MAKE_SET
diff --git a/docs/hmrc.html b/docs/hmrc.html
index 399be3ed..4953123b 100644
--- a/docs/hmrc.html
+++ b/docs/hmrc.html
@@ -1301,9 +1301,11 @@
append = 1
append_filename = [ string ]
-Specifies the filename to be used by the append option. $DIR
-may be used to specify a name relative to the directory specified
-in the -d or dir option.
+Specifies the filename to be used by the append option. $DIR may
+be used to specify a name relative to the directory specified in the
+-d or dir option. The string will be passed to strftime(3) to allow
+splitting the mailbox into yearly or monthy files, such as
+"%Y-%m.mbox".
append_filename = $DIR/INBOX
diff --git a/src/Makefile.in b/src/Makefile.in
index 7f54bad0..2838fa4b 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -40,7 +40,7 @@ CPPFLAGS=@CPPFLAGS@ @INCLUDES@
YACC=@YACC@
NETLIBS=@LIBS@
LDFLAGS=@LDFLAGS@
-MISC_LIBS= -lm -lpcre -ltrio
+MISC_LIBS= -lpcre -ltrio -lm
OPT_LIBS=@EXTRA_LIBS@
INCS= domains.h hypermail.h lang.h proto.h \
diff --git a/src/domains.c b/src/domains.c
index be4ed92d..324a1322 100644
--- a/src/domains.c
+++ b/src/domains.c
@@ -18,6 +18,13 @@ int valid_root_domain(char *eaddr)
if (!*name_to_check)
return (0);
+ /* DNS has evolved and we have now a variety of new domains, hard
+ to track and that are not covered by this function. While temporary
+ waiting to see if we evolve this function to be user configurable,
+ we're going to return 1 (valid) all the time. 19/06/2020 -JK */
+ return (1);
+
+
for (ccptr = domain_codes; ccptr->domain != NULL; ccptr++) {
if (strcasecmp(name_to_check, ccptr->domain) == 0)
return (1);
diff --git a/src/file.c b/src/file.c
index cdfa67fe..5aafc834 100644
--- a/src/file.c
+++ b/src/file.c
@@ -53,9 +53,6 @@
#endif
#endif /* HAVE_LIBFNV */
-extern int snprintf(char *str, size_t size, const char *format, ...);
-
-
/*
** Does a file exist?
*/
diff --git a/src/hypermail.h b/src/hypermail.h
index 6a63c102..38d71ac9 100644
--- a/src/hypermail.h
+++ b/src/hypermail.h
@@ -135,7 +135,7 @@
#define NUMSTRLEN 10
#define MAXLINE 1024
#define MAXFILELEN 256
-#define NAMESTRLEN 80
+#define NAMESTRLEN 320
#define MAILSTRLEN 80
#define DATESTRLEN 80
#define MSGDSTRLEN 256
diff --git a/src/parse.c b/src/parse.c
index 36da5dfb..aad1740e 100644
--- a/src/parse.c
+++ b/src/parse.c
@@ -924,7 +924,7 @@ static char *mdecodeRFC2047(char *string, int length, char *charsetsave)
char charset[129];
char encoding[33];
char dummy[129];
- char *ptr;
+ char *ptr, *endptr;
char *old_output;
#ifdef NOTUSED
@@ -959,6 +959,7 @@ static char *mdecodeRFC2047(char *string, int length, char *charsetsave)
if (!strcasecmp("q", encoding)) {
/* quoted printable decoding */
+ endptr = ptr + strlen(ptr);
#ifdef HAVE_ICONV
char *orig2,*output2,*output3;
@@ -967,7 +968,7 @@ static char *mdecodeRFC2047(char *string, int length, char *charsetsave)
memset(output2,0,strlen(string)+1);
old_output=output;
- for (; *ptr; ptr++) {
+ for (; ptr < endptr; ptr++) {
switch (*ptr) {
case '=':
sscanf(ptr + 1, "%02X", &value);
@@ -991,7 +992,7 @@ static char *mdecodeRFC2047(char *string, int length, char *charsetsave)
memcpy(charsetsave,charset,charsetlen);
charsetsave[charsetlen] = '\0';
#else
- for (; *ptr; ptr++) {
+ for (; ptr < endptr; ptr++) {
switch (*ptr) {
case '=':
sscanf(ptr + 1, "%02X", &value);
@@ -1086,6 +1087,11 @@ static char *mdecodeRFC2047(char *string, int length, char *charsetsave)
}
else {
free(storage);
+#ifdef HAVE_ICONV
+ /* make sure there are only ascii chars in the string
+ ** for messages that don't respect rfc2047 */
+ i18n_replace_non_ascii_chars(string);
+#endif
return string;
}
}
@@ -1540,6 +1546,8 @@ int parsemail(char *mbox, /* file name */
int require_filter_len, require_filter_full_len;
struct hmlist *tlist;
char filename[MAXFILELEN];
+ char directory[MAXFILELEN];
+ char pathname[MAXFILELEN];
struct emailinfo *emp;
char *att_dir = NULL; /* directory name to store attachments in */
char *meta_dir = NULL; /* directory name where we're storing the meta data
@@ -1654,21 +1662,36 @@ int parsemail(char *mbox, /* file name */
if(set_append) {
/* add to an mbox as we read */
-
- if(set_append_filename && strncmp(set_append_filename, "$DIR/", 5)) {
- if(strlen(set_append_filename) >= sizeof(filename))
- progerr("append_filename too long");
- strcpy(filename, set_append_filename);
+ *directory = 0;
+ *filename = 0;
+ *pathname = 0;
+ if (set_append_filename) {
+ time_t curtime;
+ const struct tm *local_curtime;
+
+ time(&curtime);
+ local_curtime = localtime(&curtime);
+
+ if(strncmp(set_append_filename, "$DIR/", 5) == 0) {
+ strncpy(directory, dir, MAXFILELEN - 1);
+ strftime(filename, MAXFILELEN - 1, set_append_filename+5,
+ local_curtime);
+ } else {
+ strftime(filename, MAXFILELEN - 1, set_append_filename,
+ local_curtime);
+ }
+ } else {
+ strncpy(directory, dir, MAXFILELEN - 1);
+ strncpy(filename, "mbox", MAXFILELEN - 1);
}
- else if(trio_snprintf(filename, sizeof(filename), "%s%s", dir,
- set_append_filename ? set_append_filename + 5
- : "mbox")
- == sizeof(filename)) {
+
+ if(trio_snprintf(pathname, sizeof(pathname), "%s%s", directory,
+ filename) == sizeof(pathname)) {
progerr("Can't build mbox filename");
}
- if(!(fpo = fopen(filename, "a"))) {
+ if(!(fpo = fopen(pathname, "a"))) {
trio_snprintf(errmsg, sizeof(errmsg), "%s \"%s\".",
- lang[MSG_CANNOT_OPEN_MAIL_ARCHIVE], filename);
+ lang[MSG_CANNOT_OPEN_MAIL_ARCHIVE], pathname);
progerr(errmsg);
}
}
@@ -1809,6 +1832,7 @@ int parsemail(char *mbox, /* file name */
if (head->header && !head->demimed) {
head->line =
mdecodeRFC2047(head->line, strlen(head->line),charsetsave);
+ head->demimed = TRUE;
}
if (head->parsedheader || head->attached ||
@@ -2378,6 +2402,7 @@ int parsemail(char *mbox, /* file name */
#endif
if (charset) {
free(charset);
+ charset = NULL;
}
charsetsave[0] = '\0';
diff --git a/src/proto.h b/src/proto.h
index 9fe9fc3e..affe20de 100644
--- a/src/proto.h
+++ b/src/proto.h
@@ -99,6 +99,7 @@ char *unobfuscate_email_address (char *);
char *i18n_convstring(char *, char *, char *, size_t *);
char *i18n_utf2numref(char *, int);
unsigned char *i18n_numref2utf(char *);
+int i18n_replace_non_ascii_chars(char *);
char *PushByte(struct Push *, char);
char *PushString(struct Push *, const char *);
diff --git a/src/setup.c b/src/setup.c
index 3735391f..008d4b91 100644
--- a/src/setup.c
+++ b/src/setup.c
@@ -412,7 +412,9 @@ struct Config cfg[] = {
{"append_filename", &set_append_filename, NULL, CFG_STRING,
"# Specifies the filename to be used by the append option.\n"
"# $DIR may be used to specify a name relative to the directory\n"
- "# specified in the -d or dir option.\n", FALSE},
+ "# specified in the -d or dir option.\n"
+ "# The string will be passed to strftime(3) to allow splitting the\n"
+ "# mailbox into yearly or monthy files, such as \"%Y-%m.mbox\".\n" , FALSE},
{"nonsequential", &set_nonsequential, BFALSE, CFG_SWITCH,
"# Set this to On to generate filenames that are not sequential, but\n"
diff --git a/src/string.c b/src/string.c
index 6d7003b4..78ca1336 100644
--- a/src/string.c
+++ b/src/string.c
@@ -452,6 +452,26 @@ unsigned char *i18n_numref2utf(char *string){
return headofutfstr;
}
+/* replaces all non 7-bit ascii chars in string by a ?
+** returns the number of replaced chars
+*/
+int i18n_replace_non_ascii_chars(char *string)
+{
+ char *ptr = string;
+ int count = 0;
+
+ while (*ptr) {
+ if (!isascii(*ptr) ||
+ (*ptr < 0x20 && *ptr != 0x0a && *ptr != 0x0d && *ptr != 0x09)) {
+ *ptr = '?';
+ count++;
+ }
+ ptr++;
+ }
+
+ return count;
+}
+
#endif
/* end of I18N hack */
@@ -1802,22 +1822,40 @@ char *parseurl(char *input, char *charset)
if (!input || !*input)
return NULL;
- c = strstr(input, ":");
+ /*
+ * All our protocol prefixes have this ":" substring in them. Most
+ * of the lines we process don't have any URL. Let's not spend any
+ * time looking for URLs in lines that we can prove cheaply don't
+ * have any; it will be a big win for average input if we follow
+ * this trivial heuristic.
+ */
- if (!c /* not found */
- || c == input /* first char in line */
- || *(c+1) == '\0' /* last char in line */
- || !isalpha(*(c-1)) /* not between alpha/graph */
- || !isgraph(*(c+1))) {
- /*
- * All our protocol prefixes have this ":" substring in them. Most
- * of the lines we process don't have any URL. Let's not spend any
- * time looking for URLs in lines that we can prove cheaply don't
- * have any; it will be a big win for average input if we follow
- * this trivial heuristic.
- */
+ first = FALSE;
+
+ c = strstr(input, ":");
+ if (c == input) { /* first char in line */
+ c++;
+ c = strstr(c, ":");
+ }
+
+ /* !c === not found */
+ while (c) {
+ if (*(c+1) == '\0') /* last char in line */
+ break;
+ else if ( !isalpha(*(c-1)) /* not between alpha/graph */
+ || !isgraph(*(c+1))) {
+ c++;
+ c = strstr(c, ":");
+ } else {
+ first = TRUE;
+ break;
+ }
+ }
+
+ if (!first) {
return convchars(input, charset);
}
+
INIT_PUSH(buff);
/*
diff --git a/src/uudecode.c b/src/uudecode.c
index 4b4310d2..c0c978a0 100644
--- a/src/uudecode.c
+++ b/src/uudecode.c
@@ -75,7 +75,7 @@ int uudecode(FILE *input, /* get file data from (if needed) */
if (init) {
/* search for header line */
/* AUDIT biege: BOF in buf! */
- sprintf(scanfstring, "begin %%o %%%us", sizeof(buf));
+ sprintf(scanfstring, "begin %%o %%%us", sizeof(buf) - 1);
while (2 != sscanf(iptr, scanfstring, &mode, buf)) {
if (!fgets(buf, MAXPATHLEN, input)) {
return 2;