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