Compare commits

...

91 Commits
v1.0 ... master

Author SHA1 Message Date
Leah Neukirchen 6adf988980 mdeliver: fix too eager use of MAILDIR_COLON 24 hours ago
midipix b078f8c19e Maildir: support winnt/ntfs based systems (currently midipix).
Per the Maildir specification, the info (trailing) part of message
filenames is identified by a <colon><spec_version><comma>; however,
on WINNT/NTFS, a colon may not be part of a filename, therefore use
a <semicolon> instead. Since the default remains to use a <colon>,
all targets (other than those definining the __winnt__ macro)
should remain unaffected by this change.
1 day ago
Silvan Jegen d5e202b30f fix calloc argument order
These were found through gcc warnings.

Message-ID: <20240518093029.4799-1-s.jegen@gmail.com>
4 weeks ago
Leah Neukirchen a4502e83c4 mseq: delay loading of the sequence until we need it
This fixes many cases where mseq is called from a wrapper
but only file names are passed.

Reported by ccx.
1 month ago
Leah Neukirchen cf5493bb19 mmime: don't create empty lines when wrapping long words in headers.
Reported by Solène Rapenne.
3 months ago
Leah Neukirchen 76f5656401 mblaze-profile: mention Delivered-To
This was forgotten as part of e1e5bfe5f.
5 months ago
Johannes Thyssen Tishman 5310c4ea38 mcolor: Add coloring support for diffs
Closes: #249 [via git-merge-pr]
9 months ago
Leah Neukirchen 59ee2e0ab9 mgpg: use quiet to only print errors
Else it can happen that gpg output is prepended to the message we
want to decode, and recursive decoding fails.
11 months ago
Meudwy 07d064fc11 mdirs: add Maildir profile key
When `mdirs` is executed without any arguments, look for the `Maildir`
key in the profile and use that instead (if set).

Closes: #245 [via git-merge-pr]
11 months ago
Meudwy 75de7d47da minc: read directory names from stdin
This matches `mlist` where it can take directories as arguments or via
stdin.

Closes: #244 [via git-merge-pr]
11 months ago
Leah Neukirchen 43f2cb8b49 mscan: only spawn pager when stdout is a tty
We still get the terminal size for when users manually page, e.g.
mscan | less

This is consistent with how mshow already works.

Fixes #243.
11 months ago
Johannes Thyssen Tishman 23a9e7022e Quote command expansion to prevent word splitting
This is necessary to support maildir paths that contain spaces.

Closes: #242 [via git-merge-pr]
12 months ago
Johannes Thyssen Tishman ce435c8f6f Add Drafts profile key
Allow the user to set a Drafts key in profile to store draft messages
and sent messages separately if Outbox is set.
1 year ago
Mark Hills 786238551a Document a reasonable practice for deleting mails
Discussed in #236.
1 year ago
Leah Neukirchen 1838158ef1 blaze822_priv.h: use proper lc/uc
Mail headers may contain characters where the simple definition breaks,
which results in wrong formatting on output.

