ACL: "seen" condition
[exim.git] / src / src / exim_dbutil.c
index cd29cc4a4aae5d4f2a19b3a0c00df4ddb4a2c2cb..45b778fc0aacc8039efa97b3ec22fcad392ff64f 100644 (file)
@@ -3,6 +3,7 @@
 *************************************************/
 
 /* Copyright (c) University of Cambridge 1995 - 2018 */
+/* Copyright (c) The Exim Maintainers 2020 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 
@@ -20,7 +21,9 @@ argument is the name of the database file. The available names are:
   misc:       miscellaneous hints data
   wait-<t>:   message waiting information; <t> is a transport name
   callout:    callout verification cache
+  ratelimit:  ACL 'ratelimit' condition
   tls:       TLS session resumption cache
+  seen:              ACL 'seen' condition
 
 There are a number of common subroutines, followed by three main programs,
 whose inclusion is controlled by -D on the compilation command. */
@@ -37,6 +40,7 @@ whose inclusion is controlled by -D on the compilation command. */
 #define type_callout   4
 #define type_ratelimit 5
 #define type_tls       6
+#define type_seen      7
 
 
 /* This is used by our cut-down dbfn_open(). */
@@ -68,6 +72,13 @@ struct global_flags  f;
 unsigned int           log_selector[1];
 uschar *               queue_name;
 BOOL                   split_spool_directory;
+
+
+/* These introduced by the taintwarn handling */
+#ifdef ALLOW_INSECURE_TAINTED_DATA
+BOOL    allow_insecure_tainted_data;
+#endif
+
 /******************************************************************************/
 
 
@@ -105,7 +116,6 @@ SIGNAL_BOOL sigalrm_seen;
 void
 sigalrm_handler(int sig)
 {
-sig = sig;            /* Keep picky compilers happy */
 sigalrm_seen = 1;
 }
 
@@ -119,7 +129,7 @@ static void
 usage(uschar *name, uschar *options)
 {
 printf("Usage: exim_%s%s  <spool-directory> <database-name>\n", name, options);
-printf("  <database-name> = retry | misc | wait-<transport-name> | callout | ratelimit | tls\n");
+printf("  <database-name> = retry | misc | wait-<transport-name> | callout | ratelimit | tls | seen\n");
 exit(1);
 }
 
@@ -143,6 +153,7 @@ if (argc == 3)
   if (Ustrcmp(argv[2], "callout") == 0) return type_callout;
   if (Ustrcmp(argv[2], "ratelimit") == 0) return type_ratelimit;
   if (Ustrcmp(argv[2], "tls") == 0) return type_tls;
+  if (Ustrcmp(argv[2], "seen") == 0) return type_seen;
   }
 usage(name, options);
 return -1;              /* Never obeyed */
@@ -176,8 +187,6 @@ va_start(ap, format);
 vfprintf(stderr, format, ap);
 fprintf(stderr, "\n");
 va_end(ap);
-selector = selector;     /* Keep picky compilers happy */
-flags = flags;
 }
 
 
@@ -390,7 +399,7 @@ pick out the timestamps, etc., do the copying centrally here.
 Arguments:
   dbblock   a pointer to an open database block
   key       the key of the record to be read
-  length    where to put the length (or NULL if length not wanted)
+  length    where to put the length (or NULL if length not wanted). Includes overhead.
 
 Returns: a pointer to the retrieved record, or
          NULL if the record is not found
@@ -418,7 +427,7 @@ we should store the taint status along with the data. */
 
 yield = store_get(EXIM_DATUM_SIZE(result_datum), TRUE);
 memcpy(yield, EXIM_DATUM_DATA(result_datum), EXIM_DATUM_SIZE(result_datum));
-if (length != NULL) *length = EXIM_DATUM_SIZE(result_datum);
+if (length) *length = EXIM_DATUM_SIZE(result_datum);
 
 EXIM_DATUM_FREE(result_datum);    /* Some DBM libs require freeing */
 return yield;
