test/whom/test-whom \
test/cleanup ## The "cleanup" test should always be last.
-XFAIL_TESTS = \
- test/inc/test-eom-align \
- #
+XFAIL_TESTS =
check_SCRIPTS = test/common.sh
dnl ----------------
dnl CHECK FLEX FIXUP
dnl ----------------
-dnl BUG: This stops $(LFLAGS) working as it comes after $(AM_FLAGS).
dnl Use AM_LFLAGS make variable setting to work around bugs in flex
dnl 2.5.36-37 that cause signed/unsigned mismatch,
dnl http://sourceforge.net/p/flex/bugs/140/
AS_IF([test "$LEX" = flex],
[AS_CASE([`$LEX -V`],
[flex\ 2.5.35], [AM_LFLAGS=\
-'; sed "s/ int n;/ size_t n;/" $@ >$@.tmp && mv -f $@.tmp $@'],
+'; sed "s/ int n;/ size_t n;/" $@ >$@.tmp && mv -f $@.tmp $@;'],
[flex\ 2.5.3[[67]]], [AM_LFLAGS=\
-'; sed "s/\( \)int i;/\1yy_size_t i;/" $@ >$@.tmp && mv -f $@.tmp $@'],
+'; sed "s/\( \)int i;/\1yy_size_t i;/" $@ >$@.tmp && mv -f $@.tmp $@;'],
[flex\ 2.6.0], [AM_LFLAGS=\
-'; sed "s/, num_to_read/, (size_t) num_to_read/" $@ >$@.tmp && mv -f $@.tmp $@'],
+'; sed "s/, num_to_read/, (size_t) num_to_read/" $@ >$@.tmp && mv -f $@.tmp $@;'],
[flex\ 2.6.1], [AM_LFLAGS=\
-'; sed '\''/\<int num_to_read\>/s/int/size_t/; s/\<yy_size_t i\>/int i/'\'' $@ >$@.tmp && mv -f $@.tmp $@'])
+'; sed '\''/\<int num_to_read\>/s/int/size_t/; \
+ s/\<yy_size_t i\>/int i/; \
+ s/int n; \\/yy_size_t n; \\/'\'' $@ >$@.tmp && mv -f $@.tmp $@;'])
AC_SUBST([AM_LFLAGS])])
dnl ------------------
char *input_line; /* The (unfolded) input line. */
size_t input_line_len; /* Amount of text stored in input_line. */
size_t input_line_size; /* Size of allocated input_line. */
+ charstring_t unexpected; /* Accumulate unexpected characters in input. */
cr_indicator cr_before_lf; /* To support CR before LF. If the first
line of the input has a CR before its LF,
int icalparse (void);
extern vevent vevents;
int icallex (void);
+extern int parser_status;
/* And this is for the icalendar scanner. */
extern YYSTYPE icallval;
/*
- * For directing the scanner to use a files other than stdin/stdout.
+ * For directing the scanner to use files other than stdin/stdout.
* These don't use the accessors provided by modern flex because
* flex 2.5.4 doesn't supply them.
*/
typedef struct m_getfld_state *m_getfld_state_t;
-/*
- * Maildrop styles
- */
-#define MS_DEFAULT 0 /* default (one msg per file) */
-#define MS_UNKNOWN 1 /* type not known yet */
-#define MS_MBOX 2 /* Unix-style "from" lines */
-#define MS_MMDF 3 /* string MMDF_DELIM */
-
#define NOUSE 0 /* draft being re-used */
#define TFOLDER 0 /* path() given a +folder */
*/
int scan_input (int fd, int *eightbit);
+/*
+ * Returns string representation of int, in static memory.
+ */
+char *m_str(int value);
+
+/*
+ * Returns string representation of an int, in static memory. If width
+ * == 0, does not limit the width. If width > 0 and value will not fit
+ * in field of that size, including any negative sign but excluding
+ * terminating null, then returns "?".
+ */
+char *m_strn(int value, unsigned int width);
+
/*
* program initialization
*
rp_string (int code)
{
char *text;
- static char buffer[BUFSIZ];
+ /* The additional space is to avoid warning from gcc -Wformat-truncation. */
+ static char buffer[BUFSIZ + 19];
switch (sm_reply.code != NOTOK ? code : NOTOK) {
case RP_AOK:
#include <h/mh.h> /* for snprintf() */
#include <h/nmh.h>
+#include <h/utils.h>
#include <h/tws.h>
#include <time.h>
if (!tw)
return NULL;
- snprintf (buffer, sizeof(buffer), "%.3s %.3s %02d %02d:%02d:%02d %.4d\n",
+ snprintf (buffer, sizeof(buffer), "%.3s %.3s %02d %02d:%02d:%02d %s\n",
tw_dotw[tw->tw_wday], tw_moty[tw->tw_mon], tw->tw_mday,
tw->tw_hour, tw->tw_min, tw->tw_sec,
- tw->tw_year < 100 ? tw->tw_year + 1900 : tw->tw_year);
+ m_strn(tw->tw_year < 100 ? tw->tw_year + 1900 : tw->tw_year, 4));
return buffer;
}
* return the string representation of the numeric offset.
*/
-char *
-dtimezone (int offset, int flags)
+char *dtimezone(int offset, int flags)
{
- int hours, mins;
- static char buffer[10];
-
- if (offset < 0) {
- mins = -((-offset) % 60);
- hours = -((-offset) / 60);
- } else {
- mins = offset % 60;
- hours = offset / 60;
- }
+ static char buffer[sizeof "+3579139459"]; /* 2,147,483,648 / 60 = 35,791,394 */
+ bool pos;
+ unsigned os, hours, mins;
+
+ pos = offset >= 0;
+ os = pos ? offset : ~offset + 1; /* abs(3) undefined on INT_MIN. */
+ hours = os / 60;
+ mins = os % 60;
+
+ if (flags & TW_DST) /* Shift towards +infinity. */
+ hours += pos ? 1 : -1;
+
+ snprintf(buffer, sizeof(buffer), "%c%02u%02u",
+ pos ? '+' : '-', hours, mins);
- if (flags & TW_DST)
- hours++;
- snprintf (buffer, sizeof(buffer), "%s%02d%02d",
- offset < 0 ? "-" : "+", abs (hours), abs (mins));
return buffer;
}
. {
/* By default, flex will just pass unmatched text. Catch it instead. */
- inform("unexpected input: |%s|\n", icaltext);
+ contentline *clines = vevents.last->contentlines;
+ contentline *cline;
+
+ if (clines && (cline = clines->last)) {
+ if (cline->unexpected == NULL) {
+ cline->unexpected = charstring_create (0);
+ }
+ charstring_append_cstring (cline->unexpected, icaltext);
+ }
}
<INITIAL>
#include "h/utils.h"
static char *append (contentline *, const char *, const size_t);
-static void new_input_line (contentline **);
+static void new_content_line (contentline **);
static void new_vevent (vevent *);
static void free_param_names (param_list *);
static void free_param_values (value_list *);
/* contentline = name *(";" param ) ":" value CRLF */
contentline
: ICAL_NAME {
- new_input_line (&vevents.last->contentlines);
+ new_content_line (&vevents.last->contentlines);
append (vevents.last->contentlines->last, $1, strlen ($1));
vevents.last->contentlines->last->name = $1;
} ICAL_COLON {
}
}
| ICAL_NAME {
- new_input_line (&vevents.last->contentlines);
+ new_content_line (&vevents.last->contentlines);
append (vevents.last->contentlines->last, $1, strlen ($1));
vevents.last->contentlines->last->name = $1;
} param_list ICAL_COLON {
}
static void
-new_input_line (contentline **cline) {
+new_content_line (contentline **cline) {
contentline *new_node;
NEW0(new_node);
}
free (i->value);
free (i->input_line);
+ charstring_free (i->unexpected);
next = i->next;
free (i);
}
static int
icalerror (const char *error) {
+ contentline *c;
+ charstring_t context = NULL;
+
+ /* Find last chunk of unexpected text. */
+ for (c = vevents.last->contentlines; c; c = c->next) {
+ if (c->unexpected) {
+ context = c->unexpected;
+ }
+ }
+
if (! strcmp ("syntax error, unexpected $end, expecting ICAL_NAME",
error)) {
/* Empty input: produce no output. */
} else {
- adios (NULL, "%s", error);
+ if (context) {
+ inform ("%s after \"%s\"", error, charstring_buffer (context));
+ } else {
+ inform ("%s", error);
+ }
+ parser_status = -1;
+ return -1;
}
return 0; /* The return value isn't used anyway. */
#define eom(c,s) (s->msg_style != MS_DEFAULT && \
((c) == *s->msg_delim && m_Eom(s)))
+/*
+ * Maildrop styles
+ */
+#define MS_DEFAULT 0 /* default (one msg per file) */
+#define MS_UNKNOWN 1 /* type not known yet */
+#define MS_MBOX 2 /* Unix-style "from" lines */
+#define MS_MMDF 3 /* string MMDF_DELIM */
+
/* This replaces the old approach, with its direct access to stdio
* internals. It uses one fread() to load a buffer that we manage.
*
int i;
char text[MAX_DELIMITER_SIZE];
char *cp;
+ int adjust = 1;
for (i = 0, cp = text; i < s->edelimlen; ++i, ++cp) {
int c2;
*cp = '\0';
break;
}
- *cp = c2;
+ *cp = c2;
}
if (i != s->edelimlen ||
strncmp (text, (char *)s->edelim, s->edelimlen)) {
- if (i == 0 && s->msg_style == MS_MBOX)
+ if (i == 0 && s->msg_style == MS_MBOX) {
/* the final newline in the (brain damaged) unix-format
* maildrop is part of the delimiter - delete it.
*/
return 1;
+ }
+
+ if (i <= 2 && s->msg_style == MS_MBOX &&
+ i != s->edelimlen && ! strncmp(text, s->fdelim, i)) {
+ /* If all or part of fdelim appeared at the end of the file,
+ back up even more so that the bytes are included in the
+ message. */
+ adjust = 2;
+ }
/* Did not find delimiter, so restore the read position.
Note that on input, a character had already been read
with Getc(). It will be unget by m_getfld () on return. */
- s->readpos -= s->bytes_read - 1;
- s->bytes_read = 1;
+ s->readpos -= s->bytes_read - adjust;
+ s->bytes_read = adjust;
return 0;
}
#include <limits.h>
#include <h/mh.h>
+#include <h/utils.h>
#define STR(s) #s
#define SIZE(n) (sizeof STR(n)) /* Includes NUL. */
char *
m_name (int num)
{
- static char name[SIZE(INT_MAX)];
+ if (num <= 0) return "?";
- if (num <= 0)
- return "?";
-
- snprintf(name, sizeof name, "%d", num);
-
- return name;
+ return m_strn(num, SIZE(INT_MAX));
}
if (BIO_get_ssl(nsc->ssl_io, &ssl) < 1) {
netsec_err(errstr, "Certificate verification failed, but "
"cannot retrieve SSL handle: %s",
- ERR_error_string(ERR_get_error(), NULL));
+ ERR_error_string(errcode, NULL));
} else {
netsec_err(errstr, "Server certificate verification failed: %s",
X509_verify_cert_error_string(
}
} else {
netsec_err(errstr, "TLS negotiation failed: %s",
- ERR_error_string(errcode, NULL));
+ ERR_error_string(errcode, NULL));
}
/*
#include "m_mktemp.h"
#include "makedir.h"
#include <fcntl.h>
+#include <limits.h>
extern char *mhdocdir;
return state == NOTOK ? NOTOK : OK;
}
+
+
+/*
+ * Convert an int to a char string.
+ */
+char *
+m_str(int value) {
+ return m_strn(value, 0);
+}
+
+
+/*
+ * Convert an int to a char string, of limited width if > 0.
+ */
+#define STR(s) #s
+/* SIZE(n) includes NUL. n must just be digits, not an equation. */
+#define SIZE(n) (sizeof STR(n))
+
+char *
+m_strn(int value, unsigned int width) {
+ /* Need to include space for negative sign. But don't use INT_MIN
+ because it could be a macro that would fool SIZE(n). */
+ static char buffer[SIZE(-INT_MAX)];
+ const int num_chars = snprintf(buffer, sizeof buffer, "%d", value);
+
+ return num_chars > 0 && (width == 0 || (unsigned int) num_chars <= width)
+ ? buffer
+ : "?";
+}
# Do a single test with message one's body of size sz.
do_one_test_A () {
SZ=$1
+ printf '%5s A\r' $SZ
makembox_A "$MH_TEST_DIR/eom-align.mbox" $SZ
$VALGRIND inc -silent -file "$MH_TEST_DIR/eom-align.mbox"
# We know the messages should be 11 and 12 in inbox
# Do a test type B
do_one_test_B () {
SZ=$1
+ printf '%5s B\r' $SZ
makembox_B "$MH_TEST_DIR/eom-align.mbox" $SZ
$VALGRIND inc -silent -file "$MH_TEST_DIR/eom-align.mbox"
# We know the message should be 11 in the inbox
arith_eval $STDIO_BUFSZ + $HDRSZ + $FROMLINESZ + 32; FINISH=$arith_val
[ -t 1 ] && echo \
"Testing inc of files with various alignments of eom marker with buffer size..."
+
+#### Trouble spots for the particular input, with BUFSZ of 8192:
+for sz in 3146 3147 7241 7242 7243 11338 11339 15433 15434 15435; do
+ do_one_test_A $sz
+done
+
+# Also 155, 174, 175, but they're too small for makembox_B to handle.
+for sz in 684 4074 4097 4148 4610 4611 5121 5122 6654 6655 8170 \
+ 8193 8244 8364 12266 12289 12340 16362 16385 16436; do
+ do_one_test_B $sz
+done
+
i="$START"
while test $i -le $FINISH; do
- printf ' %s' $i
do_one_test_A $i
do_one_test_B $i
i=`expr $i + 1`
check "$expected" "$actual"
+# check invalid line folding
+start_test "invalid line folding"
+
+cat >"$expected" <<'EOF'
+Summary: test
+Description: this file does not end with a newline
+At: Sun, 14 May 2017 13:00 +0000
+To: Sun, 14 May 2017 14:00
+EOF
+
+cat >"$expected_err" <<'EOF'
+mhical: syntax error, unexpected ICAL_COMMA, expecting ICAL_COLON after " this line is not folded"
+EOF
+
+set +e
+printf %s \
+"BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//TDP v3.0//EN
+BEGIN:VEVENT
+DTSTAMP:20170514T122300Z
+DTSTART:20170514T130000Z
+DTEND:20170514T140000Z
+SUMMARY:test
+DESCRIPTION:this file does not end with a newline
+and this line is not folded,
+END:VEVENT
+END:VCALENDAR" | TZ=UTC mhical >"$actual" 2>"$actual_err"
+set -e
+check "$expected" "$actual"
+check "$expected_err" "$actual_err"
+
+
finish_test
exit $failed
check "$actual" "$expected"
-# check that text is not quoted-printable encoded
+# check that text is quoted-printable encoded due to long text line
# This tests the setting of reply_ct->c_reqencoding and
# (*text_plain_ct)->c_reqencoding in expand_pseudoheader().
-start_test "don't encode text as quoted-printable"
+start_test "encode text as quoted-printable due to long text line"
cat >"$expected" <<'EOF'
From: recipient@example.com
To: sender@example.com
message dated "Thu, 11 Dec 2014 08:19:02 -0600."
MIME-Version: 1.0
Content-Type: text/plain; charset="US-ASCII"
+Content-Transfer-Encoding: quoted-printable
sender@example.com writes:
-> This is a very, very, very, very, very, very, very, very, very, very, very, long line.
+> This is a very, very, very, very, very, very, very, very, very, very, ve=
+ry, long line.
EOF
cat >`mhpath new` <<'EOF'
char *dmsg = NULL, *digest = NULL, *ed = NULL;
char *file = NULL, *filter = NULL, *folder = NULL, *fwdmsg = NULL;
char *from = NULL, *to = NULL, *cc = NULL, *subject = NULL, *fcc = NULL;
- char *form = NULL, buf[BUFSIZ], value[10];
+ char *form = NULL, buf[BUFSIZ];
char **argp, **arguments;
struct stat st;
struct msgs_array msgs = { 0, 0, NULL };
if (digest) {
snprintf (buf, sizeof(buf), IFORMAT, digest);
- snprintf (value, sizeof(value), "%d", issue);
- context_replace (buf, mh_xstrdup(value));
+ context_replace (buf, mh_xstrdup(m_str(issue)));
snprintf (buf, sizeof(buf), VFORMAT, digest);
- snprintf (value, sizeof(value), "%d", volume);
- context_replace (buf, mh_xstrdup(value));
+ context_replace (buf, mh_xstrdup(m_str(volume)));
}
context_replace (pfolder, folder); /* update current folder */
case SCNFAT:
trnflag = 0;
- noisy++;
+ noisy = true;
/* advise (cp, "unable to read"); already advised */
break;
case SCNFAT:
trnflag = 0;
- noisy++;
+ noisy = true;
/* advise (cp, "unable to read"); already advised */
break;
struct str2init *s2i;
CT reply_ct;
struct part *part;
- int eightbit = 0;
int status;
type_p = getcpy (type);
free (type_p);
if (! (convert)) {
- /* No mhbuild-convert- entry in mhn.defaults or profile
- for type. */
+ /* No mhbuild-convert- entry in mhn.defaults or profile for type. */
return;
}
/* reply_file is used to pass the output of the convert. */
reply_file = getcpy (m_mktemp2 (NULL, invo_name, NULL, NULL));
convert_command =
- concat (convert, " ", FENDNULL(argstring), " >", reply_file,
- NULL);
+ concat (convert, " ", FENDNULL(argstring), " >", reply_file, NULL);
/* Convert here . . . */
ct->c_storeproc = mh_xstrdup(convert_command);
return;
}
- /* For text content only, see if it is 8-bit text. */
- if (reply_ct->c_type == CT_TEXT) {
- int fd;
-
- if ((fd = open (reply_file, O_RDONLY)) == NOTOK ||
- scan_input (fd, &eightbit) == NOTOK) {
- free (reply_file);
- inform("failed to read %s, continuing...", reply_file);
- return;
- }
- (void) close (fd);
- }
-
/* This sets reply_ct->c_ctparams, and reply_ct->c_termproc if the
charset can't be handled natively. */
for (s2i = str2cts; s2i->si_key; s2i++) {
(*reply_ct->c_ctinitfnx)(reply_ct);
}
- if ((cp =
- get_param (reply_ct->c_ctinfo.ci_first_pm, "charset", '?', 1))) {
+ if ((cp = get_param (reply_ct->c_ctinfo.ci_first_pm, "charset", '?', 1))) {
/* The reply Content-Type had the charset. */
charset = cp;
} else {
set_charset (reply_ct, -1);
charset = get_param (reply_ct->c_ctinfo.ci_first_pm, "charset", '?', 1);
- if (reply_ct->c_reqencoding == CE_UNKNOWN &&
- reply_ct->c_type == CT_TEXT) {
- /* Assume that 8bit is sufficient (for text). In other words,
- don't allow it to be encoded as quoted printable if lines
- are too long. This also sidesteps the check for whether
- it needs to be encoded as binary; instead, it relies on
- the applicable mhbuild-convert-text directive to ensure
- that the resultant text is not binary. */
- reply_ct->c_reqencoding = eightbit ? CE_8BIT : CE_7BIT;
- }
}
/* Concatenate text/plain parts. */
- if (reply_ct->c_type == CT_TEXT &&
- reply_ct->c_subtype == TEXT_PLAIN) {
+ if (reply_ct->c_type == CT_TEXT && reply_ct->c_subtype == TEXT_PLAIN) {
if (! *text_plain_ct && m->mp_parts && m->mp_parts->mp_part &&
m->mp_parts->mp_part->c_type == CT_TEXT &&
m->mp_parts->mp_part->c_subtype == TEXT_PLAIN) {
*text_plain_ct = m->mp_parts->mp_part;
- /* Make sure that the charset is set in the text/plain
- part. */
+ /* Make sure that the charset is set in the text/plain part. */
set_charset (*text_plain_ct, -1);
- if ((*text_plain_ct)->c_reqencoding == CE_UNKNOWN) {
- /* Assume that 8bit is sufficient (for text). In other words,
- don't allow it to be encoded as quoted printable if lines
- are too long. This also sidesteps the check for whether
- it needs to be encoded as binary; instead, it relies on
- the applicable mhbuild-convert-text directive to ensure
- that the resultant text is not binary. */
- (*text_plain_ct)->c_reqencoding =
- eightbit ? CE_8BIT : CE_7BIT;
- }
}
if (*text_plain_ct) {
#undef X
vevent vevents = { NULL, NULL, NULL};
+int parser_status = 0;
int
main (int argc, char *argv[]) {
free (outfile);
}
- return 0;
+ return parser_status;
}
/*
default: {
int status;
+ char *display_prog = vecp > 2 && vec[2][0] != '\0'
+ /* Copy the real display program name. This relies on the
+ specific construction of vec[] by argsplit(). */
+ ? vec[2]
+ : NULL;
- arglist_free(file, vec);
-
- pidcheck ((status = pidXwait (child_id, NULL)));
+ pidcheck ((status = pidXwait (child_id, display_prog)));
+ arglist_free(file, vec);
if (fd != NOTOK)
(*ct->c_ceclosefnx) (ct);
return (alternate ? OK : status);