Fix MaxRevalDurationDays validation for invalidation jobs (#4718) (#4733)

* Fix MaxRevalDurationDays validation for invalidation jobs

* Address review feedback

* unindent

(cherry picked from commit 94e4edef92a4cbd7fd494d6f29811f2d0d7b0cb1)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7d6acf7..d597a3b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -63,6 +63,7 @@
 - Fixed the `GET /api/x/jobs` and `GET /api/x/jobs/:id` Traffic Ops API routes to allow falling back to Perl via the routing blacklist
 - Fixed audit logging from the `/jobs` APIs to bring them back to the same level of information provided by TO-Perl
 - Fixed #4541: traffic_ops_server.js searches based on which= and can find data for multiple servers
+- Fixed `maxRevalDurationDays` validation for `POST /api/1.x/user/current/jobs` and added that validation to the `/api/x/jobs` endpoints
 
 ### Deprecated/Removed
 - The Traffic Ops `db/admin.pl` script has now been removed. Please use the `db/admin` binary instead.
diff --git a/lib/go-tc/invalidationjobs.go b/lib/go-tc/invalidationjobs.go
index a51574c..7d109cb 100644
--- a/lib/go-tc/invalidationjobs.go
+++ b/lib/go-tc/invalidationjobs.go
@@ -257,9 +257,16 @@
 	}
 
 	if job.TTL != nil {
-		if _, err := job.TTLHours(); err != nil {
+		hours, err := job.TTLHours()
+		if err != nil {
 			errs = append(errs, "ttl: must be a number of hours, or a duration string e.g. '48h'")
 		}
+		var maxDays uint
+		err = tx.QueryRow(`SELECT value FROM parameter WHERE name='maxRevalDurationDays' AND config_file='regex_revalidate.config'`).Scan(&maxDays)
+		maxHours := maxDays * 24
+		if err == nil && hours > maxHours { // silently ignore other errors too
+			errs = append(errs, "ttl: cannot exceed "+strconv.FormatUint(uint64(maxHours), 10)+"!")
+		}
 	}
 
 	if len(errs) > 0 {
@@ -329,7 +336,7 @@
 	if job.StartTime == nil {
 		errs = append(errs, "startTime: cannot be blank")
 	} else if job.StartTime.After(time.Now().Add(twoDays)) {
-		errs = append(errs, "startTime: must be within two days!")
+		errs = append(errs, "startTime: must be within two days")
 	}
 
 	if job.Regex != nil && *(job.Regex) != "" {
@@ -343,20 +350,21 @@
 		var id uint
 		if err := row.Scan(&id); err != nil {
 			log.Errorln(err.Error())
-			errs = append(errs, "No Delivery Service corresponding to 'dsId'!")
+			errs = append(errs, "no Delivery Service corresponding to 'dsId'")
 		}
 	}
 
 	if job.TTL != nil {
 		row := tx.QueryRow(`SELECT value FROM parameter WHERE name='maxRevalDurationDays' AND config_file='regex_revalidate.config'`)
-		var max uint64
-		err := row.Scan(&max)
+		var maxDays uint64
+		err := row.Scan(&maxDays)
+		maxHours := maxDays * 24
 		if err == sql.ErrNoRows && MaxTTL < *(job.TTL) {
-			errs = append(errs, "ttl: cannot exceed "+strconv.FormatUint(MaxTTL, 10)+"!")
-		} else if err == nil && max < *(job.TTL) { //silently ignore other errors to
-			errs = append(errs, "ttl: cannot exceed "+strconv.FormatUint(max, 10)+"!")
+			errs = append(errs, "ttl: cannot exceed "+strconv.FormatUint(MaxTTL, 10))
+		} else if err == nil && maxHours < *(job.TTL) { // silently ignore other errors
+			errs = append(errs, "ttl: cannot exceed "+strconv.FormatUint(maxHours, 10))
 		} else if *(job.TTL) < 1 {
-			errs = append(errs, "ttl: must be at least 1!")
+			errs = append(errs, "ttl: must be at least 1")
 		}
 	}