dsearch: full-path return option
[exim.git] / src / src / lookups / dsearch.c
1 /*************************************************
2 *     Exim - an Internet mail transport agent    *
3 *************************************************/
4
5 /* Copyright (c) University of Cambridge 1995 - 2015 */
6 /* See the file NOTICE for conditions of use and distribution. */
7
8 /* The idea for this code came from Matthew Byng-Maddick, but his original has
9 been heavily reworked a lot for Exim 4 (and it now uses stat() (more precisely:
10 lstat()) rather than a directory scan). */
11
12
13 #include "../exim.h"
14 #include "lf_functions.h"
15
16
17
18 /*************************************************
19 *              Open entry point                  *
20 *************************************************/
21
22 /* See local README for interface description. We open the directory to test
23 whether it exists and whether it is searchable. However, we don't need to keep
24 it open, because the "search" can be done by a call to lstat() rather than
25 actually scanning through the list of files. */
26
27 static void *
28 dsearch_open(const uschar * dirname, uschar ** errmsg)
29 {
30 DIR * dp = exim_opendir(dirname);
31 if (!dp)
32   {
33   int save_errno = errno;
34   *errmsg = string_open_failed(errno, "%s for directory search", dirname);
35   errno = save_errno;
36   return NULL;
37   }
38 closedir(dp);
39 return (void *)(-1);
40 }
41
42
43 /*************************************************
44 *             Check entry point                  *
45 *************************************************/
46
47 /* The handle will always be (void *)(-1), but don't try casting it to an
48 integer as this gives warnings on 64-bit systems. */
49
50 static BOOL
51 dsearch_check(void * handle, const uschar * filename, int modemask,
52   uid_t * owners, gid_t * owngroups, uschar ** errmsg)
53 {
54 handle = handle;
55 if (*filename == '/')
56   return lf_check_file(-1, filename, S_IFDIR, modemask, owners, owngroups,
57     "dsearch", errmsg) == 0;
58 *errmsg = string_sprintf("dirname '%s' for dsearch is not absolute", filename);
59 return FALSE;
60 }
61
62
63 /*************************************************
64 *              Find entry point                  *
65 *************************************************/
66
67 #define RET_FULL        BIT(0)
68
69 /* See local README for interface description. We use lstat() instead of
70 scanning the directory, as it is hopefully faster to let the OS do the scanning
71 for us. */
72
73 static int
74 dsearch_find(void * handle, const uschar * dirname, const uschar * keystring,
75   int length, uschar ** result, uschar ** errmsg, uint * do_cache,
76   const uschar * opts)
77 {
78 struct stat statbuf;
79 int save_errno;
80 uschar * filename;
81 unsigned flags = 0;
82
83 handle = handle;  /* Keep picky compilers happy */
84 length = length;
85 do_cache = do_cache;
86
87 if (Ustrchr(keystring, '/') != 0)
88   {
89   *errmsg = string_sprintf("key for dsearch lookup contains a slash: %s",
90     keystring);
91   return DEFER;
92   }
93
94 if (opts)
95   {
96   int sep = ',';
97   uschar * ele;
98
99   while ((ele = string_nextinlist(&opts, &sep, NULL, 0)))
100     if (Ustrcmp(ele, "ret=full") == 0)
101       flags |= RET_FULL;
102   }
103
104 filename = string_sprintf("%s/%s", dirname, keystring);
105 if (Ulstat(filename, &statbuf) >= 0)
106   {
107   /* Since the filename exists in the filesystem, we can return a
108   non-tainted result. */
109   *result = string_copy_taint(flags & RET_FULL ? filename : keystring, FALSE);
110   return OK;
111   }
112
113 if (errno == ENOENT) return FAIL;
114
115 save_errno = errno;
116 *errmsg = string_sprintf("%s: lstat failed", filename);
117 errno = save_errno;
118 return DEFER;
119 }
120
121
122 /*************************************************
123 *              Close entry point                 *
124 *************************************************/
125
126 /* See local README for interface description */
127
128 void
129 static dsearch_close(void *handle)
130 {
131 handle = handle;   /* Avoid compiler warning */
132 }
133
134
135 /*************************************************
136 *         Version reporting entry point          *
137 *************************************************/
138
139 /* See local README for interface description. */
140
141 #include "../version.h"
142
143 void
144 dsearch_version_report(FILE *f)
145 {
146 #ifdef DYNLOOKUP
147 fprintf(f, "Library version: dsearch: Exim version %s\n", EXIM_VERSION_STR);
148 #endif
149 }
150
151
152 static lookup_info _lookup_info = {
153   US"dsearch",                   /* lookup name */
154   lookup_absfile,                /* uses absolute file name */
155   dsearch_open,                  /* open function */
156   dsearch_check,                 /* check function */
157   dsearch_find,                  /* find function */
158   dsearch_close,                 /* close function */
159   NULL,                          /* no tidy function */
160   NULL,                          /* no quoting function */
161   dsearch_version_report         /* version reporting */
162 };
163
164 #ifdef DYNLOOKUP
165 #define dsearch_lookup_module_info _lookup_module_info
166 #endif
167
168 static lookup_info *_lookup_list[] = { &_lookup_info };
169 lookup_module_info dsearch_lookup_module_info = { LOOKUP_MODULE_INFO_MAGIC, _lookup_list, 1 };
170
171 /* End of lookups/dsearch.c */