[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#923762: unblock: mblaze/0.5.1-1



Package: release.debian.org
Severity: normal
User: release.debian.org@packages.debian.org
Usertags: unblock

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

Dear release team,

I uploaded a new version of mblaze, which reset the package's transition
countdown, which will cause it to miss the hard freeze window:

  https://qa.debian.org/excuses.php?package=mblaze


I did so as the newest upstream release includes security-relevant changes:

  mblaze (0.5.1-1) unstable; urgency=high (security fixes)
  
    * New upstream release (2019-03-03)
      + Fixes for buffer-overflows, found by fuzzing.
      + Fixes for memleaks.
  
    * Remove unused lintian override
    * debian/rules: Remove useless dh_missing invocation.
      A single binary package is being produced.
  
   -- Nicolas Braud-Santoni <nicoo@debian.org>  Mon, 04 Mar 2019 18:40:01 +0100


Moreover, version 0.5-1 closes several Debian bugs (including a RC one):

  mblaze (0.5-1) unstable; urgency=medium
  
    * New upstream release (2019-02-09)
      + Closes: 921891
      + GNUmakefile: correct reference to SOURCE_DATE_EPOCH, patch by Chris Lamb
        Closes: #907537 
  
    * Rename mless and msort to mblaze-less and mblaze-sort
      Closes: #887988 correctly, avoids conflicts on mpdtoys & msort
  
    * Switch to debhelper 12.
      The compatibility level is now controlled by a Build-Depends.
  
    * Declare compliance with policy v4.2.1.
      No change required.
  
    * debian/rules
      + Make dh_missing fail the build
      + Symlink docs from mblaze-* to mblaze
  
   -- Nicolas Braud-Santoni <nicoo@debian.org>  Tue, 12 Feb 2019 11:44:33 +0100


The debdiff is attached.


Best,

  nicoo

unblock mblaze/0.5.1-1

- -- System Information:
Debian Release: buster/sid
  APT prefers testing
  APT policy: (990, 'testing'), (500, 'unstable'), (1, 'experimental')
Architecture: amd64 (x86_64)

Kernel: Linux 4.19.0-2-amd64 (SMP w/4 CPU cores)
Kernel taint flags: TAINT_PROPRIETARY_MODULE, TAINT_OOT_MODULE, TAINT_UNSIGNED_MODULE
Locale: LANG=en_US.UTF-8, LC_CTYPE=en_US.UTF-8 (charmap=UTF-8) (ignored: LC_ALL set to en_US.UTF-8), LANGUAGE=en_US.UTF-8 (charmap=UTF-8) (ignored: LC_ALL set to en_US.UTF-8)
Shell: /bin/sh linked to /bin/dash
Init: systemd (via /run/systemd/system)
LSM: AppArmor: enabled

-----BEGIN PGP SIGNATURE-----

iQJFBAEBCgAvFiEEU7EqA8ZVHYoLJhPE5vmO4pLV7MsFAlx9wacRHG5pY29vQGRl
Ymlhbi5vcmcACgkQ5vmO4pLV7MsdVw//X2febyz+EoW+0lN1iSGZYSeqJhIzC11m
zOippy/KMeCjbjPuqgI1xBTuLBzNYbV+Y6gwf94+4O8KdqRcxHhjP1zE5ttWqVQD
bzMmIB4hr1xIua11OG9Oy1FdG+Fs49nqmaYvo4q7k0qg0VfohwBJEuHnPZEea14y
iT/7o/nnhqNnhZIlNv1dhCJWA00hlMt18/7QjG9bSdYRo8iJUdNmkVY750Adv1+S
MicSLHtnGy+/2AGYxU/0oW6sPFPBLiXli9gpqN6KhwdF4L6aroRnMswfbThxtc/s
GmFETyK/UIJ/6N5pt3ff9fSeU3CHvJclNm4TljKFPfGg4r49Oj+rZeXepddg5Acv
7AaXhB3gMZEwgKtmM02kqwMDdWPRZ3Fwlo6La/3ZJuflNnaBgytlr90Bh8lCAyPI
rMjrtP0jcWq1EIRWUgqCaIPFMfvwPMAyTRtORfzOdGtC9ZOBnEBzuqNgHqv5qpEt
dLQGHFRQJwDrpFc0veWqXZ01UcIO39fNFIMxUf1nAYw80NRcMRvvCT8IUucCLybq
PKn6lLwUfyu+MbfIdyuw7F9hSgMVINmTX1lH8u+Mk1L9p2sn+96JXh1YWAVtrVhn
+k8sSA2P5lOBWQLrRtq34VIjv9zlNRKz+5JO4vbdQ9AuZI+0C77PKK08nnixPCrc
EL4q69REWC0=
=AOMb
-----END PGP SIGNATURE-----
diff -Nru mblaze-0.4/blaze822.c mblaze-0.5.1/blaze822.c
--- mblaze-0.4/blaze822.c	2018-08-15 17:16:45.000000000 +0200
+++ mblaze-0.5.1/blaze822.c	2019-03-03 17:12:56.000000000 +0100
@@ -152,14 +152,20 @@
 // never writes more than dstmax to dst
 // returns how many bytes were appended
 static size_t
-safe_append(char *dst, size_t dstmax, char *strbeg, char *strend)
+safe_append(char *dst, size_t dstmax, char *srcbeg, char *srcend)
 {
-	size_t dstlen = strlen(dst);
-	if (dstmax - dstlen - 1 < strend - strbeg)
-		strend = strbeg + (dstmax - dstlen - 1);
-	memcpy(dst + dstlen, strbeg, strend - strbeg);
-	dst[dstlen + (strend - strbeg)] = 0;
-	return strend - strbeg;
+	size_t srclen = srcend - srcbeg;
+	size_t dstlen = strnlen(dst, dstmax);
+
+	if (dstlen == dstmax)
+		return 0;
+
+	if (dstmax - dstlen < srclen + 1)
+		srclen = dstmax - dstlen - 1;
+	memcpy(dst + dstlen, srcbeg, srclen);
+	dst[dstlen + srclen] = 0;
+
+	return srclen;
 }
 
 static size_t
@@ -213,8 +219,9 @@
 				s++;
 				if (*addr || *disp)
 					break;
+			} else {
+				s++;
 			}
