]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
imap: parse and use UIDVALIDITY as a number
authorDaniel Stenberg <daniel@haxx.se>
Wed, 22 Oct 2025 09:25:42 +0000 (11:25 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Wed, 22 Oct 2025 12:12:07 +0000 (14:12 +0200)
Instead of a string. Saves a malloc, adds earlier format check.

RFC 3501 section 2.3.1.1 documents the value as a 32-bit value.

Closes #19188

lib/imap.c

index 0feaedf5acc857611c18e56d9a4a4544980fed94..8cbda96a050c0890e86510b7d8227c4857308b89 100644 (file)
@@ -115,8 +115,8 @@ struct imap_conn {
   struct SASL sasl;           /* SASL-related parameters */
   struct dynbuf dyn;          /* for the IMAP commands */
   char *mailbox;              /* The last selected mailbox */
-  char *mailbox_uidvalidity;  /* UIDVALIDITY parsed from select response */
   imapstate state;            /* Always use imap.c:state() to change state! */
+  unsigned int mb_uidvalidity; /* UIDVALIDITY parsed from select response */
   char resptag[5];            /* Response tag to wait for */
   unsigned char preftype;     /* Preferred authentication type */
   unsigned char cmdid;        /* Last used command ID */
@@ -125,6 +125,7 @@ struct imap_conn {
   BIT(tls_supported);         /* StartTLS capability supported by server */
   BIT(login_disabled);        /* LOGIN command disabled by server */
   BIT(ir_supported);          /* Initial response supported by server */
+  BIT(mb_uidvalidity_set);
 };
 
 /* This IMAP struct is used in the Curl_easy. All IMAP data that is
@@ -134,7 +135,6 @@ struct imap_conn {
 struct IMAP {
   curl_pp_transfer transfer;
   char *mailbox;          /* Mailbox to select */
-  char *uidvalidity;      /* UIDVALIDITY to check in select */
   char *uid;              /* Message UID to fetch */
   char *mindex;           /* Index in mail box of mail to fetch */
   char *section;          /* Message SECTION to fetch */
@@ -142,6 +142,8 @@ struct IMAP {
   char *query;            /* Query to search for */
   char *custom;           /* Custom request */
   char *custom_params;    /* Parameters for the custom request */
+  unsigned int uidvalidity; /* UIDVALIDITY to check in select */
+  BIT(uidvalidity_set);
 };
 
 
@@ -780,7 +782,6 @@ static CURLcode imap_perform_select(struct Curl_easy *data,
 
   /* Invalidate old information as we are switching mailboxes */
   Curl_safefree(imapc->mailbox);
-  Curl_safefree(imapc->mailbox_uidvalidity);
 
   /* Check we have a mailbox */
   if(!imap->mailbox) {
@@ -1222,24 +1223,19 @@ static CURLcode imap_state_select_resp(struct Curl_easy *data,
   if(imapcode == '*') {
     /* See if this is an UIDVALIDITY response */
     if(checkprefix("OK [UIDVALIDITY ", line + 2)) {
-      size_t len = 0;
+      curl_off_t value;
       const char *p = &line[2] + strlen("OK [UIDVALIDITY ");
-      while((len < 20) && p[len] && ISDIGIT(p[len]))
-        len++;
-      if(len && (p[len] == ']')) {
-        struct dynbuf uid;
-        curlx_dyn_init(&uid, 20);
-        if(curlx_dyn_addn(&uid, p, len))
-          return CURLE_OUT_OF_MEMORY;
-        free(imapc->mailbox_uidvalidity);
-        imapc->mailbox_uidvalidity = curlx_dyn_ptr(&uid);
+      if(!curlx_str_number(&p, &value, UINT_MAX)) {
+        imapc->mb_uidvalidity = (unsigned int)value;
+        imapc->mb_uidvalidity_set = TRUE;
       }
+
     }
   }
   else if(imapcode == IMAP_RESP_OK) {
     /* Check if the UIDVALIDITY has been specified and matches */
-    if(imap->uidvalidity && imapc->mailbox_uidvalidity &&
-       !curl_strequal(imap->uidvalidity, imapc->mailbox_uidvalidity)) {
+    if(imap->uidvalidity_set && imapc->mb_uidvalidity_set &&
+       (imap->uidvalidity != imapc->mb_uidvalidity)) {
       failf(data, "Mailbox UIDVALIDITY has changed");
       result = CURLE_REMOTE_FILE_NOT_FOUND;
     }
@@ -1690,8 +1686,8 @@ static CURLcode imap_perform(struct Curl_easy *data, bool *connected,
      has already been selected on this connection */
   if(imap->mailbox && imapc->mailbox &&
      curl_strequal(imap->mailbox, imapc->mailbox) &&
-     (!imap->uidvalidity || !imapc->mailbox_uidvalidity ||
-      curl_strequal(imap->uidvalidity, imapc->mailbox_uidvalidity)))
+     (!imap->uidvalidity_set || !imapc->mb_uidvalidity_set ||
+      (imap->uidvalidity == imapc->mb_uidvalidity)))
     selected = TRUE;
 
   /* Start the first command in the DO phase */
@@ -1861,7 +1857,6 @@ static CURLcode imap_regular_transfer(struct Curl_easy *data,
 static void imap_easy_reset(struct IMAP *imap)
 {
   Curl_safefree(imap->mailbox);
-  Curl_safefree(imap->uidvalidity);
   Curl_safefree(imap->uid);
   Curl_safefree(imap->mindex);
   Curl_safefree(imap->section);
@@ -1890,7 +1885,6 @@ static void imap_conn_dtor(void *key, size_t klen, void *entry)
   Curl_pp_disconnect(&imapc->pp);
   curlx_dyn_free(&imapc->dyn);
   Curl_safefree(imapc->mailbox);
-  Curl_safefree(imapc->mailbox_uidvalidity);
   free(imapc);
 }
 
@@ -2177,54 +2171,45 @@ static CURLcode imap_parse_url_path(struct Curl_easy *data,
 
     DEBUGF(infof(data, "IMAP URL parameter '%s' = '%s'", name, value));
 
-    /* Process the known hierarchical parameters (UIDVALIDITY, UID, SECTION and
-       PARTIAL) stripping of the trailing slash character if it is present.
+    /* Process the known hierarchical parameters (UIDVALIDITY, UID, SECTION
+       and PARTIAL) stripping of the trailing slash character if it is
+       present.
 
        Note: Unknown parameters trigger a URL_MALFORMAT error. */
-    if(curl_strequal(name, "UIDVALIDITY") && !imap->uidvalidity) {
-      if(valuelen > 0 && value[valuelen - 1] == '/')
-        value[valuelen - 1] = '\0';
-
-      imap->uidvalidity = value;
-      value = NULL;
-    }
-    else if(curl_strequal(name, "UID") && !imap->uid) {
-      if(valuelen > 0 && value[valuelen - 1] == '/')
-        value[valuelen - 1] = '\0';
-
-      imap->uid = value;
-      value = NULL;
-    }
-    else if(curl_strequal(name, "MAILINDEX") && !imap->mindex) {
-      if(valuelen > 0 && value[valuelen - 1] == '/')
-        value[valuelen - 1] = '\0';
-
-      imap->mindex = value;
-      value = NULL;
-    }
-    else if(curl_strequal(name, "SECTION") && !imap->section) {
-      if(valuelen > 0 && value[valuelen - 1] == '/')
-        value[valuelen - 1] = '\0';
-
-      imap->section = value;
-      value = NULL;
-    }
-    else if(curl_strequal(name, "PARTIAL") && !imap->partial) {
-      if(valuelen > 0 && value[valuelen - 1] == '/')
-        value[valuelen - 1] = '\0';
-
-      imap->partial = value;
-      value = NULL;
+    if(valuelen > 0 && value[valuelen - 1] == '/')
+      value[valuelen - 1] = '\0';
+    if(valuelen) {
+      if(curl_strequal(name, "UIDVALIDITY") && !imap->uidvalidity_set) {
+        curl_off_t num;
+        const char *p = (const char *)value;
+        if(!curlx_str_number(&p, &num, UINT_MAX)) {
+          imap->uidvalidity = (unsigned int)num;
+          imap->uidvalidity_set = TRUE;
+        }
+        free(value);
+      }
+      else if(curl_strequal(name, "UID") && !imap->uid) {
+        imap->uid = value;
+      }
+      else if(curl_strequal(name, "MAILINDEX") && !imap->mindex) {
+        imap->mindex = value;
+      }
+      else if(curl_strequal(name, "SECTION") && !imap->section) {
+        imap->section = value;
+      }
+      else if(curl_strequal(name, "PARTIAL") && !imap->partial) {
+        imap->partial = value;
+      }
+      else {
+        free(name);
+        free(value);
+        return CURLE_URL_MALFORMAT;
+      }
     }
-    else {
-      free(name);
+    else
+      /* blank? */
       free(value);
-
-      return CURLE_URL_MALFORMAT;
-    }
-
     free(name);
-    free(value);
   }
 
   /* Does the URL contain a query parameter? Only valid when we have a mailbox