DKIM: fix simple body verify for trailing empty lines after text
[exim.git] / src / src / search.c
index 3dfd80e32820de87c3b5f80426cf0d10ce657da2..ccad25021675a412a5cf58220c5ad0cc5f0607d0 100644 (file)
@@ -1,10 +1,8 @@
-/* $Cambridge: exim/src/src/search.c,v 1.1 2004/10/07 10:39:01 ph10 Exp $ */
-
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2004 */
+/* Copyright (c) University of Cambridge 1995 - 2015 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* A set of functions to search databases in various formats. An open
@@ -64,14 +62,14 @@ Returns:     +ve => valid lookup name; value is offset in lookup_list
 */
 
 int
-search_findtype(uschar *name, int len)
+search_findtype(const uschar *name, int len)
 {
 int bot = 0;
 int top = lookup_list_count;
 while (top > bot)
   {
   int mid = (top + bot)/2;
-  int c = Ustrncmp(name, lookup_list[mid].name, len);
+  int c = Ustrncmp(name, lookup_list[mid]->name, len);
 
   /* If c == 0 we have matched the incoming name with the start of the search
   type name. However, some search types are substrings of others (e.g. nis and
@@ -81,9 +79,9 @@ while (top > bot)
   are testing. By leaving c == 0 when the lengths are different, and doing a
   > 0 test below, this all falls out correctly. */
 
-  if (c == 0 && Ustrlen(lookup_list[mid].name) == len)
+  if (c == 0 && Ustrlen(lookup_list[mid]->name) == len)
     {
-    if (lookup_list[mid].find != NULL) return mid;
+    if (lookup_list[mid]->find != NULL) return mid;
     search_error_message  = string_sprintf("lookup type \"%.*s\" is not "
       "available (not in the binary - check buildtime LOOKUP configuration)",
       len, name);
@@ -123,12 +121,12 @@ Returns:     +ve => valid lookup name; value is offset in lookup_list
 */
 
 int
-search_findtype_partial(uschar *name, int *ptypeptr, uschar **ptypeaff,
+search_findtype_partial(const uschar *name, int *ptypeptr, const uschar **ptypeaff,
   int *afflen, int *starflags)
 {
 int len, stype;
 int pv = -1;
-uschar *ss = name;
+const uschar *ss = name;
 
 *starflags = 0;
 *ptypeaff = NULL;
@@ -184,18 +182,26 @@ else if (len >= 1 && ss[len-1]  == '*')
   }
 
 /* Check for the individual search type. Only those that are actually in the
-binary are valid. For query-style types, "partial" is an error. */
+binary are valid. For query-style types, "partial" and default types are
+erroneous. */
 
 stype = search_findtype(ss, len);
-if (pv >= 0 && mac_islookup(stype, lookup_querystyle))
+if (stype >= 0 && mac_islookup(stype, lookup_querystyle))
   {
-  search_error_message = string_sprintf("\"partial\" is not permitted "
-    "for lookup type \"%s\"", ss);
-  return -1;
+  if (pv >= 0)
+    {
+    search_error_message = string_sprintf("\"partial\" is not permitted "
+      "for lookup type \"%s\"", ss);
+    return -1;
+    }
+  if ((*starflags & (SEARCH_STAR|SEARCH_STARAT)) != 0)
+    {
+    search_error_message = string_sprintf("defaults using \"*\" or \"*@\" are "
+      "not permitted for lookup type \"%s\"", ss);
+    return -1;
+    }
   }
 
-/* All is well; pass back the partial type and return the lookup type. */
-
 *ptypeptr = pv;
 return stype;
 }
@@ -229,8 +235,8 @@ if (t->left != NULL) tidyup_subtree(t->left);
 if (t->right != NULL) tidyup_subtree(t->right);
 if (c != NULL &&
     c->handle != NULL &&
-    lookup_list[c->search_type].close != NULL)
-  lookup_list[c->search_type].close(c->handle);
+    lookup_list[c->search_type]->close != NULL)
+  lookup_list[c->search_type]->close(c->handle);
 }
 
 
@@ -262,7 +268,7 @@ open_filecount = 0;
 /* Call the general tidyup entry for any drivers that have one. */
 
 for (i = 0; i < lookup_list_count; i++)
-  if (lookup_list[i].tidy != NULL) (lookup_list[i].tidy)();
+  if (lookup_list[i]->tidy != NULL) (lookup_list[i]->tidy)();
 
 if (search_reset_point != NULL) store_reset(search_reset_point);
 search_reset_point = NULL;
@@ -327,7 +333,7 @@ search_open(uschar *filename, int search_type, int modemask, uid_t *owners,
 void *handle;
 tree_node *t;
 search_cache *c;
-lookup_info *lk = lookup_list + search_type;
+lookup_info *lk = lookup_list[search_type];
 uschar keybuffer[256];
 int old_pool = store_pool;
 
@@ -380,7 +386,7 @@ if (lk->type == lookup_absfile && open_filecount >= lookup_open_max)
       ((search_cache *)(open_bot->data.ptr))->down = NULL;
     else
       open_top = NULL;
-    ((lookup_list + c->search_type)->close)(c->handle);
+    ((lookup_list[c->search_type])->close)(c->handle);
     c->handle = NULL;
     open_filecount--;
     }
@@ -389,7 +395,7 @@ if (lk->type == lookup_absfile && open_filecount >= lookup_open_max)
 /* If opening is successful, call the file-checking function if there is one,
 and if all is still well, enter the open database into the tree. */
 
-handle = lk->open(filename, &search_error_message);
+handle = (lk->open)(filename, &search_error_message);
 if (handle == NULL)
   {
   store_pool = old_pool;
@@ -460,6 +466,7 @@ internal_search_find(void *handle, uschar *filename, uschar *keystring)
 {
 tree_node *t = (tree_node *)handle;
 search_cache *c = (search_cache *)(t->data.ptr);
+expiring_data *e;
 uschar *data = NULL;
 int search_type = t->name[0] - '0';
 int old_pool = store_pool;
@@ -472,7 +479,7 @@ search_find_defer = FALSE;
 
 DEBUG(D_lookup) debug_printf("internal_search_find: file=\"%s\"\n  "
   "type=%s key=\"%s\"\n", filename,
-  lookup_list[search_type].name, keystring);
+  lookup_list[search_type]->name, keystring);
 
 /* Insurance. If the keystring is empty, just fail. */
 
@@ -485,29 +492,36 @@ store_pool = POOL_SEARCH;
 /* Look up the data for the key, unless it is already in the cache for this
 file. No need to check c->item_cache for NULL, tree_search will do so. */
 
-if ((t = tree_search(c->item_cache, keystring)) == NULL)
+if (  (t = tree_search(c->item_cache, keystring))
+   && (!(e = t->data.ptr)->expiry || e->expiry > time(NULL))
+   )
+  { /* Data was in the cache already; set the pointer from the tree node */
+  data = e->ptr;
+  DEBUG(D_lookup) debug_printf("cached data used for lookup of %s%s%s\n",
+    keystring,
+    filename ? US"\n  in " : US"", filename ? filename : US"");
+  }
+else
   {
-  BOOL do_cache = TRUE;
+  uint do_cache = UINT_MAX;
   int keylength = Ustrlen(keystring);
 
   DEBUG(D_lookup)
     {
-    if (filename != NULL)
-      debug_printf("file lookup required for %s\n  in %s\n",
-        keystring, filename);
-    else
-      debug_printf("database lookup required for %s\n", keystring);
+    if (t) debug_printf("cached data found but past valid time; ");
+    debug_printf("%s lookup required for %s%s%s\n",
+      filename ? US"file" : US"database",
+      keystring,
+      filename ? US"\n  in " : US"", filename ? filename : US"");
     }
 
   /* Call the code for the different kinds of search. DEFER is handled
   like FAIL, except that search_find_defer is set so the caller can
   distinguish if necessary. */
 
-  if (lookup_list[search_type].find(c->handle, filename, keystring, keylength,
+  if (lookup_list[search_type]->find(c->handle, filename, keystring, keylength,
       &data, &search_error_message, &do_cache) == DEFER)
-    {
     search_find_defer = TRUE;
-    }
 
   /* A record that has been found is now in data, which is either NULL
   or points to a bit of dynamic store. Cache the result of the lookup if
@@ -518,10 +532,22 @@ if ((t = tree_search(c->item_cache, keystring)) == NULL)
   else if (do_cache)
     {
     int len = keylength + 1;
-    t = store_get(sizeof(tree_node) + len);
-    memcpy(t->name, keystring, len);
-    t->data.ptr = data;
-    tree_insertnode(&c->item_cache, t);
+
+    if (t)     /* Previous, out-of-date cache entry.  Update with the */
+      {        /* new result and forget the old one */
+      e->expiry = do_cache == UINT_MAX ? 0 : time(NULL)+do_cache;
+      e->ptr = data;
+      }
+    else
+      {
+      e = store_get(sizeof(expiring_data) + sizeof(tree_node) + len);
+      e->expiry = do_cache == UINT_MAX ? 0 : time(NULL)+do_cache;
+      e->ptr = data;
+      t = (tree_node *)(e+1);
+      memcpy(t->name, keystring, len);
+      t->data.ptr = e;
+      tree_insertnode(&c->item_cache, t);
+      }
     }
 
   /* If caching was disabled, empty the cache tree. We just set the cache
@@ -534,34 +560,19 @@ if ((t = tree_search(c->item_cache, keystring)) == NULL)
     }
   }
 
-/* Data was in the cache already; set the pointer from the tree node */
-
-else
-  {
-  data = US t->data.ptr;
-  DEBUG(D_lookup) debug_printf("cached data used for lookup of %s%s%s\n",
-    keystring,
-    (filename == NULL)? US"" : US"\n  in ",
-    (filename == NULL)? US"" : filename);
-  }
-
-/* Debug: output the answer */
-
 DEBUG(D_lookup)
   {
-  if (data == NULL)
-    {
-    if (search_find_defer) debug_printf("lookup deferred: %s\n",
-      search_error_message);
-    else debug_printf("lookup failed\n");
-    }
-  else debug_printf("lookup yielded: %s\n", data);
+  if (data)
+    debug_printf("lookup yielded: %s\n", data);
+  else if (search_find_defer)
+    debug_printf("lookup deferred: %s\n", search_error_message);
+  else debug_printf("lookup failed\n");
   }
 
 /* Return it in new dynamic store in the regular pool */
 
 store_pool = old_pool;
-return (data == NULL)? NULL : string_copy(data);
+return data ? string_copy(data) : NULL;
 }
 
 
@@ -595,7 +606,7 @@ Returns:         a pointer to a dynamic string containing the answer,
 
 uschar *
 search_find(void *handle, uschar *filename, uschar *keystring, int partial,
-  uschar *affix, int affixlen, int starflags, int *expand_setup)
+  const uschar *affix, int affixlen, int starflags, int *expand_setup)
 {
 tree_node *t = (tree_node *)handle;
 BOOL set_null_wild = FALSE;
@@ -614,7 +625,7 @@ DEBUG(D_lookup)
 that opens real files. */
 
 if (open_top != (tree_node *)handle &&
-    lookup_list[t->name[0]-'0'].type == lookup_absfile)
+    lookup_list[t->name[0]-'0']->type == lookup_absfile)
   {
   search_cache *c = (search_cache *)(t->data.ptr);
   tree_node *up = c->up;