-			s++;
 		} else if (*s == '<') {
 			char tok[1024] = { 0 };
 			char *c = tok;
@@ -222,7 +229,9 @@
 			s++;
 			while (*s && c < e && *s != '>') {
 				s = skip_comment(s);
-				if (*s == '"') {
+				if (!*s) {
+					break;
+				} else if (*s == '"') {
 					// local part may be quoted, allow all
 					s++;
 					while (*s && c < e && *s != '"') {
@@ -258,7 +267,7 @@
 			char *e = tok + sizeof tok;
 			s++;
 			while (*s && c < e && *s != '"') {
-				if (*s == '\\')
+				if (*s == '\\' && *(s+1))
 					s++;
 				*c++ = *s++;
 			}
@@ -274,7 +283,8 @@
 		} else if (*s == '(') {
 			char *z = skip_comment(s);
 			if (!*disp && *addr)  // user@host (name)
-				safe_append(disp, sizeof disp, s + 1, z - 1);
+				safe_append(disp, sizeof disp, s + 1,
+				    *z ? z-1 : (*(z-1) == ')' ? z-1 : z));
 			else if (*disp) {  // copy comment
 				safe_append_space(disp, sizeof disp);
 				safe_append(disp, sizeof disp, s, z);
@@ -285,6 +295,8 @@
 				// in ipv6 address
 				if (tc < te)
 					*tc++ = *s++;
+				else
+					s++;
 			} else {  // ignore group name and start over
 				s++;
 				tc = ttok;
@@ -298,6 +310,8 @@
 				s++;
 			if (tc < te)
 				*tc++ = *s++;
+			else
+				s++;
 		}
 	}
 
@@ -504,8 +518,10 @@
 	size_t hlen = end - src;
 
 	buf = malloc(hlen+1);
-	if (!buf)
+	if (!buf) {
+		free(mesg);
 		return 0;
+	}
 	memcpy(buf, src, hlen);
 
 	end = buf+hlen;
@@ -664,6 +680,7 @@
 
 	char *buf = mmap(0, len+1, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
 	if (buf == MAP_FAILED) {
+		free(mesg);
 		perror("mmap");
 		goto error;
 	}
diff -Nru mblaze-0.4/contrib/_mblaze mblaze-0.5.1/contrib/_mblaze
--- mblaze-0.4/contrib/_mblaze	1970-01-01 01:00:00.000000000 +0100
+++ mblaze-0.5.1/contrib/_mblaze	2019-03-03 17:12:56.000000000 +0100
@@ -0,0 +1,402 @@
+#compdef maddr magrep mbnc mcom mdeliver mdirs mexport mflag mflow mfwd mgenmid mhdr minc mless mlist mmime mmkdir mpick mrep mscan msed mseq mshow msort mthread
+
+_mblaze_colon_separated_headers() {
+  _message 'headers (colon separated)'
+}
+
+_mblaze_message() {
+  local ret=1 expl tmp curmsg
+  local -a mseq mseqnums mshortcuts mshortcutdescrs
+  setopt localoptions extendedglob
+
+  if [[ -prefix './' ]]; then
+    _description files expl 'message file'
+    _files "$expl[@]" && ret=0
+  fi
+
+  curmsg=$(mseq .)
+  if [[ -z $curmsg || ! -f $curmsg ]]; then
+    _message 'no current sequence'
+    return $ret
+  fi
+
+  mseq=( ${(f)"$(mscan -f '%3n %c%u%r %10d %17f %t %2i%s' : 2>/dev/null)"} )
+  mseq=( ${(M)mseq:# #<->*} )
+  mseqnums=( ${(M)${mseq## #}##<->} )
+  mseq=( ${mseq//:/\\:} )
+  _describe -V -t mseq 'seq messages' mseq mseqnums && ret=0
+
+  mshortcuts=( '.' '+' '$' '^' '=' '_' )
+  mshortcutdescrs=(
+    ' . current message'
+    ' + next message'
+    # ' - previous message'
+    ' $ last message'
+    ' ^ parent message'
+    ' = current thread'
+    ' _ current subthread'
+  )
+
+  _describe -t mshortcuts 'shortcuts' mshortcutdescrs mshortcuts -S '' && ret=0
+
+  return $ret
+}
+
+_mblaze_message_part() {
+  setopt localoptions noksharrays extendedglob
+  local ret=1 expl tmp msgarg parts partnums
+  tmp=$words[(i)(-O|-x)]
+  if (( tmp >= $#words )); then
+    _message -e mparts 'message parts (parse error)'
+    return 1
+  fi
+
+  msgarg=$words[$tmp+1]
+  parts=( ${(f)"$(mshow -t $msgarg)"} )
+  parts=( ${(M)parts:# ##<->:*} )
+  if [[ -n $parts ]]; then
+    partnums=( ${${parts## #}%%:*} )
+    parts=( ${parts//:/\\:} )
+    _describe -V -t mparts 'message parts' parts partnums -l && ret=0
+  else
+    _message -e mparts 'message parts (none found)'
+  fi
+
+  return $ret
+}
+
+_mblaze_dirs() {
+  local ret=1 expl countnew countcur
+  local -a mailboxes mailbox_descriptions
+  setopt localoptions extendedglob
+
+  mailboxes=( */cur(:h) )
+  mailbox_descriptions=( ${(f)"$(mlist -i $mailboxes)"} )
+  _describe -t mailboxes "mailboxes" mailbox_descriptions mailboxes && ret=0
+
+  _description files expl 'directories'
+  _path_files "$expl[@]" -g "^(${(j:|:)mailboxes})(/)" && ret=0
+}
+
+_mblaze_header_regex() {
+  _message header\ regex
+}
+
+_mblaze_flags() {
+  _message flags
+}
+
+
+_maddr() {
+  local context state line expl
+  local -A opt_args
+
+  _arguments \
+    '-a[print addr-spec address without display name]' \
+    '-h[search only given headers]:header list:_mblaze_colon_separated_headers' \
+    '*:message:_mblaze_message'
+}
+
+_magrep() {
+  local context state line expl
+  local -A opt_args
+
+  _arguments \
+    '-a[search for regex in RFC 2822 address header parts only]' \
+    '-c[only print a count of matching messages]' \
+    '-d[decode the header according to RFC 2047 prior to searching]' \
+    '-i[match regex case insensitively]' \
+    '-m[do not show more than max matches]:max: ' \
+    '(-c -q -v)-o[print matches only]' \
+    '(-c -q -v)-p[print filename, header and matching]' \
+    '-q[quiet mode: do not print anything]' \
+    '-v[invert match]' \
+    '*:message:_mblaze_header_regex'
+}
+
+_mbnc() {
+  local context state line expl
+  local -A opt_args
+
+  _arguments \
+    ':message:_mblaze_message'
+}
+
+_mcom() {
+  local context state line expl
+  local -A opt_args
+
+  _arguments \
+    - resume \
+      '-r[resume editing]:draft: ' \
+    - recip \
+      '*:recipients: '
+}
+
+_mdeliver() {
+  local context state line expl
+  local -A opt_args
+
+  _arguments \
+    '-c[deliver into cur/ instead of new/]' \
+    '-v[print message filename after delivery]' \
+    '-X[override flags]:flags:_mblaze_flags' \
+    '-M[deliver messages from mbox]' \
+    ':dir:_mblaze_dirs'
+}
+
+_mdirs() {
+  _mblaze_dirs
+}
+
+_mexport() {
+  local context state line expl
+  local -A opt_args
+
+  _arguments \
+    '-S[add Status and X-Status headers according to flags]' \
+    '*:messages:_mblaze_message'
+}
+
+_mflag() {
+  local context state line expl
+  local -A opt_args
+
+  _arguments \
+    '(-d)-D[mark as draft]' \
+    '(-f)-F[mark as flagged]' \
+    '(-p)-P[mark as passed (resent/forwarded/bounced)]' \
+    '(-r)-R[mark as replied-to]' \
+    '(-s)-S[mark as seen]' \
+    '(-t)-T[mark as trashed]' \
+    '-X[mark with characters]:marking characters: ' \
+    '(-D)-d[unmark as draft]' \
+    '(-F)-f[unmark as flagged]' \
+    '(-P)-p[unmark as passed (resent/forwarded/bounced)]' \
+    '(-R)-r[unmark as replied-to]' \
+    '(-S)-s[unmark as seen]' \
+    '(-T)-t[unmark as trashed]' \
+    '-x[unmark with characters]:marked chacaters: ' \
+    '-v[read messages from standard input or current sequence]' \
+    '*:messages:_mblaze_message'
+}
+
+_mflow() {
+  local context state line expl
+  local -A opt_args
+
+  _arguments \
+    '-f[force wrapping of long lines]' \
+    '*-q[prefix lines with >]' \
+    '-w[set maximum line length]:width: ' \
+    '*:dirs:_mblaze_dirs'
+}
+
+_mfwd() {
+  local context state line expl
+  local -A opt_args
+
+  _arguments \
+    '-r[forward as plain text]' \
+    ':*:message:_mblaze_message'
+}
+
+_mgenmid() {
+  _message 'no arguments'
+}
+
+_mhdr() {
+  local context state line expl
+  local -A opt_args
+
+  _arguments \
+    '-h[print specific headers only]:headers: ' \
+    '-p[print specific parameter from header only]:parameter: ' \
+    '-d[print headers decoded]' \
+    '-H[prefix output with filename]' \
+    '-M[print all occurrences, not only the first]' \
+    '(-D)-A[print addresses from headers]' \
+    '(-A)-D[print date as unix timestamp]' \
+    '*:message:_mblaze_message'
+}
+
+_minc() {
+  local context state line expl
+  local -A opt_args
+
+  _arguments \
+    '-q[quiet mode: don'\''t print filenames]' \
+    '*:dirs:_mblaze_dirs'
+}
+
+_mless() {
+  local context state line expl
+  local -A opt_args
+
+  _arguments \
+    ':message:_mblaze_message'
+}
+
+_mlist() {
+  local context state line expl
+  local -A opt_args
+
+  _arguments \
+    '(-d)-D[only list messages marked as draft]' \
+    '(-f)-F[only list messages marked as flagged]' \
+    '(-p)-P[only list messages marked as passed (resent/forwarded/bounced)]' \
+    '(-r)-R[only list messages marked as replied-to]' \
+    '(-s)-S[only list messages marked as seen]' \
+    '(-t)-T[only list messages marked as trashed]' \
+    '-X[only list messages marked with characters]:marked characters: ' \
+    '(-D)-d[don'\''t list messages marked as draft]' \
+    '(-F)-f[don'\''t list messages marked as flagged]' \
+    '(-P)-p[don'\''t list messages marked as passed (resent/forwarded/bounced)]' \
+    '(-R)-r[don'\''t list messages marked as replied-to]' \
+    '(-S)-s[don'\''t list messages marked as seen]' \
+    '(-T)-t[don'\''t list messages marked as trashed]' \
+    '-x[don'\''t list messages marked with characters]:marked chacaters: ' \
+    '(-c -N)-C[only list messages in cur]' \
+    '(-n -C)-N[only list messages in new]' \
+    '(-C -N)-c[don'\''t list messages in cur]' \
+    '(-N -C)-n[don'\''t list messages in new]' \
+    '-i[print summaries instead of folder names]' \
+    '*:dirs:_mblaze_dirs'
+}
+
+_mmkdir() {
+  local context state line expl
+  local -A opt_args
+
+  _arguments \
+    '*:directory name: '
+}
+
+_mmime() {
+  local context state line expl
+  local -A opt_args
+
+  _arguments \
+    '(-r)-c[check mode (don'\''t output anything)]' \
+    '(-c)-r[raw mode (generate text/plain)]' \
+    '-t[override Content-Type of toplevel part]:Content-Type: '
+}
+
+_mpick() {
+  local context state line expl
+  local -A opt_args
+
+  _arguments \
+    '-T[include whole thread]' \
+    '-t[limit messages to test]:test: ' \
+    '*:message:_mblaze_message'
+}
+
+_mrep() {
+  local context state line expl
+  local -A opt_args
+
+  _arguments \
+    ':message:_mblaze_message'
+}
+
+_mscan() {
+  local context state line expl
+  local -A opt_args
+
+  _arguments \
+    '-n[only print message numbers]' \
+    '-I[force ISO date output]' \
+    '-f[specify output format]:format: ' \
+    '*:messages:_mblaze_message'
+}
+
+_msed() {
+  local context state line expl
+  local -A opt_args
+
+  _arguments \
+    ':sed script: ' \
+    '*:messages:_mblaze_message'
+}
+
+_mseq() {
+  local context state line expl
+  local -A opt_args
+
+  _arguments \
+    '(Cmode)-f[fix non-existing filenames]' \
+    '(Cmode)-r[remove leading indentation from filenames]' \
+    - argmode \
+      '-c[use current message]:message:_mblaze_message' \
+      '*:messages:_mblaze_message' \
+    - Smode \
+      '-S[set message sequence from stdin]' \
+    - Amode \
+      '-A[append message sequence from stdin]' \
+    - Cmode \
+      '-C[set current message]:message:_mblaze_message'
+}
+
+_mshow() {
+  local context state line expl
+  local -A opt_args
+
+  _arguments \
+    - regular \
+      '-n[only print message numbers]' \
+      '-h[display only given headers]:header list:_mblaze_colon_separated_headers' \
+      '-A[mixed/alternative preference]:mime type: ' \
+      '-n[don'\''t update current message link]' \
+      '(-r)-q[print only header]' \
+      '(-q)-r[print body raw]' \
+      '(-q)-F[don'\''t apply MIME filters]' \
+      '-H[print headers raw]' \
+      '-L[don'\''t filter headers]' \
+      '(-q)-N[don'\''t show MIME structure markers]' \
+      '*:messages:_mblaze_message' \
+    - xmode \
+      '-x[extraction mode]:message:_mblaze_message:*:part:_mblaze_message_part' \
+    - Omode \
+      '-O[extraction mode to standard output]:message:_mblaze_message:*:part:_mblaze_message_part' \
+    - tmode \
+      '-t[list mode]:*:message:_mblaze_message' \
+    - Rmode \
+      '-R[render text parts]:message:_mblaze_message'
+}
+
+_msort() {
+  local context state line expl
+  local -A opt_args
+
+  _arguments \
+    '-r[reverse order]' \
+    '*:message:_mblaze_message' \
+    + '(order)' \
+      '-f[sort by From]' \
+      '-d[sort by Date]' \
+      '-s[sort by Subject]' \
+      '-F[sort by filename]' \
+      '-M[sort by file modification time]' \
+      '-S[sort by file size]' \
+      '-U[sort unread messages after read messages]' \
+      '-I[sort flagged messages before unflagged messages]'
+}
+
+_mthread() {
+  local context state line expl
+  local -A opt_args
+
+  _arguments \
+    '-a[don'\''t prune unresolved Message-IDs]' \
+    '-S[treat as optional messages]:optional message:_mblaze_message' \
+    '*:message:_mblaze_message'
+}
+
+_mblaze() {
+  local ret=1
+  _call_function ret _$service
+  return ret
+}
+
+_mblaze "$@"
diff -Nru mblaze-0.4/contrib/mencrypt mblaze-0.5.1/contrib/mencrypt
--- mblaze-0.4/contrib/mencrypt	2018-08-15 17:16:45.000000000 +0200
+++ mblaze-0.5.1/contrib/mencrypt	2019-03-03 17:12:56.000000000 +0100
@@ -5,17 +5,20 @@
 
 IFS='
 '
-FLAGS=$(mhdr -d -A -h from:to:cc:bcc: "$1" |sort -u |sed 's/^/--recipient=/')
+FLAGS=$(maddr -a -h from:to:cc:bcc: "$1" |sort -u |sed 's/^/--recipient=/')
+
+FROM=$(maddr -a -h from "$1" | sed 1q)
+[ "$FROM" ] && key="--default-key=$FROM"
 
 TMPD=$(mktemp -d -t mencrypt.XXXXXX)
 trap "rm -rf '$TMPD'" INT TERM EXIT
 
 awk '/^$/,0' "$1" |
 	mmime |
-	gpg --armor --encrypt --sign $FLAGS -o $TMPD/msg.asc ||
+	gpg2 "$key" --armor --encrypt --sign $FLAGS -o "$TMPD/msg.asc" ||
 	exit $?
 
-printf 'Version: 1\n' >$TMPD/version
+printf 'Version: 1\n' >"$TMPD/version"
 
 {
 	sed '/^$/q' "$1"
diff -Nru mblaze-0.4/contrib/menter mblaze-0.5.1/contrib/menter
--- mblaze-0.4/contrib/menter	2018-08-15 17:16:45.000000000 +0200
+++ mblaze-0.5.1/contrib/menter	2019-03-03 17:12:56.000000000 +0100
@@ -1,14 +1,17 @@
 #!/bin/sh -e
 # menter [MSG] - run subshell in temporary directory with MSG extracted
 
-[ "$#" -eq 0 ] && set -- :
+[ "$#" -eq 0 ] && set -- .
+
+f="$(mseq "$1" | sed 1q)"
+[ -z "$f" ] && printf 'No message %s.\n' "$1" 1>&2 && exit 1
 
 dir=$(mktemp -d -t menter.XXXXXX)
 cd "$dir"
 mshow -t "$1"
 mshow -x "$1" 2>/dev/null
 ls -l
-ln -s "$(mseq "$1")" msg
+ln -s "$f" msg
 "${SHELL:-/bin/sh}" || true
 echo rm -r "$dir"
 rm -r "$dir"
diff -Nru mblaze-0.4/contrib/mgpg mblaze-0.5.1/contrib/mgpg
--- mblaze-0.4/contrib/mgpg	2018-08-15 17:16:45.000000000 +0200
+++ mblaze-0.5.1/contrib/mgpg	2019-03-03 17:12:56.000000000 +0100
@@ -14,7 +14,7 @@
 	/: application\/octet-stream/ {if (supported) print $1}')
 
 if [ "$n" ]; then
-	mshow -O "$tmp" "$n" | gpg -d 2>&1
+	mshow -O "$tmp" "$n" | gpg2 -d 2>&1 || exit 0
 	exit 64
 fi
 exit 63
diff -Nru mblaze-0.4/contrib/mmairix mblaze-0.5.1/contrib/mmairix
--- mblaze-0.4/contrib/mmairix	2018-08-15 17:16:45.000000000 +0200
+++ mblaze-0.5.1/contrib/mmairix	2019-03-03 17:12:56.000000000 +0100
@@ -1,4 +1,4 @@
 #!/bin/sh
 # mmairix QUERY - mblaze wrapper around mairix
 
-mairix -r "$@" | sed 's,//*,/,g' | mless
+mairix -r "$@" | sed 's,//*,/,g' | msort -rd | mless
diff -Nru mblaze-0.4/contrib/msearch mblaze-0.5.1/contrib/msearch
--- mblaze-0.4/contrib/msearch	1970-01-01 01:00:00.000000000 +0100
+++ mblaze-0.5.1/contrib/msearch	2019-03-03 17:12:56.000000000 +0100
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+MBLAZE=${MBLAZE:-$HOME/.mblaze}
+engine=$(mhdr -h search-engine "$MBLAZE/profile")
+
+while getopts nmx opt; do
+    case $opt in
+	n)
+	    engine=notmuch
+	    ;;
+	m)
+	    engine=mu
+	    ;;
+	x)
+	    engine=mairix
+	    ;;
+	'?')
+	    printf "Usage: %s: [-n | -m | -x] query\n" "$0" 1>&2
+	    exit 1
+	    ;;
+    esac
+done
+shift $(($OPTIND - 1))
+
+[ -z "$engine" ] && engine=notmuch
+
+case $engine in
+    notmuch)
+	exec notmuch search --output=files "$@"
+	;;
+    mu)
+	exec mu find --fields l "$@"
+	;;
+    mairix)
+	if [ "$#" -eq 0 ]; then
+	   printf "Usage: %s -x query\n" "$0" 1>&2
+	   exit 1
+	fi
+        exec mairix -r "$@"
+	;;
+    *)
+	echo "Unsupported search engine: $engine"
+	exit 1
+	;;
+esac
diff -Nru mblaze-0.4/contrib/msearch.1 mblaze-0.5.1/contrib/msearch.1
--- mblaze-0.4/contrib/msearch.1	1970-01-01 01:00:00.000000000 +0100
+++ mblaze-0.5.1/contrib/msearch.1	2019-03-03 17:12:56.000000000 +0100
@@ -0,0 +1,75 @@
+.Dd September 27, 2018
+.Dt MSEARCH 1
+.Os
+.Sh NAME
+.Nm msearch
+.Nd search messages
+.Sh SYNOPSIS
+.Nm
+.Op Fl n | Fl m | Fl x
+.Op Fl -
+.Op Ar query
+.Sh DESCRIPTION
+.Nm
+searches for messages matching
+.Ar query ,
+using an external search engine, and prints file names separated by newlines.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl n
+Search using
+.Xr notmuch-search 1 .
+.It Fl m
+Search using
+.Xr mu-find 1 .
+.It Fl x
+Search using
+.Xr mairix 1 .
+.It Fl -
+Stop scanning for arguments.
+.It Ar query
+The search query, whose interpretation is up to the search engine.
+This can also contain additional arguments for them
+.El
+.Pp
+If no search engine is specified as an argument,
+.Sq Li Search-Engine\&:
+from
+.Pa "${MBLAZE:-$HOME/.mblaze}/profile"
+is used.
+Valid values for this setting are
+.Sq notmuch ,
+.Sq mairix ,
+and
+.Sq mu .
+If that is unset,
+.Xr notmuch-search 1
+is used.
+.Sh ENVIRONMENT
+.Bl -tag -width Ds
+.It Ev MBLAZE
+Directory containing mblaze configuration files.
+(Default:
+.Pa $HOME/.mblaze )
+.El
+.Sh EXIT STATUS
+.Ex -std
+Note that the called programs may have their own exit values.
+.Sh SEE ALSO
+.Xr mairix 1 ,
+.Xr mu-find 1 ,
+.Xr notmuch-search 1 ,
+.Xr mblaze-profile 5
+.Sh AUTHORS
+.An Thomas Schneider Aq Mt qsx@chaotikum.eu
+.Sh LICENSE
+.Nm
+is in the public domain.
+.Pp
+To the extent possible under law,
+the creator of this work
+has waived all copyright and related or
+neighboring rights to this work.
+.Pp
+.Lk http://creativecommons.org/publicdomain/zero/1.0/
diff -Nru mblaze-0.4/contrib/msign mblaze-0.5.1/contrib/msign
--- mblaze-0.4/contrib/msign	2018-08-15 17:16:45.000000000 +0200
+++ mblaze-0.5.1/contrib/msign	2019-03-03 17:12:56.000000000 +0100
@@ -9,8 +9,11 @@
 TMPD=$(mktemp -d -t msign.XXXXXX)
 trap "rm -rf '$TMPD'" INT TERM EXIT
 
+FROM=$(maddr -a -h from "$1" | sed 1q)
+[ "$FROM" ] && key="--default-key=$FROM"
+
 awk '/^$/,0' "$1" | mmime | sed 's/$/
/' >"$TMPD"/content
-gpg --armor --detach-sign -o "$TMPD"/signature.asc "$TMPD"/content ||
+gpg2 $key --armor --detach-sign -o "$TMPD"/signature.asc "$TMPD"/content ||
 	exit $?
 
 {
diff -Nru mblaze-0.4/contrib/mverify mblaze-0.5.1/contrib/mverify
--- mblaze-0.4/contrib/mverify	2018-08-15 17:16:45.000000000 +0200
+++ mblaze-0.5.1/contrib/mverify	2019-03-03 17:12:56.000000000 +0100
@@ -1,7 +1,7 @@
 #!/bin/sh
 # mverify MSG - verify a OpenPGP or SMIME message
 
-# Needs gpg (for OpenPGP) and openssl (for SMIME).
+# Needs gpg2 (for OpenPGP) and openssl (for SMIME).
 
 [ "$#" -eq 0 ] && set -- .
 
@@ -14,7 +14,7 @@
 function q(a) { gsub("\\47", "\47\\\47\47", a); return "\47"a"\47" }
 END {
 	if (type == "" && plain) {  // guess plain text armored signature
-		exit(system("mshow -r " q(msg) " | gpg --verify"));
+		exit(system("mshow -r " q(msg) " | gpg2 --verify"));
 	} else if (type == "") {
 		print("No signature found.")
 		exit(100)
@@ -22,7 +22,7 @@
 		exit(system("mshow -r -O " q(msg) " " q(content) \
 			" | sed $DOS2UNIX | " \
 			" { mshow -O " q(msg) " " q(signature) \
-			" | gpg --verify - /dev/fd/3; } 3<&0"))
+			" | gpg2 --verify - /dev/fd/3; } 3<&0"))
 	} else if (type == "application/pkcs7-signature") {
 		exit(system("mshow -r -O " q(msg) " " q(signed) \
 			" | openssl smime -verify"))
diff -Nru mblaze-0.4/debian/changelog mblaze-0.5.1/debian/changelog
--- mblaze-0.4/debian/changelog	2018-08-19 12:22:49.000000000 +0200
+++ mblaze-0.5.1/debian/changelog	2019-03-04 18:40:01.000000000 +0100
@@ -1,3 +1,37 @@
+mblaze (0.5.1-1) unstable; urgency=high (security fixes)
+
+  * New upstream release (2019-03-03)
+    + Fixes for buffer-overflows, found by fuzzing.
+    + Fixes for memleaks.
+
+  * Remove unused lintian override
+  * debian/rules: Remove useless dh_missing invocation.
+    A single binary package is being produced.
+
+ -- Nicolas Braud-Santoni <nicoo@debian.org>  Mon, 04 Mar 2019 18:40:01 +0100
+
+mblaze (0.5-1) unstable; urgency=medium
+
+  * New upstream release (2019-02-09)
+    + Closes: 921891
+    + GNUmakefile: correct reference to SOURCE_DATE_EPOCH, patch by Chris Lamb
+      Closes: #907537 
+
+  * Rename mless and msort to mblaze-less and mblaze-sort
+    Closes: #887988 correctly, avoids conflicts on mpdtoys & msort
+
+  * Switch to debhelper 12.
+    The compatibility level is now controlled by a Build-Depends.
+
+  * Declare compliance with policy v4.2.1.
+    No change required.
+
+  * debian/rules
+    + Make dh_missing fail the build
+    + Symlink docs from mblaze-* to mblaze
+
+ -- Nicolas Braud-Santoni <nicoo@debian.org>  Tue, 12 Feb 2019 11:44:33 +0100
+
 mblaze (0.4-1) unstable; urgency=medium
 
   * New upstream version (2018-08-15)
diff -Nru mblaze-0.4/debian/compat mblaze-0.5.1/debian/compat
--- mblaze-0.4/debian/compat	2018-02-13 12:47:54.000000000 +0100
+++ mblaze-0.5.1/debian/compat	1970-01-01 01:00:00.000000000 +0100
@@ -1 +0,0 @@
-11
diff -Nru mblaze-0.4/debian/control mblaze-0.5.1/debian/control
--- mblaze-0.4/debian/control	2018-08-19 12:22:49.000000000 +0200
+++ mblaze-0.5.1/debian/control	2019-03-04 18:40:01.000000000 +0100
@@ -1,17 +1,17 @@
 Source: mblaze
-Maintainer: Nicolas Braud-Santoni <nicolas@braud-santoni.eu>
+Maintainer: Nicolas Braud-Santoni <nicoo@debian.org>
 Section: utils
 Priority: optional
-Build-Depends: debhelper (>= 11)
-Standards-Version: 4.2.0
+Build-Depends: debhelper-compat (= 12)
+Standards-Version: 4.3.0
 Homepage: https://github.com/chneukirchen/mblaze
 Vcs-Browser: https://salsa.debian.org/debian/mblaze
 Vcs-Git: https://salsa.debian.org/debian/mblaze.git
 
+
 Package: mblaze
 Architecture: any
 Depends: ${shlibs:Depends}, ${misc:Depends}
-Conflicts: msort, mpdtoys
 Description: UNIX utilities to deal with Maildir
  The mblaze message system is a set of Unix utilities to deal with mail kept in
  Maildir folders. It is a classic command line MUA and has no features for
diff -Nru mblaze-0.4/debian/mblaze.lintian-overrides mblaze-0.5.1/debian/mblaze.lintian-overrides
--- mblaze-0.4/debian/mblaze.lintian-overrides	2018-02-13 12:47:54.000000000 +0100
+++ mblaze-0.5.1/debian/mblaze.lintian-overrides	1970-01-01 01:00:00.000000000 +0100
@@ -1,2 +0,0 @@
-# Ignore false-negative in hardening-check
-mblaze: hardening-no-fortify-functions usr/bin/mdirs
diff -Nru mblaze-0.4/debian/patches/Rename-mless-to-mblaze-less.patch mblaze-0.5.1/debian/patches/Rename-mless-to-mblaze-less.patch
--- mblaze-0.4/debian/patches/Rename-mless-to-mblaze-less.patch	1970-01-01 01:00:00.000000000 +0100
+++ mblaze-0.5.1/debian/patches/Rename-mless-to-mblaze-less.patch	2019-03-04 18:40:01.000000000 +0100
@@ -0,0 +1,42 @@
+Subject: Rename mless to mblaze-less
+
+---
+ contrib/_mblaze | 4 ++--
+ contrib/mmairix | 2 +-
+ 2 files changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/contrib/_mblaze b/contrib/_mblaze
+index f58cb65..41207e3 100644
+Origin: vendor
+Bug-Debian: https://bugs.debian.org/887988
+Forwarded: not-needed
+From: Nicolas Braud-Santoni <nicoo@debian.org>
+Last-Update: 2019-02-23
+
+--- a/contrib/_mblaze
++++ b/contrib/_mblaze
+@@ -1,4 +1,4 @@
+-#compdef maddr magrep mbnc mcom mdeliver mdirs mexport mflag mflow mfwd mgenmid mhdr minc mless mlist mmime mmkdir mpick mrep mscan msed mseq mshow msort mthread
++#compdef maddr magrep mbnc mcom mdeliver mdirs mexport mflag mflow mfwd mgenmid mhdr minc mblaze-less mlist mmime mmkdir mpick mrep mscan msed mseq mshow msort mthread
+ 
+ _mblaze_colon_separated_headers() {
+   _message 'headers (colon separated)'
+@@ -229,7 +229,7 @@ _minc() {
+     '*:dirs:_mblaze_dirs'
+ }
+ 
+-_mless() {
++_mblaze-less() {
+   local context state line expl
+   local -A opt_args
+ 
+diff --git a/contrib/mmairix b/contrib/mmairix
+index e4c9844..810bb59 100755
+--- a/contrib/mmairix
++++ b/contrib/mmairix
+@@ -1,4 +1,4 @@
+ #!/bin/sh
+ # mmairix QUERY - mblaze wrapper around mairix
+ 
+-mairix -r "$@" | sed 's,//*,/,g' | msort -rd | mless
++mairix -r "$@" | sed 's,//*,/,g' | msort -rd | mblaze-less
diff -Nru mblaze-0.4/debian/patches/Rename-msort-to-mblaze-sort.patch mblaze-0.5.1/debian/patches/Rename-msort-to-mblaze-sort.patch
--- mblaze-0.4/debian/patches/Rename-msort-to-mblaze-sort.patch	1970-01-01 01:00:00.000000000 +0100
+++ mblaze-0.5.1/debian/patches/Rename-msort-to-mblaze-sort.patch	2019-03-04 18:40:01.000000000 +0100
@@ -0,0 +1,56 @@
+Subject: Rename msort to mblaze-sort
+
+---
+ contrib/_mblaze | 4 ++--
+ contrib/mmairix | 2 +-
+ mcom            | 2 +-
+ 3 files changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/contrib/_mblaze b/contrib/_mblaze
+index 41207e3..3d11390 100644
+Origin: vendor
+Bug-Debian: https://bugs.debian.org/887988
+Forwarded: not-needed
+From: Nicolas Braud-Santoni <nicoo@debian.org>
+Last-Update: 2019-02-23
+
+--- a/contrib/_mblaze
++++ b/contrib/_mblaze
+@@ -1,4 +1,4 @@
+-#compdef maddr magrep mbnc mcom mdeliver mdirs mexport mflag mflow mfwd mgenmid mhdr minc mblaze-less mlist mmime mmkdir mpick mrep mscan msed mseq mshow msort mthread
++#compdef maddr magrep mbnc mcom mdeliver mdirs mexport mflag mflow mfwd mgenmid mhdr minc mblaze-less mlist mmime mmkdir mpick mrep mscan msed mseq mshow mblaze-sort mthread
+ 
+ _mblaze_colon_separated_headers() {
+   _message 'headers (colon separated)'
+@@ -365,7 +365,7 @@ _mshow() {
+       '-R[render text parts]:message:_mblaze_message'
+ }
+ 
+-_msort() {
++_mblaze-sort() {
+   local context state line expl
+   local -A opt_args
+ 
+diff --git a/contrib/mmairix b/contrib/mmairix
+index 810bb59..6caff93 100755
+--- a/contrib/mmairix
++++ b/contrib/mmairix
+@@ -1,4 +1,4 @@
+ #!/bin/sh
+ # mmairix QUERY - mblaze wrapper around mairix
+ 
+-mairix -r "$@" | sed 's,//*,/,g' | msort -rd | mblaze-less
++mairix -r "$@" | sed 's,//*,/,g' | mblaze-sort -rd | mblaze-less
+diff --git a/mcom b/mcom
+index 91498c9..73c5fbb 100755
+--- a/mcom
++++ b/mcom
+@@ -245,7 +245,7 @@ else
+ 			exit 1
+ 		fi
+ 	elif [ -z "$draft" ]; then
+-		draft=$(mlist -D "$outbox" | msort -r -M | sed 1q)
++		draft=$(mlist -D "$outbox" | mblaze-sort -r -M | sed 1q)
+ 	fi
+ 	draftmime="$(printf '%s\n' "$draft" | sed 's,\(.*\)/cur/,\1/tmp/mime-,')"
+ fi
diff -Nru mblaze-0.4/debian/patches/series mblaze-0.5.1/debian/patches/series
--- mblaze-0.4/debian/patches/series	1970-01-01 01:00:00.000000000 +0100
+++ mblaze-0.5.1/debian/patches/series	2019-03-04 18:40:01.000000000 +0100
@@ -0,0 +1,2 @@
+Rename-mless-to-mblaze-less.patch
+Rename-msort-to-mblaze-sort.patch
diff -Nru mblaze-0.4/debian/README.Debian mblaze-0.5.1/debian/README.Debian
--- mblaze-0.4/debian/README.Debian	1970-01-01 01:00:00.000000000 +0100
+++ mblaze-0.5.1/debian/README.Debian	2019-03-04 18:40:01.000000000 +0100
@@ -0,0 +1,4 @@
+Executables mless and msort got renamed to mblaze-less and mblaze-sort, due to
+conflicts with packages mpdtoys and msort.
+
+ -- Nicolas Braud-Santoni <nicoo@debian.org>, Sat, 23 Feb 2019 20:09:22 +0100
diff -Nru mblaze-0.4/debian/rules mblaze-0.5.1/debian/rules
--- mblaze-0.4/debian/rules	2018-08-19 12:22:49.000000000 +0200
+++ mblaze-0.5.1/debian/rules	2019-03-04 18:40:01.000000000 +0100
@@ -11,5 +11,15 @@
 override_dh_auto_test:
 	: # Testsuite is currently broken under sbuild
 
+override_dh_install:
+	mv debian/mblaze/usr/bin/mless              debian/mblaze/usr/bin/mblaze-less
+	mv debian/mblaze/usr/share/man/man1/mless.1 debian/mblaze/usr/share/man/man1/mblaze-less.1
+	mv debian/mblaze/usr/bin/msort              debian/mblaze/usr/bin/mblaze-sort
+	mv debian/mblaze/usr/share/man/man1/msort.1 debian/mblaze/usr/share/man/man1/mblaze-sort.1
+	dh_install
+
 override_dh_installchangelogs:
 	dh_installchangelogs NEWS.md
+
+override_dh_installdocs:
+	dh_installdocs --link-doc=mblaze
diff -Nru mblaze-0.4/GNUmakefile mblaze-0.5.1/GNUmakefile
--- mblaze-0.4/GNUmakefile	2018-08-15 17:16:45.000000000 +0200
+++ mblaze-0.5.1/GNUmakefile	2019-03-03 17:12:56.000000000 +0100
@@ -28,17 +28,16 @@
 maddr magrep mdeliver mexport mflag mflow mgenmid mhdr mpick mscan msed mshow \
   msort mthread : blaze822.o mymemmem.o mytimegm.o
 maddr magrep mdeliver mexport mflag mgenmid mhdr mlist mpick mscan msed mseq \
-  mshow msort mthread : seq.o slurp.o
+  mshow msort mthread : seq.o slurp.o mystrverscmp.o
 maddr magrep mflow mhdr mpick mscan mshow : rfc2047.o
 magrep mflow mhdr mshow : rfc2045.o
 mshow : filter.o safe_u8putstr.o rfc2231.o pipeto.o
 mscan : pipeto.o
-msort : mystrverscmp.o
 mmime : slurp.o
 minc mlist : squeeze_slash.o
 
 museragent: FRC
-	@test -n "$$SOURCE_TIME_EPOCH" || BUILDDATE=$$(date '+ (%Y-%m-%d)'); \
+	@test -n "$$SOURCE_DATE_EPOCH" || BUILDDATE=$$(date '+ (%Y-%m-%d)'); \
 		printf '#!/bin/sh\nprintf "User-Agent: mblaze/%s%s\\n"\n' \
 		"$$({ git describe --always --dirty 2>/dev/null || cat VERSION; } | sed 's/^v//')" \
 		"$$BUILDDATE" >$@
@@ -59,8 +58,6 @@
 		$(DESTDIR)$(MANDIR)/man5 \
 		$(DESTDIR)$(MANDIR)/man7
 	install -m0755 $(ALL) $(SCRIPT) $(DESTDIR)$(BINDIR)
-	ln -sf mless $(DESTDIR)$(BINDIR)/mnext
-	ln -sf mless $(DESTDIR)$(BINDIR)/mprev
 	ln -sf mcom $(DESTDIR)$(BINDIR)/mbnc
 	ln -sf mcom $(DESTDIR)$(BINDIR)/mfwd
 	ln -sf mcom $(DESTDIR)$(BINDIR)/mrep
diff -Nru mblaze-0.4/magrep.c mblaze-0.5.1/magrep.c
--- mblaze-0.4/magrep.c	2018-08-15 17:16:45.000000000 +0200
+++ mblaze-0.5.1/magrep.c	2019-03-03 17:12:56.000000000 +0100
@@ -14,6 +14,7 @@
 static int aflag;
 static int cflag;
 static int dflag;
+static int hflag;
 static int iflag;
 static int lflag;
 static int oflag;
@@ -31,7 +32,7 @@
 match(char *file, char *hdr, char *s)
 {
 	if (oflag && !cflag && !qflag && !vflag && !lflag) {
-		regmatch_t pmatch;
+		regmatch_t pmatch = {0};
 		int len, matched;
 		matched = 0;
 		while (*s && regexec(&pattern, s, 1, &pmatch, 0) == 0) {
@@ -42,6 +43,8 @@
 			}
 			if (pflag)
 				printf("%s: %s: ", file, hdr);
+			else if (hflag)
+				printf("%s: ", hdr);
 			printf("%.*s\n", len, s);
 			s += len;
 			matched++;
@@ -52,9 +55,12 @@
 			exit(0);
 		matches++;
 		if (!cflag) {
-			printf("%s", file);
+			if (vflag || !hflag)
+				printf("%s", file);
 			if (pflag && !vflag)
 				printf(": %s: %s", hdr, s);
+			else if (hflag && !vflag)
+				printf("%s: %s", hdr, s);
 			putchar('\n');
 		}
 		if (mflag && matches >= mflag)
@@ -82,7 +88,7 @@
 		    strcasecmp(charset, "utf-8") == 0 ||
 		    strcasecmp(charset, "utf8") == 0 ||
 		    strcasecmp(charset, "us-ascii") == 0) {
-			if (pflag && !cflag && !oflag && !vflag) {
+			if ((hflag || pflag) && !cflag && !oflag && !vflag) {
 				char *s, *p;
 				for (p = s = body; p < body+bodylen+1; p++) {
 					if (*p == '\r' || *p == '\n') {
@@ -117,6 +123,7 @@
 		return;
 
 	blaze822_walk_mime(msg, 0, match_part);
+	blaze822_free(msg);
 }
 
 int
@@ -183,11 +190,12 @@
 main(int argc, char *argv[])
 {
 	int c;
-	while ((c = getopt(argc, argv, "acdilm:opqv")) != -1)
+	while ((c = getopt(argc, argv, "acdhilm:opqv")) != -1)
 		switch (c) {
 		case 'a': aflag = 1; break;
 		case 'c': cflag = 1; break;
 		case 'd': dflag = 1; break;
+		case 'h': hflag = 1; break;
 		case 'i': iflag = REG_ICASE; break;
 		case 'l': lflag = 1; break;
 		case 'm': mflag = atol(optarg); break;
@@ -198,7 +206,7 @@
 		default:
 usage:
 			fprintf(stderr,
-"Usage: magrep [-c|-o|-p|-q|-m max] [-v] [-i] [-l] [-a|-d] header:regex [msgs...]\n");
+"Usage: magrep [-c|-h|-o|-p|-q|-m max] [-v] [-i] [-l] [-a|-d] header:regex [msgs...]\n");
 			exit(2);
 		}
 
diff -Nru mblaze-0.4/man/magrep.1 mblaze-0.5.1/man/magrep.1
--- mblaze-0.4/man/magrep.1	2018-08-15 17:16:45.000000000 +0200
+++ mblaze-0.5.1/man/magrep.1	2019-03-03 17:12:56.000000000 +0100
@@ -1,4 +1,4 @@
-.Dd July 7, 2018
+.Dd September 10, 2018
 .Dt MAGREP 1
 .Os
 .Sh NAME
@@ -6,7 +6,7 @@
 .Nd search messages matching a pattern
 .Sh SYNOPSIS
 .Nm
-.Op Fl c | Fl q | Fl m Ar max
+.Op Fl c | Fl h | Fl o | Fl p | Fl q | Fl m Ar max
 .Op Fl v
 .Op Fl i
 .Op Fl l
@@ -75,6 +75,10 @@
 Decode the
 .Ar header
 according to RFC 2047 prior to searching.
+.It Fl h
+Like
+.Fl p
+but do not print the file name.
 .It Fl i
 Match
 .Ar regex
diff -Nru mblaze-0.4/man/mblaze.7 mblaze-0.5.1/man/mblaze.7
--- mblaze-0.4/man/mblaze.7	2018-08-15 17:16:45.000000000 +0200
+++ mblaze-0.5.1/man/mblaze.7	2019-03-03 17:12:56.000000000 +0100
@@ -32,7 +32,7 @@
 .It Xr mexport 1
 export messages as mbox file
 .It Xr mflag 1
-manipulate maildir flags
+manipulate maildir message flags
 .It Xr mflow 1
 reflow format=flowed plain text messages
 .It Xr mfwd 1
diff -Nru mblaze-0.4/man/mcom.1 mblaze-0.5.1/man/mcom.1
--- mblaze-0.4/man/mcom.1	2018-08-15 17:16:45.000000000 +0200
+++ mblaze-0.5.1/man/mcom.1	2019-03-03 17:12:56.000000000 +0100
@@ -1,4 +1,4 @@
-.Dd January 6, 2017
+.Dd January 29, 2019
 .Dt MCOM 1
 .Os
 .Sh NAME
@@ -14,6 +14,7 @@
 .Nm mcom
 .Fl r Op draft
 .Nm mrep
+.Op Fl noquote
 .Op Fl Ar header Ar values\ ... Fl -
 .Ar msg
 .Nm mfwd
@@ -36,6 +37,9 @@
 .Nm mrep
 creates the draft such that the message will be a reply to
 .Ar msg .
+Unless
+.Fl noquote
+is passed, it will contain the original mail quoted.
 .Pp
 .Nm mfwd
 creates the draft with a subject and body appropriate
@@ -93,6 +97,12 @@
 .Nm mfwd ,
 or
 .Nm mbnc .
+.Pp
+If the flag
+.Fl send
+is passed,
+the message will be sent directly using the data on
+the command line, and no editor or edit loop will be run.
 .Sh MENU COMMANDS
 .Bl -tag -width 2n
 .It Ic s
diff -Nru mblaze-0.4/man/mdirs.1 mblaze-0.5.1/man/mdirs.1
--- mblaze-0.4/man/mdirs.1	2018-08-15 17:16:45.000000000 +0200
+++ mblaze-0.5.1/man/mdirs.1	2019-03-03 17:12:56.000000000 +0100
@@ -6,6 +6,7 @@
 .Nd list maildir folders, recursively
 .Sh SYNOPSIS
 .Nm
+.Op Fl 0
 .Ar dirs\ ...
 .Sh DESCRIPTION
 .Nm
@@ -27,7 +28,11 @@
 names must begin with
 .Sq Li \&. .
 .Pp
-There are currently no options.
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl 0
+Print folders separated by a NUL character.
+.El
 .Sh EXIT STATUS
 .Ex -std
 .Sh SEE ALSO
diff -Nru mblaze-0.4/man/mflag.1 mblaze-0.5.1/man/mflag.1
--- mblaze-0.4/man/mflag.1	2018-08-15 17:16:45.000000000 +0200
+++ mblaze-0.5.1/man/mflag.1	2019-03-03 17:12:56.000000000 +0100
@@ -3,7 +3,7 @@
 .Os
 .Sh NAME
 .Nm mflag
-.Nd manipulate maildir flags
+.Nd manipulate maildir message flags
 .Sh SYNOPSIS
 .Nm
 .Op Fl DFPRST
diff -Nru mblaze-0.4/man/mgenmid.1 mblaze-0.5.1/man/mgenmid.1
--- mblaze-0.4/man/mgenmid.1	2018-08-15 17:16:45.000000000 +0200
+++ mblaze-0.5.1/man/mgenmid.1	2019-03-03 17:12:56.000000000 +0100
@@ -1,4 +1,4 @@
-.Dd August 1, 2016
+.Dd December 25, 2018
 .Dt MGENMID 1
 .Os
 .Sh NAME
@@ -9,7 +9,7 @@
 .Sh DESCRIPTION
 .Nm
 generates and prints a unique Message-ID.
-The Message-ID consists of an encoded timestamp,
+The Message-ID consists of an encrypted timestamp,
 a random value,
 and a fully qualified domain name.
 .Pp
diff -Nru mblaze-0.4/man/mpick.1 mblaze-0.5.1/man/mpick.1
--- mblaze-0.4/man/mpick.1	2018-08-15 17:16:45.000000000 +0200
+++ mblaze-0.5.1/man/mpick.1	2019-03-03 17:12:56.000000000 +0100
@@ -7,6 +7,7 @@
 .Sh SYNOPSIS
 .Nm
 .Op Fl T
+.Op Fl v
 .Op Fl t Ar test
 .Op Ar msglist\ ...
 .Sh DESCRIPTION
@@ -29,6 +30,8 @@
 .Ar test ,
 see
 .Sx TESTS .
+.It Fl v
+Print how many messages were tested and picked to standard error.
 .El
 .Sh MSGLISTS
 .Nm
diff -Nru mblaze-0.4/man/mscan.1 mblaze-0.5.1/man/mscan.1
--- mblaze-0.4/man/mscan.1	2018-08-15 17:16:45.000000000 +0200
+++ mblaze-0.5.1/man/mscan.1	2019-03-03 17:12:56.000000000 +0100
@@ -1,4 +1,4 @@
-.Dd June 28, 2017
+.Dd September 25, 2018
 .Dt MSCAN 1
 .Os
 .Sh NAME
@@ -6,9 +6,10 @@
 .Nd generate one-line message summaries
 .Sh SYNOPSIS
 .Nm
+.Op Fl I
 .Op Fl n
+.Op Fl v
 .Op Fl f Ar format
-.Op Fl I
 .Ar msgs\ ...
 .Sh DESCRIPTION
 .Nm
@@ -55,13 +56,15 @@
 .Pp
 The options are as follows:
 .Bl -tag -width Ds
-.It Fl n
-Only print message numbers
-.Pq or filenames, if the message is not in the current sequence .
 .It Fl I
 Force ISO date output,
 even for
 .Sq Cm "%d" .
+.It Fl n
+Only print message numbers
+.Pq or filenames, if the message is not in the current sequence .
+.It Fl v
+Print how many messages were scanned to standard error.
 .It Fl f Ar format
 Format according to the string
 .Ar format ,
@@ -186,7 +189,7 @@
 .Sq Li To\&:
 .It Li \&+
 You are in
-.Sq Li Cc\:&
+.Sq Li Cc\&:
 .It Li \&:
 You are in
 .Sq Li Resent\&-To\&:
diff -Nru mblaze-0.4/man/msed.1 mblaze-0.5.1/man/msed.1
--- mblaze-0.4/man/msed.1	2018-08-15 17:16:45.000000000 +0200
+++ mblaze-0.5.1/man/msed.1	2019-03-03 17:12:56.000000000 +0100
@@ -123,6 +123,7 @@
 .Ex -std
 .Sh SEE ALSO
 .Xr sed 1 ,
+.Xr mhdr 1 ,
 .Xr mmsg 7 ,
 .Xr regex 7 /
 .Xr re_format 7
diff -Nru mblaze-0.4/man/mshow.1 mblaze-0.5.1/man/mshow.1
--- mblaze-0.4/man/mshow.1	2018-08-15 17:16:45.000000000 +0200
+++ mblaze-0.5.1/man/mshow.1	2019-03-03 17:12:56.000000000 +0100
@@ -1,4 +1,4 @@
-.Dd April 17, 2018
+.Dd August 24, 2018
 .Dt MSHOW 1
 .Os
 .Sh NAME
@@ -91,6 +91,9 @@
 can be specified by number, filename or
 .Xr fnmatch 3
 pattern.
+If no
+.Ar parts
+are specified, extracts all attachments with a filename.
 .It Fl O Ar msg
 Like
 .Fl x
@@ -121,7 +124,11 @@
 and re-encodes them into UTF-8 if necessary.
 .Pp
 Other filters can be specified in the file
-.Pa ${MBLAZE:-$HOME/.mblaze}/filter ,
+.Pa ${MBLAZE:-$HOME/.mblaze}/filter
+.Po
+or via
+.Ev MAILFILTER
+.Pc ,
 in the format:
 .Pp
 .D1 Ar type/subtype Ns Li \&: Ar command
@@ -132,6 +139,7 @@
 will then spawn a pipe to
 .Ar command ,
 write the MIME part
+to standard input
 and display the output.
 The environment variable
 .Ev PIPE_CHARSET
@@ -158,6 +166,14 @@
 All other exit statuses are regarded as errors.
 .Sh ENVIRONMENT
 .Bl -tag -width MBLAZE_NOCOLOR
+.It Ev MAILFILTER
+Path to an alternative
+.Pa filter
+file.
+.It Ev MBLAZE_NOCOLOR
+If non-empty,
+.Nm
+will not spawn a colorization filter.
 .It Ev MBLAZE_PAGER
 Any non-empty value of the environment variable
 .Ev MBLAZE_PAGER
@@ -166,10 +182,6 @@
 When empty or set to
 .Sq Ic cat ,
 no pager is spawned.
-.It Ev MBLAZE_NOCOLOR
-If non-empty,
-.Nm
-will not spawn a colorization filter.
 .El
 .Sh EXIT STATUS
 .Ex -std
diff -Nru mblaze-0.4/mbnc mblaze-0.5.1/mbnc
--- mblaze-0.4/mbnc	2018-08-15 17:16:45.000000000 +0200
+++ mblaze-0.5.1/mbnc	2019-03-03 17:12:56.000000000 +0100
@@ -11,7 +11,7 @@
 }
 
 notmine() {
-	mine="$(maddr -a -h local-mailbox:alternate-mailboxes: $MBLAZE/profile)"
+	mine="$(maddr -a -h local-mailbox:alternate-mailboxes: "$MBLAZE/profile")"
 	grep -Fv -e "$mine"
 }
 
@@ -40,6 +40,27 @@
 	fi
 }
 
+checksensible() {
+	awk '
+		/^$/ {
+			seenheader=1
+			exit
+		}
+		!(/^[^ \t][^ \t]*[ \t]*:/ || /^[ \t]/) {
+			bad=1
+			print "invalid header line: "$0 >"/dev/stderr"
+		}
+		END {
+			if (!seenheader) {
+				print "warning: message does not contain an empty line between headers and body." >"/dev/stderr"
+				exit 1
+			}
+			if (bad)
+				exit 1
+		}
+' "$1"
+}
+
 stripempty() {
 	tmp=$(mktemp -t mcom.XXXXXX)
 	msed 's/^[ \t]*$//d' "$1" >"$tmp"
@@ -55,13 +76,13 @@
 	if needs_multipart "$draft"; then
 		(
 			IFS=$NL
-			msed '/attach/d' $draft
-			for f in $(mhdr -M -h attach $draft); do
+			msed '/attach/d' "$draft"
+			for f in $(mhdr -M -h attach "$draft"); do
 				printf '#%s %s\n' \
-				       "$(file -Lbi $f | sed 's/ //g')" \
+				       "$(file -Lbi "$f" | sed 's/ //g')" \
 				       "$f"
 			done
-		) | mmime >$draftmime
+		) | mmime >"$draftmime"
 	else
 		mmime -r <"$draft" >"$draftmime"
 	fi
@@ -74,8 +95,11 @@
 sendmail="${sendmail:-sendmail} ${sendmail_args:--t}"
 default_from=$(mhdr -h local-mailbox "$MBLAZE/profile")
 
+defaultc=e
+
 hdrs=
 resume=
+noquote=
 case "$0" in
 *mcom*)
 	hdr=to
@@ -90,11 +114,22 @@
 			shift
 			resume=1
 			if [ "$#" -gt 0 ]; then
-				echo "used draft $1"
-				draft="$1"
+				case "$1" in
+					/*|./*) draft="$1";;
+					*) draft="./$1";;
+				esac
+				if ! [ -f "$draft" ]; then
+					printf 'mcom: no such draft %s\n' \
+					       "$draft" 1>&2
+					exit 1
+				fi
+				echo "using draft $draft"
 				shift
 			fi
 			;;
+		-send)
+			defaultc=justsend
+			shift;;
 		-??*)
 			hdr=${1#-}
 			shift;;
@@ -118,6 +153,9 @@
 		-r)
 			shift
 			raw=1;;
+		-send)
+			defaultc=justsend
+			shift;;
 		-??*)
 			hdr=${1#-}
 			shift;;
@@ -139,6 +177,9 @@
 		--)
 			shift
 			break;;
+		-send)
+			defaultc=justsend
+			shift;;
 		-??*)
 			hdr=${1#-}
 			shift;;
@@ -160,6 +201,12 @@
 		--)
 			shift
 			break;;
+		-send)
+			defaultc=justsend
+			shift;;
+		-noquote)
+			noquote=1
+			shift;;
 		-??*)
 			hdr=${1#-}
 			shift;;
@@ -280,8 +327,8 @@
 		if [ "$ng" ]; then
 			printf 'Newsgroups: %s\n' "$ng"
 		else
-			to=$(mhdr -h reply-to "$1")
-			[ -z "$to" ] && to=$(mhdr -h from "$1")
+			to=$(mhdr -d -h reply-to "$1")
+			[ -z "$to" ] && to=$(mhdr -d -h from "$1")
 			printf 'To: %s\n' "$to"
 			printf 'Cc: %s\n' \
 			       "$(mhdr -d -A -h to:cc: "$1" |
@@ -308,8 +355,10 @@
 		cat "$MBLAZE/headers" 2>/dev/null
 		printf '\n'
 
-		mquote "$1"
-		printf '\n'
+		if [ -z "$noquote" ]; then
+			mquote "$1"
+			printf '\n'
+		fi
 		(
 			IFS=$NL
 			cat -- /dev/null $(printf '%s' "$hdrs" | mhdr -M -h body /dev/stdin)
@@ -332,10 +381,10 @@
 			cat "$SIGNATURE"
 		fi
 	esac
-} >$draft
+} >"$draft"
 
 automime=
-c=e
+c=$defaultc
 while :; do
 	case "$c" in
 	s|send)
@@ -352,15 +401,15 @@
 			;;
 		esac
 
-		if [ -e $draftmime ]; then
-			if [ $draft -ot $draftmime ] || [ "$automime" -eq 1 ]; then
-				stampdate $draftmime
-				if $sendmail <$draftmime; then
+		if [ -e "$draftmime" ]; then
+			if [ "$draft" -ot "$draftmime" ] || [ "$automime" = 1 ]; then
+				stampdate "$draftmime"
+				if $sendmail <"$draftmime"; then
 					if [ "$outbox" ]; then
-						mv $draftmime $draft
-						mflag -d -S $draft
+						mv "$draftmime" "$draft"
+						mflag -d -S "$draft"
 					else
-						rm $draft $draftmime
+						rm "$draft" "$draftmime"
 					fi
 				else
 					printf '%s\n' "mcom: $sendmail failed, kept draft $draft"
@@ -369,15 +418,16 @@
 			else
 				printf 'mcom: re-run mmime first.\n'
 				c=
+				continue
 			fi
 		else
-			if mmime -c <$draft; then
-				stampdate $draft
-				if $sendmail <$draft; then
+			if mmime -c <"$draft"; then
+				stampdate "$draft"
+				if $sendmail <"$draft"; then
 					if [ "$outbox" ]; then
-						mflag -d -S $draft
+						mflag -d -S "$draft"
 					else
-						rm $draft
+						rm "$draft"
 					fi
 				else
 					printf '%s\n' "mcom: $sendmail failed, kept draft $draft"
@@ -386,6 +436,7 @@
 			else
 				printf '%s\n' "mcom: message needs to be MIME-encoded first."
 				c=
+				continue
 			fi
 		fi
 
@@ -398,49 +449,64 @@
 		exit 0
 		;;
 	c|cancel)
-		stampdate $draft
+		stampdate "$draft"
 		printf '%s\n' "mcom: cancelled draft $draft"
 		exit 1
 		;;
 	m|mime)
 		do_mime
-		mshow -t $draftmime
+		mshow -t "$draftmime"
 		c=
 		;;
 	e|edit)
 		c=
-		if ! ${EDITOR:-vi} $draft; then
-			c=c
+		if ! ${EDITOR:-vi} "$draft"; then
+			c=d
+		else
+			if checksensible "$draft"; then
+				stripempty "$draft"
+				if mmime -c <"$draft" && ! needs_multipart "$draft"; then
+					automime=
+				else
+					automime=1
+					do_mime
+				fi
+			else
+				printf '\n'
+			fi
 		fi
-		stripempty $draft
-		if mmime -c <$draft && ! needs_multipart $draft; then
+		;;
+	justsend)
+		stripempty "$draft"
+		if mmime -c <"$draft" && ! needs_multipart "$draft"; then
 			automime=
 		else
 			automime=1
 			do_mime
 		fi
+		c=send
 		;;
 	d|delete)
-		rm -i $draft
-		if ! [ -f $draft ]; then
-			rm -f $draftmime
+		rm -i "$draft"
+		if ! [ -f "$draft" ]; then
+			rm -f "$draftmime"
 			printf '%s\n' "mcom: deleted draft $draft"
 			exit 0
 		fi
 		c=
 		;;
 	sign)
-		msign $draft >$draftmime
-		mshow -t $draftmime
+		msign "$draft" >"$draftmime"
+		mshow -t "$draftmime"
 		c=
 		;;
 	encrypt)
-		mencrypt $draft >$draftmime
-		mshow -t $draftmime
+		mencrypt "$draft" >"$draftmime"
+		mshow -t "$draftmime"
 		c=
 		;;
 	show)
-		if [ -e $draftmime ]; then
+		if [ -e "$draftmime" ]; then
 			mshow "$draftmime"
 		else
 			mshow "$draft"
diff -Nru mblaze-0.4/mcom mblaze-0.5.1/mcom
--- mblaze-0.4/mcom	2018-08-15 17:16:45.000000000 +0200
+++ mblaze-0.5.1/mcom	2019-03-03 17:12:56.000000000 +0100
@@ -11,7 +11,7 @@
 }
 
 notmine() {
-	mine="$(maddr -a -h local-mailbox:alternate-mailboxes: $MBLAZE/profile)"
+	mine="$(maddr -a -h local-mailbox:alternate-mailboxes: "$MBLAZE/profile")"
 	grep -Fv -e "$mine"
 }
 
@@ -40,6 +40,27 @@
 	fi
 }
 
+checksensible() {
+	awk '
+		/^$/ {
+			seenheader=1
+			exit
+		}
+		!(/^[^ \t][^ \t]*[ \t]*:/ || /^[ \t]/) {
+			bad=1
+			print "invalid header line: "$0 >"/dev/stderr"
+		}
+		END {
+			if (!seenheader) {
+				print "warning: message does not contain an empty line between headers and body." >"/dev/stderr"
+				exit 1
+			}
+			if (bad)
+				exit 1
+		}
+' "$1"
+}
+
 stripempty() {
 	tmp=$(mktemp -t mcom.XXXXXX)
 	msed 's/^[ \t]*$//d' "$1" >"$tmp"
@@ -55,13 +76,13 @@
 	if needs_multipart "$draft"; then
 		(
 			IFS=$NL
-			msed '/attach/d' $draft
-			for f in $(mhdr -M -h attach $draft); do
+			msed '/attach/d' "$draft"
+			for f in $(mhdr -M -h attach "$draft"); do
 				printf '#%s %s\n' \
-				       "$(file -Lbi $f | sed 's/ //g')" \
+				       "$(file -Lbi "$f" | sed 's/ //g')" \
 				       "$f"
 			done
-		) | mmime >$draftmime
+		) | mmime >"$draftmime"
 	else
 		mmime -r <"$draft" >"$draftmime"
 	fi
@@ -74,8 +95,11 @@
 sendmail="${sendmail:-sendmail} ${sendmail_args:--t}"
 default_from=$(mhdr -h local-mailbox "$MBLAZE/profile")
 
+defaultc=e
+
 hdrs=
 resume=
+noquote=
 case "$0" in
 *mcom*)
 	hdr=to
@@ -90,11 +114,22 @@
 			shift
 			resume=1
 			if [ "$#" -gt 0 ]; then
-				echo "used draft $1"
-				draft="$1"
+				case "$1" in
+					/*|./*) draft="$1";;
+					*) draft="./$1";;
+				esac
+				if ! [ -f "$draft" ]; then
+					printf 'mcom: no such draft %s\n' \
+					       "$draft" 1>&2
+					exit 1
+				fi
+				echo "using draft $draft"
 				shift
 			fi
 			;;
+		-send)
+			defaultc=justsend
+			shift;;
 		-??*)
 			hdr=${1#-}
 			shift;;
@@ -118,6 +153,9 @@
 		-r)
 			shift
 			raw=1;;
+		-send)
+			defaultc=justsend
+			shift;;
 		-??*)
 			hdr=${1#-}
 			shift;;
@@ -139,6 +177,9 @@
 		--)
 			shift
 			break;;
+		-send)
+			defaultc=justsend
+			shift;;
 		-??*)
 			hdr=${1#-}
 			shift;;
@@ -160,6 +201,12 @@
 		--)
 			shift
 			break;;
+		-send)
+			defaultc=justsend
+			shift;;
+		-noquote)
+			noquote=1
+			shift;;
 		-??*)
 			hdr=${1#-}
 			shift;;
@@ -280,8 +327,8 @@
 		if [ "$ng" ]; then
 			printf 'Newsgroups: %s\n' "$ng"
 		else
-			to=$(mhdr -h reply-to "$1")
-			[ -z "$to" ] && to=$(mhdr -h from "$1")
+			to=$(mhdr -d -h reply-to "$1")
+			[ -z "$to" ] && to=$(mhdr -d -h from "$1")
 			printf 'To: %s\n' "$to"
 			printf 'Cc: %s\n' \
 			       "$(mhdr -d -A -h to:cc: "$1" |
@@ -308,8 +355,10 @@
 		cat "$MBLAZE/headers" 2>/dev/null
 		printf '\n'
 
-		mquote "$1"
-		printf '\n'
+		if [ -z "$noquote" ]; then
+			mquote "$1"
+			printf '\n'
+		fi
 		(
 			IFS=$NL
 			cat -- /dev/null $(printf '%s' "$hdrs" | mhdr -M -h body /dev/stdin)
@@ -332,10 +381,10 @@
 			cat "$SIGNATURE"
 		fi
 	esac
-} >$draft
+} >"$draft"
 
 automime=
-c=e
+c=$defaultc
 while :; do
 	case "$c" in
 	s|send)
@@ -352,15 +401,15 @@
 			;;
 		esac
 
-		if [ -e $draftmime ]; then
-			if [ $draft -ot $draftmime ] || [ "$automime" -eq 1 ]; then
-				stampdate $draftmime
-				if $sendmail <$draftmime; then
+		if [ -e "$draftmime" ]; then
+			if [ "$draft" -ot "$draftmime" ] || [ "$automime" = 1 ]; then
+				stampdate "$draftmime"
+				if $sendmail <"$draftmime"; then
 					if [ "$outbox" ]; then
-						mv $draftmime $draft
-						mflag -d -S $draft
+						mv "$draftmime" "$draft"
+						mflag -d -S "$draft"
 					else
-						rm $draft $draftmime
+						rm "$draft" "$draftmime"
 					fi
 				else
 					printf '%s\n' "mcom: $sendmail failed, kept draft $draft"
@@ -369,15 +418,16 @@
 			else
 				printf 'mcom: re-run mmime first.\n'
 				c=
+				continue
 			fi
 		else
-			if mmime -c <$draft; then
-				stampdate $draft
-				if $sendmail <$draft; then
+			if mmime -c <"$draft"; then
+				stampdate "$draft"
+				if $sendmail <"$draft"; then
 					if [ "$outbox" ]; then
-						mflag -d -S $draft
+						mflag -d -S "$draft"
 					else
-						rm $draft
+						rm "$draft"
 					fi
 				else
 					printf '%s\n' "mcom: $sendmail failed, kept draft $draft"
@@ -386,6 +436,7 @@
 			else
 				printf '%s\n' "mcom: message needs to be MIME-encoded first."
 				c=
+				continue
 			fi
 		fi
 
@@ -398,49 +449,64 @@
 		exit 0
 		;;
 	c|cancel)
-		stampdate $draft
+		stampdate "$draft"
 		printf '%s\n' "mcom: cancelled draft $draft"
 		exit 1
 		;;
 	m|mime)
 		do_mime
-		mshow -t $draftmime
+		mshow -t "$draftmime"
 		c=
 		;;
 	e|edit)
 		c=
-		if ! ${EDITOR:-vi} $draft; then
-			c=c
+		if ! ${EDITOR:-vi} "$draft"; then
+			c=d
+		else
+			if checksensible "$draft"; then
+				stripempty "$draft"
+				if mmime -c <"$draft" && ! needs_multipart "$draft"; then
+					automime=
+				else
+					automime=1
+					do_mime
+				fi
+			else
+				printf '\n'
+			fi
 		fi
-		stripempty $draft
-		if mmime -c <$draft && ! needs_multipart $draft; then
+		;;
+	justsend)
+		stripempty "$draft"
+		if mmime -c <"$draft" && ! needs_multipart "$draft"; then
 			automime=
 		else
 			automime=1
 			do_mime
 		fi
+		c=send
 		;;
 	d|delete)
-		rm -i $draft
-		if ! [ -f $draft ]; then
-			rm -f $draftmime
+		rm -i "$draft"
+		if ! [ -f "$draft" ]; then
+			rm -f "$draftmime"
 			printf '%s\n' "mcom: deleted draft $draft"
 			exit 0
 		fi
 		c=
 		;;
 	sign)
-		msign $draft >$draftmime
-		mshow -t $draftmime
+		msign "$draft" >"$draftmime"
+		mshow -t "$draftmime"
 		c=
 		;;
 	encrypt)
-		mencrypt $draft >$draftmime
-		mshow -t $draftmime
+		mencrypt "$draft" >"$draftmime"
+		mshow -t "$draftmime"
 		c=
 		;;
 	show)
-		if [ -e $draftmime ]; then
+		if [ -e "$draftmime" ]; then
 			mshow "$draftmime"
 		else
 			mshow "$draft"
diff -Nru mblaze-0.4/mdeliver.c mblaze-0.5.1/mdeliver.c
--- mblaze-0.4/mdeliver.c	2018-08-15 17:16:45.000000000 +0200
+++ mblaze-0.5.1/mdeliver.c	2019-03-03 17:12:56.000000000 +0100
@@ -1,3 +1,4 @@
+#include <sys/stat.h>
 #include <sys/time.h>
 #include <sys/types.h>
 
@@ -30,6 +31,8 @@
 
 char *targetdir;
 long delivery;
+int preserve_mtime;
+int try_rename;
 
 char host[64];
 void
@@ -45,9 +48,10 @@
 }
 
 int
-deliver(FILE *infile)
+deliver(char *infilename)
 {
 	int outfd;
+	FILE *infile;
 	FILE *outfile;
 	char dst[PATH_MAX];
 	char tmp[PATH_MAX];
@@ -57,6 +61,22 @@
 	char *line = 0;
 	size_t linelen = 0;
 
+	if (infilename) {
+		if (try_rename) {
+			infile = 0;
+		} else {
+			infile = fopen(infilename, "r");
+			if (!infile) {
+				fprintf(stderr, "mrefile: %s: %s\n",
+				    infilename, strerror(errno));
+				return -1;
+			}
+		}
+	} else {
+		// mdeliver
+		infile = stdin;
+	}
+
 	if (Mflag) {
 		// skip to first "From " line
 		while (1) {
@@ -65,7 +85,7 @@
 			if (rd == -1) {
 				if (errno == 0)
 					errno = EINVAL;  // invalid mbox file
-				return -1;
+				goto fail;
 			}
 
 			if (strncmp("From ", line, 5) == 0)
@@ -73,9 +93,9 @@
 		}
 	}
 
-	while (!feof(infile)) {
+	do {
 		delivery++;
-tryagain:
+try_again:
 		gettimeofday(&tv, 0);
 
 		snprintf(id, sizeof id, "%ld.M%06ldP%ldQ%ld.%s",
@@ -84,13 +104,39 @@
 
 		snprintf(tmp, sizeof tmp, "%s/tmp/%s", targetdir, id);
 
-		outfd = open(tmp, O_CREAT | O_WRONLY | O_EXCL, 0666);
+		if (try_rename) {
+			snprintf(dst, sizeof dst, "%s/%s/%s:2,%s",
+			    targetdir, cflag ? "cur" : "new", id, Xflag);
+			if (rename(infilename, dst) == 0)
+				goto success;
+
+			/* rename failed, open file and try copying */
+
+			infile = fopen(infilename, "r");
+			if (!infile) {
+				fprintf(stderr, "mrefile: %s: %s\n",
+				    infilename, strerror(errno));
+				return -1;
+			}
+		}
+
+		struct stat st;
+		if (fstat(fileno(infile), &st) < 0)
+			st.st_mode = 0600;
+		if (S_ISFIFO(st.st_mode))
+			st.st_mode = 0600;
+		outfd = open(tmp, O_CREAT | O_WRONLY | O_EXCL,
+		    st.st_mode & 07777);
 		if (outfd < 0) {
 			if (errno == EEXIST)
-				goto tryagain;
-			fprintf(stderr, "mrefile: %s: %s\n",
-			    tmp, strerror(errno));
-			return -1;
+				goto try_again;
+			if (errno == ENOENT)
+				fprintf(stderr, "mrefile: %s/tmp: %s\n",
+				    targetdir, strerror(errno));
+			else
+				fprintf(stderr, "mrefile: %s: %s\n",
+				    tmp, strerror(errno));
+			goto fail;
 		}
 
 		outfile = fdopen(outfd, "w");
@@ -104,7 +150,7 @@
 			ssize_t rd = getdelim(&line, &linelen, '\n', infile);
 			if (rd == -1) {
 				if (errno != 0)
-					return -1;
+					goto fail;
 				break;
 			}
 			char *line_start = line;
@@ -142,14 +188,14 @@
 			}
 
 			if (fwrite(line_start, 1, rd, outfile) != (size_t)rd)
-				return -1;
+				goto fail;
 		}
 		if (fflush(outfile) == EOF)
-			return -1;
+			goto fail;
 		if (fsync(outfd) < 0)
-			return -1;
+			goto fail;
 		if (fclose(outfile) == EOF)
-			return -1;
+			goto fail;
 
 		// compress flags
 		int i, j;
@@ -173,18 +219,40 @@
 					utimes(tmp, times);
 				}
 			}
+			blaze822_free(msg);
+		}
+
+		if (preserve_mtime) {
+			const struct timespec times[2] = {
+				{ tv.tv_sec, tv.tv_usec * 1000L },
+#if (defined(__APPLE__) && defined(__MACH__))
+			        st.st_mtimespec
+#else /* POSIX.1-2008 */
+				st.st_mtim
+#endif
+			};
+			utimensat(AT_FDCWD, tmp, times, 0);
 		}
 
 		snprintf(dst, sizeof dst, "%s/%s/%s:2,%s",
 		    targetdir, (cflag || is_old) ? "cur" : "new", id,
 		    Xflag ? Xflag : statusflags);
 		if (rename(tmp, dst) != 0)
-			return -1;
+			goto fail;
 
+success:
 		if (vflag)
 			printf("%s\n", dst);
-	}
+	} while (Mflag && !feof(infile));
+
+	if (infile)
+		fclose(infile);
 	return 0;
+
+fail:
+	if (infile)
+		fclose(infile);
+	return -1;
 }
 
 void
@@ -193,12 +261,6 @@
 	while (*file == ' ' || *file == '\t')
 		file++;
 
-	FILE *f = fopen(file, "r");
-	if (!f) {
-		fprintf(stderr, "mrefile: %s: %s\n", file, strerror(errno));
-		return;
-	}
-
 	// keep flags
 	char *flags = strstr(file, ":2,");
 	if (flags)
@@ -206,13 +268,12 @@
 	else
 		Xflag = "";
 
-	if (deliver(f) < 0) {
+	if (deliver(file) < 0) {
 		perror("mrefile");
 		return;
 	}
 
-	fclose(f);
-	if (!kflag)
+	if (!kflag && !try_rename)
 		unlink(file);
 }
 
@@ -223,11 +284,13 @@
 		// mrefile(1)
 
 		cflag = 1;  // use cur/
+		preserve_mtime = 1;
+		try_rename = 1;
 
 		int c;
 		while ((c = getopt(argc, argv, "kv")) != -1)
 			switch (c) {
-			case 'k': kflag = 1; break;
+			case 'k': kflag = 1; try_rename = 0; break;
 			case 'v': vflag = 1; break;
 			default:
 usage:
@@ -274,7 +337,7 @@
 
 	gethost();
 
-	if (deliver(stdin) < 0) {
+	if (deliver(0) < 0) {
 		perror("mdeliver");
 		return 2;
 	}
diff -Nru mblaze-0.4/mdirs.c mblaze-0.5.1/mdirs.c
--- mblaze-0.4/mdirs.c	2018-08-15 17:16:45.000000000 +0200
+++ mblaze-0.5.1/mdirs.c	2019-03-03 17:12:56.000000000 +0100
@@ -9,12 +9,14 @@
 
 #include "blaze822.h"
 
+static char sep = '\n';
+
 void
 pwd()
 {
 	char cwd[PATH_MAX];
 	if (getcwd(cwd, sizeof cwd))
-		puts(cwd);
+		printf("%s%c", cwd, sep);
 }
 
 void
@@ -72,19 +74,31 @@
 main(int argc, char *argv[])
 {
 	int c, i;
-	while ((c = getopt(argc, argv, "")) != -1)
+	while ((c = getopt(argc, argv, "0")) != -1)
 		switch (c) {
+		case '0': sep = '\0'; break;
 		default:
 usage:
-			fprintf(stderr, "Usage: mdirs dirs...\n");
+			fprintf(stderr, "Usage: mdirs [-0] dirs...\n");
 			exit(1);
 		}
 
 	if (argc == optind)
 		goto usage;
 
-	for (i = 0; i < argc; i++)
+	char toplevel[PATH_MAX];
+	if (!getcwd(toplevel, sizeof toplevel)) {
+		perror("mdirs: getcwd");
+		exit(-1);
+	}
+
+	for (i = 0; i < argc; i++) {
 		mdirs(argv[i]);
+		if (chdir(toplevel) < 0) {
+			perror("mdirs: chdir");
+			exit(-1);
+		}
+	}
 
 	return 0;
 }
diff -Nru mblaze-0.4/mflow.c mblaze-0.5.1/mflow.c
--- mblaze-0.4/mflow.c	2018-08-15 17:16:45.000000000 +0200
+++ mblaze-0.5.1/mflow.c	2019-03-03 17:12:56.000000000 +0100
@@ -40,7 +40,7 @@
 	if (column == 0) {
 		for (; column < quotes; column++)
 			putchar('>');
-		if (quotes)
+		if (quotes && *line != ' ')
 			putchar(' ');
 	}
 
@@ -60,7 +60,7 @@
 			for (; column < quotes; column++)
 				putchar('>');
 			column++;
-			if (quotes)
+			if (quotes && *line != ' ')
 				putchar(' ');
 		}
 
diff -Nru mblaze-0.4/mgenmid.c mblaze-0.5.1/mgenmid.c
--- mblaze-0.4/mgenmid.c	2018-08-15 17:16:45.000000000 +0200
+++ mblaze-0.5.1/mgenmid.c	2019-03-03 17:12:56.000000000 +0100
@@ -85,30 +85,35 @@
 	struct timeval tp;
 	gettimeofday(&tp, (struct timezone *)0);
 
-	uint64_t rnd;
+	uint64_t rnd1, rnd2;
 
 	int rndfd = open("/dev/urandom", O_RDONLY);
 	if (rndfd >= 0) {
-		unsigned char rndb[8];
+		unsigned char rndb[16];
 		if (read(rndfd, rndb, sizeof rndb) != sizeof rndb)
 			goto fallback;
 		close(rndfd);
 
 		int i;
-		for (i = 0, rnd = 0; i < 8; i++)
-			rnd = rnd*256 + rndb[i];
+		for (i = 0, rnd1 = 0; i < 8; i++)
+			rnd1 = rnd1*256 + rndb[i];
+		for (i = 0, rnd2 = 0; i < 8; i++)
+			rnd2 = rnd2*256 + rndb[i+8];
 	} else {
 fallback:
 		srand48(tp.tv_sec ^ tp.tv_usec ^ getpid());
-		rnd = ((uint64_t)lrand48() << 32) + lrand48();
+		rnd1 = ((uint64_t)lrand48() << 32) + lrand48();
+		rnd2 = ((uint64_t)lrand48() << 32) + lrand48();
 	}
 
-	rnd |= (1ULL << 63);  // set highest bit to force full width
+	rnd1 ^= ((uint64_t)tp.tv_sec * 1000000LL + tp.tv_usec);
+	rnd1 |= (1ULL << 63);  // set highest bit to force full width
+	rnd2 |= (1ULL << 63);  // set highest bit to force full width
 
 	putchar('<');
-	printb36(((uint64_t)tp.tv_sec * 1000000LL + tp.tv_usec));
+	printb36(rnd1);
 	putchar('.');
-	printb36(rnd);
+	printb36(rnd2);
 	putchar('@');
 	fputs(host, stdout);
 	putchar('>');
diff -Nru mblaze-0.4/mless mblaze-0.5.1/mless
--- mblaze-0.4/mless	2018-08-15 17:16:45.000000000 +0200
+++ mblaze-0.5.1/mless	2019-03-03 17:12:56.000000000 +0100
@@ -37,9 +37,9 @@
 			mshow "$2"
 		fi | mcolor
 	else
-		mseq -r $2
+		mseq -r "$2"
 		echo
-		cat "$(mseq -r $2)"
+		cat "$(mseq -r "$2")"
 	fi
 	exit $?
 fi
@@ -53,11 +53,7 @@
 	exec mseq :
 fi
 
-case "$0" in
-	*mnext) set -- +;;
-	*mprev) set -- -;;
-	*) [ "$#" -eq 1 ] && set -- ${1:-.};;
-esac
+[ "$#" -eq 1 ] && set -- ${1:-.}
 
 if [ "$#" -ge 1 ]; then
 	mseq -C "$1"
@@ -68,12 +64,12 @@
 export MLESS_RAW=0
 export MLESS_HTML=0
 while :; do
-	if [ -f $MBLAZE/mless ]; then
-		export LESSKEY=$MBLAZE/mless
-	elif [ -f $HOME/.mblaze/mless ]; then
-		export LESSKEY=$HOME/.mblaze/mless
-	elif [ -f $HOME/.mless ]; then
-		export LESSKEY=$HOME/.mless
+	if [ -f "$MBLAZE/mless" ]; then
+		export LESSKEY="$MBLAZE/mless"
+	elif [ -f "$HOME/.mblaze/mless" ]; then
+		export LESSKEY="$HOME/.mblaze/mless"
+	elif [ -f "$HOME/.mless" ]; then
+		export LESSKEY="$HOME/.mless"
 	fi
 	LESSOPEN="|$0 --filter %s" \
 		less -Ps"mless %f?m (message %i of %m).." -R \
diff -Nru mblaze-0.4/mmime.c mblaze-0.5.1/mmime.c
--- mblaze-0.4/mmime.c	2018-08-15 17:16:45.000000000 +0200
+++ mblaze-0.5.1/mmime.c	2019-03-03 17:12:56.000000000 +0100
@@ -282,6 +282,9 @@
 	char *s, *e;
 	size_t l = strlen(line);
 
+	if (l == 0)
+		return;
+
 	if (line[l-1] == '\n')
 		line[l-1] = 0;
 
@@ -299,7 +302,7 @@
 
 	int prevq = 0;  // was the previous word encoded as qp?
 
-	size_t linelen = s - line;
+	ssize_t linelen = s - line;
 
 	while (*s) {
 		size_t highbit = 0;
diff -Nru mblaze-0.4/mmkdir mblaze-0.5.1/mmkdir
--- mblaze-0.4/mmkdir	2018-08-15 17:16:45.000000000 +0200
+++ mblaze-0.5.1/mmkdir	2019-03-03 17:12:56.000000000 +0100
@@ -1,9 +1,11 @@
 #!/bin/sh
 # mmkdir DIRS... - create new maildirs
 
+umask 077
+
 r=0
 for dir; do
-	mkdir -p -m 0700 $dir/cur $dir/new $dir/tmp || r=1
+	mkdir -p "$dir"/cur "$dir"/new "$dir"/tmp || r=1
 done
 
 exit $r
diff -Nru mblaze-0.4/mnext mblaze-0.5.1/mnext
--- mblaze-0.4/mnext	2018-08-15 17:16:45.000000000 +0200
+++ mblaze-0.5.1/mnext	1970-01-01 01:00:00.000000000 +0100
@@ -1,107 +0,0 @@
-#!/bin/sh
-# mless [MSG] - less(1)-wrapper around mshow
-
-colorscan() {
-awk '
-function co(n, c) { e = ENVIRON["MCOLOR_" n]; return e ? e : c }
-function fg(c, s) { return sprintf("\033[38;5;%03dm%s\033[0m", c, s) }
-function so(s) { return sprintf("\033[1m%s\033[0m", s) }
-/^>/ { print so(fg(co("CUR",119), $0)); next }
-/^ *\\_/ { print fg(co("MISS",242), $0); next }
-{ print }'
-}
-
-if [ -n "${NO_COLOR+set}" ]; then
-	colorscan() { cat -; }
-fi
-
-if [ "$1" = --filter ]; then
-	if [ "$2" = //scan ]; then
-		mscan : 2>/dev/null | colorscan
-		exit $?
-	fi
-
-	mseq -C "$2"
-	mscan .-2:.+3 2>/dev/null | colorscan
-	echo
-
-	if ! [ -f "$(mseq -r "$2")" ]; then
-		mseq "$2"
-		exit
-	fi
-
-	if [ $MLESS_RAW -eq 0 ]; then
-		if [ $MLESS_HTML -eq 1 ]; then
-			mshow -A text/html "$2"
-		else
-			mshow "$2"
-		fi | mcolor
-	else
-		mseq -r $2
-		echo
-		cat "$(mseq -r $2)"
-	fi
-	exit $?
-fi
-
-if [ "$#" -eq 0 ] && ! [ -t 0 ]; then
-	mseq -S >/dev/null
-	set -- :
-fi
-
-if ! [ -t 1 ]; then
-	exec mseq :
-fi
-
-case "$0" in
-	*mnext) set -- +;;
-	*mprev) set -- -;;
-	*) [ "$#" -eq 1 ] && set -- ${1:-.};;
-esac
-
-if [ "$#" -ge 1 ]; then
-	mseq -C "$1"
-fi
-
-nl="
-"
-export MLESS_RAW=0
-export MLESS_HTML=0
-while :; do
-	if [ -f $MBLAZE/mless ]; then
-		export LESSKEY=$MBLAZE/mless
-	elif [ -f $HOME/.mblaze/mless ]; then
-		export LESSKEY=$HOME/.mblaze/mless
-	elif [ -f $HOME/.mless ]; then
-		export LESSKEY=$HOME/.mless
-	fi
-	LESSOPEN="|$0 --filter %s" \
-		less -Ps"mless %f?m (message %i of %m).." -R \
-			"+:e $(mscan -n .)$nl" //scan $(mscan -n :)
-	case "$?" in
-	0|1) exit $?;;
-	36) # $ goto end
-		mseq -C '$' 2>/dev/null
-		;;
-	78) # N go to next unseen message
-		nu=$(magrep -v -m1 :S .:) && mseq -C "$nu"
-		;;
-	107) # k next thread
-		mseq -C "$(mseq .+1: | sed -n '/^[^ <]/{p;q;}')"
-		;;
-	100) # d mark read
-		mflag -S .
-		mseq -f : | mseq -S
-		mseq -C +
-		;;
-	82) # R toggle raw mode
-		MLESS_RAW=$((1-$MLESS_RAW))
-		;;
-	72) # H toggle HTML mode
-		MLESS_HTML=$((1-$MLESS_HTML))
-		;;
-	94) # ^ goto parent
-		mseq -C '.^' 2>/dev/null
-		;;
-	esac
-done
diff -Nru mblaze-0.4/mpick.c mblaze-0.5.1/mpick.c
--- mblaze-0.4/mpick.c	2018-08-15 17:16:45.000000000 +0200
+++ mblaze-0.5.1/mpick.c	2019-03-03 17:12:56.000000000 +0100
@@ -284,7 +284,7 @@
 		pos++;
 		while (*pos &&
 		    (*pos != '"' || (*pos == '"' && *(pos+1) == '"'))) {
-			if (len >= bufsiz) {
+			if (len+1 >= bufsiz) {
 				bufsiz = 2*bufsiz + 16;
 				buf = realloc(buf, bufsiz);
 				if (!buf)
@@ -809,7 +809,7 @@
 	if (!m->msg || !(b = blaze822_chdr(m->msg, h)))
 		goto err;
 
-	char buf[100];
+	char buf[4096];
 	blaze822_decode_rfc2047(buf, b, sizeof buf - 1, "UTF-8");
 	if (!*buf)
 		goto err;
@@ -1142,17 +1142,20 @@
 {
 	long i;
 	int c;
+	int vflag;
 
 	argv0 = argv[0];
 	now = time(0);
 	num = 1;
+	vflag = 0;
 
-	while ((c = getopt(argc, argv, "Tt:")) != -1)
+	while ((c = getopt(argc, argv, "Tt:v")) != -1)
 		switch (c) {
 		case 'T': Tflag = need_thr = 1; break;
 		case 't': expr = chain(expr, EXPR_AND, parse_expr(optarg)); break;
+		case 'v': vflag = 1; break;
 		default:
-			fprintf(stderr, "Usage: %s [-T] [-t test] [msglist ...]\n", argv0);
+			fprintf(stderr, "Usage: %s [-Tv] [-t test] [msglist ...]\n", argv0);
 			exit(1);
 		}
 
@@ -1169,6 +1172,7 @@
 	if (Tflag && thr)
 		do_thr();
 
-	fprintf(stderr, "%ld mails tested, %ld picked.\n", i, kept);
+	if (vflag)
+		fprintf(stderr, "%ld mails tested, %ld picked.\n", i, kept);
 	return 0;
 }
diff -Nru mblaze-0.4/mprev mblaze-0.5.1/mprev
--- mblaze-0.4/mprev	2018-08-15 17:16:45.000000000 +0200
+++ mblaze-0.5.1/mprev	1970-01-01 01:00:00.000000000 +0100
@@ -1,107 +0,0 @@
-#!/bin/sh
-# mless [MSG] - less(1)-wrapper around mshow
-
-colorscan() {
-awk '
-function co(n, c) { e = ENVIRON["MCOLOR_" n]; return e ? e : c }
-function fg(c, s) { return sprintf("\033[38;5;%03dm%s\033[0m", c, s) }
-function so(s) { return sprintf("\033[1m%s\033[0m", s) }
-/^>/ { print so(fg(co("CUR",119), $0)); next }
-/^ *\\_/ { print fg(co("MISS",242), $0); next }
-{ print }'
-}
-
-if [ -n "${NO_COLOR+set}" ]; then
-	colorscan() { cat -; }
-fi
-
-if [ "$1" = --filter ]; then
-	if [ "$2" = //scan ]; then
-		mscan : 2>/dev/null | colorscan
-		exit $?
-	fi
-
-	mseq -C "$2"
-	mscan .-2:.+3 2>/dev/null | colorscan
-	echo
-
-	if ! [ -f "$(mseq -r "$2")" ]; then
-		mseq "$2"
-		exit
-	fi
-
-	if [ $MLESS_RAW -eq 0 ]; then
-		if [ $MLESS_HTML -eq 1 ]; then
-			mshow -A text/html "$2"
-		else
-			mshow "$2"
-		fi | mcolor
-	else
-		mseq -r $2
-		echo
-		cat "$(mseq -r $2)"
-	fi
-	exit $?
-fi
-
-if [ "$#" -eq 0 ] && ! [ -t 0 ]; then
-	mseq -S >/dev/null
-	set -- :
-fi
-
-if ! [ -t 1 ]; then
-	exec mseq :
-fi
-
-case "$0" in
-	*mnext) set -- +;;
-	*mprev) set -- -;;
-	*) [ "$#" -eq 1 ] && set -- ${1:-.};;
-esac
-
-if [ "$#" -ge 1 ]; then
-	mseq -C "$1"
-fi
-
-nl="
-"
-export MLESS_RAW=0
-export MLESS_HTML=0
-while :; do
-	if [ -f $MBLAZE/mless ]; then
-		export LESSKEY=$MBLAZE/mless
-	elif [ -f $HOME/.mblaze/mless ]; then
-		export LESSKEY=$HOME/.mblaze/mless
-	elif [ -f $HOME/.mless ]; then
-		export LESSKEY=$HOME/.mless
-	fi
-	LESSOPEN="|$0 --filter %s" \
-		less -Ps"mless %f?m (message %i of %m).." -R \
-			"+:e $(mscan -n .)$nl" //scan $(mscan -n :)
-	case "$?" in
-	0|1) exit $?;;
-	36) # $ goto end
-		mseq -C '$' 2>/dev/null
-		;;
-	78) # N go to next unseen message
-		nu=$(magrep -v -m1 :S .:) && mseq -C "$nu"
-		;;
-	107) # k next thread
-		mseq -C "$(mseq .+1: | sed -n '/^[^ <]/{p;q;}')"
-		;;
-	100) # d mark read
-		mflag -S .
-		mseq -f : | mseq -S
-		mseq -C +
-		;;
-	82) # R toggle raw mode
-		MLESS_RAW=$((1-$MLESS_RAW))
-		;;
-	72) # H toggle HTML mode
-		MLESS_HTML=$((1-$MLESS_HTML))
-		;;
-	94) # ^ goto parent
-		mseq -C '.^' 2>/dev/null
-		;;
-	esac
-done
diff -Nru mblaze-0.4/mrep mblaze-0.5.1/mrep
--- mblaze-0.4/mrep	2018-08-15 17:16:45.000000000 +0200
+++ mblaze-0.5.1/mrep	2019-03-03 17:12:56.000000000 +0100
@@ -11,7 +11,7 @@
 }
 
 notmine() {
-	mine="$(maddr -a -h local-mailbox:alternate-mailboxes: $MBLAZE/profile)"
+	mine="$(maddr -a -h local-mailbox:alternate-mailboxes: "$MBLAZE/profile")"
 	grep -Fv -e "$mine"
 }
 
