]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
parsedate: make Curl_getdate_capped able to return epoch
authorDaniel Stenberg <daniel@haxx.se>
Mon, 1 Sep 2025 14:36:53 +0000 (16:36 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Tue, 2 Sep 2025 05:55:34 +0000 (07:55 +0200)
By returning error separately on parse errors and avoiding magic
numbers, this function can now return 0 or -1 as proper dates when such
a date string is provided.

Closes #18445

lib/altsvc.c
lib/cookie.c
lib/ftp.c
lib/hsts.c
lib/http.c
lib/parsedate.c
lib/parsedate.h
lib/vssh/libssh.c
lib/vssh/libssh2.c

index a06f40bc2a2b5cbb97f3184474e47b72fc1f7b8c..c7448692fb56a97ef04fdcf4aabc6e9ef6c6b276 100644 (file)
@@ -187,13 +187,13 @@ static CURLcode altsvc_add(struct altsvcinfo *asi, const char *line)
   else {
     struct altsvc *as;
     char dbuf[MAX_ALTSVC_DATELEN + 1];
-    time_t expires;
+    time_t expires = 0;
 
     /* The date parser works on a null-terminated string. The maximum length
        is upheld by curlx_str_quotedword(). */
     memcpy(dbuf, curlx_str(&date), curlx_strlen(&date));
     dbuf[curlx_strlen(&date)] = 0;
-    expires = Curl_getdate_capped(dbuf);
+    Curl_getdate_capped(dbuf, &expires);
     as = altsvc_create(&srchost, &dsthost, &srcalpn, &dstalpn,
                        (size_t)srcport, (size_t)dstport);
     if(as) {
index 29252da41a68883ed88f810bb0e7fa5bde084c26..35d33268f9664b32d3153bfcb0994dd87a08ee7e 100644 (file)
@@ -107,7 +107,7 @@ static void strstore(char **str, const char *newstr, size_t len);
 */
 static void cap_expires(time_t now, struct Cookie *co)
 {
-  if((TIME_T_MAX - COOKIES_MAXAGE - 30) > now) {
+  if(co->expires && (TIME_T_MAX - COOKIES_MAXAGE - 30) > now) {
     timediff_t cap = now + COOKIES_MAXAGE;
     if(co->expires > cap) {
       cap += 30;
@@ -388,17 +388,17 @@ static void remove_expired(struct CookieInfo *ci)
     for(n = Curl_llist_head(&ci->cookielist[i]); n; n = e) {
       co = Curl_node_elem(n);
       e = Curl_node_next(n);
-      if(co->expires && co->expires < now) {
-        Curl_node_remove(n);
-        freecookie(co);
-        ci->numcookies--;
-      }
-      else {
-        /*
-         * If this cookie has an expiration timestamp earlier than what we
-         * have seen so far then record it for the next round of expirations.
-         */
-        if(co->expires && co->expires < ci->next_expiration)
+      if(co->expires) {
+        if(co->expires < now) {
+          Curl_node_remove(n);
+          freecookie(co);
+          ci->numcookies--;
+        }
+        else if(co->expires < ci->next_expiration)
+          /*
+           * If this cookie has an expiration timestamp earlier than what we
+           * have seen so far then record it for the next round of expirations.
+           */
           ci->next_expiration = co->expires;
       }
     }
@@ -666,7 +666,6 @@ parse_cookie_header(struct Curl_easy *data,
         if(*maxage == '\"')
           maxage++;
         rc = curlx_str_number(&maxage, &co->expires, CURL_OFF_T_MAX);
-
         switch(rc) {
         case STRE_OVERFLOW:
           /* overflow, used max value */
@@ -678,8 +677,7 @@ parse_cookie_header(struct Curl_easy *data,
           break;
         case STRE_OK:
           if(!co->expires)
-            /* already expired */
-            co->expires = 1;
+            co->expires = 1; /* expire now */
           else if(CURL_OFF_T_MAX - now < co->expires)
             /* would overflow */
             co->expires = CURL_OFF_T_MAX;
@@ -698,18 +696,15 @@ parse_cookie_header(struct Curl_easy *data,
            * will be treated as a session cookie
            */
           char dbuf[MAX_DATE_LENGTH + 1];
+          time_t date = 0;
           memcpy(dbuf, curlx_str(&val), curlx_strlen(&val));
           dbuf[curlx_strlen(&val)] = 0;
-          co->expires = Curl_getdate_capped(dbuf);
-
-          /*
-           * Session cookies have expires set to 0 so if we get that back
-           * from the date parser let's add a second to make it a
-           * non-session cookie
-           */
-          if(co->expires == 0)
-            co->expires = 1;
-          else if(co->expires < 0)
+          if(!Curl_getdate_capped(dbuf, &date)) {
+            if(!date)
+              date++;
+            co->expires = (curl_off_t)date;
+          }
+          else
             co->expires = 0;
           cap_expires(now, co);
         }
@@ -1103,7 +1098,7 @@ Curl_cookie_add(struct Curl_easy *data,
 
   if(!ci->running &&    /* read from a file */
      ci->newsession &&  /* clean session cookies */
-     !co->expires)      /* this is a session cookie since it does not expire */
+     !co->expires)      /* this is a session cookie */
     goto fail;
 
   co->livecookie = ci->running;
index bfd772b637bf67673694a5e029435cd9bc0d17cb..6d7c210ce54d7083703e0276dcf5604c1935dcc1 100644 (file)
--- a/lib/ftp.c
+++ b/lib/ftp.c
@@ -2102,6 +2102,7 @@ static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data,
       int year, month, day, hour, minute, second;
       struct pingpong *pp = &ftpc->pp;
       char *resp = curlx_dyn_ptr(&pp->recvbuf) + 4;
+      bool showtime = FALSE;
       if(ftp_213_date(resp, &year, &month, &day, &hour, &minute, &second)) {
         /* we have a time, reformat it */
         char timebuf[24];
@@ -2109,7 +2110,8 @@ static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data,
                   "%04d%02d%02d %02d:%02d:%02d GMT",
                   year, month, day, hour, minute, second);
         /* now, convert this into a time() value: */
-        data->info.filetime = Curl_getdate_capped(timebuf);
+        if(!Curl_getdate_capped(timebuf, &data->info.filetime))
+          showtime = TRUE;
       }
 
 #ifdef CURL_FTP_HTTPSTYLE_HEAD
@@ -2122,10 +2124,8 @@ static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data,
    warning: comparison of unsigned expression in '>= 0' is always true */
 #pragma GCC diagnostic ignored "-Wtype-limits"
 #endif
-      if(data->req.no_body &&
-         ftpc->file &&
-         data->set.get_filetime &&
-         (data->info.filetime >= 0) ) {
+      if(data->req.no_body && ftpc->file &&
+         data->set.get_filetime && showtime) {
 #if defined(__GNUC__) && (defined(__DJGPP__) || defined(__AMIGA__))
 #pragma GCC diagnostic pop
 #endif
index 91f32276f95c5b17f3b8b0edc9aa63a93a19bb60..9525158bcc7c56cb48a4f57cfcbe41db5be54a23 100644 (file)
@@ -426,7 +426,7 @@ static CURLcode hsts_add(struct hsts *h, const char *line)
     bool subdomain = FALSE;
     struct stsentry *e;
     char dbuf[MAX_HSTS_DATELEN + 1];
-    time_t expires;
+    time_t expires = 0;
     const char *hp = curlx_str(&host);
 
     /* The date parser works on a null-terminated string. The maximum length
@@ -434,8 +434,10 @@ static CURLcode hsts_add(struct hsts *h, const char *line)
     memcpy(dbuf, curlx_str(&date), curlx_strlen(&date));
     dbuf[curlx_strlen(&date)] = 0;
 
-    expires = strcmp(dbuf, UNLIMITED) ? Curl_getdate_capped(dbuf) :
-      TIME_T_MAX;
+    if(!strcmp(dbuf, UNLIMITED))
+      expires = TIME_T_MAX;
+    else
+      Curl_getdate_capped(dbuf, &expires);
 
     if(hp[0] == '.') {
       curlx_str_nudge(&host, 1);
@@ -478,14 +480,14 @@ static CURLcode hsts_pull(struct Curl_easy *data, struct hsts *h)
       e.name[0] = 0; /* just to make it clean */
       sc = data->set.hsts_read(data, &e, data->set.hsts_read_userp);
       if(sc == CURLSTS_OK) {
-        time_t expires;
+        time_t expires = 0;
         CURLcode result;
         DEBUGASSERT(e.name[0]);
         if(!e.name[0])
           /* bail out if no name was stored */
           return CURLE_BAD_FUNCTION_ARGUMENT;
         if(e.expire[0])
-          expires = Curl_getdate_capped(e.expire);
+          Curl_getdate_capped(e.expire, &expires);
         else
           expires = TIME_T_MAX; /* the end of time */
         result = hsts_create(h, e.name, strlen(e.name),
index 0ce3b25bc3b15b62b3ee71f494ed35eb0e694d32..bcbe6e9e7526bc288d9ae13c2912e699d335fca4 100644 (file)
@@ -3196,7 +3196,8 @@ static CURLcode http_header_l(struct Curl_easy *data,
                    (data->set.timecondition || data->set.get_filetime)) ?
     HD_VAL(hd, hdlen, "Last-Modified:") : NULL;
   if(v) {
-    k->timeofdoc = Curl_getdate_capped(v);
+    if(Curl_getdate_capped(v, &k->timeofdoc))
+      k->timeofdoc = 0;
     if(data->set.get_filetime)
       data->info.filetime = k->timeofdoc;
     return CURLE_OK;
@@ -3311,14 +3312,12 @@ static CURLcode http_header_r(struct Curl_easy *data,
   if(v) {
     /* Retry-After = HTTP-date / delay-seconds */
     curl_off_t retry_after = 0; /* zero for unknown or "now" */
-    time_t date;
+    time_t date = 0;
     curlx_str_passblanks(&v);
 
     /* try it as a date first, because a date can otherwise start with and
        get treated as a number */
-    date = Curl_getdate_capped(v);
-
-    if((time_t)-1 != date) {
+    if(!Curl_getdate_capped(v, &date)) {
       time_t current = time(NULL);
       if(date >= current)
         /* convert date to number of seconds into the future */
index eedab7c411b9f5511a385cbf3c60724e16d33085..b9429db7f67b2515c3c55533e2835d30444c8175 100644 (file)
@@ -589,26 +589,12 @@ time_t curl_getdate(const char *p, const time_t *now)
 
 /* Curl_getdate_capped() differs from curl_getdate() in that this will return
    TIME_T_MAX in case the parsed time value was too big, instead of an
-   error. */
+   error. Returns non-zero on error. */
 
-time_t Curl_getdate_capped(const char *p)
+int Curl_getdate_capped(const char *p, time_t *tp)
 {
-  time_t parsed = -1;
-  int rc = parsedate(p, &parsed);
-
-  switch(rc) {
-  case PARSEDATE_OK:
-    if(parsed == (time_t)-1)
-      /* avoid returning -1 for a working scenario */
-      parsed++;
-    return parsed;
-  case PARSEDATE_LATER:
-    /* this returns the maximum time value */
-    return parsed;
-  default:
-    return -1; /* everything else is fail */
-  }
-  /* UNREACHABLE */
+  int rc = parsedate(p, tp);
+  return (rc == PARSEDATE_FAIL);
 }
 
 /*
index 84c37f1675cffe190f4956c5b4591eda794cb6e2..e5efb53f617d0b16d2bd7c132c9cdefc42d1a0e1 100644 (file)
@@ -29,10 +29,10 @@ extern const char * const Curl_month[12];
 
 CURLcode Curl_gmtime(time_t intime, struct tm *store);
 
-/* Curl_getdate_capped() differs from curl_getdate() in that this will return
+/* Curl_getdate_capped() differs from curl_getdate() in that this returns
    TIME_T_MAX in case the parsed time value was too big, instead of an
    error. */
 
-time_t Curl_getdate_capped(const char *p);
+int Curl_getdate_capped(const char *p, time_t *store);
 
 #endif /* HEADER_CURL_PARSEDATE_H */
index 8c366aafae10a5cc7045969190ae6144df701fe4..2898a70f0aabfd1eba26d1ff12ac059273ac9803 100644 (file)
@@ -1855,9 +1855,9 @@ static int myssh_in_SFTP_QUOTE_STAT(struct Curl_easy *data,
   }
   else if(!strncmp(cmd, "atime", 5) ||
           !strncmp(cmd, "mtime", 5)) {
-    time_t date = Curl_getdate_capped(sshc->quote_path1);
+    time_t date;
     bool fail = FALSE;
-    if(date == -1) {
+    if(Curl_getdate_capped(sshc->quote_path1, &date)) {
       failf(data, "incorrect date format for %.*s", 5, cmd);
       fail = TRUE;
     }
index ea76482394efa3c10ac8a7faed433d039161f071..13f374564f70accd0b8cf71d7f131a51b3115bce 100644 (file)
@@ -1390,16 +1390,16 @@ sftp_quote_stat(struct Curl_easy *data,
   }
   else if(!strncmp(cmd, "atime", 5) ||
           !strncmp(cmd, "mtime", 5)) {
-    time_t date = Curl_getdate_capped(sshc->quote_path1);
+    time_t date;
     bool fail = FALSE;
 
-    if(date == -1) {
+    if(Curl_getdate_capped(sshc->quote_path1, &date)) {
       failf(data, "incorrect date format for %.*s", 5, cmd);
       fail = TRUE;
     }
 #if SIZEOF_TIME_T > SIZEOF_LONG
     if(date > 0xffffffff) {
-      /* if 'long' cannot old >32-bit, this date cannot be sent */
+      /* if 'long' cannot hold >32-bit, this date cannot be sent */
       failf(data, "date overflow");
       fail = TRUE;
     }