mirror of
https://github.com/leahneukirchen/mblaze
synced 2024-11-07 15:20:37 +00:00
Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
0839a3ecc2
@ -17,18 +17,18 @@ PREFIX=/usr/local
|
||||
BINDIR=$(PREFIX)/bin
|
||||
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
|
||||
|
||||
all: $(ALL) museragent
|
||||
|
||||
$(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
|
||||
maddr magrep mexport mflag mgenmid mhdr mlist mpick mscan msed mseq mshow msort \
|
||||
mthread : seq.o slurp.o
|
||||
maddr magrep mhdr mpick mscan mshow : rfc2047.o
|
||||
magrep mshow : rfc2045.o
|
||||
maddr magrep mflow mhdr mpick mscan mshow : rfc2047.o
|
||||
magrep mflow mshow : rfc2045.o
|
||||
mshow : filter.o safe_u8putstr.o rfc2231.o pipeto.o
|
||||
mscan : pipeto.o
|
||||
msort : mystrverscmp.o
|
||||
|
17
NEWS.md
Normal file
17
NEWS.md
Normal 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
1
README
@ -18,6 +18,7 @@ DESCRIPTION
|
||||
mdirs(1) find Maildir folders
|
||||
mexport(1) export Maildir folders as mailboxes
|
||||
mflag(1) change flags (marks) of mail
|
||||
mflow(1) reflow format=flowed plain text mails
|
||||
mfwd(1) forward mail
|
||||
mgenmid(1) generate Message-IDs
|
||||
mhdr(1) extract mail headers
|
||||
|
@ -8,9 +8,9 @@
|
||||
mshow -t "$1" | awk -v "msg=$1" '
|
||||
{ match($0, "^ *"); indent = RLENGTH }
|
||||
$2 == "text/plain" { plain++ }
|
||||
$2 == "multipart/signed" { signed = +$1; si = indent; next }
|
||||
signed && !content && indent == si+2 { content = +$1; next }
|
||||
signed && content && !signature && indent == si+2 { signature = +$1; type = $2 }
|
||||
$2 == "multipart/signed" { signed = 0+$1; si = indent; next }
|
||||
signed && !content && indent == si+2 { content = 0+$1; next }
|
||||
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
|
||||
@ -26,7 +26,7 @@ END {
|
||||
exit(system("mshow -r -O " q(msg) " " q(signed) \
|
||||
" | openssl smime -verify"))
|
||||
} else {
|
||||
print("Cant verify signatures of type " type ".")
|
||||
print("Cannot verify signatures of type " type ".")
|
||||
exit(2)
|
||||
}
|
||||
}
|
||||
|
14
filter.c
14
filter.c
@ -1,9 +1,13 @@
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <poll.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/wait.h>
|
||||
#include <time.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)
|
||||
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 };
|
||||
|
||||
if (!(pid = fork())) {
|
||||
@ -88,7 +96,9 @@ filter(char *input, size_t inlen, char *cmd, char **outputo, size_t *outleno)
|
||||
input += ret;
|
||||
inlen -= ret;
|
||||
}
|
||||
if (ret <= 0 || inlen == 0)
|
||||
if (ret <= 0 && errno == EAGAIN) {
|
||||
/* ignore */
|
||||
} else if (ret <= 0 || inlen == 0)
|
||||
close(fds[1].fd);
|
||||
} else if (fds[1].revents & (POLLERR | POLLHUP | POLLNVAL)) {
|
||||
fds[1].fd = -1;
|
||||
|
@ -1,3 +1,4 @@
|
||||
text/plain: mflow
|
||||
text/html: lynx -dump -stdin -nomargins ${PIPE_CHARSET:+-assume_charset $PIPE_CHARSET}
|
||||
application/pdf: pdftotext - - | par
|
||||
application: file -b -
|
||||
|
@ -30,6 +30,8 @@ find Maildir folders
|
||||
export Maildir folders as mailboxes
|
||||
.It Xr mflag 1
|
||||
change flags (marks) of mail
|
||||
.It Xr mflow 1
|
||||
reflow format=flowed plain text mails
|
||||
.It Xr mfwd 1
|
||||
forward mail
|
||||
.It Xr mgenmid 1
|
||||
|
55
man/mflow.1
Normal file
55
man/mflow.1
Normal 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
2
mcom
@ -51,7 +51,7 @@ case "$0" in
|
||||
shift
|
||||
resume=1
|
||||
if [ "$#" -gt 0 ]; then
|
||||
echo "used dreaft $1"
|
||||
echo "used draft $1"
|
||||
draft="$1"
|
||||
shift
|
||||
fi
|
||||
|
187
mflow.c
Normal file
187
mflow.c
Normal 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
6
mseq.c
@ -252,6 +252,9 @@ stdinmode()
|
||||
void
|
||||
overridecur(char *file)
|
||||
{
|
||||
static int once = 0;
|
||||
if (once++)
|
||||
return;
|
||||
while (*file == ' ')
|
||||
file++;
|
||||
setenv("MAILDOT", file, 1);
|
||||
@ -260,6 +263,9 @@ overridecur(char *file)
|
||||
void
|
||||
setcur(char *file)
|
||||
{
|
||||
static int once = 0;
|
||||
if (once++)
|
||||
return;
|
||||
while (*file == ' ')
|
||||
file++;
|
||||
unsetenv("MAILDOT");
|
||||
|
8
mshow.c
8
mshow.c
@ -29,6 +29,8 @@ static char *hflag = defaulthflags;
|
||||
static char *xflag;
|
||||
static char *Oflag;
|
||||
|
||||
static char fallback_ct[] = "text/plain";
|
||||
|
||||
struct message *filters;
|
||||
|
||||
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");
|
||||
if (!ct)
|
||||
ct = "text/x-unknown";
|
||||
ct = fallback_ct;
|
||||
char *mt = mimetype(ct);
|
||||
char *tlmt = tlmimetype(ct);
|
||||
char *filename = mime_filename(msg);
|
||||
@ -310,7 +312,7 @@ choose_alternative(struct message *msg, int depth)
|
||||
m++;
|
||||
char *ict = blaze822_hdr(imsg, "content-type");
|
||||
if (!ict)
|
||||
ict = "text/x-unknown";
|
||||
ict = fallback_ct;
|
||||
char *imt = mimetype(ict);
|
||||
|
||||
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");
|
||||
if (!ct)
|
||||
ct = "text/x-unknown";
|
||||
ct = fallback_ct;
|
||||
char *mt = mimetype(ct);
|
||||
char *filename = mime_filename(msg);
|
||||
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
char *buf = malloc((e - s) / 4 * 3);
|
||||
char *buf = malloc((e - s) / 4 * 3 + 1);
|
||||
if (!buf)
|
||||
return 0;
|
||||
|
||||
@ -118,6 +118,8 @@ error:
|
||||
if (c3 != '=') *buf++ = d2;
|
||||
}
|
||||
|
||||
*buf = 0;
|
||||
|
||||
*decleno = buf - *deco;
|
||||
return 1;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user