@@ -40,6 +40,27 @@
 	fi
 }
 
+checksensible() {
+	awk '
+		/^$/ {
+			seenheader=1
+			exit
+		}
+		!(/^[^ \t][^ \t]*[ \t]*:/ || /^[ \t]/) {
+			bad=1
+			print "invalid header line: "$0 >"/dev/stderr"
+		}
+		END {
+			if (!seenheader) {
+				print "warning: message does not contain an empty line between headers and body." >"/dev/stderr"
+				exit 1
+			}
+			if (bad)
+				exit 1
+		}
+' "$1"
+}
+
 stripempty() {
 	tmp=$(mktemp -t mcom.XXXXXX)
 	msed 's/^[ \t]*$//d' "$1" >"$tmp"
@@ -55,13 +76,13 @@
 	if needs_multipart "$draft"; then
 		(
 			IFS=$NL
-			msed '/attach/d' $draft
-			for f in $(mhdr -M -h attach $draft); do
+			msed '/attach/d' "$draft"
+			for f in $(mhdr -M -h attach "$draft"); do
 				printf '#%s %s\n' \
-				       "$(file -Lbi $f | sed 's/ //g')" \
+				       "$(file -Lbi "$f" | sed 's/ //g')" \
 				       "$f"
 			done
-		) | mmime >$draftmime
+		) | mmime >"$draftmime"
 	else
 		mmime -r <"$draft" >"$draftmime"
 	fi
