From b19b165203124e56cdcb89539efa6f93ed14f99d Mon Sep 17 00:00:00 2001 From: David Levine Date: Sat, 1 Feb 2014 10:09:13 -0600 Subject: [PATCH 01/16] Look for w3m if lynx isn't available for mhshow-show-text. --- etc/mhn.defaults.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/etc/mhn.defaults.sh b/etc/mhn.defaults.sh index f5a3e693..2f91a965 100755 --- a/etc/mhn.defaults.sh +++ b/etc/mhn.defaults.sh @@ -207,7 +207,12 @@ EOF # that another netscape is already running and certain things can't be done). PGM="`$SEARCHPROG $SEARCHPATH lynx`" if [ ! -z "$PGM" ]; then - echo "mhshow-show-text/html: %p$PGM '%F'" >> $TMP + echo "mhshow-show-text/html: %p$PGM -force-html '%F'" >> $TMP +else + PGM="`$SEARCHPROG $SEARCHPATH w3m`" + if [ ! -z "$PGM" ]; then + echo "mhshow-show-text/html: %p$PGM -T text/html '%F'" >> $TMP + fi fi PGM="`$SEARCHPROG $SEARCHPATH richtext`" -- 2.48.1 From 07661005b9a36338ab158bcbe7762788a1df4030 Mon Sep 17 00:00:00 2001 From: David Levine Date: Sat, 1 Feb 2014 10:11:08 -0600 Subject: [PATCH 02/16] Added note that suffixes were removed from filenames of temporary files. --- docs/pending-release-notes | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/pending-release-notes b/docs/pending-release-notes index ca1bff37..eeaa2e25 100644 --- a/docs/pending-release-notes +++ b/docs/pending-release-notes @@ -120,7 +120,9 @@ OBSOLETE FEATURES - Temporary files are stored in the first non-null location of {MHTMPDIR environment variable, TMPDIR environment variable, MH Path}. They are no longer be stored in the location specified by the TMP - environment variable. + environment variable. As part of this change, suffixes were removed + from filenames of temporary files. This will break the use of display + applications that rely on the suffix to determine the file type. ------------------- DEPRECATED FEATURES -- 2.48.1 From d722ee2a5489b9cfcb28903d8b564863099917bf Mon Sep 17 00:00:00 2001 From: David Levine Date: Sat, 1 Feb 2014 11:24:16 -0600 Subject: [PATCH 03/16] Fixed test-mhfixmsg check attempted -decode of binary text to work with the fixed base64 decoder. --- test/mhfixmsg/test-mhfixmsg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/mhfixmsg/test-mhfixmsg b/test/mhfixmsg/test-mhfixmsg index cd2a5caf..6536029f 100755 --- a/test/mhfixmsg/test-mhfixmsg +++ b/test/mhfixmsg/test-mhfixmsg @@ -560,7 +560,7 @@ check "$expected" "$actual" # check attempted -decode of binary text #### Generated the encoded text below with: -#### $ printf '\x0d\xbd\xb2=\xbc\n' | base64 +#### $ printf '\x0\xbd\xb2=\xbc\n' | base64 cat >`mhpath new` < Date: Sat, 1 Feb 2014 11:32:53 -0600 Subject: [PATCH 04/16] Removed set +e where not needed in test-mhfixmsg. --- test/mhfixmsg/test-mhfixmsg | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/mhfixmsg/test-mhfixmsg b/test/mhfixmsg/test-mhfixmsg index cd2a5caf..9df3f657 100755 --- a/test/mhfixmsg/test-mhfixmsg +++ b/test/mhfixmsg/test-mhfixmsg @@ -22,7 +22,6 @@ actual_err="$MH_TEST_DIR/test-mhfixmsg$$.actual_err" #### Make sure that html-to-text conversion is what we expect. LC_ALL=en_US.UTF-8; export LC_ALL -set +e if grep mhfixmsg-format-text/html "${MH_TEST_DIR}/Mail/mhn.defaults" \ >/dev/null; then can_reformat_texthtml=1 @@ -39,7 +38,6 @@ else echo "$0: skipping -reformat check because no text browser was found" can_reformat_texthtml=0 fi -set -e # check -help @@ -578,9 +576,7 @@ Db2yPbwK EOF cp -p `mhpath last` "$expected" -set +e run_prog mhfixmsg last -set -e check `mhpath last` "$expected" 'keep first' -- 2.48.1 From b68ed2bccdade2c3ac04ca1005d261be89c71c51 Mon Sep 17 00:00:00 2001 From: Ken Hornstein Date: Sat, 1 Feb 2014 15:57:49 -0500 Subject: [PATCH 05/16] Document changes to base64 encoder/decoder. --- docs/pending-release-notes | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/pending-release-notes b/docs/pending-release-notes index eeaa2e25..1adbc857 100644 --- a/docs/pending-release-notes +++ b/docs/pending-release-notes @@ -166,3 +166,4 @@ BUG FIXES replied to [Bug #26780]. - Fix segmentation fauls for %(putlit) and %(zputlit) format escapes when the "str" register was NULL. +- Encode and decode text MIME types with canonical line breaks properly. -- 2.48.1 From 4c7e4c2d01cc991359289762aaf07f319d1e5879 Mon Sep 17 00:00:00 2001 From: Ken Hornstein Date: Sat, 1 Feb 2014 16:13:00 -0500 Subject: [PATCH 06/16] Change mhstore test slightly to test canonical line ending handling for text MIME parts that are encoded with base64. --- test/mhstore/test-mhstore | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/mhstore/test-mhstore b/test/mhstore/test-mhstore index 2dc03571..2a420394 100755 --- a/test/mhstore/test-mhstore +++ b/test/mhstore/test-mhstore @@ -116,7 +116,8 @@ Content-Type: text/plain; charset="iso-8859-1"; name="test4.txt" Content-Disposition: attachment; filename="test4.txt" Content-Transfer-Encoding: base64 -VGhpcyBpcyB0aGUgZm91cnRoIHRleHQvcGxhaW4gcGFydC4K +VGhpcyBpcyB0aGUgZm91cnRoIHRleHQvcGxhaW4gcGFydC4NClRoaXMgdGVzdCBoYXMgbXVsdGlw +bGUgbGluZXMuDQo= ------- =_aaaaaaaaaa0-- EOF @@ -153,12 +154,14 @@ EOF check $expected 11.3.txt cat > $expected < $expected <> $MH cat > $expected < Date: Sun, 2 Feb 2014 08:46:25 -0600 Subject: [PATCH 07/16] Added m_mktemps(), which creates a temporary file with a specified suffix. It uses mkstemps(3) where available, which should be most modern platforms. If not available, it tries link(2), and if that fails, rename(2). --- configure.ac | 2 +- docs/pending-release-notes | 4 +- h/prototypes.h | 1 + sbr/m_mktemp.c | 113 +++++++++++++++++++++++++++++++++++++ uip/mhparse.c | 75 +++++++++++++++--------- 5 files changed, 164 insertions(+), 31 deletions(-) diff --git a/configure.ac b/configure.ac index 35118f34..7c35047a 100644 --- a/configure.ac +++ b/configure.ac @@ -341,7 +341,7 @@ AC_CHECK_HEADER([sys/ptem.h], AC_DEFINE(WINSIZE_IN_PTEM,1, dnl --------------- dnl CHECK FUNCTIONS dnl --------------- -AC_CHECK_FUNCS([wcwidth mbtowc getutxent arc4random]) +AC_CHECK_FUNCS([wcwidth mbtowc getutxent arc4random mkstemps]) dnl Check for multibyte character set support AS_IF([test "x$ac_cv_header_wchar_h" = "xyes" -a \ diff --git a/docs/pending-release-notes b/docs/pending-release-notes index 1adbc857..464554b0 100644 --- a/docs/pending-release-notes +++ b/docs/pending-release-notes @@ -120,9 +120,7 @@ OBSOLETE FEATURES - Temporary files are stored in the first non-null location of {MHTMPDIR environment variable, TMPDIR environment variable, MH Path}. They are no longer be stored in the location specified by the TMP - environment variable. As part of this change, suffixes were removed - from filenames of temporary files. This will break the use of display - applications that rely on the suffix to determine the file type. + environment variable. ------------------- DEPRECATED FEATURES diff --git a/h/prototypes.h b/h/prototypes.h index e8be9fe6..fceef407 100644 --- a/h/prototypes.h +++ b/h/prototypes.h @@ -191,6 +191,7 @@ int m_putenv (char *, char *); int m_rand (unsigned char *, size_t); char *m_mktemp(const char *, int *, FILE **); char *m_mktemp2(const char *, const char *, int *, FILE **); +char *m_mktemps(const char *pfx, const char *, int *, FILE **); char *get_temp_dir(); void m_unknown(m_getfld_state_t *, FILE *); int makedir (char *); diff --git a/sbr/m_mktemp.c b/sbr/m_mktemp.c index a9dab5a2..17cf7458 100644 --- a/sbr/m_mktemp.c +++ b/sbr/m_mktemp.c @@ -132,6 +132,119 @@ m_mktemp2 ( } +/* + * This version supports a suffix for the temp filename. + * It has two other differences from m_mktemp() and m_mktemp2(): + * 1) It does not support specification of a directory for the temp + * file. Instead it always uses get_temp_dir(). + * 2) It returns a dynamically allocated string. The caller is + * responsible for freeing the allocated memory. + */ +char * +m_mktemps( + const char *pfx_in, /* Basename prefix for temp file. */ + const char *suffix, /* Suffix, including any '.', for temp file. */ + int *fd_ret, /* (return,optional) File descriptor to temp file. */ + FILE **fp_ret /* (return,optional) FILE pointer to temp file. */ +) +{ + char *tmpfil; + int fd = -1; + int keep_open = 0; + mode_t oldmode = umask(077); + + if (suffix == NULL) { + if ((tmpfil = m_mktemp2(NULL, pfx_in, fd_ret, fp_ret))) { + return add(tmpfil, NULL); + } else { + return NULL; + } + } + +#if HAVE_MKSTEMPS + if (pfx_in == NULL) { + tmpfil = concat(get_temp_dir(), "/nmhXXXXXX", suffix, NULL); + } else { + tmpfil = concat(get_temp_dir(), "/", pfx_in, "XXXXXX", suffix, NULL); + } + + fd = mkstemps(tmpfil, (int) strlen(suffix)); +#else /* ! HAVE_MKSTEMPS */ + /* Solaris 10, e.g. */ + + if (pfx_in == NULL) { + tmpfil = concat(get_temp_dir(), "/nmhXXXXXX", NULL); + } else { + tmpfil = concat(get_temp_dir(), "/", pfx_in, "XXXXXX", NULL); + } + + fd = mkstemp(tmpfil); + { + char *oldfilename = tmpfil; + + tmpfil = concat(oldfilename, suffix, NULL); + + /* link(2) requires that the new path not exist. And if we + have to resort to rename(2), at least try to remove a file + that would be in the way. */ + if (unlink(tmpfil) != 0 && errno != ENOENT) { + advise("unlink", "Failed to unlink \"%s\"", tmpfil); + (void) unlink(oldfilename); + free(oldfilename); + free(tmpfil); + return NULL; + } + + /* link() doesn't always work, such as on Windows FAT + filesystems. If it fails, try rename(). */ + if (link(oldfilename, tmpfil) != 0 && + rename(oldfilename, tmpfil) != 0) { + (void) unlink(oldfilename); + free(oldfilename); + free(tmpfil); + return NULL; + } + + (void) unlink(oldfilename); + free(oldfilename); + } +#endif /* ! HAVE_MKSTEMPS */ + + if (fd < 0) { + umask(oldmode); + free(tmpfil); + return NULL; + } + + register_for_removal(tmpfil); + + if (fd_ret != NULL) { + *fd_ret = fd; + keep_open = 1; + } + if (fp_ret != NULL) { + FILE *fp = fdopen(fd, "w+"); + if (fp == NULL) { + int olderr = errno; + (void) m_unlink(tmpfil); + close(fd); + errno = olderr; + umask(oldmode); + free(tmpfil); + return NULL; + } + *fp_ret = fp; + keep_open = 1; + } + if (!keep_open) { + close(fd); + } + umask(oldmode); + + return tmpfil; +} + + char * get_temp_dir() { diff --git a/uip/mhparse.c b/uip/mhparse.c index d12fbb30..8af26906 100644 --- a/uip/mhparse.c +++ b/uip/mhparse.c @@ -1700,12 +1700,6 @@ openBase64 (CT ct, char **file) } if (*file == NULL) { - char *tempfile; - if ((tempfile = m_mktemp2(NULL, invo_name, NULL, NULL)) == NULL) { - adios(NULL, "unable to create temporary file in %s", - get_temp_dir()); - } - ce->ce_file = add (tempfile, NULL); ce->ce_unlink = 1; } else { ce->ce_file = add (*file, NULL); @@ -1723,9 +1717,22 @@ openBase64 (CT ct, char **file) cp = context_find (buffer); } if (cp != NULL && *cp != '\0') { - if (! ce->ce_unlink) { - ce->ce_file = add (cp, ce->ce_file); - } + if (ce->ce_unlink) { + /* Create temporary file with filename extension. */ + if ((ce->ce_file = m_mktemps(invo_name, cp, NULL, NULL)) == NULL) { + adios(NULL, "unable to create temporary file in %s", + get_temp_dir()); + } + } else { + ce->ce_file = add (cp, ce->ce_file); + } + } else if (*file == NULL) { + char *tempfile; + if ((tempfile = m_mktemp2(NULL, invo_name, NULL, NULL)) == NULL) { + adios(NULL, "unable to create temporary file in %s", + get_temp_dir()); + } + ce->ce_file = add (tempfile, NULL); } if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) { @@ -1932,12 +1939,6 @@ openQuoted (CT ct, char **file) } if (*file == NULL) { - char *tempfile; - if ((tempfile = m_mktemp2(NULL, invo_name, NULL, NULL)) == NULL) { - adios(NULL, "unable to create temporary file in %s", - get_temp_dir()); - } - ce->ce_file = add (tempfile, NULL); ce->ce_unlink = 1; } else { ce->ce_file = add (*file, NULL); @@ -1955,9 +1956,22 @@ openQuoted (CT ct, char **file) cp = context_find (buffer); } if (cp != NULL && *cp != '\0') { - if (! ce->ce_unlink) { - ce->ce_file = add (cp, ce->ce_file); - } + if (ce->ce_unlink) { + /* Create temporary file with filename extension. */ + if ((ce->ce_file = m_mktemps(invo_name, cp, NULL, NULL)) == NULL) { + adios(NULL, "unable to create temporary file in %s", + get_temp_dir()); + } + } else { + ce->ce_file = add (cp, ce->ce_file); + } + } else if (*file == NULL) { + char *tempfile; + if ((tempfile = m_mktemp2(NULL, invo_name, NULL, NULL)) == NULL) { + adios(NULL, "unable to create temporary file in %s", + get_temp_dir()); + } + ce->ce_file = add (tempfile, NULL); } if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) { @@ -2151,12 +2165,6 @@ open7Bit (CT ct, char **file) } if (*file == NULL) { - char *tempfile; - if ((tempfile = m_mktemp2(NULL, invo_name, NULL, NULL)) == NULL) { - adios(NULL, "unable to create temporary file in %s", - get_temp_dir()); - } - ce->ce_file = add (tempfile, NULL); ce->ce_unlink = 1; } else { ce->ce_file = add (*file, NULL); @@ -2174,9 +2182,22 @@ open7Bit (CT ct, char **file) cp = context_find (buffer); } if (cp != NULL && *cp != '\0') { - if (! ce->ce_unlink) { - ce->ce_file = add (cp, ce->ce_file); - } + if (ce->ce_unlink) { + /* Create temporary file with filename extension. */ + if ((ce->ce_file = m_mktemps(invo_name, cp, NULL, NULL)) == NULL) { + adios(NULL, "unable to create temporary file in %s", + get_temp_dir()); + } + } else { + ce->ce_file = add (cp, ce->ce_file); + } + } else if (*file == NULL) { + char *tempfile; + if ((tempfile = m_mktemp2(NULL, invo_name, NULL, NULL)) == NULL) { + adios(NULL, "unable to create temporary file in %s", + get_temp_dir()); + } + ce->ce_file = add (tempfile, NULL); } if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) { -- 2.48.1 From 05dba91fcb152e1941e7f7f145b1d830c5e8f6d0 Mon Sep 17 00:00:00 2001 From: David Levine Date: Sun, 2 Feb 2014 09:58:45 -0600 Subject: [PATCH 08/16] Removed all unnecessary setuid/setgid calls. Using setuid as an example and not showing the setgid analogues: 1) setuid(getuid()); This dropped privileges before an exec and is normally a good thing. Except here, the return value isn't checked. And, we don't have any setuid programs in nmh now, so it was unnecessary. 2) if (geteuid() == 0) setuid(pw->pw_uid); This would have been a security hole if the executable was setuid root because the user specifies the source of the pw data. This was in slocal(1), which is not setuid, so this was certainly not needed. 3) setuid(geteuid()); This was in post(8) for when it called the sendmail executable directly (-mts sendmail or -mts sendmail/pipe. It's not necessary with modern sendmail or replacements. --- mts/smtp/smtp.c | 2 -- sbr/context_save.c | 2 -- sbr/makedir.c | 3 --- uip/popsbr.c | 3 --- uip/slocal.c | 10 ---------- 5 files changed, 20 deletions(-) diff --git a/mts/smtp/smtp.c b/mts/smtp/smtp.c index 7177e5d5..984f50d3 100644 --- a/mts/smtp/smtp.c +++ b/mts/smtp/smtp.c @@ -481,8 +481,6 @@ sendmail_init (char *client, char *server, int watch, int verbose, vec[vecp++] = "-ov"; vec[vecp++] = NULL; - setgid (getegid ()); - setuid (geteuid ()); execvp (sendmail, vec); fprintf (stderr, "unable to exec "); perror (sendmail); diff --git a/sbr/context_save.c b/sbr/context_save.c index d7d87e45..9105a596 100644 --- a/sbr/context_save.c +++ b/sbr/context_save.c @@ -87,8 +87,6 @@ m_chkids (void) case 0: /* It's not necessary to call unregister_for_removal(0) because the child calls _exit() in context_save(). */ - setgid (getgid ()); - setuid (getuid ()); break; default: diff --git a/sbr/makedir.c b/sbr/makedir.c index 67f69c86..a5386f22 100644 --- a/sbr/makedir.c +++ b/sbr/makedir.c @@ -89,9 +89,6 @@ makedir (char *dir) return 0; case 0: - setgid (getgid ()); - setuid (getuid ()); - execl ("/bin/mkdir", "mkdir", dir, (void *) NULL); execl ("/usr/bin/mkdir", "mkdir", dir, (void *) NULL); fprintf (stderr, "unable to exec "); diff --git a/uip/popsbr.c b/uip/popsbr.c index 714a8da7..be0540c7 100644 --- a/uip/popsbr.c +++ b/uip/popsbr.c @@ -435,9 +435,6 @@ pop_init (char *host, char *port, char *user, char *pass, char *proxy, int inpipe[2]; /* for reading from the server */ int outpipe[2]; /* for sending to the server */ - /* first give up any root priviledges we may have for rpop */ - setuid(getuid()); - pipe(inpipe); pipe(outpipe); diff --git a/uip/slocal.c b/uip/slocal.c index eaa7cb7c..a807d816 100644 --- a/uip/slocal.c +++ b/uip/slocal.c @@ -34,10 +34,6 @@ #include #include -/* Hopefully, grp.h declares initgroups(). If we run into a platform - where it doesn't, we could consider declaring it here as well. */ -#include - /* This define is needed for Berkeley db v2 and above to * make the header file expose the 'historical' ndbm APIs. * We define it unconditionally because this is simple and @@ -282,12 +278,6 @@ main (int argc, char **argv) chdir ("/"); umask (0077); - if (geteuid() == 0) { - setgid (pw->pw_gid); - initgroups (pw->pw_name, pw->pw_gid); - setuid (pw->pw_uid); - } - if (info == NULL) info = ""; -- 2.48.1 From c0c501a665bb3d96fcf69b438dbd231b91b2c736 Mon Sep 17 00:00:00 2001 From: David Levine Date: Sun, 2 Feb 2014 16:45:23 -0600 Subject: [PATCH 09/16] Removed some unused code that forked /bin/mkdir to make a new directory if an the effective and real uids differed. There are no setuid executables in nmh. --- sbr/makedir.c | 89 ++++++++++++++++----------------------------------- 1 file changed, 27 insertions(+), 62 deletions(-) diff --git a/sbr/makedir.c b/sbr/makedir.c index a5386f22..562949be 100644 --- a/sbr/makedir.c +++ b/sbr/makedir.c @@ -13,7 +13,7 @@ #include #include - + int makedir (char *dir) { @@ -21,14 +21,13 @@ makedir (char *dir) char* folder_perms_ASCII; int had_an_error = 0; mode_t folder_perms, saved_umask; - pid_t pid; register char* c; - context_save(); /* save the context file */ + context_save(); /* save the context file */ fflush(stdout); if (!(folder_perms_ASCII = context_find ("folder-protect"))) - folder_perms_ASCII = foldprot; /* defaults to "700" */ + folder_perms_ASCII = foldprot; /* defaults to "700" */ /* Because mh-profile.man documents "Folder-Protect:" as an octal constant, and we don't want to force the user to remember to include a leading @@ -43,71 +42,37 @@ makedir (char *dir) to interact with the umask. Clear it temporarily. */ saved_umask = umask(0); - if (getuid () == geteuid ()) { - c = strncpy(path, dir, sizeof(path)); - - while (!had_an_error && (c = strchr((c + 1), '/')) != NULL) { - *c = (char)0; - if (access(path, X_OK)) { - if (errno != ENOENT){ - advise (dir, "unable to create directory"); - had_an_error = 1; - } - /* Create an outer directory. */ - if (mkdir(path, folder_perms)) { - advise (dir, "unable to create directory"); - had_an_error = 1; - } - } - *c = '/'; - } + c = strncpy(path, dir, sizeof(path)); - if (!had_an_error) { - /* Create the innermost nested subdirectory of the path we're being - asked to create. */ - if (mkdir (dir, folder_perms) == -1) { - advise (dir, "unable to create directory"); - had_an_error = 1; - } - } + while (!had_an_error && (c = strchr((c + 1), '/')) != NULL) { + *c = (char)0; + if (access(path, X_OK)) { + if (errno != ENOENT){ + advise (dir, "unable to create directory"); + had_an_error = 1; + } + /* Create an outer directory. */ + if (mkdir(path, folder_perms)) { + advise (dir, "unable to create directory"); + had_an_error = 1; + } + } + *c = '/'; } - else { - /* Ummm, why do we want to avoid creating directories with the effective - user ID? None of the nmh tools are installed such that the effective - should be different from the real, and if some parent process made - the two be different, I don't see why it should be our job to enforce - the real UID. Also, why the heck do we call the mkdir executable - rather than the library function in this case?? If we do want to - call the mkdir executable, we should at least be giving it -p (and - change the single chmod() call below) so it can successfully create - nested directories like the above code can. - - -- Dan Harkless */ - switch (pid = fork()) { - case -1: - advise ("fork", "unable to"); - return 0; - - case 0: - execl ("/bin/mkdir", "mkdir", dir, (void *) NULL); - execl ("/usr/bin/mkdir", "mkdir", dir, (void *) NULL); - fprintf (stderr, "unable to exec "); - perror ("mkdir"); - _exit (-1); - - default: - if (pidXwait(pid, "mkdir")) - return 0; - break; - } - chmod (dir, folder_perms); + if (!had_an_error) { + /* Create the innermost nested subdirectory of the path we're being + asked to create. */ + if (mkdir (dir, folder_perms) == -1) { + advise (dir, "unable to create directory"); + had_an_error = 1; + } } umask(saved_umask); /* put the user's umask back */ if (had_an_error) - return 0; /* opposite of UNIX error return convention */ + return 0; /* opposite of UNIX error return convention */ else - return 1; + return 1; } -- 2.48.1 From 025153a3cc83c227e9a3dec95b94eb56dd5e3c58 Mon Sep 17 00:00:00 2001 From: David Levine Date: Sun, 2 Feb 2014 22:16:25 -0600 Subject: [PATCH 10/16] Restored setuid/setgid to slocal: it needs them when run as root, as a mail delivery agent. --- uip/slocal.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/uip/slocal.c b/uip/slocal.c index a807d816..fb21c880 100644 --- a/uip/slocal.c +++ b/uip/slocal.c @@ -34,6 +34,10 @@ #include #include +/* Hopefully, grp.h declares initgroups(). If we run into a platform + where it doesn't, we could consider declaring it here as well. */ +#include + /* This define is needed for Berkeley db v2 and above to * make the header file expose the 'historical' ndbm APIs. * We define it unconditionally because this is simple and @@ -278,6 +282,16 @@ main (int argc, char **argv) chdir ("/"); umask (0077); + if (geteuid() == 0) { + if (setgid (pw->pw_gid) != 0) { + adios ("setgid", "unable to set group to %ld", (long) pw->pw_gid); + } + initgroups (pw->pw_name, pw->pw_gid); + if (setuid (pw->pw_uid) != 0) { + adios ("setuid", "unable to set user to %ld", (long) pw->pw_uid); + } + } + if (info == NULL) info = ""; -- 2.48.1 From 5b83e4c3cdafdcb5a4e8b6302314dca974a67092 Mon Sep 17 00:00:00 2001 From: David Levine Date: Sun, 2 Feb 2014 22:34:14 -0600 Subject: [PATCH 11/16] netbsd uses flock by default for mail spool locking, as reported by Robert Elz. Use *netbsd* to also include knetbsd, though I don't know for sure that it uses flock. --- m4/locking.m4 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/m4/locking.m4 b/m4/locking.m4 index 2895d882..e5b143b5 100644 --- a/m4/locking.m4 +++ b/m4/locking.m4 @@ -11,7 +11,7 @@ AC_DEFUN([NMH_LOCKING], AS_CASE(["$host_os"], [aix*|cygwin*|linux*], [default_locktype="fcntl"], - [freebsd*|openbsd*|darwin*], [default_locktype="flock"], + [freebsd*|*netbsd*|openbsd*|darwin*], [default_locktype="flock"], [default_locktype="dot"]) AC_MSG_CHECKING([default locking method for the mail spool]) -- 2.48.1 From f93ce652c5d1361b00a28db7bbb9e638197a6676 Mon Sep 17 00:00:00 2001 From: Ken Hornstein Date: Mon, 3 Feb 2014 20:14:55 -0500 Subject: [PATCH 12/16] Support for selectable Content-Transfer-Encoding. Now default to 8bit for CTE for text types. --- h/mhparse.h | 6 +- h/mime.h | 2 + test/mhbuild/test-attach | 10 +- test/mhbuild/test-utf8-body | 5 +- uip/mhbuild.c | 17 ++- uip/mhbuildsbr.c | 215 +++++++++++++++++++++--------------- 6 files changed, 155 insertions(+), 100 deletions(-) diff --git a/h/mhparse.h b/h/mhparse.h index 85b1bc16..bb55153f 100644 --- a/h/mhparse.h +++ b/h/mhparse.h @@ -304,14 +304,16 @@ CT parse_mime (char *); * processed by default. * encoding - The default encoding to use when doing RFC 2047 header * encoding. Must be one of CE_UNKNOWN, CE_BASE64, or - * CE_QUOTED; + * CE_QUOTED. + * maxunencoded - The maximum line length before the default encoding for + * text parts is quoted-printable. * * Returns a CT structure describing the resulting MIME message. If the * -auto flag is set and a MIME-Version header is encountered, the return * value is NULL. */ CT build_mime (char *infile, int autobuild, int dist, int directives, - int encoding); + int encoding, size_t maxunencoded); int add_header (CT, char *, char *); int get_ctinfo (char *, CT, int); diff --git a/h/mime.h b/h/mime.h index 2ed5378c..d152094a 100644 --- a/h/mime.h +++ b/h/mime.h @@ -35,6 +35,8 @@ && (c) != '/' && (c) != '[' && (c) != ']' \ && (c) != '?' && (c) != '=') +#define MAXTEXTPERLN 78 +#define MAXLONGLINE 998 #define CPERLIN 76 #define BPERLIN (CPERLIN / 4) #define LPERMSG 632 diff --git a/test/mhbuild/test-attach b/test/mhbuild/test-attach index 9f652698..12d3da39 100755 --- a/test/mhbuild/test-attach +++ b/test/mhbuild/test-attach @@ -233,23 +233,25 @@ Fcc: +outbox Subject: A more complete multipart test MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="----- =_aaaaaaaaaa0" +Content-Transfer-Encoding: 8bit ------- =_aaaaaaaaaa0 Content-Type: multipart/alternative; boundary="----- =_aaaaaaaaaa1" +Content-Transfer-Encoding: 8bit ------- =_aaaaaaaaaa1 Content-Type: text/plain; charset="UTF-8" -Content-Transfer-Encoding: quoted-printable +Content-Transfer-Encoding: 8bit -This is some t=C3=ABxt. +This is some tëxt. ------- =_aaaaaaaaaa1 Content-Type: text/html; charset="UTF-8" -Content-Transfer-Encoding: quoted-printable +Content-Transfer-Encoding: 8bit -This is some HTML t=C3=ABxt. +This is some HTML tëxt. diff --git a/test/mhbuild/test-utf8-body b/test/mhbuild/test-utf8-body index 033a2ef8..9798ac26 100755 --- a/test/mhbuild/test-utf8-body +++ b/test/mhbuild/test-utf8-body @@ -102,13 +102,14 @@ To: Somebody Subject: Test MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="----- =_aaaaaaaaaa0" +Content-Transfer-Encoding: 8bit Date: ------- =_aaaaaaaaaa0 Content-Type: text/plain -Content-Transfer-Encoding: quoted-printable +Content-Transfer-Encoding: 8bit -=C2=A1Ay, caramba! +¡Ay, caramba! ------- =_aaaaaaaaaa0 Content-Type: text/plain; name="attachment.txt" diff --git a/uip/mhbuild.c b/uip/mhbuild.c index de849a39..85bfef47 100644 --- a/uip/mhbuild.c +++ b/uip/mhbuild.c @@ -41,6 +41,7 @@ X("nocontentid", 0, NCONTENTIDSW) \ X("headerencoding encoding-algorithm", 0, HEADERENCSW) \ X("autoheaderencoding", 0, AUTOHEADERENCSW) \ + X("maxunencoded", 0, MAXUNENCSW) \ X("version", 0, VERSIONSW) \ X("help", 0, HELPSW) \ X("debug", -5, DEBUGSW) \ @@ -102,6 +103,7 @@ int main (int argc, char **argv) { int sizesw = 1, headsw = 1, directives = 1, autobuild = 0, dist = 0; + size_t maxunencoded = MAXTEXTPERLN; int *icachesw; char *cp, buf[BUFSIZ]; char buffer[BUFSIZ], *compfile = NULL; @@ -253,6 +255,15 @@ main (int argc, char **argv) header_encoding = CE_UNKNOWN; continue; + case MAXUNENCSW: + if (!(cp = *argp++) || *cp == '-') + adios (NULL, "missing argument to %s", argp[-2]); + if ((maxunencoded = atoi(cp)) < 1) + adios (NULL, "Invalid argument for %s: %s", argp[-2], cp); + if (maxunencoded > 998) + adios (NULL, "limit of -maxunencoded is 998"); + continue; + case VERBSW: verbosw++; continue; @@ -326,7 +337,8 @@ main (int argc, char **argv) unlink_infile = 1; /* build the content structures for MIME message */ - ct = build_mime (infile, autobuild, dist, directives, header_encoding); + ct = build_mime (infile, autobuild, dist, directives, header_encoding, + maxunencoded); /* * If ct == NULL, that means that -auto was set and a MIME version @@ -356,7 +368,8 @@ main (int argc, char **argv) */ /* build the content structures for MIME message */ - ct = build_mime (compfile, autobuild, dist, directives, header_encoding); + ct = build_mime (compfile, autobuild, dist, directives, header_encoding, + maxunencoded); /* * If ct == NULL, that means -auto was set and we found a MIME version diff --git a/uip/mhbuildsbr.c b/uip/mhbuildsbr.c index 70747c85..e44f0141 100644 --- a/uip/mhbuildsbr.c +++ b/uip/mhbuildsbr.c @@ -77,7 +77,7 @@ static char *fgetstr (char *, int, FILE *); static int user_content (FILE *, char *, CT *); static void set_id (CT, int); static int compose_content (CT); -static int scan_content (CT); +static int scan_content (CT, size_t); static int build_headers (CT); static char *calculate_digest (CT, int); @@ -124,7 +124,7 @@ static void directive_pop(void) CT build_mime (char *infile, int autobuild, int dist, int directives, - int header_encoding) + int header_encoding, size_t maxunencoded) { int compnum, state; char buf[BUFSIZ], name[NAMESZ]; @@ -449,7 +449,7 @@ finish_field: * check if prefix for multipart boundary clashes with * any of the contents. */ - while (scan_content (ct) == NOTOK) { + while (scan_content (ct, maxunencoded) == NOTOK) { if (*cp < 'z') { (*cp)++; } else { @@ -1278,11 +1278,12 @@ raw: */ static int -scan_content (CT ct) +scan_content (CT ct, size_t maxunencoded) { int len; - int check8bit = 0, contains8bit = 0; /* check if contains 8bit data */ - int checklinelen = 0, linelen = 0; /* check for long lines */ + int check8bit = 0, contains8bit = 0; /* check if contains 8bit data */ + int checklinelen = 0, linelen = 0; /* check for long lines */ + int checkllinelen = 0; /* check for extra-long lines */ int checkboundary = 0, boundaryclash = 0; /* check if clashes with multipart boundary */ int checklinespace = 0, linespace = 0; /* check if any line ends with space */ char *cp = NULL, buffer[BUFSIZ]; @@ -1304,7 +1305,7 @@ scan_content (CT ct) for (part = m->mp_parts; part; part = part->mp_next) { CT p = part->mp_part; - if (scan_content (p) == NOTOK) /* choose encoding for subpart */ + if (scan_content (p, maxunencoded) == NOTOK) /* choose encoding for subpart */ return NOTOK; /* if necessary, enlarge encoding for enclosing multipart */ @@ -1320,56 +1321,72 @@ scan_content (CT ct) /* * Decide what to check while scanning this content. */ - switch (ct->c_type) { - case CT_TEXT: - check8bit = 1; + + switch (ct->c_reqencoding) { + case CE_8BIT: + checkllinelen = 1; checkboundary = 1; - if (ct->c_subtype == TEXT_PLAIN) { - checklinelen = 0; - checklinespace = 0; - } else { + break; + case CE_QUOTED: + checkboundary = 1; + break; + case CE_BASE64: + /* We check nothing here */ + break; + case CE_UNKNOWN: + /* Use the default rules based on content-type */ + switch (ct->c_type) { + case CT_TEXT: + checkboundary = 1; + check8bit = 1; checklinelen = 1; - checklinespace = 1; - } + if (ct->c_subtype == TEXT_PLAIN) { + checklinespace = 0; + } else { + checklinespace = 1; + } break; - case CT_APPLICATION: - check8bit = 1; - checklinelen = 1; - checklinespace = 1; - checkboundary = 1; + case CT_APPLICATION: + check8bit = 1; + checklinelen = 1; + checklinespace = 1; + checkboundary = 1; break; - case CT_MESSAGE: - check8bit = 0; - checklinelen = 0; - checklinespace = 0; + case CT_MESSAGE: + check8bit = 0; + checklinelen = 0; + checklinespace = 0; - /* don't check anything for message/external */ - if (ct->c_subtype == MESSAGE_EXTERNAL) - checkboundary = 0; - else - checkboundary = 1; - break; + /* don't check anything for message/external */ + if (ct->c_subtype == MESSAGE_EXTERNAL) + checkboundary = 0; + else + checkboundary = 1; + break; - case CT_AUDIO: - case CT_IMAGE: - case CT_VIDEO: - /* - * Don't check anything for these types, - * since we are forcing use of base64. - */ - check8bit = 0; - checklinelen = 0; - checklinespace = 0; - checkboundary = 0; - break; + case CT_AUDIO: + case CT_IMAGE: + case CT_VIDEO: + /* + * Don't check anything for these types, + * since we are forcing use of base64, unless + * the content-type was specified by a mhbuild directive. + */ + check8bit = 0; + checklinelen = 0; + checklinespace = 0; + checkboundary = 0; + break; + } } /* * Scan the unencoded content */ - if (check8bit || checklinelen || checklinespace || checkboundary) { + if (check8bit || checklinelen || checklinespace || checkboundary || + checkllinelen) { if ((in = fopen (ce->ce_file, "r")) == NULL) adios (ce->ce_file, "unable to open for reading"); len = strlen (prefix); @@ -1390,11 +1407,23 @@ scan_content (CT ct) /* * Check line length. */ - if (checklinelen && (strlen (buffer) > CPERLIN + 1)) { + if (checklinelen && (strlen (buffer) > maxunencoded + 1)) { linelen = 1; checklinelen = 0; /* no need to keep checking */ } + /* + * RFC 5322 specifies that a message cannot contain a line + * greater than 998 characters (excluding the CRLF). If we + * get one of those lines and linelen is NOT set, then abort. + */ + + if (checkllinelen && !linelen && + (strlen(buffer) > MAXLONGLINE + 1)) { + adios(NULL, "Line in content exceeds maximum line limit (%d)", + MAXLONGLINE); + } + /* * Check if line ends with a space. */ @@ -1424,59 +1453,65 @@ scan_content (CT ct) /* * Decide which transfer encoding to use. */ - switch (ct->c_type) { - case CT_TEXT: - /* - * If the text content didn't specify a character - * set, we need to figure out which one was used. - */ - t = (struct text *) ct->c_ctparams; - if (t->tx_charset == CHARSET_UNSPECIFIED) { - CI ci = &ct->c_ctinfo; - char **ap, **ep; - for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) - continue; + if (ct->c_reqencoding != CE_UNKNOWN) + ct->c_encoding = ct->c_reqencoding; + else + switch (ct->c_type) { + case CT_TEXT: + /* + * If the text content didn't specify a character + * set, we need to figure out which one was used. + */ + t = (struct text *) ct->c_ctparams; + if (t->tx_charset == CHARSET_UNSPECIFIED) { + CI ci = &ct->c_ctinfo; + char **ap, **ep; - if (contains8bit) { - *ap = concat ("charset=", write_charset_8bit(), NULL); - } else { - *ap = add ("charset=us-ascii", NULL); + for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) + continue; + + if (contains8bit) { + *ap = concat ("charset=", write_charset_8bit(), NULL); + } else { + *ap = add ("charset=us-ascii", NULL); + } + t->tx_charset = CHARSET_SPECIFIED; + + cp = strchr(*ap++, '='); + *ap = NULL; + *cp++ = '\0'; + *ep = cp; } - t->tx_charset = CHARSET_SPECIFIED; - cp = strchr(*ap++, '='); - *ap = NULL; - *cp++ = '\0'; - *ep = cp; - } + if (contains8bit && !linelen && !linespace && !checksw) + ct->c_encoding = CE_8BIT; + else if (contains8bit || linelen || linespace || checksw) + ct->c_encoding = CE_QUOTED; + else + ct->c_encoding = CE_7BIT; + break; - if (contains8bit || linelen || linespace || checksw) - ct->c_encoding = CE_QUOTED; - else - ct->c_encoding = CE_7BIT; - break; + case CT_APPLICATION: + /* For application type, use base64, except when postscript */ + if (contains8bit || linelen || linespace || checksw) + ct->c_encoding = (ct->c_subtype == APPLICATION_POSTSCRIPT) + ? CE_QUOTED : CE_BASE64; + else + ct->c_encoding = CE_7BIT; + break; - case CT_APPLICATION: - /* For application type, use base64, except when postscript */ - if (contains8bit || linelen || linespace || checksw) - ct->c_encoding = (ct->c_subtype == APPLICATION_POSTSCRIPT) - ? CE_QUOTED : CE_BASE64; - else + case CT_MESSAGE: ct->c_encoding = CE_7BIT; - break; - - case CT_MESSAGE: - ct->c_encoding = CE_7BIT; - break; + break; - case CT_AUDIO: - case CT_IMAGE: - case CT_VIDEO: - /* For audio, image, and video contents, just use base64 */ - ct->c_encoding = CE_BASE64; - break; - } + case CT_AUDIO: + case CT_IMAGE: + case CT_VIDEO: + /* For audio, image, and video contents, just use base64 */ + ct->c_encoding = CE_BASE64; + break; + } return (boundaryclash ? NOTOK : OK); } -- 2.48.1 From 9b706433f5b980baf270695f836d8cfe0510f4e6 Mon Sep 17 00:00:00 2001 From: Ken Hornstein Date: Mon, 3 Feb 2014 21:45:41 -0500 Subject: [PATCH 13/16] Document changes to mhbuild. --- docs/pending-release-notes | 2 ++ man/mhbuild.man | 25 +++++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/docs/pending-release-notes b/docs/pending-release-notes index 464554b0..35bc26a3 100644 --- a/docs/pending-release-notes +++ b/docs/pending-release-notes @@ -72,6 +72,8 @@ NEW FEATURES - A new header, "Attach", is supported by mhbuild; it is used to replace previous functionality (which by default used a header named Nmh-Attachment). +- The default Content-Transfer-Encoding for text parts is now 8bit. +- mhbuild(1) now supports a selectable Content-Transfer-Encoding ----------------- OBSOLETE FEATURES diff --git a/man/mhbuild.man b/man/mhbuild.man index e4bd7933..db80e232 100644 --- a/man/mhbuild.man +++ b/man/mhbuild.man @@ -21,6 +21,8 @@ mhbuild \- translate MIME composition draft .RB [ \-headerencoding .IR encoding\-algorithm .RB " | " \-autoheaderencoding ] +.RB [ \-maxunencoded +.IR line\-length ] .RB [ \-dist ] .RB [ \-version ] .RB [ \-help ] @@ -401,6 +403,18 @@ characters. The switch suppresses creation of all \*(lqContent-ID:\*(rq headers, even in the top level of the message. .PP +Normally +.B mhbuild +will choose an appropriate Content\-Transfer\-Encoding based on the content +and the MIME Content\-Type. However, you can override that in an +.B mhbuild +directive by specifying \*(lq*\*(rq and the encoding. Acceptable encoding +values are \*(lq8bit\*(rq, \*(lqqp\(*rq (for quoted\-printable), and +\*(lqb64\*(rq (for base64 encoding). It should be noted that undesired +results may occur if 8bit or quoted\-printable is selected for binary +content, due to the translation between Unix line endings and the line +endings use by the mail transport system. +.PP In addition to the various directives, plaintext can be present. Plaintext is gathered, until a directive is found or the draft is exhausted, and this is made to form a text content. If the plaintext @@ -473,6 +487,13 @@ If a text content contains only 7\-bit characters and the character set is not specified as above, then the character set will be labeled as \*(lqus-ascii\*(rq. .PP +By default text content with the high bit set is encoded with a 8bit +Content\-Transfer\-Encoding. If the text has lines longer than the value +of +.B \-maxunencoded +(which defaults to 78) then the text is encoded using the quoted\-printable +encoding. +.PP The .B \-headerencoding switch will indicate which algorithm to use when encoding any message headers @@ -643,6 +664,7 @@ directive ::= "#" type "/" subtype [ "<" id ">" ] [ "[" description "]" ] [ "{" disposition "}" ] + [ "*8bit" | "*qp" | "*b64" ] [ filename ] EOL @@ -652,6 +674,7 @@ directive ::= "#" type "/" subtype [ "<" id ">" ] [ "[" description "]" ] [ "{" disposition "}" ] + [ "*8bit" | "*qp" | "*b64" ] external-parameters EOL @@ -683,6 +706,7 @@ plaintext ::= [ "Content-Description:" [ "(" comment ")" ] [ "[" description "]" ] [ "{" disposition "}" ] + [ "*8bit" | "*qp" | "*b64" ] EOL 1*line [ "#" EOL ] @@ -754,4 +778,5 @@ is checked. .RB ` \-nocheck ' .RB ` \-noverbose ' .RB ` \-autoheaderencoding ' +.RB ` "\-maxunencoded\ 78"' .fi -- 2.48.1 From e5078d5e41a7277c4f060e52f9ed7cb24f8a99f0 Mon Sep 17 00:00:00 2001 From: Ken Hornstein Date: Mon, 3 Feb 2014 21:46:08 -0500 Subject: [PATCH 14/16] Fix up mhbuild so the character set is always output when needed, even when the CTE is specified. --- uip/mhbuildsbr.c | 66 ++++++++++++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 28 deletions(-) diff --git a/uip/mhbuildsbr.c b/uip/mhbuildsbr.c index e44f0141..b2949c39 100644 --- a/uip/mhbuildsbr.c +++ b/uip/mhbuildsbr.c @@ -1319,9 +1319,18 @@ scan_content (CT ct, size_t maxunencoded) } /* - * Decide what to check while scanning this content. + * Decide what to check while scanning this content. Note that + * for text content we always check for 8bit characters if the + * charset is unspecified, because that controls whether or not the + * character set is us-ascii or retrieved from the locale. */ + if (ct->c_type == CT_TEXT) { + t = (struct text *) ct->c_ctparams; + if (t->tx_charset == CHARSET_UNSPECIFIED) + check8bit = 1; + } + switch (ct->c_reqencoding) { case CE_8BIT: checkllinelen = 1; @@ -1331,14 +1340,12 @@ scan_content (CT ct, size_t maxunencoded) checkboundary = 1; break; case CE_BASE64: - /* We check nothing here */ break; case CE_UNKNOWN: /* Use the default rules based on content-type */ switch (ct->c_type) { case CT_TEXT: checkboundary = 1; - check8bit = 1; checklinelen = 1; if (ct->c_subtype == TEXT_PLAIN) { checklinespace = 0; @@ -1450,6 +1457,34 @@ scan_content (CT ct, size_t maxunencoded) fclose (in); } + /* + * If the content is text and didn't specify a character set, + * we need to figure out which one was used. + */ + + if (ct->c_type == CT_TEXT) { + t = (struct text *) ct->c_ctparams; + if (t->tx_charset == CHARSET_UNSPECIFIED) { + CI ci = &ct->c_ctinfo; + char **ap, **ep; + + for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) + continue; + + if (contains8bit) { + *ap = concat ("charset=", write_charset_8bit(), NULL); + } else { + *ap = add ("charset=us-ascii", NULL); + } + t->tx_charset = CHARSET_SPECIFIED; + + cp = strchr(*ap++, '='); + *ap = NULL; + *cp++ = '\0'; + *ep = cp; + } + } + /* * Decide which transfer encoding to use. */ @@ -1459,31 +1494,6 @@ scan_content (CT ct, size_t maxunencoded) else switch (ct->c_type) { case CT_TEXT: - /* - * If the text content didn't specify a character - * set, we need to figure out which one was used. - */ - t = (struct text *) ct->c_ctparams; - if (t->tx_charset == CHARSET_UNSPECIFIED) { - CI ci = &ct->c_ctinfo; - char **ap, **ep; - - for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) - continue; - - if (contains8bit) { - *ap = concat ("charset=", write_charset_8bit(), NULL); - } else { - *ap = add ("charset=us-ascii", NULL); - } - t->tx_charset = CHARSET_SPECIFIED; - - cp = strchr(*ap++, '='); - *ap = NULL; - *cp++ = '\0'; - *ep = cp; - } - if (contains8bit && !linelen && !linespace && !checksw) ct->c_encoding = CE_8BIT; else if (contains8bit || linelen || linespace || checksw) -- 2.48.1 From 95a485d2b8a2bf28fdb80ca62c12d0541ac0a2fc Mon Sep 17 00:00:00 2001 From: Ken Hornstein Date: Mon, 3 Feb 2014 21:46:33 -0500 Subject: [PATCH 15/16] Add a new (but incomplete) test for mhbuild's new functionality. --- Makefile.am | 1 + test/mhbuild/test-cte | 50 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100755 test/mhbuild/test-cte diff --git a/Makefile.am b/Makefile.am index 626fd829..bdcca089 100644 --- a/Makefile.am +++ b/Makefile.am @@ -66,6 +66,7 @@ TESTS = test/ali/test-ali test/anno/test-anno \ test/locking/test-datalocking test/locking/test-spoollocking \ test/manpages/test-manpages \ test/mhbuild/test-attach \ + test/mhbuild/test-cte \ test/mhbuild/test-forw test/mhbuild/test-header-encode \ test/mhbuild/test-utf8-body \ test/mhfixmsg/test-mhfixmsg \ diff --git a/test/mhbuild/test-cte b/test/mhbuild/test-cte new file mode 100755 index 00000000..e3f151a5 --- /dev/null +++ b/test/mhbuild/test-cte @@ -0,0 +1,50 @@ +#!/bin/sh +######################################################### +# +# Test the use of explicit Content-Transfer-Encoding tags +# +######################################################### + +if test -z "${MH_OBJ_DIR}"; then + srcdir=`dirname "$0"`/../.. + MH_OBJ_DIR=`cd "$srcdir" && pwd`; export MH_OBJ_DIR +fi + +. "$MH_OBJ_DIR/test/common.sh" + +setup_test + +LC_ALL=en_US.UTF-8; export LC_ALL + +draft="$MH_TEST_DIR/$$.draft" +expected="$MH_TEST_DIR/$$.expected" + +# +# Force some text to be quoted-printable +# + +cat > "$draft" < +cc: +Fcc: +outbox +------ +# "$expected" < +cc: +Fcc: +outbox +MIME-Version: 1.0 +Content-Type: text/plain; charset="UTF-8" +Content-Transfer-Encoding: quoted-printable + +This is a test of a message with a sh=C3=B8rt line. +EOF + +check "$draft" "$expected" + +exit ${failed:-0} -- 2.48.1 From 034abb1cc56e6b4d15282635ea46f276b97680d8 Mon Sep 17 00:00:00 2001 From: David Levine Date: Mon, 3 Feb 2014 21:42:44 -0600 Subject: [PATCH 16/16] Use rename(2), not link(2), in m_mktemps() if mkstemps() is not available. --- h/prototypes.h | 2 +- sbr/m_mktemp.c | 18 +----------------- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/h/prototypes.h b/h/prototypes.h index fceef407..1ff92f64 100644 --- a/h/prototypes.h +++ b/h/prototypes.h @@ -191,7 +191,7 @@ int m_putenv (char *, char *); int m_rand (unsigned char *, size_t); char *m_mktemp(const char *, int *, FILE **); char *m_mktemp2(const char *, const char *, int *, FILE **); -char *m_mktemps(const char *pfx, const char *, int *, FILE **); +char *m_mktemps(const char *pfx, const char *suffix, int *, FILE **); char *get_temp_dir(); void m_unknown(m_getfld_state_t *, FILE *); int makedir (char *); diff --git a/sbr/m_mktemp.c b/sbr/m_mktemp.c index 17cf7458..009df0a1 100644 --- a/sbr/m_mktemp.c +++ b/sbr/m_mktemp.c @@ -181,31 +181,15 @@ m_mktemps( fd = mkstemp(tmpfil); { char *oldfilename = tmpfil; - tmpfil = concat(oldfilename, suffix, NULL); - /* link(2) requires that the new path not exist. And if we - have to resort to rename(2), at least try to remove a file - that would be in the way. */ - if (unlink(tmpfil) != 0 && errno != ENOENT) { - advise("unlink", "Failed to unlink \"%s\"", tmpfil); - (void) unlink(oldfilename); - free(oldfilename); - free(tmpfil); - return NULL; - } - - /* link() doesn't always work, such as on Windows FAT - filesystems. If it fails, try rename(). */ - if (link(oldfilename, tmpfil) != 0 && - rename(oldfilename, tmpfil) != 0) { + if (rename(oldfilename, tmpfil) != 0) { (void) unlink(oldfilename); free(oldfilename); free(tmpfil); return NULL; } - (void) unlink(oldfilename); free(oldfilename); } #endif /* ! HAVE_MKSTEMPS */ -- 2.48.1