#define MLMMJ_GENERIC_INCLUDES
#include "config.h"
+#include <stdbool.h>
+#include <time.h>
#define RELAYHOST "127.0.0.1"
#define READ_BUFSIZE 2048
extern char * subreason_strs[6]; /* count matches enum above; defined in subscriberfuncs.c */
void print_version(const char *prg);
+bool parse_lastdigest(char *line, long *lastindex, time_t *lasttime,
+ long *lastissue, const char **errstr);
#define MY_ASSERT(expression) if (!(expression)) { \
errno = 0; \
do_all_the_voodoo_here.c log_oper.c send_list.c \
unistr.c gethdrline.c send_digest.c \
writen.c getlistaddr.c strgen.c statctrl.c \
- ctrlvalue.c readn.c getlistdelim.c ctrlvalues.c utils.c
+ ctrlvalue.c readn.c getlistdelim.c ctrlvalues.c \
+ utils.c mlmmj.c
mlmmj_send_SOURCES = mlmmj-send.c
mlmmj_send_LDADD= libmlmmj.a
static bool
run_digests(int dfd, const char *mlmmjsend, const char *listdir, int logfd)
{
- char *lasttimestr, *lastindexstr, *lastissuestr;
char *s1, *s2;
time_t digestinterval, t, lasttime;
long digestmaxmails, lastindex, index, lastissue;
s1 = mygetline(fd);
/* Syntax is lastindex:lasttime or lastindex:lasttime:lastissue */
- if (s1 && (lasttimestr = strchr(s1, ':'))) {
- *(lasttimestr++) = '\0';
- if ((lastissuestr = strchr(lasttimestr, ':'))) {
- *(lastissuestr++) = '\0';
- lastissue = strtoim(lastissuestr, LONG_MIN, LONG_MAX,
- &errstr);
- if (errstr != NULL) {
- log(" - 'lastdigests' contains malformed "
- "data: Impossible to parse lastissue: '%s':"
- " %s", lastissuestr, errstr);
- close(fd);
- return (false);
- }
- } else {
- lastissue = 0;
- }
- lasttime = strtotimet(lasttimestr, &errstr);
- if (errstr) {
- log(" - 'lastdigest' invalid last time string: "
- "'%s': %s", lasttimestr, errstr);
- close(fd);
- return (false);
- }
- lastindexstr = s1;
- lastindex = strtoim(lastindexstr, LONG_MIN, LONG_MAX, &errstr);
- if (errstr != NULL) {
- log("'lastdigests' invalid last index: '%s': %s\n",
- lastindexstr, errstr);
- return (false);
- }
- } else {
- if (s1 && (strlen(s1) > 0)) {
- log_error(LOG_ARGS, "'lastdigest' invalid first line");
- free(s1);
- close(fd);
- return (false);
- }
- /* If lastdigest is empty, we start from scratch */
- lasttime = 0;
- lastindex = 0;
- lastissue = 0;
+ if (!parse_lastdigest(s1, &lastindex, &lasttime, &lastissue, &errstr)) {
+ log(" - malformerd lastdigest '%s': %s", s1, errstr);
+ return (false);
}
indexfd = openat(dfd, "index", O_RDONLY);
--- /dev/null
+/*
+ * Copyright (C) 2021-2022 Baptiste Daroussin <bapt@FreeBSD.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <sys/mman.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "mlmmj.h"
+#include "utils.h"
+#include "log_error.h"
+#include "xmalloc.h"
+#include "chomp.h"
+
+bool
+parse_lastdigest(char *line, long *lastindex, time_t *lasttime,
+ long *lastissue, const char **errstr)
+{
+ size_t nfield = 0;
+ char *walk;
+
+ *errstr = NULL;
+
+ if (line == NULL) {
+ *lastindex = 0;
+ *lasttime = 0;
+ *lastissue = 0;
+ return (true);
+ }
+ walk = line;
+ while (strsep(&walk, ":") != NULL)
+ nfield++;
+ if (nfield < 2 || nfield > 3) {
+ *errstr = "Invalid format, expecting 2 or 3 fields";
+ return (false);
+ }
+ *lastindex = strtoim(line, 0, LONG_MAX, errstr);
+ if (*errstr != NULL) {
+ *errstr = "Invalid value for lastindex";
+ return (false);
+ }
+ walk = line + strlen(line) + 1;
+ *lasttime = strtotimet(walk, errstr);
+ if (*errstr != NULL) {
+ *errstr = "Invalid value for lasttime";
+ return (false);
+ }
+ if (nfield == 2) {
+ *lastissue = 0;
+ return true;
+ }
+ walk = walk + strlen(walk) + 1;
+ *lastissue = strtoim(walk, LONG_MIN, LONG_MAX, errstr);
+ if (*errstr != NULL) {
+ *errstr = "Invalid value for lastissue";
+ return (false);
+ }
+ return (true);
+}
ATF_TC_WITHOUT_HEAD(write_mailbody_from_map);
ATF_TC_WITHOUT_HEAD(strtotimet);
ATF_TC_WITHOUT_HEAD(decode_qp);
+ATF_TC_WITHOUT_HEAD(parse_lastdigest);
#ifndef NELEM
#define NELEM(array) (sizeof(array) / sizeof((array)[0]))
ATF_REQUIRE_STREQ(decode_qp("This_ is a subject"), "This_ is a subject");
}
+ATF_TC_BODY(parse_lastdigest, tc)
+{
+ time_t lasttime;
+ long lastissue;
+ long lastindex;
+ const char *errstr;
+ char *line;
+
+ line = xstrdup("1:2:3");
+ ATF_REQUIRE(parse_lastdigest(line, &lastindex, &lasttime, &lastissue,
+ &errstr));
+ ATF_REQUIRE(errstr == NULL);
+ ATF_REQUIRE(lastindex == 1);
+ ATF_REQUIRE(lasttime == 2);
+ ATF_REQUIRE(lastissue == 3);
+ free(line);
+
+ line = xstrdup("1");
+ ATF_REQUIRE(!parse_lastdigest(line, &lastindex, &lasttime, &lastissue,
+ &errstr));
+ ATF_REQUIRE_STREQ(errstr, "Invalid format, expecting 2 or 3 fields");
+ free(line);
+
+ line = xstrdup("1:2:3:4");
+ ATF_REQUIRE(!parse_lastdigest(line, &lastindex, &lasttime, &lastissue,
+ &errstr));
+ ATF_REQUIRE_STREQ(errstr, "Invalid format, expecting 2 or 3 fields");
+ free(line);
+
+ ATF_REQUIRE(parse_lastdigest(NULL, &lastindex, &lasttime, &lastissue,
+ &errstr));
+ ATF_REQUIRE(errstr == NULL);
+ ATF_REQUIRE(lastindex == 0);
+ ATF_REQUIRE(lasttime == 0);
+ ATF_REQUIRE(lastissue == 0);
+
+ line = xstrdup("1:2");
+ ATF_REQUIRE(parse_lastdigest(line, &lastindex, &lasttime, &lastissue,
+ &errstr));
+ ATF_REQUIRE(errstr == NULL);
+ ATF_REQUIRE(lastindex == 1);
+ ATF_REQUIRE(lasttime == 2);
+ ATF_REQUIRE(lastissue == 0);
+ free(line);
+
+ line = xstrdup("a:2");
+ ATF_REQUIRE(!parse_lastdigest(line, &lastindex, &lasttime, &lastissue,
+ &errstr));
+ ATF_REQUIRE_STREQ(errstr, "Invalid value for lastindex");
+ free(line);
+
+ line = xstrdup("-1:2");
+ ATF_REQUIRE(!parse_lastdigest(line, &lastindex, &lasttime, &lastissue,
+ &errstr));
+ ATF_REQUIRE_STREQ(errstr, "Invalid value for lastindex");
+ free(line);
+
+ line = xstrdup("1:");
+ ATF_REQUIRE(!parse_lastdigest(line, &lastindex, &lasttime, &lastissue,
+ &errstr));
+ ATF_REQUIRE_STREQ(errstr, "Invalid value for lasttime");
+ free(line);
+
+ line = xstrdup("1:a");
+ ATF_REQUIRE(!parse_lastdigest(line, &lastindex, &lasttime, &lastissue,
+ &errstr));
+ ATF_REQUIRE_STREQ(errstr, "Invalid value for lasttime");
+ free(line);
+
+ line = xstrdup("1:1:");
+ ATF_REQUIRE(!parse_lastdigest(line, &lastindex, &lasttime, &lastissue,
+ &errstr));
+ ATF_REQUIRE_STREQ(errstr, "Invalid value for lastissue");
+ free(line);
+
+ line = xstrdup("1:1:a");
+ ATF_REQUIRE(!parse_lastdigest(line, &lastindex, &lasttime, &lastissue,
+ &errstr));
+ ATF_REQUIRE_STREQ(errstr, "Invalid value for lastissue");
+ free(line);
+}
+
ATF_TP_ADD_TCS(tp)
{
ATF_TP_ADD_TC(tp, random_int);
ATF_TP_ADD_TC(tp, write_mailbody_from_map);
ATF_TP_ADD_TC(tp, strtotimet);
ATF_TP_ADD_TC(tp, decode_qp);
+ ATF_TP_ADD_TC(tp, parse_lastdigest);
return (atf_no_error());
}