Fixes #235.
1 year ago
Leah Neukirchen cccf01cb9a mrep/mbnc: use mseq -r as leading whitespace is now significant.
Reported by lhynes.
2 years ago
Leah Neukirchen 2365f48f06 mcolor: print out form-feed 2 years ago
Michael Forney 74e77845c3 mmime: include strings.h for strncasecmp 2 years ago
Michael Forney 1babebc12c mlist: use fixed-width integer types for struct linux_dirent64 d_ino and d_off 2 years ago
codesoap 8d543fdb21 mcom: allow spaces in sequence for mbnc and mrep
Closes: #231 [via git-merge-pr]
2 years ago
Omar Polo 9d66764434 mrefile/mdeliver: -M needs the `fattr' pledge promise for utimes(2)
Message-Id: <2Z5ISO2SNIGZU.2E8B1F41F84RV@venera>
2 years ago
gco e6813fd07c solaris needs termios.h and __EXTENSIONS__ for TIOCGWINSZ and winsize 2 years ago
gco 877569a6f3 if -fstack-protector-strong is used, it needs to be specified for both compiling and linking
Closes: #229 [via git-merge-pr]
2 years ago
jgart bbc754e2a1 List commands ~respectively
Closes: #228 [via git-merge-pr]
2 years ago
Alyssa Ross bb3dacc604 blaze822: don't try to open /dev/stdin
Use the same check is blaze822() as in blaze822_file() to ensure that
we don't try to open /dev/stdin, which is non-POSIX.
Message-Id: <20220523170921.2623516-1-hi@alyssa.is>
2 years ago
Julian Rother 793e22ecb7 mthread: reduce memory usage
mthread keeps header data of all messages in memory until it exits without
ever using it. With this change it frees the header data of each message
right after processing it.

Closes: #222 [via git-merge-pr]
2 years ago
Leah Neukirchen 47c5707d50 add contrib/mopenall 2 years ago
Leah Neukirchen cd5bc471f3 man: misc manpages nits
Found by Omar Polo.
2 years ago
Lucas e951b0ba27 mmime: allow to relax body line length limit
Message-Id: <10e22a318b5c70d89432d85ecd8cb8ce1796a51d.1643116719.git.lucas@sexy.is>
2 years ago
Leah Neukirchen fa27d73ddd mshow: don't spawn pager with -t 2 years ago
Leah Neukirchen 3496545358 mflow: fixed lines were not wrapped, add test suite
Reported by ninewise.
2 years ago
Leah Neukirchen a60147f633 NEWS.md: update for 1.2 3 years ago
Stacy Harper e1e5bfe5ff mcom: take Delivered-To into account for choosing From address
I just received a mail coming from a Google Group mail list and it had
the group email as To. So my reply used the default configured mail as
from and not the correct one.

Googles mails provide the target email as Delivered-To. This patch add
this check before the To to prevent this error.

Closes: #217 [via git-merge-pr]
3 years ago
Leah Neukirchen e44aaad66e mlesskey.example: clarify installation 3 years ago
Leah Neukirchen b962fb6be0 rfc2047: skip whitespace everywhere during base64 decoding
> The encoded output stream must be represented in lines of no more
> than 76 characters each.  All line breaks or other characters not
> found in Table 1 must be ignored by decoding software.  In base64
> data, characters other than those in Table 1, line breaks, and other
> white space probably indicate a transmission error, about which a
> warning message or even a message rejection might be appropriate
> under some circumstances.
3 years ago
Leah Neukirchen 958e3e08bd mless: prefer setting LESSKEYIN and .mlesskey
Fixes #215.
3 years ago
Leah Neukirchen fcd8428b72 mrep/mbnc/mfwd: fix sequence after changing flags of (likely) cur
Else tools like mless lose track of cur and jump around after these
actions.

Closes #214.
3 years ago
Leah Neukirchen 41bd429452 blaze822: blaze822/blaze822_mem: detect line ending before scanning end of header
A mail using CRLF which contained (for some reason) a LFLF pair would
be misparsed as the header was read until the LFLF.

Instead, scan for the first LF, check if it's preceded by CR,
and then search for the proper header terminator only.

Closes #212.
3 years ago
Leah Neukirchen 4be6e0ce91 blaze822: remove blaze822_mmap, never used. 3 years ago
Leah Neukirchen 4ccf2f08c1 mmime: ensure no empty parts are generated after an include.
Reported by lhynes.
3 years ago
Léo Villeveygoux 65d43e3cea mpick: remove dead code
It would load the seq file even when unused (and complain when it's not found).
Message-Id: <20210706174750.246026-1-l@vgx.fr>
3 years ago
Leah Neukirchen 386b65fcb4 mblaze-profile.5: clarify Reply-From 3 years ago
Leah Neukirchen 014f82ef0c t/1000-mmime.t: add tests for recent changes 3 years ago
Leah Neukirchen d324b92dd4 mmime: keep (none) whitespace after quoted strings 3 years ago
Leah Neukirchen 0370916357 mmime: generate valid empty mime multipart/mixed when no body is given 3 years ago
Leah Neukirchen 5c7432aa7b mcom: use mhdr -d -A even when extracting From: headers
We only want to decode the display part of the addresses only.

Mails with a From: like
  From: "Non-ASCII Lastname, Firstname" <mail@example.org>
resulted in To: lines like
  To: Non-ASCII Lastname, Firstname <mail@example.org>
which would send to two addresses.  Use -A to ensure proper decoding
and quoting, even if its just a single address in From.
3 years ago
Leah Neukirchen 4f1f0ea2b3 maddr: decode only display part of address
RFC 2047.6.2:
   NOTE: Decoding and display of encoded-words occurs *after* a
   structured field body is parsed into tokens.
3 years ago
Leah Neukirchen 0ed7f72d31 mhdr: print_addresses: decode only display part of address
RFC 2047.6.2:
   NOTE: Decoding and display of encoded-words occurs *after* a
   structured field body is parsed into tokens.
3 years ago
Leah Neukirchen 4e0de11300 mmime: print_header: encode quoted-strings at once
This is maybe a foul compromise between correctness and complexity of
implementation, but it should do the right thing in most cases, and
does not require fully parsing and reconstructing all headers that can
contain phrases.

An 'encoded-word' MUST NOT appear within a 'quoted-string'.  We thus
completely encode the quoted-string (if necessary) as a single
encoded-word, and strip off the quotes.

This should fix encoding of addresses that have both non-ASCII and
special chars such as , and ;.
3 years ago
Leah Neukirchen 9713264f70 mmime: be more careful when qp-encoding in headers
Characters such as , or ; mustn't appear in qp-encoded strings,
as they have a meaning in phrases.  To be safe, encode all special
characters except for the safe ones in RFC 2047 5.(3).

_ is dealt with already.
3 years ago
Leah Neukirchen 23941c6c19 t/1500-maddr.t: deactivate invalid test case
An 'encoded-word' MUST NOT appear in any portion of an 'addr-spec'.
3 years ago
Dominik Honnef 6e8e4e01ed mless: list same number of mails whether we're at the beginning or end 3 years ago
Leah Neukirchen 6684f74f07 mblaze.7: IRC moved to libera.chat. 3 years ago
Leah Neukirchen 605509cc06 t/1900-mdeliver.t: fix plan 3 years ago
Leah Neukirchen 2b517f97c0 msed: match header names case insensitively
Found by skarnet.
3 years ago
Leah Neukirchen c4008e43d9 mexport: use UTC timestamp in the postmark line 3 years ago
Leah Neukirchen ce900601cb mexport: default timestamp to unix second 0, not -1
This is better recognizable as "no data".
3 years ago
Leah Neukirchen 41c681362d NEWS.md: update 3 years ago
Leah Neukirchen 8ba44643e4 t/1900-mdeliver.t: add test for malformed mboxes 3 years ago
Leah Neukirchen 669af4ffca mdeliver: ignore last empty line of mbox entries
https://www.loc.gov/preservation/digital/formats/fdd/fdd000383.shtml
> Each message is immediately prefaced by a separation line and
> terminated by an empty line.

Bug discovered by skarnet.

Fixes #207.
3 years ago
Leah Neukirchen 7d04932813 mexport: ensure mbox entries are separated by an empty line
https://www.loc.gov/preservation/digital/formats/fdd/fdd000383.shtml
> Each message is immediately prefaced by a separation line and
> terminated by an empty line.

Bug discovered by skarnet.
3 years ago
Leah Neukirchen 5b52110399 mmime: do not duplicate Content* headers
Do not add additional Content-Type and Content-Transfer-Encoding headers
when using mmime on input already containing them.

Do not reencode the message if Content-Transfer-Encoding is set.

Based on a patch by Felix Van der Jeugt and duncaen.
3 years ago
Leah Neukirchen 93e8a4a892 mcom: also read $VISUAL to find the editor
Closes #206.
3 years ago
Leah Neukirchen e8981b723d safe_u8putstr: add oneline mode that prints CR/LF as C0 sequences 3 years ago
Leah Neukirchen 709f8f1121 mdeliver.1: spelling 3 years ago
Leah Neukirchen 41cf6272a5 mdeliver.1: small tweaks 3 years ago
Leah Neukirchen ff7537f6a7 GNUmakefile: run tests without stdin connected to a terminal 3 years ago
Leah Neukirchen 741972a416 t/8000-mflag.t: fix when stdin is not a tty 3 years ago
Leah Neukirchen c2d497b139 t/6000-msort.t: fix when stdin is not a tty 3 years ago
Leah Neukirchen 2d6de31cd7 t/3000-magrep.t: fix when stdin is not a tty 3 years ago
Leah Neukirchen e96f0707de mshow: show last part of multipart/mixed when none matched
This is in conformance with RFC 1341.
3 years ago
Leah Neukirchen 318ac214f1 mshow: add "-A all" to render all attachments
Closes #198.
3 years ago
nicoo 8e0d57425f Fix typo in NEWS
Closes: #199 [via git-merge-pr]
3 years ago
Leah Neukirchen 4fcb4ae0a4 NEWS.md: update for 1.1 3 years ago
codesoap 8ad63494bf mcom: do not match shebang in needs_multipart()
Avoids matching lines like '#!/usr/bin/env sh'.

Closes: #196 [via git-merge-pr]
3 years ago
Leah Neukirchen c525c0097e mmime: allow # in include lines, used to override the content-disposition 4 years ago
Leah Neukirchen f95439a988 rfc2045: blaze822_multipart: parse rest when boundary is missing
Either the mail got truncated or was wrongly generated, try to go on
by taking the remains of the buffer as part.
4 years ago
James Rowe bc021c53b6 Allow gpg and gpg2 executables for gnupg interaction
Closes: #193 [via git-merge-pr]
4 years ago
Leah Neukirchen 6a8543e321 rfc2045: mymemmemnl: make more check idiomatic 4 years ago
Leah Neukirchen c43538fae9 rfc2045: fix overread in blaze822_mime_parameter
This triggered an invalid read in strchr on 'Content-Type: text/html; '
4 years ago
Leah Neukirchen 5167bbfe39 rfc2045: ensure mime boundaries end with a newline or - 4 years ago
Leah Neukirchen 5e6151e3bf mcom: detect and report mmime errors
Closes #191.
4 years ago
Leah Neukirchen 48c20b7efa mmime: propagate errors when attaching a file fails 4 years ago
nicoo a825b4f8f4 Fix typo in NEWS
Closes: #190 [via git-merge-pr]
4 years ago
Leah Neukirchen b076d09f3a add contrib/mmailto
Closes #189.

This does not work with terminal emulators that don't use -e or
properly support arbitrary arguments after it.  Here's a nickel,
get yourself a proper terminal emulator.
4 years ago
James Rowe 5d8f070e26 contrib/_mblaze: silence mseq errors
On the first run or if you manually delete your sequence file, mseq
errors will break your prompt when you hit tab.

Closes: #188 [via git-merge-pr]
4 years ago
Leah Neukirchen 4d566a6afe t/7000-mseq.t: use printf for robustness 4 years ago
James Rowe 0180f649d2 mcom: allow tilde prefixed path for profile's outbox setting
Closes: #187 [via git-merge-pr]
4 years ago
Michael Forney 02e4cf4001 mpick: use function pointer type for callback
Though POSIX requires this conversion to work correctly (for dlsym),
it is not valid in ISO C, so it is better to just uses the appropriate
function pointer type.

Closes: #185 [via git-merge-pr]
4 years ago
Michael Forney 50dfdf5605 include strings.h for str(n)casecmp
These POSIX functions are declared in strings.h, so include this
header explicitly instead of relying on the libc's default feature-test
macros to include it through string.h.
4 years ago
Leah Neukirchen d2621a715b README: update 4 years ago

@ -1,5 +1,6 @@
CFLAGS?=-g -O2 -fstack-protector-strong -D_FORTIFY_SOURCE=2
override CFLAGS:=-Wall -Wno-switch -Wextra $(CFLAGS)
LDFLAGS?=-fstack-protector-strong
LDLIBS=-lrt
OS := $(shell uname)
@ -25,10 +26,10 @@ SCRIPT = mcolor mcom mless mmkdir mquote museragent
all: $(ALL) museragent
$(ALL) : % : %.o
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 mystrverscmp.o
maddr magrep mdeliver mdirs mexport mflag mflow mgenmid mhdr mpick mscan msed \
mshow msort mthread : blaze822.o mymemmem.o mytimegm.o
maddr magrep mdeliver mdirs mexport mflag mgenmid mhdr minc mlist mpick mscan \
msed mseq 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
@ -50,7 +51,7 @@ clean: FRC
-rm -f $(ALL) *.o museragent
check: FRC all
PATH=$$(pwd):$$PATH prove -v
PATH=$$(pwd):$$PATH prove -v </dev/null
install: FRC all
mkdir -p $(DESTDIR)$(BINDIR) \

@ -1,9 +1,28 @@
## 1.2 (2021-12-12)
* Caution! The tools mdeliver and mexport were buggy in handling and
generation of trailing empty lines in MBOX-RD. Do not import
mbox files generated by mexport >=1.2 with mdeliver <1.2 if you
require verbatim message delivery.
* mshow: add "-A all" to render all attachments
* msed: match header names case insensitively
* mless: prefer setting LESSKEYIN and using .mlesskey
* mcom: take Delivered-To into account for choosing From address
* Many bug fixes.
## 1.1 (2021-01-14)
* mcom: allow tilde prefixed path for profile's outbox setting
* mcom: detect and report mmime errors
* add contrib/mmailto, a handler for mailto: links
* Bug fixes.
## 1.0 (2020-09-12)
* Caution! Backwards incompatible changes:
* As a message name, `-` now refers to the message on the standard input,
and not the the previous message anymore. Use `.-` to refer to
the previous message in a short way.
and not the previous message anymore. Use `.-` to refer to the previous
message in a short way.
The tools will print a warning if you use `-` and the standard input
comes from a TTY.
* mpick: use the -F flag to read script files.

@ -118,9 +118,10 @@ AUTHORS
Leah Neukirchen <leah@vuxu.org>
There is a mailing list available at mblaze@googlegroups.com (to
subscribe, send a message to mblaze+subscribe@googlegroups.com) and an
IRC channel #vuxu on irc.freenode.net. Please report security-related
bugs directly to the author.
subscribe, send a message to mblaze+subscribe@googlegroups.com); archives
are available at https://inbox.vuxu.org/mblaze/. There also is an IRC
channel #vuxu on irc.libera.chat. Please report security-related bugs
directly to the author.
LICENSE
mblaze is in the public domain.
@ -130,4 +131,4 @@ LICENSE
http://creativecommons.org/publicdomain/zero/1.0/
Void Linux January 6, 2018 Void Linux
Void Linux January 18, 2020 Void Linux

@ -1 +1 @@
1.0
1.2

@ -420,10 +420,19 @@ unfold_hdr(char *buf, char *end)
compress_hdr(l, end);
}
static int
is_crlf(char *s, size_t len)
{
char *firsteol = memchr(s, '\n', len);
return firsteol && firsteol > s && firsteol[-1] == '\r';
}
struct message *
blaze822(char *file)
{
int fd;
int crlf;
ssize_t rd;
char *buf;
ssize_t bufalloc;
@ -434,7 +443,10 @@ blaze822(char *file)
if (!mesg)
return 0;
fd = open(file, O_RDONLY);
if (strcmp(file, "/dev/stdin") == 0)
fd = dup(0);
else
fd = open(file, O_RDONLY);
if (fd < 0) {
free(mesg);
return 0;
@ -466,15 +478,21 @@ blaze822(char *file)
close(fd);
return 0;
}
if ((end = mymemmem(buf-overlap+used, rd+overlap, "\n\n", 2))) {
end++;
break;
if (used == 0) {
crlf = is_crlf(buf, rd);
}
if ((end = mymemmem(buf-overlap+used, rd+overlap, "\r\n\r\n", 4))) {
end++;
end++;
break;
if (crlf) {
if ((end = mymemmem(buf-overlap+used, rd+overlap, "\r\n\r\n", 4))) {
end++;
end++;
break;
}
} else {
if ((end = mymemmem(buf-overlap+used, rd+overlap, "\n\n", 2))) {
end++;
break;
}
}
used += rd;
@ -502,11 +520,15 @@ blaze822_mem(char *src, size_t len)
if (!mesg)
return 0;
if ((end = mymemmem(src, len, "\n\n", 2))) {
mesg->body = end+2;
} else if ((end = mymemmem(src, len, "\r\n\r\n", 4))) {
mesg->body = end+4;
if (is_crlf(src, len)) {
if ((end = mymemmem(src, len, "\r\n\r\n", 4)))
mesg->body = end+4;
} else {
if ((end = mymemmem(src, len, "\n\n", 2)))
mesg->body = end+2;
}
if (!end) {
end = src + len;
mesg->body = end;
mesg->bodyend = end;
@ -664,55 +686,6 @@ error:
return 0;
}
struct message *
blaze822_mmap(char *file)
{
int fd = open(file, O_RDONLY);
if (fd < 0)
return 0;
struct stat st;
if (fstat(fd, &st) < 0)
goto error;
size_t len = st.st_size;
struct message *mesg = malloc(sizeof (struct message));
if (!mesg)
goto error;
char *buf = mmap(0, len+1, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
if (buf == MAP_FAILED) {
free(mesg);
perror("mmap");
goto error;
}
close(fd);
char *end;
if ((end = mymemmem(buf, len, "\n\n", 2))) {
mesg->body = end+2;
} else if ((end = mymemmem(buf, len, "\r\n\r\n", 4))) {
mesg->body = end+4;
} else {
end = buf + len;
mesg->body = end;
}
unfold_hdr(buf, end);
mesg->msg = mesg->bodychunk = buf;
mesg->end = end;
mesg->bodyend = buf + len;
mesg->orig_header = 0;
return mesg;
error:
close(fd);
return 0;
}
size_t
blaze822_headerlen(struct message *mesg)
{

@ -8,13 +8,23 @@
#define PATH_MAX 4096
#endif
// maildir filename suffix: use a semicolon on winnt/ntfs, otherwise a colon
#ifdef __winnt__
#define MAILDIR_COLON ';'
#define MAILDIR_COLON_SPEC_VER ";2"
#define MAILDIR_COLON_SPEC_VER_COMMA ";2,"
#else
#define MAILDIR_COLON ':'
#define MAILDIR_COLON_SPEC_VER ":2"
#define MAILDIR_COLON_SPEC_VER_COMMA ":2,"
#endif
struct message;
// blaze822.c
struct message *blaze822(char *file); // just header
struct message *blaze822_file(char *file); // header + body (read(2))
struct message *blaze822_mmap(char *file); // header + body (mmap(2))
struct message *blaze822_mem(char *buf, size_t len); // header + body
char *blaze822_hdr_(struct message *mesg, const char *hdr, size_t len);
@ -95,7 +105,7 @@ int slurp(char *filename, char **bufo, off_t *leno);
// safe_u8putstr.c
#include <stdio.h>
void safe_u8putstr(char *s0, size_t l, FILE *stream);
void safe_u8putstr(char *s0, size_t l, int oneline, FILE *stream);
// pipeto.c

@ -12,9 +12,9 @@ struct message {
#define isfws(c) (((unsigned char)(c) == ' ' || (unsigned char)(c) == '\t' || (unsigned char)(c) == '\n' || (unsigned char)(c) == '\r'))
// ASCII lowercase/uppercase without alpha check (wrong for "@[\]^_")
#define lc(c) ((c) | 0x20)
#define uc(c) ((c) & 0xdf)
// 7bit-ASCII only lowercase/uppercase
#define lc(c) (((unsigned)(c)-'A') < 26 ? ((c) | 0x20) : (c))
#define uc(c) (((unsigned)(c)-'a') < 26 ? ((c) & 0xdf) : (c))
// dirent type that can be a mail/dir (following symlinks)
#if defined(DT_REG) && defined(DT_LNK) && defined(DT_UNKNOWN)

@ -14,7 +14,7 @@ _mblaze_message() {
_files "$expl[@]" && ret=0
fi
curmsg=$(mseq .)
curmsg=$(mseq . 2>/dev/null)
if [[ -z $curmsg || ! -f $curmsg ]]; then
_message 'no current sequence'
return $ret
@ -346,7 +346,7 @@ _mshow() {
- regular \
'-n[only print message numbers]' \
'-h[display only given headers]:header list:_mblaze_colon_separated_headers' \
'-A[mixed/alternative preference]:mime type: ' \
'-A[multipart/alternative preference]:mime type: ' \
'-n[don'\''t update current message link]' \
'(-r)-q[print only header]' \
'(-q)-r[print body raw]' \

@ -10,12 +10,18 @@ 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"
if command -v gpg2 >/dev/null; then
GPG=gpg2
else
GPG=gpg
fi
TMPD=$(mktemp -d -t mencrypt.XXXXXX)
trap "rm -rf '$TMPD'" INT TERM EXIT
awk '/^$/,0' "$1" |
mmime |
gpg2 "$key" --armor --encrypt --sign $FLAGS -o "$TMPD/msg.asc" ||
$GPG "$key" --armor --encrypt --sign $FLAGS -o "$TMPD/msg.asc" ||
exit $?
printf 'Version: 1\n' >"$TMPD/version"

@ -1,5 +1,11 @@
#!/bin/sh -e
if command -v gpg2 >/dev/null; then
GPG=gpg2
else
GPG=gpg
fi
tmp=$(mktemp -t mgpg.XXXXXX)
trap "rm -f '$tmp'" INT TERM EXIT
@ -14,7 +20,7 @@ n=$(mshow -t "$tmp" | awk -F: '
/: application\/octet-stream/ {if (supported) print $1}')
if [ "$n" ]; then
mshow -O "$tmp" "$n" | gpg2 -d 2>&1 || exit 0
mshow -O "$tmp" "$n" | $GPG --quiet -d 2>&1 || exit 0
exit 64
fi
exit 63

@ -0,0 +1,58 @@
#!/bin/sh
# mmailto mailto:... - mailto: handler spawning mcom in a terminal emulator
tryterm() {
if [ -z "$TERMINAL" ] && command -v "$1" >/dev/null; then
TERMINAL="$*"
fi
}
tryterm x-terminal-emulator
tryterm urxvt
tryterm xterm
if [ -z "$TERMINAL" ]; then
echo 'No terminal emulator found, set $TERMINAL.' 1>&2
exit 1
fi
IFS='
'
exec $TERMINAL -e mcom $(
awk -v url="$1" '
function decode(s) {
hexdigits = "0123456789abcdef"
for (i = 1; i < length(s); i++) {
if (substr(s, i, 3) ~ /%[0-9a-fA-F][0-9a-fA-F]/) {
c = sprintf("%c", (index(hexdigits, tolower(substr(s, i+1, 1)))-1) * 16 + \
index(hexdigits, tolower(substr(s, i+2, 1)))-1)
if (c == "\n") c = " "
s = substr(s, 1, i-1) c substr(s, i+3)
i += 2
}
}
return s
}
BEGIN {
url = decode(url)
sub(/^mailto:/, "", url)
split(url, parts, "?")
to = parts[1]
split(parts[2], fields, "&")
args[1] = to
for (i in fields) {
split(fields[i], kv, "=")
if (kv[1] != "r") {
args[length(args)+1] = "-" kv[1]
args[length(args)+1] = kv[2]
}
}
for (i in args) {
print decode(args[i])
}
}
'
)

@ -0,0 +1,18 @@
#!/bin/sh -e
# mopenall [MSG] - open every attachements in xdg-open
[ "$#" -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 -B -x "$1" 2>/dev/null
for f in * ; do
xdg-open "$f" &
done
wait
echo rm -r "$dir"
rm -r "$dir"

@ -3,6 +3,12 @@
[ -f "$1" ] || exit 1
if command -v gpg2 >/dev/null; then
GPG=gpg2
else
GPG=gpg
fi
IFS='
'
@ -13,7 +19,7 @@ FROM=$(maddr -a -h from "$1" | sed 1q)
[ "$FROM" ] && key="--default-key=$FROM"
awk '/^$/,0' "$1" | mmime | sed 's/$/ /' >"$TMPD"/content
gpg2 $key --armor --detach-sign -o "$TMPD"/signature.asc "$TMPD"/content ||
$GPG $key --armor --detach-sign -o "$TMPD"/signature.asc "$TMPD"/content ||
exit $?
{

@ -3,6 +3,12 @@
# Needs gpg2 (for OpenPGP) and openssl (for SMIME).
if command -v gpg2 >/dev/null; then
GPG=gpg2
else
GPG=gpg
fi
[ "$#" -eq 0 ] && set -- .
mshow -t "$1" | DOS2UNIX='/ $/!s/$/ /' awk -v "msg=$1" '
@ -14,7 +20,7 @@ signed && content && !signature && indent == si+2 { signature = 0+$1; type = $2
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) " | gpg2 --verify"));
exit(system("mshow -r " q(msg) " | '$GPG' --verify"));
} else if (type == "") {
print("No signature found.")
exit(100)
@ -22,7 +28,7 @@ END {
exit(system("mshow -r -O " q(msg) " " q(content) \
" | sed $DOS2UNIX | " \
" { mshow -O " q(msg) " " q(signature) \
" | gpg2 --verify - /dev/fd/3; } 3<&0"))
" | '$GPG' --verify - /dev/fd/3; } 3<&0"))
} else if (type == "application/pkcs7-signature") {
exit(system("mshow -r -O " q(msg) " " q(signed) \
" | openssl smime -verify"))

@ -57,12 +57,14 @@ addr(char *file)
v = blaze822_chdr(msg, h);
if (v) {
char *disp, *addr;
char vdec[16384];
blaze822_decode_rfc2047(vdec, v, sizeof vdec - 1, "UTF-8");
vdec[sizeof vdec - 1] = 0;
v = vdec;
char ddec[16384];
while ((v = blaze822_addr(v, &disp, &addr))) {
if (disp) {
blaze822_decode_rfc2047(ddec, disp, sizeof ddec - 1, "UTF-8");
ddec[sizeof ddec - 1] = 0;
disp = ddec;
}
if (disp && addr && strcmp(disp, addr) == 0)
disp = 0;

@ -7,6 +7,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include "blaze822.h"
@ -150,7 +151,7 @@ void
magrep(char *file)
{
if (!*header) {
char *flags = strstr(file, ":2,");
char *flags = strstr(file, MAILDIR_COLON_SPEC_VER_COMMA);
if (flags)
match(file, "flags", flags+3);
return;

@ -1,4 +1,4 @@
.Dd February 20, 2017
.Dd January 27, 2024
.Dt MBLAZE-PROFILE 5
.Os
.Sh NAME
@ -49,23 +49,31 @@ The fully qualified domain name used for
.Li Message\&-Id\&:
generation in
.Xr mgenmid 1 .
.It Li Maildir\&:
If set,
.Xr mdirs 1
will use this maildir when no directories are supplied.
.It Li Outbox\&:
If set,
.Xr mcom 1
will create draft messages in this maildir,
and save messages there after sending.
will save messages in this maildir after sending.
.It Li Drafts\&:
If set,
.Xr mcom 1
will create draft messages in this maildir (defaults to Outbox).
.It Li Reply-From\&:
A comma-separated list of mail addresses (formatted like this:
.Sq Li My Name <username@domain> )
that are automatically used for
the
.Li From\&:
header in a reply if present in the
A comma-separated list of display name and mail address pairs,
formatted like this:
.Dl Li Primary Name <myname1@domain1>, Name v.2 <myname2@domain2>, \[dq]Name, My Third\[dq] <myname3@domain3>, ...
The first of these that appears in the
.Li Delivered-To\&: ,
.Li To\&: ,
.Li Cc\&: ,
or
.Li Bcc\&:
header.
header will be used as the
.Li From\&:
address in a reply.
If not set,
.Li Alternate\&-Mailboxes\&:
will be used as a default.

@ -163,6 +163,9 @@ thread it and look at it interactively:
Or you could list the attachments of the 20 largest messages in your INBOX:
.Dl mlist ~/Maildir/INBOX | msort -S | tail -20 | mshow -t
.Pp
Or delete messages beyond a certain age:
.Dl mlist ~/Maildir/INBOX | mpick -t 'mtime < \&"-365d\&"' | xargs rm
.Pp
Or apply the patches from the current message:
.Dl mshow -O . '*.diff' | patch
.Pp
@ -211,7 +214,7 @@ archives are available at
.Lk https://inbox.vuxu.org/mblaze/ .
There also is an IRC channel
.Li #vuxu
on irc.freenode.net.
on irc.libera.chat.
Please report security-related bugs directly to the author.
.Sh LICENSE
.Nm

@ -1,4 +1,4 @@
.Dd February 5, 2020
.Dd April 21, 2021
.Dt MCOM 1
.Os
.Sh NAME
@ -6,7 +6,7 @@
.Nm mfwd ,
.Nm mbnc ,
.Nm mrep
.Nd compose, reply, forward, bounce, send messages
.Nd compose, forward, bounce, reply, send messages
.Sh SYNOPSIS
.Nm mcom
.Op Fl Ar header Ar values\ ...
@ -132,8 +132,10 @@ Preview the draft, using
.El
.Sh ENVIRONMENT
.Bl -tag -width Ds
.It Ev EDITOR
.It Ev EDITOR , Ev VISUAL
Editor used to compose messages.
(Default:
.Xr vi 1 )
.It Ev MBLAZE
Directory containing mblaze configuration files.
(Default:

@ -1,4 +1,4 @@
.Dd July 26, 2016
.Dd February 4, 2021
.Dt MDELIVER 1
.Os
.Sh NAME
@ -35,6 +35,7 @@ an MBOXRD mailbox, split it on
.Dq Li "From "
and deliver each message,
decoding it according to the MBOXRD convention.
In this case,
.Nm
will set the mtime according to the value of
.Sq Li Date\&:

@ -1,4 +1,4 @@
.Dd January 22, 2020
.Dd July 25, 2023
.Dt MDIRS 1
.Os
.Sh NAME
@ -17,6 +17,14 @@ for maildir
folders and prints them,
separated by newlines.
.Pp
If
.Ar dirs
is not present then use
.Sq Li Maildir\&:
from
.Pa "${MBLAZE:-$HOME/.mblaze}/profile"
.Pq if set .
.Pp
To
.Nm ,
a maildir folder is a directory containing
@ -36,10 +44,20 @@ Print folders separated by a NUL character.
.It Fl a
Traverse into all subfolders, without considering the maildir++ name conventions.
.El
.Sh ENVIRONMENT
.Bl -tag -width Ds
.It Ev MBLAZE
Directory containing mblaze configuration.
.Po
Default:
.Pa $HOME/.mblaze
.Pc
.El
.Sh EXIT STATUS
.Ex -std
.Sh SEE ALSO
.Xr find 1
.Xr find 1 ,
.Xr mblaze-profile 5
.Sh AUTHORS
.An Leah Neukirchen Aq Mt leah@vuxu.org
.Sh LICENSE

@ -17,6 +17,9 @@ by moving them from
to
.Pa cur ,
and adjusting the filenames.
If used non-interactively with no specified folders,
.Nm
reads directory names from the standard input.
.Pp
By default, the new filenames are printed,
separated by newlines.

@ -43,6 +43,14 @@ Use
and
.Sq Ic ":p"
to read the next (resp. previous) message.
.Sh FILES
.Bl -tag -width Ds
.It Pa ${MBLAZE:-$HOME/.mblaze}/mlesskey , Pa $HOME/.mlesskey
Additional keybindings loaded for convenience.
.It Pa ${MBLAZE:-$HOME/.mblaze}/mless , Pa $HOME/.mless
Additional compiled keybindings loaded for convenience.
(Only needed for less earlier than version 590.)
.El
.Sh EXIT STATUS
.Ex -std
.Sh SEE ALSO

@ -46,6 +46,14 @@ Override Content-Type for the toplevel part.
Defaults to
.Sq Li multipart/mixed .
.El
.Sh ENVIRONMENT
.Bl -tag -width Ds
.It Ev MBLAZE_RELAXED_MIME
If set,
.Nm Fl c
will relax the body line length check and only consider bodies with lines
longer than 998 characters need to be MIME formatted.
.El
.Sh EXIT STATUS
.Ex -std
.Sh SEE ALSO

@ -29,7 +29,6 @@ Read expression from
.Ar file
and only show matching messages, see
.Sx TESTS .
.Ar file ,
.It Fl T
Include whole thread.
.It Fl t Ar test

@ -1,4 +1,4 @@
.Dd August 24, 2018
.Dd January 17, 2021
.Dt MSHOW 1
.Os
.Sh NAME
@ -47,16 +47,25 @@ instead of the default headers
.Sq Li from\&:subject\&:to\&:cc\&:date\&:reply\&-to\&: .
.It Fl A Ar mimetypes
Define
.Sq Li "mixed/alternative"
.Sq Li "multipart/alternative"
preference.
.Ar mimetypes
is a colon-separated list of
MIME types which will be preferred,
in the order given,
when rendering
.Sq Li "mixed/alternative"
.Sq Li "multipart/alternative"
parts.
If no MIME type matches, the first MIME part will be rendered.
If no MIME type matches, the last MIME part will be rendered.
.Pp
When
.Ar mimetypes
is
.Sq Li all ,
.Nm
will render all parts of a
.Sq Li "multipart/alternative"
part.
.Pp
Defaults to
.Sq Li "text/plain:text/html" .

@ -7,15 +7,19 @@ function so(s) { return sprintf("\033[1m%s\033[0m", s) }
BEGIN { hdr = 1; if ("NO_COLOR" in ENVIRON || match(ENVIRON["TERM"], "^(dumb|network|9term)")) no_color = 1 }
no_color { print; next }
/\r$/ { sub(/\r$/, "") }
/^\014$/ { nextmail = 1; next }
/^$/ { hdr = 0 }
/^\014$/ { nextmail = 1; print(fg(co("FF",232), $0)); next }
/^$/ { hdr = 0; diff = 0 }
/^-- $/ { ftr = 1 }
/^diff -/ { diff = 1 }
/^--- .* ---/ { print fg(co("SEP",242), $0); ftr = 0; sig = 0; next }
/^-----BEGIN .* SIGNATURE-----/ { sig = 1 }
nextmail && /^From:/ { hdr = 1 }
hdr && /^From:/ { print so(fg(co("FROM",119), $0)); next }
hdr { print fg(co("HEADER",120), $0); next }
ftr { print fg(co("FOOTER",244), $0); next }
diff && /^-/ { print fg(co("DIFF_D",160), $0); next }
diff && /^\+/ { print fg(co("DIFF_I",40), $0); next }
diff && /^@/ { print fg(co("DIFF_R",226), $0); next }
/^-----BEGIN .* MESSAGE-----/ ||
/^-----END .* SIGNATURE-----/ { print fg(co("SIG",244), $0); sig = 0; next }
sig { print fg(co("SIG",244), $0); next }

47
mcom

@ -75,7 +75,7 @@ stripempty() {
needs_multipart() {
mhdr -h attach "$1" >/dev/null ||
grep -q '^#[^ ]*/[^ ]* ' "$1"
grep -qE '^#[a-zA-Z]+/[a-zA-Z0-9+.;=#-]+ ' "$1"
}
do_mime() {
@ -92,6 +92,10 @@ do_mime() {
else
mmime -r <"$draft" >"$draftmime"
fi
if [ $? -ne 0 ]; then
printf 'mcom: fix above errors before continuing\n' 1>&2
rm -f "$draftmime"
fi
}
MBLAZE=${MBLAZE:-$HOME/.mblaze}
@ -231,8 +235,10 @@ esac
hdrs="$(printf '%s\n' "${hdrs#$NL}" | mhdr -)"
outbox=$(mhdr -h outbox "$MBLAZE/profile")
if [ -z "$outbox" ]; then
outbox=$(mhdr -h outbox "$MBLAZE/profile" | sed "s:^~/:$HOME/:")
draftbox=$(mhdr -h drafts "$MBLAZE/profile" | sed "s:^~/:$HOME/:")
draftbox="${draftbox:-$outbox}"
if [ -z "$draftbox" ]; then
if [ -z "$resume" ]; then
i=0
while [ -f "snd.$i" ]; do
@ -245,13 +251,13 @@ if [ -z "$outbox" ]; then
draftmime="$draft.mime"
else
if [ -z "$resume" ]; then
draft="$(true | mdeliver -v -c -XD "$outbox")"
draft="$(true | mdeliver -v -c -XD "$draftbox")"
if [ -z "$draft" ]; then
printf '%s\n' "$0: failed to create draft in outbox $outbox." 1>&2
printf '%s\n' "$0: failed to create draft in outbox $draftbox." 1>&2
exit 1
fi
elif [ -z "$draft" ]; then
draft=$(mlist -D "$outbox" | msort -r -M | sed 1q)
draft=$(mlist -D "$draftbox" | msort -r -M | sed 1q)
fi
draftmime="$(printf '%s\n' "$draft" | sed 's,\(.*\)/cur/,\1/tmp/mime-,')"
fi
@ -308,7 +314,7 @@ fi
IFS=$NL
for f in $(mseq -r "$@"); do
printf '%s Forwarded message from %s %s\n\n' \
$SEP "$(mhdr -d -h from "$f")" $SEP
$SEP "$(mhdr -d -A -h from "$f")" $SEP
DISPLAY= mshow -n -N "$f" </dev/null |
sed 's/^-/- &/' # RFC 934
printf '\n%s %s %s\n\n' \
@ -317,7 +323,10 @@ fi
) fi
;;
*mbnc*)
set -- $(mseq -- "$@")
old_ifs="$IFS"
IFS=$NL
set -- $(mseq -r -- "$@")
IFS="$old_ifs"
if [ "$#" -ne 1 ]; then
printf 'mbnc: needs exactly one mail to bounce\n' 1>&2
exit 1
@ -336,11 +345,14 @@ fi
printf 'Resent-Date: %s\n' "$(mdate)"
(
IFS=$NL
cat $(mseq -- "$@")
cat $(mseq -r -- "$@")
)
;;
*mrep*)
set -- $(mseq -- "$@")
old_ifs="$IFS"
IFS=$NL
set -- $(mseq -r -- "$@")
IFS="$old_ifs"
if [ "$#" -ne 1 ]; then
printf 'mrep: needs exactly one mail to reply to\n' 1>&2
exit 1
@ -351,7 +363,7 @@ fi
printf 'Newsgroups: %s\n' "$ng"
else
to=$(mhdr -d -h reply-to "$1")
[ -z "$to" ] && to=$(mhdr -d -h from "$1")
[ -z "$to" ] && to=$(mhdr -d -A -h from "$1")
printf 'To: %s\n' "$to"
printf 'Cc: %s\n' \
"$(mhdr -d -A -h to:cc: "$1" |
@ -364,7 +376,7 @@ fi
printf 'Subject: Re: %s\n' "$(COLUMNS=10000 mscan -f '%S' "$1")"
if ! printf '%s\n' "$hdrs" | awk '{ print }' |
mhdr -h from: - >/dev/null; then
addr=$(maddr -a -h to:cc:bcc: "$1" | replyfrom | head -n1)
addr=$(maddr -a -h delivered-to:to:cc:bcc: "$1" | replyfrom | head -n1)
[ -n "$addr" ] && from=$(maddr -h reply-from "$MBLAZE/profile" | grep -Fi "<$addr>" | head -n1)
[ -n "$addr" ] && [ -z "$from" ] && from=$(maddr -h alternate-mailboxes "$MBLAZE/profile" | grep -Fi "<$addr>" | head -n1)
[ -z "$from" ] && from=$(mhdr -h local-mailbox "$MBLAZE/profile")
@ -436,7 +448,7 @@ while :; do
if $sendmail <"$draftmime"; then
if [ "$outbox" ]; then
mv "$draftmime" "$draft"
mflag -d -S "$draft"
mrefile "$(mflag -d -S "$draft")" "$outbox"
else
rm "$draft" "$draftmime"
fi
@ -450,11 +462,11 @@ while :; do
continue
fi
else
if mmime -c <"$draft"; then
if mmime -c <"$draft" && ! [ "$automime" = 1 ]; then
stampdate "$draft"
if $sendmail <"$draft"; then
if [ "$outbox" ]; then
mflag -d -S "$draft"
mrefile "$(mflag -d -S "$draft")" "$outbox"
else
rm "$draft"
fi
@ -474,6 +486,9 @@ while :; do
*mbnc*) mflag -P -- "$1" ;;
*mfwd*) mflag -P -- "$@" ;;
esac
case "$0" in
*mrep*|*mbnc*|*mfwd*) mseq -f | mseq -S ;;
esac
exit 0
;;
@ -489,7 +504,7 @@ while :; do
;;
e|edit)
c=
if ! ${EDITOR:-vi} "$draft"; then
if ! ${VISUAL:-${EDITOR:-vi}} "$draft"; then
c=d
else
if checksensible "$draft"; then

@ -10,6 +10,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include "blaze822.h"
@ -106,7 +107,7 @@ try_again:
snprintf(tmp, sizeof tmp, "%s/tmp/%s", targetdir, id);
if (try_rename) {
snprintf(dst, sizeof dst, "%s/%s/%s:2,%s",
snprintf(dst, sizeof dst, "%s/%s/%s"MAILDIR_COLON_SPEC_VER_COMMA"%s",
targetdir, cflag ? "cur" : "new", id, Xflag);
if (rename(infilename, dst) == 0)
goto success;
@ -146,6 +147,8 @@ try_again:
int in_header = 1;
int is_old = 0;
int prev_line_empty = 0;
int this_line_empty = 0; // only for mbox parsing
while (1) {
errno = 0;
ssize_t rd = getdelim(&line, &linelen, '\n', infile);
@ -157,8 +160,12 @@ try_again:
char *line_start = line;
if (line[0] == '\n' && (!line[1] ||
(line[1] == '\r' && !line[2])))
(line[1] == '\r' && !line[2]))) {
this_line_empty = Mflag ? 1 : 0;
in_header = 0;
} else {
this_line_empty = 0;
}
if (Mflag && strncmp("From ", line, 5) == 0)
break;
@ -188,8 +195,15 @@ try_again:
}
}
if (fwrite(line_start, 1, rd, outfile) != (size_t)rd)
goto fail;
// print delayed empty line
if (prev_line_empty)
if (fputc('\n', outfile) == EOF)
goto fail;
if (!this_line_empty)
if (fwrite(line_start, 1, rd, outfile) != (size_t)rd)
goto fail;
prev_line_empty = this_line_empty;
}
if (fflush(outfile) == EOF)
goto fail;
@ -243,7 +257,7 @@ try_again:
#endif
}
snprintf(dst, sizeof dst, "%s/%s/%s:2,%s",
snprintf(dst, sizeof dst, "%s/%s/%s"MAILDIR_COLON_SPEC_VER_COMMA"%s",
targetdir, (cflag || is_old) ? "cur" : "new", id,
Xflag ? Xflag : statusflags);
if (rename(tmp, dst) != 0)
@ -271,7 +285,7 @@ refile(char *file)
file++;
// keep flags
char *flags = strstr(file, ":2,");
char *flags = strstr(file, MAILDIR_COLON_SPEC_VER_COMMA);
if (flags)
Xflag = flags + 3;
else
@ -341,7 +355,11 @@ usage2:
if (argc != optind+1)
goto usage2;
xpledge("stdio rpath wpath cpath", "");
xpledge("stdio rpath wpath cpath fattr", "");
if (!preserve_mtime && !Mflag) {
/* drop fattr */
xpledge("stdio rpath wpath cpath", "");
}
targetdir = argv[optind];

@ -3,8 +3,10 @@
#include <dirent.h>
#include <limits.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "blaze822.h"
@ -72,6 +74,33 @@ mdirs(char *fpath)
closedir(dir);
}
char *
profile_maildir()
{
char *f = blaze822_home_file("profile");
struct message *config = blaze822(f);
char *maildir;
static char path[PATH_MAX];
if (!config)
return 0;
if (!(maildir = blaze822_hdr(config, "maildir")))
return 0;
if (strncmp(maildir, "~/", 2) == 0) {
const char *home = getenv("HOME");
if (!home) {
struct passwd *pw = getpwuid(getuid());
home = pw ? pw->pw_dir : "/dev/null/homeless";
}
snprintf(path, sizeof path, "%s/%s", home, maildir+2);
maildir = path;
}
return maildir;
}
int
main(int argc, char *argv[])
{
@ -86,11 +115,17 @@ usage:
exit(1);
}
if (argc == optind)
goto usage;
xpledge("stdio rpath", "");
if (argc == optind) {
char *maildir = profile_maildir();
if (maildir) {
mdirs(maildir);
return 0;
}
goto usage;
}
char toplevel[PATH_MAX];
if (!getcwd(toplevel, sizeof toplevel)) {
perror("mdirs: getcwd");

@ -33,7 +33,7 @@ export(char *file)
}
char from[1024] = "nobody";
time_t date = -1;
time_t date = 0;
if (fseek(infile, 0L, SEEK_SET) && errno == ESPIPE) {
date = time(0);
@ -68,7 +68,7 @@ export(char *file)
char *line = 0;
size_t linelen = 0;
printf("From %s %s", from, ctime(&date));
printf("From %s %s", from, asctime(gmtime(&date)));
int in_header = 1;
int final_nl = 0;
@ -87,7 +87,7 @@ export(char *file)
if (in_header && line[0] == '\n' && !line[1]) {
if (Sflag) {
char *flags = strstr(file, ":2,");
char *flags = strstr(file, MAILDIR_COLON_SPEC_VER_COMMA);
if (!flags)
flags = "";
@ -125,6 +125,9 @@ export(char *file)
if (!final_nl)
putchar('\n');
// ensure empty line at end of message
putchar('\n');
fclose(infile);
}

@ -47,7 +47,7 @@ flag(char *file)
while (file[indent] == ' ' || file[indent] == '\t')
indent++;
char *f = strstr(file, ":2,");
char *f = strstr(file, MAILDIR_COLON_SPEC_VER_COMMA);
if (!f)
goto skip;
@ -145,7 +145,7 @@ main(int argc, char *argv[])
return 0;
}
args = calloc(sizeof (char *), argsalloc);
args = calloc(argsalloc, sizeof (char *));
if (!args)
exit(-1);

@ -1,3 +1,7 @@
#ifdef __sun
#define __EXTENSIONS__ /* to get TIOCGWINSZ */
#endif
#include <sys/ioctl.h>
#include <sys/stat.h>
@ -7,7 +11,9 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include <termios.h>
#include "blaze822.h"
#include "xpledge.h"
@ -28,28 +34,6 @@ chgquote(int quotes)
}
}
void
fixed(int quotes, char *line, size_t linelen)
{
chgquote(quotes);
if (column && linelen > (size_t)(maxcolumn - column)) {
putchar('\n');
column = 0;
}
if (column == 0) {
for (; column < quotes; column++)
putchar('>');
if (quotes && *line != ' ')
putchar(' ');
}
fwrite(line, 1, linelen, stdout);
putchar('\n');
column = 0;
}
void
flowed(int quotes, char *line, ssize_t linelen)
{
@ -95,6 +79,15 @@ flowed(int quotes, char *line, ssize_t linelen)
}
}
void
fixed(int quotes, char *line, size_t linelen)
{
flowed(quotes, line, linelen);
putchar('\n');
column = 0;
}
int
main(int argc, char *argv[])
{
@ -199,6 +192,11 @@ main(int argc, char *argv[])
if (delsp)
line[--rd] = 0;
flowed(quotes, line, rd);
} else if (rd == 0) { // empty line is fixed
if (column > 0)
putchar('\n');
putchar('\n');
column = 0;
} else {
if (force && rd > maxcolumn) {
flowed(quotes, line, rd);

@ -7,6 +7,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
#include "blaze822.h"
@ -86,19 +87,19 @@ void
print_addresses(char *s)
{
char *disp, *addr;
char sdec[4096];
if (dflag) {
blaze822_decode_rfc2047(sdec, s, sizeof sdec, "UTF-8");
sdec[sizeof sdec - 1] = 0;
s = sdec;
}
char ddec[4096];
while ((s = blaze822_addr(s, &disp, &addr))) {
if (Hflag && addr)
printf("%s\t", curfile);
if (disp && addr) {
if (dflag) {
blaze822_decode_rfc2047(ddec, disp, sizeof ddec, "UTF-8");
ddec[sizeof ddec - 1] = 0;
disp = ddec;
}
print_quoted(disp);
printf(" <%s>\n", addr);
} else if (addr) {

@ -46,7 +46,8 @@ inc(char *dir)
snprintf(src, sizeof src, "%s/new/%s",
dir, d->d_name);
snprintf(dst, sizeof dst, "%s/cur/%s%s",
dir, d->d_name, strstr(d->d_name, ":2,") ? "" : ":2,");
dir, d->d_name,
strstr(d->d_name, MAILDIR_COLON_SPEC_VER_COMMA) ? "" : MAILDIR_COLON_SPEC_VER_COMMA);
if (rename(src, dst) < 0) {
fprintf(stderr, "minc: can't rename '%s' to '%s': %s\n",
src, dst, strerror(errno));
@ -74,14 +75,17 @@ usage:
exit(1);
}
if (optind == argc)
goto usage;
xpledge("stdio rpath cpath", "");
status = 0;
for (i = optind; i < argc; i++)
inc(argv[i]);
if (optind == argc) {
if (isatty(0))
goto usage;
blaze822_loop(0, 0, inc);
} else {
for (i = optind; i < argc; i++)
inc(argv[i]);
}
return status;
}

19
mless

@ -22,7 +22,16 @@ if [ "$1" = --filter ]; then
fi
mseq -C "$2"
mscan .-2:.+3 2>/dev/null | colorscan
total=$(mscan -n -- -1)
case $2 in
1) mscan .-0:.+5 ;;
2) mscan .-1:.+4 ;;
$((total - 2))) mscan .-3:.+2 ;;
$((total - 1))) mscan .-4:.+1 ;;
$total) mscan .-5:.+0 ;;
*) mscan .-2:.+3 ;;
esac 2>/dev/null | colorscan
echo
if ! [ -f "$(mseq -r "$2")" ]; then
@ -63,7 +72,13 @@ nl="
"
export MLESS_RAW=0
export MLESS_HTML=0
if [ -f "$MBLAZE/mless" ]; then
if [ -f "$MBLAZE/mlesskey" ]; then
export LESSKEYIN="$MBLAZE/mlesskey"
elif [ -f "$HOME/.mblaze/mlesskey" ]; then
export LESSKEYIN="$HOME/.mblaze/mlesskey"
elif [ -f "$HOME/.mlesskey" ]; then
export LESSKEYIN="$HOME/.mlesskey"
elif [ -f "$MBLAZE/mless" ]; then
export LESSKEY="$MBLAZE/mless"
elif [ -f "$HOME/.mblaze/mless" ]; then
export LESSKEY="$HOME/.mblaze/mless"

@ -1,5 +1,7 @@
# mless(1) keybindings
# to update: lesskey -o ~/.mless ~/.mlesskey
# When using less <590:
# compile with lesskey -o .mless mlesskey and install as ~/.mless
# When using less >=590: copy this file to ~/.mlesskey
Q quit \1
:cq quit \1
[ prev-file

@ -58,14 +58,14 @@ list(char *prefix, char *file)
if (flagset || iflag) {
size_t prefixlen;
f = strstr(file, ":2,");
f = strstr(file, MAILDIR_COLON_SPEC_VER_COMMA);
if (!f &&
prefix &&
(prefixlen = strlen(prefix)) &&
prefixlen >= 4 &&
strcmp(prefix + prefixlen - 4, "/new") == 0)
f = ":2,";
f = MAILDIR_COLON_SPEC_VER_COMMA;
}
if (flagset) {
@ -111,8 +111,8 @@ list(char *prefix, char *file)
#include <sys/syscall.h>
struct linux_dirent64 {
ino64_t d_ino; /* 64-bit inode number */
off64_t d_off; /* 64-bit offset to next structure */
uint64_t d_ino; /* 64-bit inode number */
int64_t d_off; /* 64-bit offset to next structure */
unsigned short d_reclen; /* Size of this dirent */
unsigned char d_type; /* File type */
char d_name[]; /* Filename (null-terminated) */

@ -12,6 +12,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <time.h>
#include <unistd.h>
@ -59,6 +60,11 @@ int gen_b64(uint8_t *s, off_t size)
return 0;
}
#define qphrasevalid(c) ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || \
(c >= 'a' && c <= 'z') || \
c == '!' || c == '*' || c == '+' || c == '-' || \
c == '/')
size_t
gen_qp(uint8_t *s, off_t size, size_t maxlinelen, size_t linelen)
{
@ -123,6 +129,10 @@ gen_qp(uint8_t *s, off_t size, size_t maxlinelen, size_t linelen)
putc_unlocked('\n', stdout);
linelen = 0;
prev = 0;
} else if (header && !qphrasevalid(s[i])) {
printf("=%02X", s[i]);
linelen += 3;
prev = '_';
} else {
putc_unlocked(s[i], stdout);
linelen++;
@ -303,23 +313,41 @@ print_header(char *line) {
}
int prevq = 0; // was the previous word encoded as qp?
char prevqs = 0; // was the previous word a quoted-string?
ssize_t linelen = s - line;
while (*s) {
size_t highbit = 0;
int qs = 0;
e = s;
while (*e && *e == ' ')
e++;
for (; *e && *e != ' '; e++) {
if ((uint8_t)*e >= 127)
highbit++;
if (*e == '"') { // scan quoted-string, encode at once
s = e;
for (e++; *e && *e != '"'; e++) {
if (*e == '\\')
e++;
if ((uint8_t)*e >= 127)
highbit++;
}
if (*e == '"')
e++;
qs = 1;
} else { // scan word
while (*e && *e == ' ')
e++;
for (; *e && *e != ' '; e++) {
if ((uint8_t)*e >= 127)
highbit++;
}
}
if (!highbit) {
if (e-s >= 998)
goto force_qp;
if (e-s >= 78 - linelen) {
if (e-s >= 78 - linelen && linelen > 0) {
// wrap in advance before long word
printf("\n");
linelen = 0;
@ -328,7 +356,7 @@ print_header(char *line) {
// space at beginning of line
goto force_qp;
}
if (*s != ' ') {
if (*s != ' ' && !(prevqs && !prevq && *(s-1) != ' ')) {
printf(" ");
linelen++;
}
@ -339,6 +367,12 @@ print_header(char *line) {
force_qp:
if (!prevq && *s == ' ')
s++;
if (qs && *s == '"')
s++;
if (qs && e > s && *(e-1) == '"')
e--;
if (linelen >= 78 - 13 - 4 ||
(e-s < (78 - 13)/3 &&
e-s >= (78 - linelen - 13)/3)) {
@ -352,7 +386,11 @@ force_qp:
printf("?=");
linelen += 2;
prevq = 1;
if (qs && *e == '"')
e++;
}
prevqs = qs;
s = e;
}
printf("\n");
@ -367,7 +405,7 @@ valid_content_type(char *s)
if (*s == '/')
slash++;
else if (isalnum(*s) || *s == '-' || *s == '+' || *s == '.' ||
*s == ';' || *s == '=')
*s == ';' || *s == '=' || *s == '#')
; /* ok */
else
return 0;
@ -386,30 +424,45 @@ gen_build()
size_t linelen = 0;
int inheader = 1;
int intext = 0;
int emptybody = 1;
int ret = 0;
char *contenttype = 0;
char *contenttransferenc = 0;
while (1) {
ssize_t read = getdelim(&line, &linelen, '\n', stdin);
if (read == -1) {
if (feof(stdin))
break;
else
if (feof(stdin)) {
if (!emptybody)
break;
line = strdup(inheader ? "\n" : "");
} else { // errored
exit(1);
}
}
if (inheader) {
if (line[0] == '\n') {
inheader = 0;
printf("MIME-Version: 1.0\n");
if (rflag) {
printf("Content-Type: text/plain; charset=UTF-8\n");
printf("Content-Transfer-Encoding: quoted-printable\n\n");
printf("Content-Type:%s", contenttype ? contenttype : " text/plain; charset=UTF-8\n");
printf("Content-Transfer-Encoding:%s", contenttransferenc ? contenttransferenc : " quoted-printable\n");
printf("\n");
} else {
printf("Content-Type: %s; boundary=\"%s\"\n", tflag, sep);
printf("\n");
printf("This is a multipart message in MIME format.\n");
}
} else {
print_header(line);
if (strncasecmp(line, "Content-Type:", 13) == 0) {
free(contenttype);
contenttype = strdup(line+13);
} else if (strncasecmp(line, "Content-Transfer-Encoding:", 26) == 0) {
free(contenttransferenc);
contenttransferenc = strdup(line+26);
} else {
print_header(line);
}
}
continue;
}
@ -423,8 +476,10 @@ gen_build()
printf("\n--%s\n", sep);
if (line[read-1] == '\n')
line[read-1] = 0;
gen_file(f+1, (char *)line+1);
if (gen_file(f+1, line+1) != 0)
ret = 1;
intext = 0;
emptybody = 0;
continue;
}
*f = of;
@ -433,20 +488,26 @@ gen_build()
if (!rflag && !intext) {
printf("\n--%s\n", sep);
printf("Content-Type: text/plain; charset=UTF-8\n");
printf("Content-Type:%s", contenttype ? contenttype : " text/plain; charset=UTF-8\n");
printf("Content-Disposition: inline\n");
printf("Content-Transfer-Encoding: quoted-printable\n\n");
printf("Content-Transfer-Encoding:%s", contenttransferenc ? contenttransferenc : " quoted-printable\n");
printf("\n");
intext = 1;
}
gen_qp((uint8_t *)line, strlen(line), 78, 0);
if (contenttransferenc)
printf("%s", line);
else
gen_qp((uint8_t *)line, strlen(line), 78, 0);
emptybody = 0;
}
if (!rflag && !inheader)
printf("\n--%s--\n", sep);
free(line);
return 0;
return ret;
}
int
@ -457,6 +518,7 @@ check()
off_t linelen = 0;
off_t maxheadlinelen = 0;
off_t maxbodylinelen = 0;
off_t bodylinelenlimit = getenv("MBLAZE_RELAXED_MIME") ? 998 : 78;
int c;
int l = -1;
@ -494,7 +556,7 @@ check()
}
if (bitlow == 0 && bithigh == 0 &&
maxheadlinelen < 998 && maxbodylinelen <= 78 &&
maxheadlinelen < 998 && maxbodylinelen <= bodylinelenlimit &&
l == '\n')
return 0;
else

@ -1256,10 +1256,8 @@ mailfile(struct mailinfo *m, char *file)
{
static int init;
if (!init) {
// delay loading of the seqmap until we need to scan the first
// file, in case someone in the pipe updated the map before
char *seqmap = blaze822_seq_open(0);
blaze822_seq_load(seqmap);
// delay loading of the cur mail until we need to scan the first
// file, in case someone in the pipe updated it before
cur = blaze822_seq_cur();
init = 1;
}
@ -1292,7 +1290,7 @@ mailfile(struct mailinfo *m, char *file)
cur_idx = m->index;
}
char *f = strstr(fpath, ":2,");
char *f = strstr(fpath, MAILDIR_COLON_SPEC_VER_COMMA);
if (f) {
if (strchr(f, 'P'))
m->flags |= FLAG_PASSED;
@ -1466,7 +1464,7 @@ main(int argc, char *argv[])
xpledge("stdio rpath wpath cpath proc exec", 0);
void *cb = need_thr ? collect : oneline;
void (*cb)(char *) = need_thr ? collect : oneline;
if (argc == optind && isatty(0))
i = blaze822_loop1(":", cb);
else

@ -1,6 +1,9 @@
#ifndef _XOPEN_SOURCE
#define _XOPEN_SOURCE 700
#endif
#ifdef __sun
#define __EXTENSIONS__ /* to get TIOCGWINSZ */
#endif
#include "xpledge.h"
@ -19,6 +22,7 @@
#include <time.h>
#include <unistd.h>
#include <wchar.h>
#include <termios.h>
#include "blaze822.h"
#include "u8decode.h"
@ -318,7 +322,7 @@ oneline(char *file)
}
struct message *msg = blaze822(file);
char *flags = msg ? strstr(file, ":2,") : 0;
char *flags = msg ? strstr(file, MAILDIR_COLON_SPEC_VER_COMMA) : 0;
if (!flags)
flags = "";
else
@ -571,9 +575,13 @@ main(int argc, char *argv[])
struct winsize w;
int ttyfd = open("/dev/tty", O_RDONLY | O_NOCTTY);
if (ttyfd >= 0 && ioctl(ttyfd, TIOCGWINSZ, &w) == 0) {
cols = w.ws_col;
if (ttyfd >= 0) {
if (ioctl(ttyfd, TIOCGWINSZ, &w) == 0)
cols = w.ws_col;
close(ttyfd);
}
if (isatty(1)) {
char *pg;
pg = getenv("MBLAZE_PAGER");
if (!pg)
@ -586,8 +594,6 @@ main(int argc, char *argv[])
pg, strerror(errno));
}
}
if (ttyfd >= 0)
close(ttyfd);
xpledge("stdio rpath", "");

@ -138,7 +138,7 @@ sed(char *file)
if (*s == ':')
*s = '|';
int rv;
if ((rv = regcomp(&headerrx, headersel, REG_EXTENDED)) != 0) {
if ((rv = regcomp(&headerrx, headersel, REG_EXTENDED | REG_ICASE)) != 0) {
char buf[100];
regerror(rv, &headerrx, buf, sizeof buf);
fprintf(stderr, "msed: %s\n", buf);

@ -70,7 +70,7 @@ namescan(char *dir)
snprintf(file, sizeof file, "%s/%s", dir, d->d_name);
char *e;
if ((e = strstr(d->d_name, ":2,")))
if ((e = strstr(d->d_name, MAILDIR_COLON_SPEC_VER_COMMA)))
*e = 0;
struct name *c = malloc(sizeof (struct name));
@ -101,7 +101,7 @@ search(char *file)
if (!namefind(dir))
namescan(dir);
if ((e = strstr(file, ":2,")))
if ((e = strstr(file, MAILDIR_COLON_SPEC_VER_COMMA)))
*e = 0;
return namefind(file);
@ -131,11 +131,11 @@ fix(FILE *out, char *file)
char *e;
char *sep;
if ((e = strstr(file, ":2,"))) {
if ((e = strstr(file, MAILDIR_COLON_SPEC_VER_COMMA))) {
sep = "";
e[3] = 0;
} else {
sep = ":2,";
sep = MAILDIR_COLON_SPEC_VER_COMMA;
}
snprintf(buf, sizeof buf, "%s%s", file, sep);
if (access(buf, F_OK) == 0) goto ok;
@ -317,13 +317,10 @@ usage:
if (optind == argc && !isatty(0))
return stdinmode();
char *seq = blaze822_seq_open(0);
if (!seq)
return 1;
int i;
char *f;
char *a;
char *seq = 0;
if (optind == argc) {
a = ":";
@ -337,6 +334,13 @@ hack:
printf("%s\n", a);
continue;
}
if (!seq) {
seq = blaze822_seq_open(0);
if (!seq)
return 1;
}
struct blaze822_seq_iter iter = { 0 };
while ((f = blaze822_seq_next(seq, a, &iter))) {
char *s = f;

@ -51,10 +51,10 @@ printable(int c)
}
size_t
print_ascii(char *body, size_t bodylen)
print_ascii(char *body, size_t bodylen, int oneline)
{
if (safe_output) {
safe_u8putstr(body, bodylen, stdout);
safe_u8putstr(body, bodylen, oneline, stdout);
return bodylen;
} else {
return fwrite(body, 1, bodylen, stdout);
@ -73,7 +73,7 @@ printhdr(char *hdr)
}
if (*hdr) {
print_ascii(hdr, strlen(hdr));
print_ascii(hdr, strlen(hdr), 1);
fputc('\n', stdout);
}
}
@ -98,7 +98,7 @@ print_u8recode(char *body, size_t bodylen, char *srcenc)
size_t r = iconv(ic, &body, &bodylen, &bufptr, &buflen);
if (bufptr != buf) {
print_ascii(buf, bufptr-buf);
print_ascii(buf, bufptr-buf, 0);
final_char = bufptr[-1];
}
@ -107,7 +107,7 @@ print_u8recode(char *body, size_t bodylen, char *srcenc)
buflen = sizeof buf;
r = iconv(ic, 0, 0, &bufptr, &buflen);
if (bufptr != buf) {
print_ascii(buf, bufptr-buf);
print_ascii(buf, bufptr-buf, 0);
final_char = bufptr[-1];
}
if (r != (size_t)-1)
@ -185,7 +185,7 @@ print_filename(char *filename)
{
if (filename) {
printf(" name=\"");
safe_u8putstr(filename, strlen(filename), stdout);
safe_u8putstr(filename, strlen(filename), 1, stdout);
printf("\"");
}
}
@ -252,7 +252,7 @@ render_mime(int depth, struct message *msg, char *body, size_t bodylen)
printf(" render=\"%s\" ---\n", cmd);
if (outlen) {
if (e == 0)
print_ascii(output, outlen);
print_ascii(output, outlen, 0);
else
fwrite(output, 1, outlen, stdout);
if (output[outlen-1] != '\n')
@ -299,7 +299,7 @@ nofilter:
if (blaze822_mime_parameter(ct, "charset", &cs, &cse))
charset = strndup(cs, cse-cs);
if (probably_utf8(charset)) {
print_ascii(body, bodylen);
print_ascii(body, bodylen, 0);
if (bodylen > 0 && body[bodylen-1] != '\n')
putchar('\n');
} else {
@ -316,9 +316,10 @@ nofilter:
}
printf("\n");
} else if (strncmp(ct, "multipart/alternative", 21) == 0) {
choose_alternative(msg, depth);
r = MIME_PRUNE;
if (strcmp(Aflag, "all") != 0) {
choose_alternative(msg, depth);
r = MIME_PRUNE;
} // else default blaze822_mime_walk action
} else if (strncmp(ct, "multipart/", 10) == 0) {
; // default blaze822_mime_walk action
} else {
@ -335,7 +336,7 @@ nofilter:
static void
choose_alternative(struct message *msg, int depth)
{
int n = 1;
int n = 0;
int m = 0;
char *p = Aflag + strlen(Aflag);
@ -362,6 +363,13 @@ choose_alternative(struct message *msg, int depth)
}
blaze822_free(imsg);
if (n == 0) {
// No part matched, use last part as per RFC1341 7.2.3
// "In general, choosing the best type means displaying
// only the LAST part that can be displayed."
n = m;
}
imsg = 0;
while (blaze822_multipart(msg, &imsg))
if (--n == 0)
@ -383,7 +391,7 @@ reply_mime(int depth, struct message *msg, char *body, size_t bodylen)
if (blaze822_mime_parameter(ct, "charset", &cs, &cse))
charset = strndup(cs, cse-cs);
if (probably_utf8(charset))
print_ascii(body, bodylen);
print_ascii(body, bodylen, 0);
else
print_u8recode(body, bodylen, charset);
reply_found++;
@ -494,7 +502,7 @@ extract_mime(int depth, struct message *msg, char *body, size_t bodylen)
fwrite(body, 1, bodylen, stdout);
} else { // extract all named attachments
if (filename) {
safe_u8putstr(filename, strlen(filename), stdout);
safe_u8putstr(filename, strlen(filename), 1, stdout);
printf("\n");
writefile(filename, body, bodylen);
}
@ -555,7 +563,7 @@ extract_mime(int depth, struct message *msg, char *body, size_t bodylen)
fwrite(body, 1, bodylen, stdout);
}
} else {
safe_u8putstr(filename, strlen(filename), stdout);
safe_u8putstr(filename, strlen(filename), 1, stdout);
printf("\n");
writefile(filename, body, bodylen);
}
@ -601,7 +609,7 @@ print_date_header(char *v)
}
printf("Date: ");
print_ascii(v, strlen(v));
print_ascii(v, strlen(v), 1);
time_t t = blaze822_date(v);
if (t == -1) {
@ -687,7 +695,7 @@ print_decode_header(char *h, char *v)
printhdr(h);
fputc(':', stdout);
fputc(' ', stdout);
print_ascii(d, strlen(d));
print_ascii(d, strlen(d), 1);
fputc('\n', stdout);
}
@ -754,7 +762,7 @@ show(char *file)
printf("\n");
if (rflag) { // raw body
print_ascii(blaze822_body(msg), blaze822_bodylen(msg));
print_ascii(blaze822_body(msg), blaze822_bodylen(msg), 0);
goto done;
}
@ -800,7 +808,7 @@ main(int argc, char *argv[])
xpledge("stdio rpath wpath cpath proc exec", NULL);
if (!rflag && !xflag && !Oflag && !Rflag)
if (!rflag && !xflag && !Oflag && !Rflag && !tflag)
safe_output = 1;
if (safe_output && isatty(1)) {

@ -219,8 +219,8 @@ unreadorder(const void *a, const void *b)
struct mail *ia = (struct mail *)a;
struct mail *ib = (struct mail *)b;
char *fa = strstr(ia->file, ":2,");
char *fb = strstr(ib->file, ":2,");
char *fa = strstr(ia->file, MAILDIR_COLON_SPEC_VER_COMMA);
char *fb = strstr(ib->file, MAILDIR_COLON_SPEC_VER_COMMA);
int unreada = fa ? !strchr(fa, 'S') : 0;
int unreadb = fb ? !strchr(fb, 'S') : 0;
@ -234,8 +234,8 @@ flaggedorder(const void *a, const void *b)
struct mail *ia = (struct mail *)a;
struct mail *ib = (struct mail *)b;
char *fa = strstr(ia->file, ":2,");
char *fb = strstr(ib->file, ":2,");
char *fa = strstr(ia->file, MAILDIR_COLON_SPEC_VER_COMMA);
char *fb = strstr(ib->file, MAILDIR_COLON_SPEC_VER_COMMA);
int unreada = fa ? !!strchr(fa, 'F') : 0;
int unreadb = fb ? !!strchr(fb, 'F') : 0;
@ -319,7 +319,7 @@ main(int argc, char *argv[])
xpledge("stdio rpath", "");
mails = calloc(sizeof (struct mail), mailalloc);
mails = calloc(mailalloc, sizeof (struct mail));
if (!mails)
exit(-1);

@ -29,7 +29,6 @@ static int optional;
struct container {
char *mid;
char *file;
struct message *msg;
time_t date;
struct container *parent;
struct container *child;
@ -87,7 +86,6 @@ midcont(char *mid)
exit(111);
c->mid = mid;
c->file = 0;
c->msg = 0;
c->date = -1;
c->optional = 0;
c->parent = c->child = c->next = 0;
@ -104,7 +102,6 @@ store_id(char *file, struct message *msg)
c = midcont(mid(msg));
c->file = strdup(file);
c->msg = msg;
c->optional = optional;
return c;
@ -239,6 +236,8 @@ out2:
c->child = 0;
}
}
blaze822_free(msg);
}
time_t
@ -290,7 +289,6 @@ find_roots()
top = malloc(sizeof (struct container));
if (!top)
exit(111);
top->msg = 0;
top->date = -1;
top->file = 0;
top->next = top->child = top->parent = 0;
@ -315,7 +313,6 @@ prune_tree(struct container *c, int depth)
// turn into child if we don't exist and only have a child
c->mid = c->child->mid;
c->file = c->child->file;
c->msg = c->child->msg;
if (c->child->date > c->date)
c->date = c->child->date;
c->optional = c->child->optional;
@ -368,7 +365,7 @@ sort_tree(struct container *c, int depth)
if (i == 1) // no sort needed
return;
struct container **a = calloc(sizeof (struct container *), i);
struct container **a = calloc(i, sizeof (struct container *));
if (!a)
return;

@ -86,6 +86,8 @@ blaze822_mime_parameter(char *s, char *name, char **starto, char **stopo)
while (*s) {
while (iswsp(*s))
s++;
if (!*s)
return 0;
if (strncasecmp(s, name, namelen) == 0 && s[namelen] == '=') {
s += namelen + 1;
break;
@ -114,6 +116,26 @@ blaze822_mime_parameter(char *s, char *name, char **starto, char **stopo)
return 1;
}
// like mymemmem but check the match is followed by \r, \n or -.
static char *
mymemmemnl(const char *h0, size_t k, const char *n0, size_t l)
{
char *r;
while (k && (r = mymemmem(h0, k, n0, l))) {
if (r + l < h0 + k && // check if r[l] safe to access
(r[l] == '\r' || r[l] == '\n' || r[l] == '-'))
return r;
else {
// skip over this match
k -= (r - h0) + 1;
h0 = r + 1;
}
}
return 0;
}
int
blaze822_multipart(struct message *msg, struct message **imsg)
{
@ -144,7 +166,7 @@ blaze822_multipart(struct message *msg, struct message **imsg)
else
prevpart = msg->body;
char *part = mymemmem(prevpart, msg->bodyend - prevpart, mboundary, boundarylen);
char *part = mymemmemnl(prevpart, msg->bodyend - prevpart, mboundary, boundarylen);
if (!part)
return 0;
@ -158,10 +180,10 @@ blaze822_multipart(struct message *msg, struct message **imsg)
else
return 0; // XXX error condition?
char *nextpart = mymemmem(part, msg->bodyend - part, mboundary, boundarylen);
char *nextpart = mymemmemnl(part, msg->bodyend - part, mboundary, boundarylen);
if (!nextpart)
return 0; // XXX error condition
if (nextpart == part) // invalid empty MIME part
nextpart = msg->bodyend; // no boundary found, take all
else if (nextpart == part) // invalid empty MIME part
return 0; // XXX error condition
if (*(nextpart-1) == '\n')

@ -82,16 +82,25 @@ blaze822_decode_b64(char *s, char *e, char **deco, size_t *decleno)
*deco = buf;
while (s + 4 <= e) {
while (s < e && isfws((unsigned char)*s))
s++;
if (s >= e)
break;
uint32_t v = 0;
unsigned char t = 0;
unsigned char c0 = s[0], c1 = s[1], c2 = s[2], c3 = s[3];
s += 4;
#define SKIP_WS \
while (s < e && isfws((unsigned char)*s)) \
s++; \
if (s >= e) \
break;
SKIP_WS;
unsigned char c0 = *s++;
SKIP_WS;
unsigned char c1 = *s++;
SKIP_WS;
unsigned char c2 = *s++;
SKIP_WS;
unsigned char c3 = *s++;
#undef SKIP_WS
if ((c0 | c1 | c2 | c3) > 127)
goto error;
@ -192,7 +201,8 @@ blaze822_decode_rfc2047(char *dst, char *src, size_t dlen, char *tgtenc)
if (ic == (iconv_t)-1)
goto nocode;
char enc = lc(*e++);
char enc = lc(*e);
e++;
if (*e++ != '?')
goto nocode;
char *start = e;

@ -4,7 +4,7 @@
#include "u8decode.h"
void
safe_u8putstr(char *s0, size_t l, FILE *stream)
safe_u8putstr(char *s0, size_t l, int oneline, FILE *stream)
{
// tty-safe output of s, with relaxed utf-8 semantics:
// - C0 and C1 are displayed as escape sequences
@ -35,7 +35,8 @@ safe_u8putstr(char *s0, size_t l, FILE *stream)
fputc(0x80 | (*s & 0x3f), stream);
}
} else if (c < 32 &&
*s != ' ' && *s != '\t' && *s != '\n' && *s != '\r') {
*s != ' ' && *s != '\t' &&
(oneline || (*s != '\n' && *s != '\r'))) {
// NUL
if (l == 0)
l = 1;

@ -1,7 +1,8 @@
#!/bin/sh -e
cd ${0%/*}
. ./lib.sh
plan 4
plan 21
cat <<EOF >tmp
References: <aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@a> <bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb@b> <ccccccccccccccccccccccccccccccc@c>
@ -15,3 +16,137 @@ check 'mime -r runs' 'mmime -r <tmp >tmp2'
check 'no overlong lines' 'awk "{if(length(\$0)>=80)exit 1}" <tmp2'
check 'no QP when unecessary' ! grep -qF "=?" tmp2
check 'no further mime necessary' 'mmime -c <tmp2'
cat <<EOF >tmp2
Subject: inclusion test
#message/rfc822 $PWD/tmp
EOF
check 'include works' 'mmime <tmp2 | grep Body'
check 'include sets filename' 'mmime <tmp2 | grep filename=tmp'
cat <<EOF >tmp2
Subject: inclusion test no filename
#message/rfc822 $PWD/tmp>
EOF
check 'include works, overriding filename' 'mmime <tmp2 | grep Disposition | grep -v filename=tmp'
cat <<EOF >tmp2
Subject: inclusion test with other disposition
#message/rfc822#inline $PWD/tmp>
EOF
check 'include works, overriding filename' 'mmime <tmp2 | grep Disposition | grep inline'
cat <<EOF >tmp2
Subject: message with content-type
Content-Type: text/plain; format=flowed
This message has format-flowed.
EOF
check 'content-type is respected if found in input' 'mmime -r <tmp2 |grep format=flowed'
cat <<EOF >tmp2
Subject: message with content-transfer-encoding
Content-Transfer-Encoding: quoted-printable
This message has already encoded. f=C3=B6=C3=B6.
EOF
check 'content-transfer-encoding is respected if found in input' 'mmime -r <tmp2 |grep f=C3=B6=C3=B6'
cat <<EOF >tmp2
Subject: message with content-type
Content-Type: text/plain; format=flowed
This message has format-flowed.
#message/rfc822 $PWD/tmp
This part too.
EOF
check 'content-type is respected if found in input, for multipart/mixed' 'mmime <tmp2 |grep format=flowed'
cat <<EOF >tmp2
Subject: message with content-transfer-encoding
Content-Transfer-Encoding: Quoted-Printable
This message has already encoded. f=C3=B6=C3=B6.
#message/rfc822 $PWD/tmp
This part too.
EOF
check 'content-transfer-encoding is respected if found in input, for multipart/mixed' 'mmime <tmp2 |grep f=C3=B6=C3=B6'
cat <<EOF >tmp2
From: Kerstin Krüger <krueger@example.com>
Body.
EOF
check 'non-ASCII is encoded as UTF-8' 'mmime <tmp2 | grep "UTF-8.*=C3=BC"'
cat <<EOF >tmp2
From: "Krüger, Kerstin" <krueger@example.com>
Body.
EOF
check 'non-ASCII quoted-strings are encoded as one encoded-word' 'mmime <tmp2 | grep "UTF-8.*=2C_"'
check 'non-ASCII quoted-strings are encoded without quotes' 'mmime <tmp2 | grep -v "=22"'
cat <<EOF >tmp2
From: "kerstin krueger"@example.com
Body.
EOF
check 'non-encoded quoted-strings are kept correctly' 'mmime <tmp2 | grep \"@'
cat <<EOF >tmp2
Subject: inclusion without further content
#message/rfc822#inline $PWD/tmp
EOF
check 'no empty parts are generated after inclusion lines' '! mmime <tmp2 | mshow -t - | grep -q size=0'
cat <<EOF >tmp2
Subject: Strict mode
Body with lines longer than 78 characters
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
EOF
check 'body lines longer than 78 characters needs MIME formatting' '! mmime -c <tmp2'
check 'MBLAZE_RELAXED_MIME allows body lines longer than 78 characters' 'MBLAZE_RELAXED_MIME= mmime -c <tmp2'
cat <<EOF >tmp2
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; t=1710013705;
s=s1; d=tuta.io;
h=From:From:To:To:Subject:Subject:Content-Description:Content-ID:Content-Type:Content-Type:Content-Transfer-Encoding:Cc:Date:Date:In-Reply-To:MIME-Version:MIME-Version:Message-ID:Message-ID:Reply-To:References:Sender; bh=Jr8DQlZ7RwdJv94m7ZT/v+cv/WFsgjxpMRsHvnNfgGY=;
b=NXRl0YxYtVsWrR8v7tVKnvsnCSrBqqaf2h3m8OVGlzG0OqMqGcWg7fVk6x4nTYV+
+05afZrGfIwcfFwIe/LLvT0d3/12t4+cs/FQvmEcFUN+n2buQwt5sn8f76UUlvNMrGz
Xbq8HAdwhA364yWABa7DrF1EGysC8bEDJcCtSs/Wz3TL2A/MEeItEF+VijtgWUwoOwn
rFKkCg5Df+IOd4gEBS/KYLbzcMB1dvqy+ut2LA2+NZpzJQPgbJzWAYieT9KYgoS+hKS
5FfknNT+hKZz18IBEWH1UWbI+CcLRR8Sr80x2DZUKq8ryC5RmV5/uAc5Up03b/KZGRU
NsiBAQCx3w==
EOF
check 'header words longer then 78 characters do not cause empty lines (#257)' 'mmime < tmp2 | awk "NR < 5 && length == 0 { exit 1 }"'
check 'header words longer then 78 characters are printed on their own line' 'mmime < tmp2 |grep "^[ ]*h=From.*Sender;$"'

@ -0,0 +1,26 @@
#!/bin/sh -e
cd ${0%/*}
. ./lib.sh
plan 9
cat <<EOF >tmp
Header: foo
Header2: bar
Header-Three: quux
Header_Four: ding
Body
EOF
check_same 'Header' 'mhdr -h Header ./tmp' 'echo foo'
check_same 'Header2' 'mhdr -h Header2 ./tmp' 'echo bar'
check_same 'Header-Three' 'mhdr -h Header-Three ./tmp' 'echo quux'
check_same 'Header_Four' 'mhdr -h Header_Four ./tmp' 'echo ding'
check_same 'header' 'mhdr -h header ./tmp' 'echo foo'
check_same 'header2' 'mhdr -h header2 ./tmp' 'echo bar'
check_same 'header-Three' 'mhdr -h header-Three ./tmp' 'echo quux'
check_same 'header_Four' 'mhdr -h header_Four ./tmp' 'echo ding'
check 'issue 235' 'mhdr ./tmp |grep -i header_four'

@ -20,7 +20,7 @@ Message-Id: <EOH1F3NUOY.2KBVMHSBFATNY@example.org>
!
cat <<! | mmime >"inbox/cur/2:2,"
From: имярек <имярек@example.com>, Rajwinder Kaur <rajwinder@example.com>
From: имярек <namearek@example.com>, Rajwinder Kaur <rajwinder@example.com>
Subject: Здравствуйте
Date: Thu, 30 Mar 2017 15:42:10 +0200
Message-Id: <EOH8EKEA1Z.2YAIFN5KVCO6Z@example.org>
@ -70,7 +70,7 @@ check_same 'from address' 'maddr -a 1' 'echo "rajwinder@example.com"'
check_same 'from one' 'maddr -h obs-test 1' 'echo "Rajwinder Kaur <rajwinder@example.com>"'
cat <<! >expect
имярек <имярек@example.com>
имярек <namearek@example.com>
Rajwinder Kaur <rajwinder@example.com>
!
check_same 'from two' 'maddr 2' 'cat expect'

@ -0,0 +1,41 @@
#!/bin/sh -e
cd ${0%/*}
. ./lib.sh
plan 2
# Nested MIME where the outer boundary is a prefix of the inner boundary
cat <<EOF >tmp
MIME-Version: 1.0
Content-type: multipart/mixed; charset=iso-8859-1;
boundary="_xec5AqfRYxfhARmklHx"
--_xec5AqfRYxfhARmklHx
Content-type: Multipart/alternative; charset=iso-8859-1;
boundary="_xec5AqfRYxfhARmklHx8"
--_xec5AqfRYxfhARmklHx8
Content-type: text/plain; charset=iso-8859-1
Content-Transfer-Encoding: 7bit
Content-Disposition: inline
foo
--_xec5AqfRYxfhARmklHx8
Content-type: text/html; charset=iso-8859-1
Content-Transfer-Encoding: Quoted-printable
Content-Disposition: inline
bar
--_xec5AqfRYxfhARmklHx8--
--_xec5AqfRYxfhARmklHx
Content-Type: application/zip
Content-Transfer-Encoding: Base64
quux
--_xec5AqfRYxfhARmklHx--
EOF
check 'nested mail has 5 attachments' 'mshow -t ./tmp | wc -l | grep 6'
check 'nested mail has text/html attachment' 'mshow -t ./tmp | grep text/html'

@ -0,0 +1,53 @@
#!/bin/sh -e
cd ${0%/*}
. ./lib.sh
plan 3
# Mail with \n\n and \r\n\r\n
cr=$(printf '\r')
cat <<EOF >tmp
Content-Type: multipart/form-data; boundary=------------------------55a586f81559face$cr
$cr
--------------------------55a586f81559face$cr
Content-Disposition: form-data; name="a"; filename="foo"$cr
Content-Type: application/octet-stream$cr
$cr
foo$cr
previously there are two NL$cr
$cr
--------------------------55a586f81559face$cr
Content-Disposition: form-data; name="a"; filename="bar"$cr
Content-Type: application/octet-stream$cr
$cr
bar$cr
$cr
--------------------------55a586f81559face--$cr
EOF
check 'mail has 3 attachments' 'mshow -t ./tmp | wc -l | grep 4'
check 'mail attachment foo has size 35' 'mshow -t ./tmp | grep size=35.*name=\"foo\"'
# Mail with linebreaks in base64 quartets
cat <<EOF >tmp
Subject: base64
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="----_=_2f8f1e2243b55f8618eaf0d9_=_"
This is a multipart message in MIME format.
------_=_2f8f1e2243b55f8618eaf0d9_=_
Content-Disposition: attachment; filename=base64
Content-Type: application/binary
Content-Transfer-Encoding: base64
dGhp
cyBpc
yBzb21
lIGJhc2
U2NAo=
------_=_2f8f1e2243b55f8618eaf0d9_=_--
EOF
check 'mail decodes correctly' 'mshow -O ./tmp 2 | grep -q "this is some base64"'

@ -0,0 +1,26 @@
#!/bin/sh -e
cd ${0%/*}
. ./lib.sh
plan 2
cat <<EOF >tmp.1
Subject: message 1
This is message 1.
EOF
cat <<EOF >tmp.2
Subject: message 2
This is message 2. It has a trailing empty line.
EOF
printf >tmp.3 'Subject: message 3
This is message 3. It has a no trailing newline, oops.'
mexport ./tmp.1 ./tmp.2 ./tmp.3 >./tmp.mbox
check 'generated mbox has 16 lines' 'cat ./tmp.mbox | wc -l | grep 16'
check 'generated mbox has 7 empty lines' 'grep -c "^$" ./tmp.mbox | grep 7'

@ -0,0 +1,85 @@
#!/bin/sh -e
cd ${0%/*}
. ./lib.sh
plan 9
rm -rf test.dir
mkdir test.dir
cd test.dir
mmkdir inbox
cat <<EOF >tmp.1
Subject: message 1
This is message 1.
EOF
cat <<EOF >tmp.2
Subject: message 2
This is message 2. It has a trailing empty line.
EOF
printf >tmp.3 'Subject: message 3
This is message 3. It has a no trailing newline, oops.'
cat <<EOF >tmp.4
Subject: message 4
This is message 4. It has multiple trailing empty lines.
EOF
mexport ./tmp.1 | mdeliver -M inbox/
check 'message 1 is delivered verbatim via mbox' 'cmp ./tmp.1 ./inbox/new/*:2,'
rm -f ./inbox/new/*
mexport ./tmp.2 | mdeliver -M inbox/
check 'message 2 is delivered verbatim via mbox' 'cmp ./tmp.2 ./inbox/new/*:2,'
rm -f ./inbox/new/*
mexport ./tmp.3 | mdeliver -M inbox/
check 'message 3 gets a newline via mbox' 'awk 1 ./tmp.3 | cmp - ./inbox/new/*:2,'
rm -f ./inbox/new/*
mexport ./tmp.4 | mdeliver -M inbox/
check 'message 4 gets delivered verbatim via mbox' 'cmp ./tmp.4 ./inbox/new/*:2,'
rm -f ./inbox/new/*
mdeliver inbox/ <./tmp.1
check 'message 1 is delivered verbatim via stdin' 'cmp ./tmp.1 ./inbox/new/*:2,'
rm -f ./inbox/new/*
mdeliver inbox/ <./tmp.2
check 'message 2 is delivered verbatim via stdin' 'cmp ./tmp.2 ./inbox/new/*:2,'
rm -f ./inbox/new/*
mdeliver inbox/ <./tmp.3
check 'message 3 gets a newline via stdin' 'cmp ./tmp.3 ./inbox/new/*:2,'
rm -f ./inbox/new/*
mdeliver inbox/ <./tmp.4
check 'message 4 is delivered verbatim via stdin' 'cmp ./tmp.4 ./inbox/new/*:2,'
rm -f ./inbox/new/*
cat <<EOF >tmp.mbox
From nobody Thu Jan 1 00:59:59 1970
Subject: message 1
This is message 1.
From nobody Thu Jan 1 00:59:59 1970
Subject: message 2
This is message 2.
EOF
mdeliver -M inbox/ <./tmp.mbox
check 'mdeliver -M is tolerant with missing empty lines' 'ls inbox/new | wc -l | grep 2'

@ -172,23 +172,23 @@ inbox/cur/4:2,
export MAILSEQ=seq
check_test 'subject' -eq 1 'magrep subject:nice | wc -l'
check_test 'ignorecase' -eq 1 'magrep -i subject:NICE | wc -l'
check_test 'invert' -eq 2 'magrep -v subject:nice | wc -l'
check_test 'max matches' -eq 2 'magrep -m 2 from:Piet | wc -l'
check_test 'long subject' -eq 1 'magrep subject:aliqua | wc -l'
check_test 'decode large rfc2047 header' -eq 1 'magrep -d to:John | wc -l'
check_test 'subject' -eq 1 'magrep subject:nice : | wc -l'
check_test 'ignorecase' -eq 1 'magrep -i subject:NICE : | wc -l'
check_test 'invert' -eq 2 'magrep -v subject:nice : | wc -l'
check_test 'max matches' -eq 2 'magrep -m 2 from:Piet : | wc -l'
check_test 'long subject' -eq 1 'magrep subject:aliqua : | wc -l'
check_test 'decode large rfc2047 header' -eq 1 'magrep -d to:John : | wc -l'
echo 'inbox/cur/1:2,: subject: wow nice subject' >expect
check_same 'print' 'magrep -p subject:nice' 'cat expect'
check_same 'print' 'magrep -p subject:nice :' 'cat expect'
echo 'inbox/cur/1:2,: subject: nice' >expect
check_same 'print match' 'magrep -po subject:nice' 'cat expect'
check_same 'print match' 'magrep -po subject:nice :' 'cat expect'
echo 'nice' >expect
check_same 'print match only' 'magrep -o subject:nice' 'cat expect'
check_same 'print match only' 'magrep -o subject:nice :' 'cat expect'
echo 'inbox/cur/3:2,' >expect
check_same 'multiple subjects' 'magrep subject:multi' 'cat expect'
check_same 'multiple subjects' 'magrep subject:multi :' 'cat expect'
)

@ -0,0 +1,166 @@
#!/bin/sh -e
cd ${0%/*}
. ./lib.sh
plan 12
rm -rf test.dir
mkdir test.dir
(
cd test.dir
export PIPE_CONTENTTYPE='text/plain; format=flowed'
export COLUMNS=80
cat <<! >a
this
is
flowed.
!
cat <<! >b
this is flowed.
!
check 'simple reflow' 'mflow <a | cmp - b'
cat <<! >a
this
is
two spaces.
!
cat <<! >b
this is two spaces.
!
check 'simple space stuffing' 'mflow <a | cmp - b'
cat <<! >a
this
is
flowed.
this is fixed.
!
cat <<! >b
this is flowed.
this is fixed.
!
check 'simple fixed' 'mflow <a | cmp - b'
cat <<! >a
> this
> is
> quoted.
this
is
unquoted.
!
cat <<! >b
> this is quoted.
this is unquoted.
!
check 'simple quoted' 'mflow <a | cmp - b'
(
export PIPE_CONTENTTYPE='text/plain; format=flowed; delsp=yes'
cat <<! >a
> this
> is
> delsp.
> double
> spaced
!
cat <<! >b
> thisisdelsp.
> double spaced
!
check 'simple delsp' 'mflow <a | cmp - b'
)
cat <<! >a
this
is
way more than eighty chars which is the terminal width to flow.
this
is
way more than eighty chars which is the terminal width to flow.
!
cat <<! >b
this is way more than eighty chars which is the terminal width to flow. this is
way more than eighty chars which is the terminal width to flow.
!
check 'simple wrap' 'mflow <a | cmp - b'
cat <<! >a
this
is
way more than eighty chars which is the terminal width to flow.
averylongwordcomeshere.
this
is
way more than eighty chars which is the terminal width to flow.
!
cat <<! >b
this is way more than eighty chars which is the terminal width to flow.
averylongwordcomeshere. this is way more than eighty chars which is the
terminal width to flow.
!
check 'more complex wrap' 'mflow <a | cmp - b'
cat <<! >a
foo
bar.
quux.
!
cat <<! >b
foo bar.
quux.
!
check 'space before empty line' 'mflow <a | cmp - b'
cat <<! >a
Aaaaa bbbbb ccccc ddddd eeeee aaaaa bbbbb ccccc ddddd eeeee
aaaaa bbbbb ccccc ddddd eeeee aaaaa bbbbb ccccc ddddd eeeee
aaaaa bbbbb ccccc
ffffff gggggg hhhhhh iiiiii.
!
cat <<! >b
Aaaaa bbbbb ccccc ddddd eeeee aaaaa bbbbb ccccc ddddd eeeee aaaaa bbbbb ccccc
ddddd eeeee aaaaa bbbbb ccccc ddddd eeeee aaaaa bbbbb ccccc ffffff gggggg
hhhhhh iiiiii.
!
check 'fixed lines are wrapped too' 'mflow <a | cmp - b'
cat <<! >a
some
wrapped.
--
signature
!
cat <<! >b
some wrapped.
--
signature
!
check 'passes usenet signature marker as is' 'mflow <a | cmp - b'
cat <<! >a
some regular text being force wrapped because the line is way too long oh no who writes so long lines.
!
cat <<! >b
some regular text being force wrapped because the line is way too long oh no
who writes so long lines.
!
check 'force wrapping' 'mflow -f <a | cmp - b'
cat <<! >a
> some regular text being force wrapped because the line is way too long oh no who writes so long lines.
!
cat <<! >b
> some regular text being force wrapped because the line is way too long oh no
> who writes so long lines.
!
check 'force wrapping of quoted text' 'mflow -f <a | cmp - b'
)

@ -51,7 +51,7 @@ inbox/cur/4:2,
export MAILSEQ=seq
check_same 'filename' 'msort -F' 'cat seq'
check_same 'filename' 'msort -F :' 'cat seq'
cat <<! >expect
inbox/cur/3:2,
@ -59,7 +59,7 @@ inbox/cur/1:2,
inbox/cur/2:2,
inbox/cur/4:2,
!
check_same 'date' 'msort -d' 'cat expect'
check_same 'date' 'msort -d :' 'cat expect'
cat <<! >expect
inbox/cur/4:2,
@ -67,7 +67,7 @@ inbox/cur/2:2,
inbox/cur/1:2,
inbox/cur/3:2,
!
check_same 'reverse date' 'msort -dr' 'cat expect'
check_same 'reverse date' 'msort -dr :' 'cat expect'
cat <<! >expect
inbox/cur/2:2,
@ -75,7 +75,7 @@ inbox/cur/3:2,
inbox/cur/4:2,
inbox/cur/1:2,
!
check_same 'from' 'msort -f' 'cat expect'
check_same 'from' 'msort -f :' 'cat expect'
cat <<! >expect
inbox/cur/3:2,
@ -83,6 +83,6 @@ inbox/cur/2:2,
inbox/cur/4:2,
inbox/cur/1:2,
!
check_same 'subject' 'msort -s' 'cat expect'
check_same 'subject' 'msort -s :' 'cat expect'
)

@ -31,7 +31,7 @@ check_test 'whole thread' -eq 4 'mseq 6= | wc -l'
check_test 'subthread' -eq 2 'mseq 7_ | wc -l'
check 'parent' 'mseq 6^ | grep "inbox/cur/5_1:2,"'
check_test 'range' -eq 3 'mseq 1:3 | wc -l'
check_same 'multiple mmsg' 'mseq 1 2' 'echo "inbox/cur/1:2,\ninbox/cur/2:2,"'
check_same 'multiple mmsg' 'mseq 1 2' 'printf "inbox/cur/1:2,\ninbox/cur/2:2,\n"'
cat <<! >seq
inbox/cur/1:2,

@ -20,20 +20,20 @@ ln -sf inbox/cur/1:2, cur
export MAILSEQ=seq MAILCUR=cur
check 'mark seen' 'mflag -S 1 && [ -e "inbox/cur/1:2,S" ]'
check_test 'fix seq' -eq 2 'mseq -f | mseq -S | wc -l'
check_test 'fix seq' -eq 2 'mseq -f : | mseq -S | wc -l'
check 'mark replied' 'mflag -R 1 && [ -e "inbox/cur/1:2,RS" ]'
check_test 'fix seq' -eq 2 'mseq -f | mseq -S | wc -l'
check_test 'fix seq' -eq 2 'mseq -f : | mseq -S | wc -l'
check 'unmark replied' 'mflag -r 1 && [ -e "inbox/cur/1:2,S" ]'
check_test 'fix seq' -eq 2 'mseq -f | mseq -S | wc -l'
check_test 'fix seq' -eq 2 'mseq -f : | mseq -S | wc -l'
check 'mark flagged' 'mflag -F 1 && [ -e "inbox/cur/1:2,FS" ]'
check_test 'fix seq' -eq 2 'mseq -f | mseq -S | wc -l'
check_test 'fix seq' -eq 2 'mseq -f : | mseq -S | wc -l'
check 'unmark flagged' 'mflag -f 1 && [ -e "inbox/cur/1:2,S" ]'
check_test 'fix seq' -eq 2 'mseq -f | mseq -S | wc -l'
check_test 'fix seq' -eq 2 'mseq -f : | mseq -S | wc -l'
check 'unmark seen' 'mflag -s 1 && [ -e "inbox/cur/1:2," ]'
check_test 'fix seq' -eq 2 'mseq -f | mseq -S | wc -l'
check_test 'fix seq' -eq 2 'mseq -f : | mseq -S | wc -l'
check 'mark trashed' 'mflag -T 1 && [ -e "inbox/cur/1:2,T" ]'
check_test 'fix seq' -eq 2 'mseq -f | mseq -S | wc -l'
check_test 'fix seq' -eq 2 'mseq -f : | mseq -S | wc -l'
check 'unmark trashed' 'mflag -t 1 && [ -e "inbox/cur/1:2," ]'
check_test 'fix seq' -eq 2 'mseq -f | mseq -S | wc -l'
check_test 'fix seq' -eq 2 'mseq -f : | mseq -S | wc -l'
)

@ -1,7 +1,7 @@
#!/bin/sh -e
cd ${0%/*}
. ./lib.sh
plan 1
plan 2
rm -rf test.dir
mkdir test.dir
@ -16,4 +16,10 @@ inbox/new/2
check_test 'minc' -eq 2 'minc inbox | wc -l'
while read f; do touch "$f"; done <<!
inbox/new/3:2,
inbox/new/4
!
check_test 'minc stdin' -eq 2 'echo inbox | minc | wc -l'
)

Loading…
Cancel
Save