@@ -517,7 +526,6 @@ dbfn_scan(open_db *dbblock, BOOL start, EXIM_CURSOR **cursor)
 {
 EXIM_DATUM key_datum, value_datum;
 uschar *yield;
-value_datum = value_datum;    /* dummy; not all db libraries use this */
 
 /* Some dbm require an initialization */
 
@@ -554,6 +562,8 @@ EXIM_CURSOR *cursor;
 uschar **argv = USS cargv;
 uschar keybuffer[1024];
 
+store_init();
+
 /* Check the arguments, and open the database */
 
 dbdata_type = check_args(argc, argv, US"dumpdb", US"");
@@ -575,6 +585,7 @@ for (uschar * key = dbfn_scan(dbm, TRUE, &cursor);
   dbdata_ratelimit *ratelimit;
   dbdata_ratelimit_unique *rate_unique;
   dbdata_tls_session *session;
+  dbdata_seen *seen;
   int count_bad = 0;
   int length;
   uschar *t;
@@ -619,6 +630,7 @@ for (uschar * key = dbfn_scan(dbm, TRUE, &cursor);
        t = wait->text;
        name[MESSAGE_ID_LENGTH] = 0;
 
+    /* Leave corrupt records alone */
        if (wait->count > WAIT_NAME_MAX)
          {
          fprintf(stderr,
@@ -713,6 +725,11 @@ for (uschar * key = dbfn_scan(dbm, TRUE, &cursor);
        session = (dbdata_tls_session *)value;
        printf("  %s %.*s\n", keybuffer, length, session->session);
        break;
+
+      case type_seen:
+       seen = (dbdata_seen *)value;
+       printf("%s\t%s\n", keybuffer, print_time(seen->time_stamp));
+       break;
       }
     }
   store_reset(reset_point);
@@ -767,6 +784,7 @@ uschar buffer[256];
 uschar name[256];
 rmark reset_point;
 
+store_init();
 name[0] = 0;  /* No name set */
 
 /* Sort out the database type, verify what we are working on and then process
@@ -1136,6 +1154,8 @@ uschar **argv = USS cargv;
 uschar buffer[256];
 uschar *key;
 
+store_init();
+
 /* Scan the options */
 
 for (i = 1; i < argc; i++)
@@ -1221,7 +1241,7 @@ for (; keychain && (reset_point = store_mark()); store_reset(reset_point))
   /* A continuation record may have been deleted or renamed already, so
   non-existence is not serious. */
 
-  if (value == NULL) continue;
+  if (!value) continue;
 
   /* Delete if too old */
 
@@ -1242,12 +1262,33 @@ for (; keychain && (reset_point = store_mark()); store_reset(reset_point))
 
     /* Leave corrupt records alone */
 
+    if (wait->time_stamp > time(NULL))
+      {
+      printf("**** Data for '%s' corrupted\n  time in future: %s\n",
+        key, print_time(((dbdata_generic *)value)->time_stamp));
+      continue;
+      }
     if (wait->count > WAIT_NAME_MAX)
       {
-      printf("**** Data for %s corrupted\n  count=%d=0x%x max=%d\n",
+      printf("**** Data for '%s' corrupted\n  count=%d=0x%x max=%d\n",
         key, wait->count, wait->count, WAIT_NAME_MAX);
       continue;
       }
+    if (wait->sequence > WAIT_CONT_MAX)
+      {
+      printf("**** Data for '%s' corrupted\n  sequence=%d=0x%x max=%d\n",
+        key, wait->sequence, wait->sequence, WAIT_CONT_MAX);
+      continue;
+      }
+
+    /* Record over 1 year old; just remove it */
+
+    if (wait->time_stamp < time(NULL) - 365*24*60*60)
+      {
+      dbfn_delete(dbm, key);
+      printf("deleted %s (too old)\n", key);
+      continue;
+      }
 
     /* Loop for renamed continuation records. For each message id,
     check to see if the message exists, and if not, remove its entry