Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Alex Holst 2017-08-15 09:39:30 +02:00
commit 0839a3ecc2
14 changed files with 299 additions and 16 deletions

View File

@ -17,18 +17,18 @@ PREFIX=/usr/local
BINDIR=$(PREFIX)/bin BINDIR=$(PREFIX)/bin
MANDIR=$(PREFIX)/share/man MANDIR=$(PREFIX)/share/man
ALL = maddr magrep mdate mdeliver mdirs mexport mflag mgenmid mhdr minc mlist mmime mpick mscan msed mseq mshow msort mthread ALL = maddr magrep mdate mdeliver mdirs mexport mflag mflow mgenmid mhdr minc mlist mmime mpick mscan msed mseq mshow msort mthread
SCRIPT = mcolor mcom mless mmkdir mquote museragent SCRIPT = mcolor mcom mless mmkdir mquote museragent
all: $(ALL) museragent all: $(ALL) museragent
$(ALL) : % : %.o $(ALL) : % : %.o
maddr magrep mdeliver mexport mflag mgenmid mhdr mpick mscan msed mshow \ maddr magrep mdeliver mexport mflag mflow mgenmid mhdr mpick mscan msed mshow \
msort mthread : blaze822.o mymemmem.o mytimegm.o msort mthread : blaze822.o mymemmem.o mytimegm.o
maddr magrep mexport mflag mgenmid mhdr mlist mpick mscan msed mseq mshow msort \ maddr magrep mexport mflag mgenmid mhdr mlist mpick mscan msed mseq mshow msort \
mthread : seq.o slurp.o mthread : seq.o slurp.o
maddr magrep mhdr mpick mscan mshow : rfc2047.o maddr magrep mflow mhdr mpick mscan mshow : rfc2047.o
magrep mshow : rfc2045.o magrep mflow mshow : rfc2045.o
mshow : filter.o safe_u8putstr.o rfc2231.o pipeto.o mshow : filter.o safe_u8putstr.o rfc2231.o pipeto.o
mscan : pipeto.o mscan : pipeto.o
msort : mystrverscmp.o msort : mystrverscmp.o

17
NEWS.md Normal file
View File

@ -0,0 +1,17 @@
## 0.2 (2017-07-17)
* New sequence syntax `m:+n` for `n` messages after message `m`.
* Threading shortcuts `=`, `_`, `^` for `.=`, `._`, `.^`.
* Sequence related errors are now reported.
* minc and mlist normalize slashes in paths.
* mfwd now generates conforming message/rfc822 parts.
* mthread can add optional folders (e.g. your outbox) to resolve message ids.
* mcom now adds Date: just before sending or cancelling the mail.
* VIOLATIONS.md documents how mblaze works with certain common mistakes.
* Full documentation revamp by Larry Hynes.
* Fix rare crash looking for mail body.
* Numerous small bug and portability fixes.
## 0.1 (2017-06-24)
* Initial release

1
README
View File

@ -18,6 +18,7 @@ DESCRIPTION
mdirs(1) find Maildir folders mdirs(1) find Maildir folders
mexport(1) export Maildir folders as mailboxes mexport(1) export Maildir folders as mailboxes
mflag(1) change flags (marks) of mail mflag(1) change flags (marks) of mail
mflow(1) reflow format=flowed plain text mails
mfwd(1) forward mail mfwd(1) forward mail
mgenmid(1) generate Message-IDs mgenmid(1) generate Message-IDs
mhdr(1) extract mail headers mhdr(1) extract mail headers

View File

@ -1 +1 @@
0.1 0.2

View File