@@ -74,8 +95,11 @@
 sendmail="${sendmail:-sendmail} ${sendmail_args:--t}"
 default_from=$(mhdr -h local-mailbox "$MBLAZE/profile")
 
+defaultc=e
+
 hdrs=
 resume=
+noquote=
 case "$0" in
 *mcom*)
 	hdr=to
@@ -90,11 +114,22 @@
 			shift
 			resume=1
 			if [ "$#" -gt 0 ]; then
-				echo "used draft $1"
-				draft="$1"
+				case "$1" in
+					/*|./*) draft="$1";;
+					*) draft="./$1";;
+				esac
+				if ! [ -f "$draft" ]; then
+					printf 'mcom: no such draft %s\n' \
+					       "$draft" 1>&2
+					exit 1
+				fi
+				echo "using draft $draft"
 				shift
 			fi
 			;;
+		-send)
+			defaultc=justsend
+			shift;;
 		-??*)
 			hdr=${1#-}
 			shift;;
@@ -118,6 +153,9 @@
 		-r)
 			shift
 			raw=1;;
+		-send)
+			defaultc=justsend
+			shift;;
 		-??*)
 			hdr=${1#-}
 			shift;;
@@ -139,6 +177,9 @@
 		--)
 			shift
 			break;;
+		-send)
+			defaultc=justsend
+			shift;;
 		-??*)
 			hdr=${1#-}
 			shift;;
@@ -160,6 +201,12 @@
 		--)
 			shift
 			break;;
+		-send)
+			defaultc=justsend
+			shift;;
+		-noquote)
+			noquote=1
+			shift;;
 		-??*)
 			hdr=${1#-}
 			shift;;
@@ -280,8 +327,8 @@
 		if [ "$ng" ]; then
 			printf 'Newsgroups: %s\n' "$ng"
 		else
-			to=$(mhdr -h reply-to "$1")
-			[ -z "$to" ] && to=$(mhdr -h from "$1")
+			to=$(mhdr -d -h reply-to "$1")
+			[ -z "$to" ] && to=$(mhdr -d -h from "$1")
 			printf 'To: %s\n' "$to"
 			printf 'Cc: %s\n' \
 			       "$(mhdr -d -A -h to:cc: "$1" |
@@ -308,8 +355,10 @@
 		cat "$MBLAZE/headers" 2>/dev/null
 		printf '\n'
 
-		mquote "$1"
-		printf '\n'
+		if [ -z "$noquote" ]; then
+			mquote "$1"
+			printf '\n'
+		fi
 		(
 			IFS=$NL
 			cat -- /dev/null $(printf '%s' "$hdrs" | mhdr -M -h body /dev/stdin)
@@ -332,10 +381,10 @@
 			cat "$SIGNATURE"
 		fi
 	esac
-} >$draft
+} >"$draft"
 
 automime=
-c=e
+c=$defaultc
 while :; do
 	case "$c" in
 	s|send)
@@ -352,15 +401,15 @@
 			;;
 		esac
 
-		if [ -e $draftmime ]; then
-			if [ $draft -ot $draftmime ] || [ "$automime" -eq 1 ]; then
-				stampdate $draftmime
-				if $sendmail <$draftmime; then
+		if [ -e "$draftmime" ]; then
+			if [ "$draft" -ot "$draftmime" ] || [ "$automime" = 1 ]; then
+				stampdate "$draftmime"
+				if $sendmail <"$draftmime"; then
 					if [ "$outbox" ]; then
-						mv $draftmime $draft
-						mflag -d -S $draft
+						mv "$draftmime" "$draft"
+						mflag -d -S "$draft"
 					else
-						rm $draft $draftmime
+						rm "$draft" "$draftmime"
 					fi
 				else
 					printf '%s\n' "mcom: $sendmail failed, kept draft $draft"
@@ -369,15 +418,16 @@
 			else
 				printf 'mcom: re-run mmime first.\n'
 				c=
+				continue
 			fi
 		else
-			if mmime -c <$draft; then
-				stampdate $draft
-				if $sendmail <$draft; then
+			if mmime -c <"$draft"; then
+				stampdate "$draft"
+				if $sendmail <"$draft"; then
 					if [ "$outbox" ]; then
-						mflag -d -S $draft
+						mflag -d -S "$draft"
 					else
-						rm $draft
+						rm "$draft"
 					fi
 				else
 					printf '%s\n' "mcom: $sendmail failed, kept draft $draft"
@@ -386,6 +436,7 @@
 			else
 				printf '%s\n' "mcom: message needs to be MIME-encoded first."
 				c=
+				continue
 			fi
 		fi
 
@@ -398,49 +449,64 @@
 		exit 0
 		;;
 	c|cancel)
-		stampdate $draft
+		stampdate "$draft"
 		printf '%s\n' "mcom: cancelled draft $draft"
 		exit 1
 		;;
 	m|mime)
 		do_mime
-		mshow -t $draftmime
+		mshow -t "$draftmime"
 		c=
 		;;
 	e|edit)
 		c=
-		if ! ${EDITOR:-vi} $draft; then
-			c=c
+		if ! ${EDITOR:-vi} "$draft"; then
+			c=d
+		else
+			if checksensible "$draft"; then
+				stripempty "$draft"
+				if mmime -c <"$draft" && ! needs_multipart "$draft"; then
+					automime=
+				else
+					automime=1
+					do_mime
+				fi
+			else
+				printf '\n'
+			fi
 		fi
-		stripempty $draft
-		if mmime -c <$draft && ! needs_multipart $draft; then
+		;;
+	justsend)
+		stripempty "$draft"
+		if mmime -c <"$draft" && ! needs_multipart "$draft"; then
 			automime=
 		else
 			automime=1
 			do_mime
 		fi
+		c=send
 		;;
 	d|delete)
-		rm -i $draft
-		if ! [ -f $draft ]; then
-			rm -f $draftmime
+		rm -i "$draft"
+		if ! [ -f "$draft" ]; then
+			rm -f "$draftmime"
 			printf '%s\n' "mcom: deleted draft $draft"
 			exit 0
 		fi
 		c=
 		;;
 	sign)
-		msign $draft >$draftmime
-		mshow -t $draftmime
+		msign "$draft" >"$draftmime"
+		mshow -t "$draftmime"
 		c=
 		;;
 	encrypt)
-		mencrypt $draft >$draftmime
-		mshow -t $draftmime
+		mencrypt "$draft" >"$draftmime"
+		mshow -t "$draftmime"
 		c=
 		;;
 	show)
-		if [ -e $draftmime ]; then
+		if [ -e "$draftmime" ]; then
 			mshow "$draftmime"
 		else
 			mshow "$draft"
diff -Nru mblaze-0.4/mscan.c mblaze-0.5.1/mscan.c
--- mblaze-0.4/mscan.c	2018-08-15 17:16:45.000000000 +0200
+++ mblaze-0.5.1/mscan.c	2019-03-03 17:12:56.000000000 +0100
@@ -29,6 +29,7 @@
 
 static int Iflag;
 static int nflag;
+static int vflag;
 static int curyear;
 static time_t now;
 static char default_fflag[] = "%c%u%r %-3n %10d %17f %t %2i%s";
@@ -52,6 +53,7 @@
 			int r = u8decode(s, &c);
 			if (r < 0) {
 				r = 1;
+				l--;
 				fprintf(out, "%lc", (wint_t)replacement);
 			} else {
 				l -= wcwidth((wchar_t)c);
@@ -338,12 +340,12 @@
 			wleft--;
 			break;
 		case 'u':  // unseen
-			if (strchr(flags, 'F'))
+			if (strchr(flags, 'T'))
+				putchar('x');
+			else if (strchr(flags, 'F'))
 				putchar('*');
 			else if (msg && !strchr(flags, 'S'))
 				putchar('.');
-			else if (strchr(flags, 'T'))
-				putchar('x');
 			else
 				putchar(' ');
 			wleft--;
@@ -489,16 +491,17 @@
 int
 main(int argc, char *argv[])
 {
-	pid_t pid1 = -1;
+	pid_t pager_pid = -1;
 
 	int c;
-	while ((c = getopt(argc, argv, "If:n")) != -1)
+	while ((c = getopt(argc, argv, "If:nv")) != -1)
 		switch (c) {
 		case 'I': Iflag++; break;
 		case 'f': fflag = optarg; break;
 		case 'n': nflag = 1; break;
+		case 'v': vflag = 1; break;
 		default:
-			fprintf(stderr, "Usage: mscan [-n] [-f format] [-I] [msgs...]\n");
+			fprintf(stderr, "Usage: mscan [-Inv] [-f format] [msgs...]\n");
 			exit(1);
 		}
 
@@ -528,8 +531,8 @@
 		if (!pg)
 			pg = getenv("PAGER");
 		if (pg && *pg && strcmp(pg, "cat") != 0) {
-			pid1 = pipeto(pg);
-			if (pid1 < 0)
+			pager_pid = pipeto(pg);
+			if (pager_pid < 0)
 				fprintf(stderr,
 				    "mscan: spawning pager '%s': %s\n",
 				    pg, strerror(errno));
@@ -567,10 +570,12 @@
 		i = blaze822_loop1(":", oneline);
 	else
 		i = blaze822_loop(argc-optind, argv+optind, oneline);
-	fprintf(stderr, "%ld mails scanned\n", i);
 
-	if (pid1 > 0)
-		pipeclose(pid1);
+	if (pager_pid > 0)
+		pipeclose(pager_pid);
+
+	if (vflag)
+		fprintf(stderr, "%ld mails scanned\n", i);
 
 	return 0;
 }
diff -Nru mblaze-0.4/msed.c mblaze-0.5.1/msed.c
--- mblaze-0.4/msed.c	2018-08-15 17:16:45.000000000 +0200
+++ mblaze-0.5.1/msed.c	2019-03-03 17:12:56.000000000 +0100
@@ -28,7 +28,7 @@
 #define APPC(c) do { if (b >= bufe) return str; *b++ = c; } while (0)
 
 	regex_t srchrx;
-	regmatch_t pmatch[10];
+	regmatch_t pmatch[10] = {{0}};
 	if (regcomp(&srchrx, srch, iflag ? REG_ICASE : 0) != 0)
 		return str;
 
@@ -218,6 +218,8 @@
 					free(flags);
 
 					break;
+				case '\0':
+					break;
 				default:
 					fprintf(stderr, "msed: unknown command: '%c'\n", *e);
 					exit(1);
diff -Nru mblaze-0.4/mshow.c mblaze-0.5.1/mshow.c
--- mblaze-0.4/mshow.c	2018-08-15 17:16:45.000000000 +0200
+++ mblaze-0.5.1/mshow.c	2019-03-03 17:12:56.000000000 +0100
@@ -35,6 +35,7 @@
 struct message *filters;
 
 static int mimecount;
+static int extractcount;
 static int safe_output;
 
 static int reply_found;
@@ -224,6 +225,11 @@
 		}
 		setenv("PIPE_CONTENTTYPE", ct, 1);
 
+		char *messageid = blaze822_hdr(msg, "message-id");
+		if (messageid) {
+			setenv("MESSAGE_ID", messageid, 1);
+		}
+
 		char *output;
 		size_t outlen;
 		int e = filter(body, bodylen, cmd, &output, &outlen);
@@ -326,6 +332,10 @@
 	struct message *imsg = 0;
 	while (blaze822_multipart(msg, &imsg)) {
 		m++;
+
+		if (blaze822_bodylen(imsg) == 0)
+			continue;
+
 		char *ict = blaze822_hdr(imsg, "content-type");
 		if (!ict)
 			ict = fallback_ct;
@@ -521,6 +531,7 @@
 					printf("%s\n", bufptr);
 					writefile(bufptr, body, bodylen);
 				}
+				extractcount++;
 			} else if (filename &&
 			    fnmatch(a, filename, FNM_PATHNAME) == 0) {
 				// extract by name
@@ -541,6 +552,7 @@
 					printf("\n");
 					writefile(filename, body, bodylen);
 				}
+				extractcount++;
 			}
 		}
 	}
@@ -551,6 +563,8 @@
 void
 extract_cb(char *file)
 {
+	while (*file == ' ' || *file == '\t')
+		file++;
 	struct message *msg = blaze822_file(file);
 	if (!msg)
 		return;
@@ -833,5 +847,10 @@
 	if (pid1 > 0)
 		pipeclose(pid1);
 
+	if ((xflag || Oflag) && extractcount < argc-optind) {
+		fprintf(stderr, "mshow: could not extract all attachments\n");
+		return 1;
+	}
+
 	return 0;
 }
diff -Nru mblaze-0.4/mthread.c mblaze-0.5.1/mthread.c
--- mblaze-0.4/mthread.c	2018-08-15 17:16:45.000000000 +0200
+++ mblaze-0.5.1/mthread.c	2019-03-03 17:12:56.000000000 +0100
@@ -64,7 +64,7 @@
 	} else {
 		// invent new message-id for internal tracking
 		static long i;
-		char buf[32];
+		char buf[40];
 		snprintf(buf, sizeof buf, "thread%08ld@localhost", ++i);
 		return strdup(buf);
 	}
@@ -307,7 +307,8 @@
 			c->mid = c->child->mid;
 			c->file = c->child->file;
 			c->msg = c->child->msg;
-			c->date = c->child->date;
+			if (c->child->date > c->date)
+				c->date = c->child->date;
 			c->optional = c->child->optional;
 			c->child = c->child->child;
 		}
diff -Nru mblaze-0.4/NEWS.md mblaze-0.5.1/NEWS.md
--- mblaze-0.4/NEWS.md	2018-08-15 17:16:45.000000000 +0200
+++ mblaze-0.5.1/NEWS.md	2019-03-03 17:12:56.000000000 +0100
@@ -1,3 +1,29 @@
+## 0.5.1 (2019-03-03)
+
+* mdeliver: preserve mtime in mrefile
+* mdirs: add -0 to separate folders by NUL characters
+* Fixes for buffer-overflows, found by fuzzing.
+* Fixes for memleaks.
+
+## 0.5 (2019-02-09)
+
+* New tool msearch to wrap several mail indexers.
+* New zsh completion _mblaze.
+* mnext/mprev were removed (you can call `mless +`/`mless -`).
+* The GnuPG tools in contrib/ now use gpg2.
+* mshow exits with error if it could not extract all attachments
+* mrep: add -noquote to disable quoting the message replied to
+* mdeliver: keep permissions of messages
+* mcom: aborting the editor is now more like delete than cancel
+* mcom: add -send to send directly without editing
+* mcom: check if mail is formatted sensibly
+* mpick: new flag -v for statistics
+* mscan: new flag -v for statistics
+* magrep: add -h, which is like -p but doesn't print the file name
+* mscan: prioritize displaying trashed mail over other markers
+* mpick: fix off-by-one in expression parsing
+* Many bug fixes
+
 ## 0.4 (2018-08-15)
 
 * New tool mrefile to move or copy messages.
diff -Nru mblaze-0.4/README mblaze-0.5.1/README
--- mblaze-0.4/README	2018-08-15 17:16:45.000000000 +0200
+++ mblaze-0.5.1/README	2019-03-03 17:12:56.000000000 +0100
@@ -19,7 +19,7 @@
      mdeliver(1)  deliver messages or import mbox file
      mdirs(1)     list maildir folders, recursively
      mexport(1)   export messages as mbox file
-     mflag(1)     manipulate maildir flags
+     mflag(1)     manipulate maildir message flags
      mflow(1)     reflow format=flowed plain text messages
      mfwd(1)      forward messages
      mgenmid(1)   generate a Message-ID
@@ -128,6 +128,6 @@
      To the extent possible under law, the creator of this work has waived all
      copyright and related or neighboring rights to this work.
 
-           http://creativecommons.org/publicdomain/zero/1.0/
+     http://creativecommons.org/publicdomain/zero/1.0/
 
 Void Linux                      January 6, 2018                     Void Linux
diff -Nru mblaze-0.4/rfc2047.c mblaze-0.5.1/rfc2047.c
--- mblaze-0.4/rfc2047.c	2018-08-15 17:16:45.000000000 +0200
+++ mblaze-0.5.1/rfc2047.c	2019-03-03 17:12:56.000000000 +0100
@@ -130,6 +130,10 @@
 	iconv_t ic = (iconv_t)-1;
 	char *srcenc = 0;
 
+	// need space for trailing nul
+	if (dlen-- == 0)
+		return 0;
+
 	char *startdst = dst;
 	size_t startdlen = dlen;
 
diff -Nru mblaze-0.4/rfc2231.c mblaze-0.5.1/rfc2231.c
--- mblaze-0.4/rfc2231.c	2018-08-15 17:16:45.000000000 +0200
+++ mblaze-0.5.1/rfc2231.c	2019-03-03 17:12:56.000000000 +0100
@@ -54,8 +54,10 @@
 				if (!srcenc)
 					return 0;
 				sbuf = strchr(sbuf+1, '\'');
-				if (!sbuf)
+				if (!sbuf) {
+					free(srcenc);
 					return 0;
+				}
 				sbuf++;
 			}
 			while (sbuf < ebuf && dst < dstend) {
diff -Nru mblaze-0.4/safe_u8putstr.c mblaze-0.5.1/safe_u8putstr.c
--- mblaze-0.4/safe_u8putstr.c	2018-08-15 17:16:45.000000000 +0200
+++ mblaze-0.5.1/safe_u8putstr.c	2019-03-03 17:12:56.000000000 +0100
@@ -34,8 +34,11 @@
 				fputc(0xc0 | (*s >> 6), stream);
 				fputc(0x80 | (*s & 0x3f), stream);
 			}
-		} else if (c < 32 && 
+		} else if (c < 32 &&
 		    *s != ' ' && *s != '\t' && *s != '\n' && *s != '\r') {
+			// NUL
+			if (l == 0)
+				l = 1;
 			// C0
 			fputc(0xe2, stream);
 			fputc(0x90, stream);
diff -Nru mblaze-0.4/seq.c mblaze-0.5.1/seq.c
--- mblaze-0.4/seq.c	2018-08-15 17:16:45.000000000 +0200
+++ mblaze-0.5.1/seq.c	2019-03-03 17:12:56.000000000 +0100
@@ -467,44 +467,53 @@
 	return r;
 }
 
+int mystrverscmp(const char *, const char *);
+
+static int
+mailsort(const struct dirent **a, const struct dirent **b)
+{
+	return mystrverscmp((*a)->d_name, (*b)->d_name);
+}
+
 static long
 iterdir(char *dir, void (*cb)(char *))
 {
-	DIR *fd, *fd2;
-	struct dirent *d;
+	struct dirent **namelist;
 
-	long i = 0;
+	int n;
+
+	char sub[PATH_MAX];
+	snprintf(sub, sizeof sub, "%s/cur", dir);
 
-	fd = opendir(dir);
-	if (!fd) {
+	char *m = "/cur";
+
+	n = scandir(sub, &namelist, 0, mailsort);
+	if (n == -1 && (errno == ENOENT || errno == ENOTDIR)) {
+		m = "";
+		n = scandir(dir, &namelist, 0, mailsort);
+	}
+		
+	if (n == -1) {	
 		if (errno == ENOTDIR)
 			cb(dir);
 		return 1;
 	}
 
-	char sub[PATH_MAX];
-	snprintf(sub, sizeof sub, "%s/cur", dir);
-	fd2 = opendir(sub);
-	if (fd2) {
-		closedir(fd);
-		fd = fd2;
-	}
-
-	while ((d = readdir(fd))) {
+	long i = 0;
+	for (i = 0; i < n; i++) {
+		if (namelist[i]->d_name[0] != '.')
 #if defined(DT_REG) && defined(DT_UNKNOWN)
-		if (d->d_type != DT_REG && d->d_type != DT_UNKNOWN)
-			continue;
+		if (namelist[i]->d_type == DT_REG ||
+		    namelist[i]->d_type == DT_UNKNOWN)
 #endif
-		if (d->d_name[0] == '.')
-			continue;
-		if (fd2)
-			snprintf(sub, sizeof sub, "%s/cur/%s", dir, d->d_name);
-		else
-			snprintf(sub, sizeof sub, "%s/%s", dir, d->d_name);
-		cb(sub);
-		i++;
+		{
+			snprintf(sub, sizeof sub, "%s%s/%s",
+			    dir, m, namelist[i]->d_name);
+			cb(sub);
+		}
+		free(namelist[i]);
 	}
-	closedir(fd);
+	free(namelist);
 
 	return i;
 }
diff -Nru mblaze-0.4/t/1000-mmime.t mblaze-0.5.1/t/1000-mmime.t
--- mblaze-0.4/t/1000-mmime.t	2018-08-15 17:16:45.000000000 +0200
+++ mblaze-0.5.1/t/1000-mmime.t	2019-03-03 17:12:56.000000000 +0100
@@ -9,7 +9,7 @@
 Body
 EOF
 
-# https://github.com/chneukirchen/mblaze/issues/20
+# https://github.com/leahneukirchen/mblaze/issues/20
 
 check 'mime -r runs' 'mmime -r <tmp >tmp2'
 check 'no overlong lines' 'awk "{if(length(\$0)>=80)exit 1}" <tmp2'
diff -Nru mblaze-0.4/VERSION mblaze-0.5.1/VERSION
--- mblaze-0.4/VERSION	2018-08-15 17:16:45.000000000 +0200
+++ mblaze-0.5.1/VERSION	2019-03-03 17:12:56.000000000 +0100
@@ -1 +1 @@
-0.4
+0.5.1

Reply to: