Use %ld not %l
[exim.git] / src / src / exim.c
index a42c48b2ca81e488cf556ac54353b7a39c0c3a61..ff1aa49db56762a6192bf1132e4942d8ce0299d7 100644 (file)
@@ -45,7 +45,7 @@ are two sets of functions; one for use when we want to retain the compiled
 regular expression for a long time; the other for short-term use. */
 
 static void *
 regular expression for a long time; the other for short-term use. */
 
 static void *
-function_store_get(size_t size)
+function_store_get(PCRE2_SIZE size, void * tag)
 {
 /* For now, regard all RE results as potentially tainted.  We might need
 more intelligence on this point. */
 {
 /* For now, regard all RE results as potentially tainted.  We might need
 more intelligence on this point. */
@@ -53,16 +53,16 @@ return store_get((int)size, TRUE);
 }
 
 static void
 }
 
 static void
-function_dummy_free(void * block) {}
+function_dummy_free(void * block, void * tag) {}
 
 static void *
 
 static void *
-function_store_malloc(size_t size)
+function_store_malloc(PCRE2_SIZE size, void * tag)
 {
 return store_malloc((int)size);
 }
 
 static void
 {
 return store_malloc((int)size);
 }
 
 static void
-function_store_free(void * block)
+function_store_free(void * block, void * tag)
 {
 store_free(block);
 }
 {
 store_free(block);
 }
@@ -98,29 +98,51 @@ Argument:
 Returns:      pointer to the compiled pattern
 */
 
 Returns:      pointer to the compiled pattern
 */
 
-const pcre *
-regex_must_compile(const uschar *pattern, BOOL caseless, BOOL use_malloc)
+const pcre2_code *
+regex_must_compile(const uschar * pattern, BOOL caseless, BOOL use_malloc)
 {
 {
-int offset;
-int options = PCRE_COPT;
-const pcre *yield;
-const uschar *error;
+size_t offset;
+int options = caseless ? PCRE_COPT|PCRE2_CASELESS : PCRE_COPT;
+const pcre2_code * yield;
+int err;
+pcre2_general_context * gctx;
+pcre2_compile_context * cctx;
+
 if (use_malloc)
   {
 if (use_malloc)
   {
-  pcre_malloc = function_store_malloc;
-  pcre_free = function_store_free;
+  gctx = pcre2_general_context_create(function_store_malloc, function_store_free, NULL);
+  cctx = pcre2_compile_context_create(gctx);
   }
   }
-if (caseless) options |= PCRE_CASELESS;
-yield = pcre_compile(CCS pattern, options, CCSS &error, &offset, NULL);
-pcre_malloc = function_store_get;
-pcre_free = function_dummy_free;
-if (yield == NULL)
+else
+  cctx = pcre_cmp_ctx;
+
+if (!(yield = pcre2_compile((PCRE2_SPTR)pattern, PCRE2_ZERO_TERMINATED, options,
+  &err, &offset, cctx)))
+  {
+  uschar errbuf[128];
+  pcre2_get_error_message(err, errbuf, sizeof(errbuf));
   log_write(0, LOG_MAIN|LOG_PANIC_DIE, "regular expression error: "
   log_write(0, LOG_MAIN|LOG_PANIC_DIE, "regular expression error: "
-    "%s at offset %d while compiling %s", error, offset, pattern);
+    "%s at offset %d while compiling %s", errbuf, (long)offset, pattern);
+  }
+
+if (use_malloc)
+  {
+  pcre2_compile_context_free(cctx);
+  pcre2_general_context_free(gctx);
+  }
 return yield;
 }
 
 
 return yield;
 }
 
 
+static void
+pcre_init(void)
+{
+pcre_gen_ctx = pcre2_general_context_create(function_store_malloc, function_store_free, NULL);
+pcre_cmp_ctx = pcre2_compile_context_create(pcre_gen_ctx);
+pcre_mtc_ctx = pcre2_match_context_create(pcre_gen_ctx);
+}
+
+
 
 
 /*************************************************
 
 
 /*************************************************
@@ -128,7 +150,12 @@ return yield;
 *************************************************/
 
 /* This function runs a regular expression match, and sets up the pointers to
 *************************************************/
 
 /* This function runs a regular expression match, and sets up the pointers to
-the matched substrings.
+the matched substrings.  The matched strings are copied.
+
+We might consider tracing the uses of expand_nstring to see if consitification
+is viable, and save the copy cost by just using the pointers into the subject string.
+Pre-pcre2 we did that without noticing, so it might just work - or might have been
+a bug. It was certainly a risk in the implemenation.
 
 Arguments:
   re          the compiled expression
 
 Arguments:
   re          the compiled expression
@@ -138,32 +165,67 @@ Arguments:
               if >= 0 setup from setup+1 onwards,
                 excluding the full matched string
 
               if >= 0 setup from setup+1 onwards,
                 excluding the full matched string
 
-Returns:      TRUE or FALSE
+Returns:      TRUE if matched, or FALSE
 */
 
 BOOL
 */
 
 BOOL
-regex_match_and_setup(const pcre *re, const uschar *subject, int options, int setup)
+regex_match_and_setup(const pcre2_code * re, const uschar * subject, int options, int setup)
 {
 {
-int ovector[3*(EXPAND_MAXN+1)];
-uschar * s = string_copy(subject);     /* de-constifying */
-int n = pcre_exec(re, NULL, CS s, Ustrlen(s), 0,
-  PCRE_EOPT | options, ovector, nelem(ovector));
-BOOL yield = n >= 0;
-if (n == 0) n = EXPAND_MAXN + 1;
-if (yield)
+pcre2_match_data * md = pcre2_match_data_create_from_pattern(re, pcre_gen_ctx);
+int res = pcre2_match(re, (PCRE2_SPTR)subject, PCRE2_ZERO_TERMINATED, 0,
+                       PCRE_EOPT | options, md, pcre_mtc_ctx);
+BOOL yield;
+
+if ((yield = (res >= 0)))
   {
   {
+  res = pcre2_get_ovector_count(md);
   expand_nmax = setup < 0 ? 0 : setup + 1;
   expand_nmax = setup < 0 ? 0 : setup + 1;
-  for (int nn = setup < 0 ? 0 : 2; nn < n*2; nn += 2)
+  for (int matchnum = setup < 0 ? 0 : 1; matchnum < res; matchnum++)
     {
     {
-    expand_nstring[expand_nmax] = s + ovector[nn];
-    expand_nlength[expand_nmax++] = ovector[nn+1] - ovector[nn];
+    PCRE2_SIZE len;
+    pcre2_substring_get_bynumber(md, matchnum,
+      (PCRE2_UCHAR **)&expand_nstring[expand_nmax], &len);
+    expand_nlength[expand_nmax++] = (int)len;
     }
   expand_nmax--;
   }
     }
   expand_nmax--;
   }
+else if (res != PCRE2_ERROR_NOMATCH) DEBUG(D_any)
+  {
+  uschar errbuf[128];
+  pcre2_get_error_message(res, errbuf, sizeof(errbuf));
+  debug_printf_indent("pcre2: %s\n", errbuf);
+  }
+pcre2_match_data_free(md);
 return yield;
 }
 
 
 return yield;
 }
 
 
+/* Check just for match with regex.  Uses the common memory-handling.
+
+Arguments:
+       re      compiled regex
+       subject string to be checked
+       slen    length of subject; -1 for nul-terminated
+       rptr    pointer for matched string, copied, or NULL
+
+Return: TRUE for a match.
+*/
+
+BOOL
+regex_match(const pcre2_code * re, const uschar * subject, int slen, uschar ** rptr)
+{
+pcre2_match_data * md = pcre2_match_data_create(1, pcre_gen_ctx);
+int rc = pcre2_match(re, (PCRE2_SPTR)subject,
+                     slen >= 0 ? slen : PCRE2_ZERO_TERMINATED,
+                     0, PCRE_EOPT, md, pcre_mtc_ctx);
+PCRE2_SIZE * ovec = pcre2_get_ovector_pointer(md);
+if (rc < 0)
+  return FALSE;
+if (rptr)
+  *rptr = string_copyn(subject + ovec[0], ovec[1] - ovec[0]);
+return TRUE;
+}
+
 
 
 /*************************************************
 
 
 /*************************************************
@@ -444,9 +506,10 @@ function prepares for the time when things are faster - and it also copes with
 clocks that go backwards.
 
 Arguments:
 clocks that go backwards.
 
 Arguments:
-  tgt_tv       A timeval which was used to create uniqueness; its usec field
+  prev_tv      A timeval which was used to create uniqueness; its usec field
                  has been rounded down to the value of the resolution.
                  We want to be sure the current time is greater than this.
                  has been rounded down to the value of the resolution.
                  We want to be sure the current time is greater than this.
+                On return, updated to current (rounded down).
   resolution   The resolution that was used to divide the microseconds
                  (1 for maildir, larger for message ids)
 
   resolution   The resolution that was used to divide the microseconds
                  (1 for maildir, larger for message ids)
 
@@ -454,7 +517,7 @@ Returns:       nothing
 */
 
 void
 */
 
 void
-exim_wait_tick(struct timeval * tgt_tv, int resolution)
+exim_wait_tick(struct timeval * prev_tv, int resolution)
 {
 struct timeval now_tv;
 long int now_true_usec;
 {
 struct timeval now_tv;
 long int now_true_usec;
@@ -463,13 +526,13 @@ exim_gettime(&now_tv);
 now_true_usec = now_tv.tv_usec;
 now_tv.tv_usec = (now_true_usec/resolution) * resolution;
 
 now_true_usec = now_tv.tv_usec;
 now_tv.tv_usec = (now_true_usec/resolution) * resolution;
 
-while (exim_tvcmp(&now_tv, tgt_tv) <= 0)
+while (exim_tvcmp(&now_tv, prev_tv) <= 0)
   {
   struct itimerval itval;
   itval.it_interval.tv_sec = 0;
   itval.it_interval.tv_usec = 0;
   {
   struct itimerval itval;
   itval.it_interval.tv_sec = 0;
   itval.it_interval.tv_usec = 0;
-  itval.it_value.tv_sec = tgt_tv->tv_sec - now_tv.tv_sec;
-  itval.it_value.tv_usec = tgt_tv->tv_usec + resolution - now_true_usec;
+  itval.it_value.tv_sec = prev_tv->tv_sec - now_tv.tv_sec;
+  itval.it_value.tv_usec = prev_tv->tv_usec + resolution - now_true_usec;
 
   /* We know that, overall, "now" is less than or equal to "then". Therefore, a
   negative value for the microseconds is possible only in the case when "now"
 
   /* We know that, overall, "now" is less than or equal to "then". Therefore, a
   negative value for the microseconds is possible only in the case when "now"
@@ -487,7 +550,7 @@ while (exim_tvcmp(&now_tv, tgt_tv) <= 0)
     if (!f.running_in_test_harness)
       {
       debug_printf("tick check: " TIME_T_FMT ".%06lu " TIME_T_FMT ".%06lu\n",
     if (!f.running_in_test_harness)
       {
       debug_printf("tick check: " TIME_T_FMT ".%06lu " TIME_T_FMT ".%06lu\n",
-        tgt_tv->tv_sec, (long) tgt_tv->tv_usec,
+        prev_tv->tv_sec, (long) prev_tv->tv_usec,
                now_tv.tv_sec, (long) now_tv.tv_usec);
       debug_printf("waiting " TIME_T_FMT ".%06lu sec\n",
         itval.it_value.tv_sec, (long) itval.it_value.tv_usec);
                now_tv.tv_sec, (long) now_tv.tv_usec);
       debug_printf("waiting " TIME_T_FMT ".%06lu sec\n",
         itval.it_value.tv_sec, (long) itval.it_value.tv_usec);
@@ -503,6 +566,7 @@ while (exim_tvcmp(&now_tv, tgt_tv) <= 0)
   now_true_usec = now_tv.tv_usec;
   now_tv.tv_usec = (now_true_usec/resolution) * resolution;
   }
   now_true_usec = now_tv.tv_usec;
   now_tv.tv_usec = (now_true_usec/resolution) * resolution;
   }
+*prev_tv = now_tv;
 }
 
 
 }
 
 
@@ -1179,11 +1243,15 @@ show_db_version(fp);
 #endif
 #define QUOTE(X) #X
 #define EXPAND_AND_QUOTE(X) QUOTE(X)
 #endif
 #define QUOTE(X) #X
 #define EXPAND_AND_QUOTE(X) QUOTE(X)
-  fprintf(fp, "Library version: PCRE: Compile: %d.%d%s\n"
-             "                       Runtime: %s\n",
-          PCRE_MAJOR, PCRE_MINOR,
-          EXPAND_AND_QUOTE(PCRE_PRERELEASE) "",
-          pcre_version());
+  {
+  uschar buf[24];
+  pcre2_config(PCRE2_CONFIG_VERSION, buf);
+  fprintf(fp, "Library version: PCRE2: Compile: %d.%d%s\n"
+              "                        Runtime: %s\n",
+          PCRE2_MAJOR, PCRE2_MINOR,
+          EXPAND_AND_QUOTE(PCRE2_PRERELEASE) "",
+          buf);
+  }
 #undef QUOTE
 #undef EXPAND_AND_QUOTE
 
 #undef QUOTE
 #undef EXPAND_AND_QUOTE
 
@@ -1536,14 +1604,8 @@ for (macro_item * m = macros_user; m; m = m->next) if (m->command_line)
     continue;
   if ((len = m->replen) == 0)
     continue;
     continue;
   if ((len = m->replen) == 0)
     continue;
-  n = pcre_exec(regex_whitelisted_macro, NULL, CS m->replacement, len,
-   0, PCRE_EOPT, NULL, 0);
-  if (n < 0)
-    {
-    if (n != PCRE_ERROR_NOMATCH)
-      debug_printf("macros_trusted checking %s returned %d\n", m->name, n);
+  if (!regex_match(regex_whitelisted_macro, m->replacement, len, NULL))
     return FALSE;
     return FALSE;
-    }
   }
 DEBUG(D_any) debug_printf("macros_trusted overridden to true by whitelisting\n");
 return TRUE;
   }
 DEBUG(D_any) debug_printf("macros_trusted overridden to true by whitelisting\n");
 return TRUE;
@@ -1644,7 +1706,6 @@ BOOL list_queue = FALSE;
 BOOL list_options = FALSE;
 BOOL list_config = FALSE;
 BOOL local_queue_only;
 BOOL list_options = FALSE;
 BOOL list_config = FALSE;
 BOOL local_queue_only;
-BOOL more = TRUE;
 BOOL one_msg_action = FALSE;
 BOOL opt_D_used = FALSE;
 BOOL queue_only_set = FALSE;
 BOOL one_msg_action = FALSE;
 BOOL opt_D_used = FALSE;
 BOOL queue_only_set = FALSE;
@@ -1699,6 +1760,7 @@ extern char **environ;
 #endif
 
 store_init();  /* Initialise the memory allocation susbsystem */
 #endif
 
 store_init();  /* Initialise the memory allocation susbsystem */
+pcre_init();   /* Set up memory handling for pcre */
 
 /* If the Exim user and/or group and/or the configuration file owner/group were
 defined by ref:name at build time, we must now find the actual uid/gid values.
 
 /* If the Exim user and/or group and/or the configuration file owner/group were
 defined by ref:name at build time, we must now find the actual uid/gid values.
@@ -1799,15 +1861,6 @@ indirection, because some systems don't allow writing to the variable "stderr".
 
 if (fstat(fileno(stderr), &statbuf) >= 0) log_stderr = stderr;
 
 
 if (fstat(fileno(stderr), &statbuf) >= 0) log_stderr = stderr;
 
-/* Arrange for the PCRE regex library to use our store functions. Note that
-the normal calls are actually macros that add additional arguments for
-debugging purposes so we have to assign specially constructed functions here.
-The default is to use store in the stacking pool, but this is overridden in the
-regex_must_compile() function. */
-
-pcre_malloc = function_store_get;
-pcre_free = function_dummy_free;
-
 /* Ensure there is a big buffer for temporary use in several places. It is put
 in malloc store so that it can be freed for enlargement if necessary. */
 
 /* Ensure there is a big buffer for temporary use in several places. It is put
 in malloc store so that it can be freed for enlargement if necessary. */
 
@@ -4844,7 +4897,7 @@ for (i = 0;;)
 
         if (gecos_pattern && gecos_name)
           {
 
         if (gecos_pattern && gecos_name)
           {
-          const pcre *re;
+          const pcre2_code *re;
           re = regex_must_compile(gecos_pattern, FALSE, TRUE); /* Use malloc */
 
           if (regex_match_and_setup(re, name, 0, -1))
           re = regex_must_compile(gecos_pattern, FALSE, TRUE); /* Use malloc */
 
           if (regex_match_and_setup(re, name, 0, -1))
@@ -5498,7 +5551,7 @@ real_sender_address = sender_address;
 messages to be read (SMTP input), or FALSE otherwise (not SMTP, or SMTP channel
 collapsed). */
 
 messages to be read (SMTP input), or FALSE otherwise (not SMTP, or SMTP channel
 collapsed). */
 
-while (more)
+for (BOOL more = TRUE; more; )
   {
   rmark reset_point = store_mark();
   message_id[0] = 0;
   {
   rmark reset_point = store_mark();
   message_id[0] = 0;
@@ -5540,10 +5593,10 @@ while (more)
       /* Now get the data for the message */
 
       more = receive_msg(extract_recipients);
       /* Now get the data for the message */
 
       more = receive_msg(extract_recipients);
-      if (message_id[0] == 0)
+      if (!message_id[0])
         {
        cancel_cutthrough_connection(TRUE, US"receive dropped");
         {
        cancel_cutthrough_connection(TRUE, US"receive dropped");
-        if (more) goto moreloop;
+        if (more) goto MORELOOP;
         smtp_log_no_mail();               /* Log no mail if configured */
         exim_exit(EXIT_FAILURE);
         }
         smtp_log_no_mail();               /* Log no mail if configured */
         exim_exit(EXIT_FAILURE);
         }
@@ -5709,7 +5762,7 @@ while (more)
     for real; when reading the headers of a message for filter testing,
     it is TRUE if the headers were terminated by '.' and FALSE otherwise. */
 
     for real; when reading the headers of a message for filter testing,
     it is TRUE if the headers were terminated by '.' and FALSE otherwise. */
 
-    if (message_id[0] == 0) exim_exit(EXIT_FAILURE);
+    if (!message_id[0]) exim_exit(EXIT_FAILURE);
     }  /* Non-SMTP message reception */
 
   /* If this is a filter testing run, there are headers in store, but
     }  /* Non-SMTP message reception */
 
   /* If this is a filter testing run, there are headers in store, but
@@ -5902,11 +5955,11 @@ while (more)
   finished subprocesses here, in case there are lots of messages coming in
   from the same source. */
 
   finished subprocesses here, in case there are lots of messages coming in
   from the same source. */
 
-  #ifndef SIG_IGN_WORKS
+#ifndef SIG_IGN_WORKS
   while (waitpid(-1, NULL, WNOHANG) > 0);
   while (waitpid(-1, NULL, WNOHANG) > 0);
-  #endif
+#endif
 
 
-moreloop:
+MORELOOP:
   return_path = sender_address = NULL;
   authenticated_sender = NULL;
   deliver_localpart_orig = NULL;
   return_path = sender_address = NULL;
   authenticated_sender = NULL;
   deliver_localpart_orig = NULL;