@ -8,9 +8,9 @@
mshow -t "$1" | awk -v "msg=$1" ' mshow -t "$1" | awk -v "msg=$1" '
{ match($0, "^ *"); indent = RLENGTH } { match($0, "^ *"); indent = RLENGTH }
$2 == "text/plain" { plain++ } $2 == "text/plain" { plain++ }
$2 == "multipart/signed" { signed = +$1; si = indent; next } $2 == "multipart/signed" { signed = 0+$1; si = indent; next }
signed && !content && indent == si+2 { content = +$1; next } signed && !content && indent == si+2 { content = 0+$1; next }
signed && content && !signature && indent == si+2 { signature = +$1; type = $2 } signed && content && !signature && indent == si+2 { signature = 0+$1; type = $2 }
function q(a) { gsub("\\47", "\47\\\47\47", a); return "\47"a"\47" } function q(a) { gsub("\\47", "\47\\\47\47", a); return "\47"a"\47" }
END { END {
if (type == "" && plain) { // guess plain text armored signature if (type == "" && plain) { // guess plain text armored signature
@ -26,7 +26,7 @@ END {
exit(system("mshow -r -O " q(msg) " " q(signed) \ exit(system("mshow -r -O " q(msg) " " q(signed) \
" | openssl smime -verify")) " | openssl smime -verify"))
} else { } else {
print("Cant verify signatures of type " type ".") print("Cannot verify signatures of type " type ".")
exit(2) exit(2)
} }
} }

View File

