readsocket expansion: response caching
[exim.git] / src / src / search.c
index d67bdc59e2d958095d5bb693ad349ad730f6ff84..d3511df31b58999dd2fc84ddfe9aab3ad1373251 100644 (file)
@@ -115,6 +115,7 @@ Arguments:
                  otherwise it's a literal string
   afflen       the length of the affix
   starflags    where to put the SEARCH_STAR and SEARCH_STARAT flags
+  opts        where to put the options
 
 Returns:     +ve => valid lookup name; value is offset in lookup_list
              -ve => invalid name; message in search_error_message.
@@ -122,11 +123,12 @@ Returns:     +ve => valid lookup name; value is offset in lookup_list
 
 int
 search_findtype_partial(const uschar *name, int *ptypeptr, const uschar **ptypeaff,
-  int *afflen, int *starflags)
+  int *afflen, int *starflags, const uschar ** opts)
 {
 int len, stype;
 int pv = -1;
 const uschar *ss = name;
+const uschar * t;
 
 *starflags = 0;
 *ptypeaff = NULL;
@@ -167,19 +169,26 @@ if (Ustrncmp(name, "partial", 7) == 0)
     }
   }
 
-/* Now we are left with a lookup name, possibly followed by * or *@. */
+/* Now we are left with a lookup name, possibly followed by * or *@,
+and then by options starting with a "," */
 
 len = Ustrlen(ss);
-if (len >= 2 && Ustrncmp(ss + len - 2, "*@", 2) == 0)
+if ((t = Ustrchr(ss, '*')))
   {
-  *starflags |= SEARCH_STARAT;
-  len -= 2;
+  len = t - ss;
+  *starflags |= (t[1] == '@' ? SEARCH_STARAT : SEARCH_STAR);
   }
-else if (len >= 1 && ss[len-1]  == '*')
+else
+  t = ss;
+
+if ((t = Ustrchr(t, ',')))
   {
-  *starflags |= SEARCH_STAR;
-  len--;
+  int l = t - ss;
+  if (l < len) len = l;
+  *opts = string_copy(t+1);
   }
+else
+  * opts = NULL;
 
 /* Check for the individual search type. Only those that are actually in the
 binary are valid. For query-style types, "partial" and default types are
@@ -230,12 +239,10 @@ Returns:     nothing
 static void
 tidyup_subtree(tree_node *t)
 {
-search_cache *c = (search_cache *)(t->data.ptr);
-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)
+search_cache * c = (search_cache *)(t->data.ptr);
+if (t->left)  tidyup_subtree(t->left);
+if (t->right) tidyup_subtree(t->right);
+if (c && c->handle && lookup_list[c->search_type]->close)
   lookup_list[c->search_type]->close(c->handle);
 }
 
@@ -454,6 +461,7 @@ Arguments:
                NULL for query-style searches
   keystring    the keystring for single-key+file lookups, or
                the querystring for query-style lookups
+  opts        type-specific options
 
 Returns:       a pointer to a dynamic string containing the answer,
                or NULL if the query failed or was deferred; in the
@@ -462,7 +470,8 @@ Returns:       a pointer to a dynamic string containing the answer,
 */
 
 static uschar *
-internal_search_find(void * handle, const uschar * filename, uschar * keystring)
+internal_search_find(void * handle, const uschar * filename, uschar * keystring,
+  const uschar * opts)
 {
 tree_node * t = (tree_node *)handle;
 search_cache * c = (search_cache *)(t->data.ptr);
@@ -478,8 +487,9 @@ search_error_message = US"";
 f.search_find_defer = FALSE;
 
 DEBUG(D_lookup) debug_printf_indent("internal_search_find: file=\"%s\"\n  "
-  "type=%s key=\"%s\"\n", filename,
-  lookup_list[search_type]->name, keystring);
+  "type=%s key=\"%s\" opts=%s%s%s\n", filename,
+  lookup_list[search_type]->name, keystring,
+  opts ? "\"" : "", opts, opts ? "\"" : "");
 
 /* Insurance. If the keystring is empty, just fail. */
 
