From fd635150a8bf131c3a8cec2bf41feaf469832dc5 Mon Sep 17 00:00:00 2001
From: Lars Henriksen <LarsHenriksen@get2net.dk>
Date: Mon, 6 Nov 2017 19:30:32 +0100
Subject: Start and end time validation.

All appointment times are checked for validity. Overflow by time
arithmetic is detected. End times are checked when appointments
are moved. Three functions are involved: parse_datetime(),
parse_duration() and parse_date_duration(); they all have a
new argument for validation purposes.

Signed-off-by: Lukas Fleischer <lfleischer@calcurse.org>
---
 src/calcurse.h |  6 +++---
 src/utils.c    | 49 ++++++++++++++++++++++++++++++++++++++++++++-----
 2 files changed, 47 insertions(+), 8 deletions(-)

diff --git a/src/calcurse.h b/src/calcurse.h
index 178af1f..88a0916 100644
--- a/src/calcurse.h
+++ b/src/calcurse.h
@@ -1162,9 +1162,9 @@ int parse_date_interactive(const char *, int *, int *, int *);
 int check_sec(time_t *);
 int check_time(unsigned, unsigned);
 int parse_time(const char *, unsigned *, unsigned *);
-int parse_duration(const char *, unsigned *);
-int parse_date_duration(const char *, unsigned *);
-int parse_datetime(const char *, long *);
+int parse_duration(const char *, unsigned *, time_t);
+int parse_date_duration(const char *, unsigned *, time_t);
+int parse_datetime(const char *, time_t *, time_t);
 void file_close(FILE *, const char *);
 void psleep(unsigned);
 int fork_exec(int *, int *, const char *, const char *const *);
diff --git a/src/utils.c b/src/utils.c
index 8728dfb..0690d4e 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -936,10 +936,11 @@ parse_date_interactive(const char *datestr, int *year, int *month, int *day)
 
 /*
  * Convert a date duration string into a number of days.
+ * If start is non-zero, the final end time is validated.
  *
  * Returns 1 on success and 0 on failure.
  */
-int parse_date_duration(const char *string, unsigned *days)
+int parse_date_duration(const char *string, unsigned *days, time_t start)
 {
 	enum {
 		STATE_INITIAL,
@@ -1000,6 +1001,17 @@ int parse_date_duration(const char *string, unsigned *days)
 		return 0;
 
 	dur += in;
+	if (start) {
+		/* wanted: start = start + dur * DAYINSEC */
+		int p, s;
+		if (overflow_mul(dur, DAYINSEC, &p))
+			return 0;
+		if (overflow_add(start, p, &s))
+			return 0;
+		start = s;
+		if (!check_sec(&start))
+			return 0;
+	}
 	*days = dur;
 
 	return 1;
@@ -1051,6 +1063,7 @@ int parse_time(const char *string, unsigned *hour, unsigned *minute)
 
 /*
  * Converts a duration string into minutes.
+ * If start time is non-zero, the final end time is validated.
  *
  * Allowed formats (noted as regular expressions):
  *
@@ -1062,7 +1075,7 @@ int parse_time(const char *string, unsigned *hour, unsigned *minute)
  *
  * Returns 1 on success and 0 on failure.
  */
-int parse_duration(const char *string, unsigned *duration)
+int parse_duration(const char *string, unsigned *duration, time_t start)
 {
 	enum {
 		STATE_INITIAL,
@@ -1140,13 +1153,23 @@ int parse_duration(const char *string, unsigned *duration)
 			denom = 1;
 		}
 	}
-
 	if ((state == STATE_HHMM_MM && in >= HOURINMIN) ||
 	    ((state == STATE_DDHHMM_HH || state == STATE_DDHHMM_MM)
 	     && in > 0))
 		return 0;
-
 	dur += in;
+	if (start) {
+		/* wanted: end = start + dur * MININSEC */
+		time_t end;
+		int p, s;
+		if (overflow_mul(dur, MININSEC, &p))
+			return 0;
+		if (overflow_add(start, p, &s))
+			return 0;
+		end = s;
+		if (!check_sec(&end) || end < start)
+			return 0;
+	}
 	*duration = dur;
 
 	return 1;
@@ -1161,10 +1184,13 @@ int parse_duration(const char *string, unsigned *duration)
  * updated and the date remains the same. If the string contains both a date
  * and a time, the time stamp is updated to match the given string.
  *
+ * The final time is validated. In addition, if a positive duration is given,
+ * time + duration validated (zero duration needs no validation).
+ *
  * Returns a positive value on success and 0 on failure. The least-significant
  * bit is set if the date was updated. Bit 1 is set if the time was updated.
  */
-int parse_datetime(const char *string, long *ts)
+int parse_datetime(const char *string, time_t *ts, time_t dur)
 {
 	char *t = mem_strdup(string);
 	char *p = strchr(t, ' ');
@@ -1197,6 +1223,19 @@ int parse_datetime(const char *string, long *ts)
 		*ts = update_time_in_date(*ts, hour, minute);
 
 	mem_free(t);
+	/* Is the resulting time a valid (start or end) time? */
+	if (!check_sec(ts))
+		return 0;
+	/* Is the resulting time + dur a valid end time? */
+	if (dur) {
+		/* want: sec = *ts + dur */
+		int s;
+		if (overflow_add(*ts, dur, &s))
+			return 0;
+		time_t sec = s;
+		if (!check_sec(&sec))
+			return 0;
+	}
 	return ret;
 }
 
-- 
cgit v1.2.3-70-g09d2