@ -1,9 +1,13 @@
#include <sys/wait.h>
#include <fcntl.h>
#include <errno.h>
#include <limits.h>
#include <poll.h> #include <poll.h>
#include <signal.h> #include <signal.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/wait.h>
#include <time.h> #include <time.h>
#include <unistd.h> #include <unistd.h>
@ -32,6 +36,10 @@ filter(char *input, size_t inlen, char *cmd, char **outputo, size_t *outleno)
if (pipe(pipe0) != 0 || pipe(pipe1) != 0) if (pipe(pipe0) != 0 || pipe(pipe1) != 0)
goto fail; goto fail;
int got = fcntl(pipe0[1], F_GETFL);
if (got > 0)
fcntl(pipe0[1], F_SETFL, got | O_NONBLOCK);
char *argv[] = { "/bin/sh", "-c", cmd, (char *)0 }; char *argv[] = { "/bin/sh", "-c", cmd, (char *)0 };
if (!(pid = fork())) { if (!(pid = fork())) {
@ -88,7 +96,9 @@ filter(char *input, size_t inlen, char *cmd, char **outputo, size_t *outleno)
input += ret; input += ret;
inlen -= ret; inlen -= ret;
} }
if (ret <= 0 || inlen == 0) if (ret <= 0 && errno == EAGAIN) {
/* ignore */
} else if (ret <= 0 || inlen == 0)
close(fds[1].fd); close(fds[1].fd);
} else if (fds[1].revents & (POLLERR | POLLHUP | POLLNVAL)) { } else if (fds[1].revents & (POLLERR | POLLHUP | POLLNVAL)) {
fds[1].fd = -1; fds[1].fd = -1;

View File

@ -1,3 +1,4 @@
text/plain: mflow
text/html: lynx -dump -stdin -nomargins ${PIPE_CHARSET:+-assume_charset $PIPE_CHARSET} text/html: lynx -dump -stdin -nomargins ${PIPE_CHARSET:+-assume_charset $PIPE_CHARSET}
application/pdf: pdftotext - - | par application/pdf: pdftotext - - | par
application: file -b - application: file -b -

View File

@ -30,6 +30,8 @@ find Maildir folders
export Maildir folders as mailboxes export Maildir folders as mailboxes
.It Xr mflag 1 .It Xr mflag 1
change flags (marks) of mail change flags (marks) of mail
.It Xr mflow 1
reflow format=flowed plain text mails
.It Xr mfwd 1 .It Xr mfwd 1
forward mail forward mail
.It Xr mgenmid 1 .It Xr mgenmid 1

55
man/mflow.1 Normal file
View File

@ -0,0 +1,55 @@
.Dd July 26, 2017
.Dt MFLOW 1
.Os
.Sh NAME
.Nm mflow
.Nd reflow format=flowed plain text mails
.Sh SYNOPSIS
.Nm
\&<
.Ar file
.Sh DESCRIPTION
.Nm
reformats the standard input according to the rules
of RFC 3676.
.Ev PIPE_CONTENTTYPE
is inspected, making this a suitable filter
for
.Sq text/plain
messages for
.Xr mshow 1 .
Mails not using
.Sq format=flowed
are output as is.
.Pp
Text is reflowed (where allowed) to
fit the width given in the environment variable
.Ev COLUMNS ,
the terminal width, or 80 characters by default.
.Pp
If defined,
the environment variable
.Ev MAXCOLUMNS
specifies the maximum line length.
.Sh EXIT STATUS
.Ex -std
.Sh SEE ALSO
.Xr mshow 1
.Rs
.%A R. Gellens
.%D February 2004
.%R RFC 3676
.%T The Text/Plain Format and DelSp Parameters
.Re
.Sh AUTHORS
.An Leah Neukirchen Aq Mt leah@vuxu.org
.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/

2
mcom
View File

@ -51,7 +51,7 @@ case "$0" in
shift shift
resume=1 resume=1
if [ "$#" -gt 0 ]; then if [ "$#" -gt 0 ]; then
echo "used dreaft $1" echo "used draft $1"
draft="$1" draft="$1"
shift shift
fi fi

187
mflow.c Normal file
View File

@ -0,0 +1,187 @@
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "blaze822.h"
int column = 0;
int maxcolumn = 80;
void
chgquote(int quotes)
{
static int oquotes;
if (quotes != oquotes) {
if (column)
putchar('\n');
column = 0;
oquotes = quotes;
}
}
void
fixed(int quotes, char *line, size_t linelen)
{
chgquote(quotes);
if (linelen > (size_t)(maxcolumn - column)) {
putchar('\n');
column = 0;
}
if (column == 0) {
for (; column < quotes; column++)
putchar('>');
if (quotes)
putchar(' ');
}
fwrite(line, 1, linelen, stdout);
putchar('\n');
column = 0;
}
void
flowed(int quotes, char *line, ssize_t linelen)
{
chgquote(quotes);
int done = 0;
while (!done) {
if (column == 0) {
for (; column < quotes; column++)
putchar('>');
column++;
if (quotes)
putchar(' ');
}
char *eow;
if (*line == ' ')
eow = memchr(line + 1, ' ', linelen - 1);
else
eow = memchr(line, ' ', linelen);
if (!eow) {
eow = line + linelen;
done = 1;
}
if (column + (eow - line) > maxcolumn) {
putchar('\n');
column = 0;
if (*line == ' ') {
line++;
linelen--;
}
} else {
fwrite(line, 1, eow - line, stdout);
column += eow - line;
linelen -= eow - line;
line = eow;
}
}
}
int
main()
{
char *linebuf = 0;
char *line;
size_t linelen = 0;
int quotes = 0;
int reflow = 1; // re-evaluated on $PIPE_CONTENTTYPE
int delsp = 0;
char *ct = getenv("PIPE_CONTENTTYPE");
if (ct) {
char *s, *se;
blaze822_mime_parameter(ct, "format", &s, &se);
reflow = s && (strncasecmp(s, "flowed", 6) == 0);
blaze822_mime_parameter(ct, "delsp", &s, &se);
delsp = s && (strncasecmp(s, "yes", 3) == 0);
}
char *cols = getenv("COLUMNS");
if (cols && isdigit(*cols)) {
maxcolumn = atoi(cols);
} else {
struct winsize w;
int fd = open("/dev/tty", O_RDONLY | O_NOCTTY);
if (fd >= 0) {
if (ioctl(fd, TIOCGWINSZ, &w) == 0)
maxcolumn = w.ws_col;
close(fd);
}
}
char *maxcols = getenv("MAXCOLUMNS");
if (maxcols && isdigit(*maxcols)) {
int m = atoi(maxcols);
if (maxcolumn > m)
maxcolumn = m;
}
while (1) {
errno = 0;
ssize_t rd = getdelim(&linebuf, &linelen, '\n', stdin);
if (rd == -1) {
if (errno == 0)
break;
fprintf(stderr, "mflow: error reading: %s\n",
strerror(errno));
exit(1);
}
line = linebuf;
if (!reflow) {
fwrite(line, 1, rd, stdout);
continue;
}
if (rd > 0 && line[rd-1] == '\n')
line[--rd] = 0;
if (rd > 0 && line[rd-1] == '\r')
line[--rd] = 0;
quotes = 0;
while (*line == '>') { // measure quote depth
line++;
quotes++;
rd--;
}
if (*line == ' ') { // space stuffing
line++;
rd--;
}
if (strcmp(line, "-- ") == 0) { // usenet signature convention
if (column)
fixed(quotes, "", 0); // flush paragraph
fixed(quotes, line, rd);
continue;
}
if (rd > 0 && line[rd-1] == ' ') { // flowed line
if (delsp)
line[--rd] = 0;
flowed(quotes, line, rd);
} else {
fixed(quotes, line, rd);
}
}
if (reflow && column != 0)
putchar('\n');
}

6
mseq.c
View File

@ -252,6 +252,9 @@ stdinmode()
void void
overridecur(char *file) overridecur(char *file)
{ {
static int once = 0;
if (once++)
return;
while (*file == ' ') while (*file == ' ')
file++; file++;
setenv("MAILDOT", file, 1); setenv("MAILDOT", file, 1);
@ -260,6 +263,9 @@ overridecur(char *file)
void void
setcur(char *file) setcur(char *file)
{ {
static int once = 0;
if (once++)
return;
while (*file == ' ') while (*file == ' ')
file++; file++;
unsetenv("MAILDOT"); unsetenv("MAILDOT");

View File

@ -29,6 +29,8 @@ static char *hflag = defaulthflags;
static char *xflag; static char *xflag;
static char *Oflag; static char *Oflag;
static char fallback_ct[] = "text/plain";
struct message *filters; struct message *filters;
static int mimecount; static int mimecount;
@ -182,7 +184,7 @@ render_mime(int depth, struct message *msg, char *body, size_t bodylen)
{ {
char *ct = blaze822_hdr(msg, "content-type"); char *ct = blaze822_hdr(msg, "content-type");
if (!ct) if (!ct)
ct = "text/x-unknown"; ct = fallback_ct;
char *mt = mimetype(ct); char *mt = mimetype(ct);
char *tlmt = tlmimetype(ct); char *tlmt = tlmimetype(ct);
char *filename = mime_filename(msg); char *filename = mime_filename(msg);
@ -310,7 +312,7 @@ choose_alternative(struct message *msg, int depth)
m++; m++;
char *ict = blaze822_hdr(imsg, "content-type"); char *ict = blaze822_hdr(imsg, "content-type");
if (!ict) if (!ict)
ict = "text/x-unknown"; ict = fallback_ct;
char *imt = mimetype(ict); char *imt = mimetype(ict);
char *s = strstr(Aflag, imt); char *s = strstr(Aflag, imt);
@ -367,7 +369,7 @@ list_mime(int depth, struct message *msg, char *body, size_t bodylen)
char *ct = blaze822_hdr(msg, "content-type"); char *ct = blaze822_hdr(msg, "content-type");
if (!ct) if (!ct)
ct = "text/x-unknown"; ct = fallback_ct;
char *mt = mimetype(ct); char *mt = mimetype(ct);
char *filename = mime_filename(msg); char *filename = mime_filename(msg);

View File

@ -75,7 +75,7 @@ blaze822_decode_b64(char *s, char *e, char **deco, size_t *decleno)
41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
}; };
char *buf = malloc((e - s) / 4 * 3); char *buf = malloc((e - s) / 4 * 3 + 1);
if (!buf) if (!buf)
return 0; return 0;
@ -118,6 +118,8 @@ error:
if (c3 != '=') *buf++ = d2; if (c3 != '=') *buf++ = d2;
} }
*buf = 0;
*decleno = buf - *deco; *decleno = buf - *deco;
return 1; return 1;
} }