@@ -494,6 +504,7 @@ file. No need to check c->item_cache for NULL, tree_search will do so. */
 
 if (  (t = tree_search(c->item_cache, keystring))
    && (!(e = t->data.ptr)->expiry || e->expiry > time(NULL))
+   && (!opts && !e->opts  ||  opts && e->opts && Ustrcmp(opts, e->opts) == 0)
    )
   { /* Data was in the cache already; set the pointer from the tree node */
   data = e->data.ptr;
@@ -508,7 +519,9 @@ else
 
   DEBUG(D_lookup)
     {
-    if (t) debug_printf_indent("cached data found but past valid time; ");
+    if (t)
+      debug_printf_indent("cached data found but %s; ",
+       e->expiry && e->expiry <= time(NULL) ? "out-of-date" : "wrong opts");
     debug_printf_indent("%s lookup required for %s%s%s\n",
       filename ? US"file" : US"database",
       keystring,
@@ -520,7 +533,7 @@ else
   distinguish if necessary. */
 
   if (lookup_list[search_type]->find(c->handle, filename, keystring, keylength,
-      &data, &search_error_message, &do_cache) == DEFER)
+      &data, &search_error_message, &do_cache, opts) == DEFER)
     f.search_find_defer = TRUE;
 
   /* A record that has been found is now in data, which is either NULL
@@ -531,23 +544,20 @@ else
 
   else if (do_cache)
     {
-    int len = keylength + 1;
-
-    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->data.ptr = data;
-      }
-    else
+    if (!t)    /* No existing entry.  Create new one. */
       {
+      int len = keylength + 1;
       e = store_get(sizeof(expiring_data) + sizeof(tree_node) + len, is_tainted(keystring));
-      e->expiry = do_cache == UINT_MAX ? 0 : time(NULL)+do_cache;
-      e->data.ptr = data;
       t = (tree_node *)(e+1);
       memcpy(t->name, keystring, len);
       t->data.ptr = e;
       tree_insertnode(&c->item_cache, t);
       }
+      /* Else 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->opts = opts ? string_copy(opts) : NULL;
+    e->data.ptr = data;
     }
 
   /* If caching was disabled, empty the cache tree. We just set the cache
@@ -556,7 +566,7 @@ else
   else
     {
     DEBUG(D_lookup) debug_printf_indent("lookup forced cache cleanup\n");
-    c->item_cache = NULL;
+    c->item_cache = NULL;      /* forget all lookups on this connection */
     }
   }
 
@@ -598,6 +608,7 @@ Arguments:
   starflags      SEARCH_STAR and SEARCH_STARAT flags
   expand_setup   pointer to offset for setting up expansion strings;
                  don't do any if < 0
+  opts          type-specific options
 
 Returns:         a pointer to a dynamic string containing the answer,
                  or NULL if the query failed or was deferred; in the
@@ -607,7 +618,7 @@ Returns:         a pointer to a dynamic string containing the answer,
 uschar *
 search_find(void * handle, const uschar * filename, uschar * keystring,
   int partial, const uschar * affix, int affixlen, int starflags,
-  int * expand_setup)
+  int * expand_setup, const uschar * opts)
 {
 tree_node *t = (tree_node *)handle;
 BOOL set_null_wild = FALSE;
@@ -617,9 +628,11 @@ DEBUG(D_lookup)
   {
   if (partial < 0) affixlen = 99;   /* So that "NULL" prints */
   debug_printf_indent("search_find: file=\"%s\"\n  key=\"%s\" "
-    "partial=%d affix=%.*s starflags=%x\n",
-    (filename == NULL)? US"NULL" : filename,
-    keystring, partial, affixlen, affix, starflags);
+    "partial=%d affix=%.*s starflags=%x opts=%s%s%s\n",
+    filename ? filename : US"NULL",
+    keystring, partial, affixlen, affix, starflags,
+    opts ? "\"" : "", opts, opts ? "\"" : "");
+
   }
 
 /* Arrange to put this database at the top of the LRU chain if it is a type
@@ -669,7 +682,7 @@ DEBUG(D_lookup)
 /* First of all, try to match the key string verbatim. If matched a complete
 entry but could have been partial, flag to set up variables. */
 
-yield = internal_search_find(handle, filename, keystring);
+yield = internal_search_find(handle, filename, keystring, opts);
 if (f.search_find_defer) return NULL;
 
 if (yield) { if (partial >= 0) set_null_wild = TRUE; }
@@ -694,7 +707,7 @@ else if (partial >= 0)
     Ustrncpy(keystring2, affix, affixlen);
     Ustrcpy(keystring2 + affixlen, keystring);
     DEBUG(D_lookup) debug_printf_indent("trying partial match %s\n", keystring2);
-    yield = internal_search_find(handle, filename, keystring2);
+    yield = internal_search_find(handle, filename, keystring2, opts);
     if (f.search_find_defer) return NULL;
     }
 
@@ -732,7 +745,7 @@ else if (partial >= 0)
         }
 
       DEBUG(D_lookup) debug_printf_indent("trying partial match %s\n", keystring3);
-      yield = internal_search_find(handle, filename, keystring3);
+      yield = internal_search_find(handle, filename, keystring3, opts);
       if (f.search_find_defer) return NULL;
       if (yield)
         {
@@ -773,7 +786,7 @@ if (!yield  &&  starflags & SEARCH_STARAT)
     *atat = '*';
 
     DEBUG(D_lookup) debug_printf_indent("trying default match %s\n", atat);
-    yield = internal_search_find(handle, filename, atat);
+    yield = internal_search_find(handle, filename, atat, opts);
     *atat = savechar;
     if (f.search_find_defer) return NULL;
 
@@ -796,7 +809,7 @@ and the second is empty. */
 if (!yield  &&  starflags & (SEARCH_STAR|SEARCH_STARAT))
   {
   DEBUG(D_lookup) debug_printf_indent("trying to match *\n");
-  yield = internal_search_find(handle, filename, US"*");
+  yield = internal_search_find(handle, filename, US"*", opts);
   if (yield && expand_setup && *expand_setup >= 0)
     {
     *expand_setup += 1;