1 diff -r 938614e333a1 .hgignore |
|
2 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 |
|
3 +++ b/.hgignore Fri Aug 21 11:48:06 2009 +0200 |
|
4 @@ -0,0 +1,7 @@ |
|
5 +syntax: glob |
|
6 +tags |
|
7 + |
|
8 +syntax: regexp |
|
9 + |
|
10 +^Local/Makefile$ |
|
11 +^build-Linux-i386/ |
|
12 diff -r 938614e333a1 .vimrc |
|
13 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 |
|
14 +++ b/.vimrc Fri Aug 21 11:48:06 2009 +0200 |
|
15 @@ -0,0 +1,3 @@ |
|
16 +set sw=2 |
|
17 +set cino={1s |
|
18 +set cindent |
|
19 diff -r 938614e333a1 OS/Makefile-Base |
|
20 --- a/OS/Makefile-Base Fri Aug 14 08:39:52 2009 +0200 |
|
21 +++ b/OS/Makefile-Base Fri Aug 21 11:48:06 2009 +0200 |
|
22 @@ -298,7 +298,7 @@ |
|
23 |
|
24 OBJ_WITH_CONTENT_SCAN = malware.o mime.o regex.o spam.o spool_mbox.o |
|
25 OBJ_WITH_OLD_DEMIME = demime.o |
|
26 -OBJ_EXPERIMENTAL = bmi_spam.o spf.o srs.o dk.o dkim-exim.o |
|
27 +OBJ_EXPERIMENTAL = bmi_spam.o spf.o srs.o dk.o dkim-exim.o memcache.o |
|
28 |
|
29 # Targets for final binaries; the main one has a build number which is |
|
30 # updated each time. We don't bother with that for the auxiliaries. |
|
31 @@ -543,7 +543,7 @@ |
|
32 dns.o: $(HDRS) dns.c |
|
33 enq.o: $(HDRS) enq.c |
|
34 exim.o: $(HDRS) exim.c |
|
35 -expand.o: $(HDRS) expand.c |
|
36 +expand.o: $(HDRS) memcache.h expand.c |
|
37 filter.o: $(HDRS) filter.c |
|
38 filtertest.o: $(HDRS) filtertest.c |
|
39 globals.o: $(HDRS) globals.c |
|
40 @@ -600,6 +600,7 @@ |
|
41 srs.o: $(HDRS) srs.h srs.c |
|
42 dk.o: $(HDRS) dk.h dk.c |
|
43 dkim-exim.o: $(HDRS) dkim-exim.h dkim-exim.c |
|
44 +memcache.o: $(HDRS) memcache.h memcache.c |
|
45 |
|
46 # The module containing tables of available lookups, routers, auths, and |
|
47 # transports must be rebuilt if any of them are. However, because the makefiles |
|
48 diff -r 938614e333a1 OS/os.h-Linux |
|
49 --- a/OS/os.h-Linux Fri Aug 14 08:39:52 2009 +0200 |
|
50 +++ b/OS/os.h-Linux Fri Aug 21 11:48:06 2009 +0200 |
|
51 @@ -53,3 +53,8 @@ |
|
52 #endif /* __linux__ */ |
|
53 |
|
54 /* End */ |
|
55 + |
|
56 +/* Fudge added because this Linux doesn't appear to have a definition |
|
57 +for ip_options in /usr/include/linux/ip.h. */ |
|
58 + |
|
59 +#define ip_options options |
|
60 diff -r 938614e333a1 doc/OptionLists.txt |
|
61 --- a/doc/OptionLists.txt Fri Aug 14 08:39:52 2009 +0200 |
|
62 +++ b/doc/OptionLists.txt Fri Aug 21 11:48:06 2009 +0200 |
|
63 @@ -11,7 +11,7 @@ |
|
64 4. Those that can appear in the build time configuration for the Exim monitor |
|
65 (Local/eximon.conf). |
|
66 |
|
67 -This file was last updated for Exim release 4.67. |
|
68 +This file was last updated for Exim release 4.69. |
|
69 |
|
70 |
|
71 1. RUN TIME OPTIONS |
|
72 @@ -887,6 +887,7 @@ |
|
73 SUPPORT_CRYPTEQ optional support crypteq (if no auths) |
|
74 SUPPORT_MAILDIR optional support for maildir delivery |
|
75 SUPPORT_MAILSTORE optional support for mailstore delivery |
|
76 +SUPPORT_MEMCACHE optional* support for memcache storage and retrieval |
|
77 SUPPORT_MBX optional support for MBX delivery |
|
78 SUPPORT_MOVE_FROZEN_MESSAGES optional* support for frozen message moving |
|
79 SUPPORT_PAM optional support for PAM authentication |
|
80 diff -r 938614e333a1 doc/memcache.txt |
|
81 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 |
|
82 +++ b/doc/memcache.txt Fri Aug 21 11:48:06 2009 +0200 |
|
83 @@ -0,0 +1,118 @@ |
|
84 +* set some globals |
|
85 + |
|
86 + hostlist memcache_servers = <; host1:port1 ; host2:port2 ; host3 |
|
87 + memcache_timeout = 3s |
|
88 + memcache_expiration = 0s # keep forever or until memcache needs free memory |
|
89 + |
|
90 +* store some value under key; expands to 'true' if successful |
|
91 + |
|
92 + ${memcache \ |
|
93 + {set} \ |
|
94 + {key} \ |
|
95 + {value} \ |
|
96 + {expiration} \ # optional |
|
97 + {timeout} \ |
|
98 + {servers}} |
|
99 + |
|
100 + |
|
101 +* lookup the value of some key; expands to 'true' if the key has been found; |
|
102 + stores the value in $value |
|
103 + |
|
104 + ${memcache \ |
|
105 + {get} \ |
|
106 + {key} \ |
|
107 + {timeout} \ # optional |
|
108 + {servers}} |
|
109 + |
|
110 + |
|
111 +Currently only tcp connections are supported. Whitespace and control characters |
|
112 +in keys will be urlencoded. Expansion will fail if the resulting key is then |
|
113 +longer than 250 characters. You can put multiple hosts in the hostlist, but its |
|
114 +probably not useful. Each host is tried in the order listed until one succeeds. |
|
115 +Expansion will fail if none suceeds. |
|
116 + |
|
117 +example usage for greylisting: |
|
118 + |
|
119 + [...] |
|
120 + |
|
121 + hostlist memcache_servers = <; host1:port1 ; host2:port2 ; host3 |
|
122 + |
|
123 + [...] |
|
124 + |
|
125 + acl_check_rcpt: |
|
126 + |
|
127 + # greylisting with memcache |
|
128 + # do we already have a timestamp for this sender/receiver combo? try to store |
|
129 + # one if we dont and defer if this succeeds, deny otherwise; accept the |
|
130 + # message if we find a timestamp which is older than 5 minutes, deny |
|
131 + # otherwise (if the timestamp is younger that is) |
|
132 + accept |
|
133 + logwrite = memcache \ |
|
134 + get \ |
|
135 + exim-lenny:${sender_address}*${local_part}@${domain} |
|
136 + condition = ${memcache \ |
|
137 + {get} \ |
|
138 + {${sender_address}*${local_part}@${domain}} \ |
|
139 + {5s} |
|
140 + {+memcache_servers}} |
|
141 + endpass |
|
142 + condition = ${if or { \ |
|
143 + {eq {$value}{}} \ |
|
144 + { > {$tod_epoch}{${eval: $value + 300}}} \ |
|
145 + }} |
|
146 + |
|
147 + defer |
|
148 + logwrite = memcache \ |
|
149 + set \ |
|
150 + exim-lenny:${sender_address}*${local_part}@${domain} \ |
|
151 + $tod_epoch |
|
152 + condition = ${memcache \ |
|
153 + {set} \ |
|
154 + {${sender_address}*${local_part}@${domain}} \ |
|
155 + {$tod_epoch} \ |
|
156 + {10s} \ |
|
157 + {3w} \ |
|
158 + {<; host1:port1 ; host2:port2 ; host3}} |
|
159 + deny |
|
160 + |
|
161 +or shorter: |
|
162 + |
|
163 + [...] |
|
164 + |
|
165 + hostlist memcache_servers = host1 |
|
166 + |
|
167 + [...] |
|
168 + |
|
169 + acl_check_rcpt: |
|
170 + |
|
171 + # greylisting with memcache |
|
172 + # do we already have a timestamp for this sender/receiver combo? try to store |
|
173 + # one if we dont and defer if this succeeds, deny otherwise; accept the |
|
174 + # message if we find a timestamp which is older than 5 minutes, deny |
|
175 + # otherwise (if the timestamp is younger that is) |
|
176 + accept |
|
177 + logwrite = memcache \ |
|
178 + get \ |
|
179 + exim-lenny:${sender_address}*${local_part}@${domain} |
|
180 + condition = ${memcache \ |
|
181 + {get} \ |
|
182 + {${sender_address}*${local_part}@${domain}}} |
|
183 + endpass |
|
184 + condition = ${if or { \ |
|
185 + {eq {$value}{}} \ |
|
186 + { > {$tod_epoch}{${eval: $value + 300}}} \ |
|
187 + }} |
|
188 + |
|
189 + defer |
|
190 + logwrite = memcache \ |
|
191 + set \ |
|
192 + exim-lenny:${sender_address}*${local_part}@${domain} \ |
|
193 + $tod_epoch |
|
194 + condition = ${memcache \ |
|
195 + {set} \ |
|
196 + {${sender_address}*${local_part}@${domain}} \ |
|
197 + {$tod_epoch}} |
|
198 + deny |
|
199 + |
|
200 +* todo: |
|
201 + Currently we just use the readsocket expansion internally. This limits us to tcp connections. Key validation and encoding will not work if the key contains null characters. Maybe we should just interface to one of the available memcache libraries. Some testcases would be useful. |
|
202 diff -r 938614e333a1 scripts/MakeLinks |
|
203 --- a/scripts/MakeLinks Fri Aug 14 08:39:52 2009 +0200 |
|
204 +++ b/scripts/MakeLinks Fri Aug 21 11:48:06 2009 +0200 |
|
205 @@ -200,6 +200,7 @@ |
|
206 ln -s ../src/globals.h globals.h |
|
207 ln -s ../src/local_scan.h local_scan.h |
|
208 ln -s ../src/macros.h macros.h |
|
209 +ln -s ../src/memcache.h memcache.h |
|
210 ln -s ../src/mytypes.h mytypes.h |
|
211 ln -s ../src/osfunctions.h osfunctions.h |
|
212 ln -s ../src/store.h store.h |
|
213 @@ -232,6 +233,7 @@ |
|
214 ln -s ../src/log.c log.c |
|
215 ln -s ../src/lss.c lss.c |
|
216 ln -s ../src/match.c match.c |
|
217 +ln -s ../src/memcache.c memcache.c |
|
218 ln -s ../src/moan.c moan.c |
|
219 ln -s ../src/parse.c parse.c |
|
220 ln -s ../src/perl.c perl.c |
|
221 diff -r 938614e333a1 src/EDITME |
|
222 --- a/src/EDITME Fri Aug 14 08:39:52 2009 +0200 |
|
223 +++ b/src/EDITME Fri Aug 21 11:48:06 2009 +0200 |
|
224 @@ -1133,4 +1133,9 @@ |
|
225 |
|
226 # ENABLE_DISABLE_FSYNC=yes |
|
227 |
|
228 +#------------------------------------------------------------------------------ |
|
229 +# support storage/retrieval of values to and from memcache servers via |
|
230 +# expansions |
|
231 +SUPPORT_MEMCACHE=yes |
|
232 + |
|
233 # End of EDITME for Exim 4. |
|
234 diff -r 938614e333a1 src/config.h.defaults |
|
235 --- a/src/config.h.defaults Fri Aug 14 08:39:52 2009 +0200 |
|
236 +++ b/src/config.h.defaults Fri Aug 21 11:48:06 2009 +0200 |
|
237 @@ -120,6 +120,7 @@ |
|
238 #define SUPPORT_MAILDIR |
|
239 #define SUPPORT_MAILSTORE |
|
240 #define SUPPORT_MBX |
|
241 +#define SUPPORT_MEMCACHE |
|
242 #define SUPPORT_MOVE_FROZEN_MESSAGES |
|
243 #define SUPPORT_PAM |
|
244 #define SUPPORT_TLS |
|
245 diff -r 938614e333a1 src/expand.c |
|
246 --- a/src/expand.c Fri Aug 14 08:39:52 2009 +0200 |
|
247 +++ b/src/expand.c Fri Aug 21 11:48:06 2009 +0200 |
|
248 @@ -25,6 +25,10 @@ |
|
249 |
|
250 #ifdef LOOKUP_LDAP |
|
251 #include "lookups/ldap.h" |
|
252 +#endif |
|
253 + |
|
254 +#ifdef SUPPORT_MEMCACHE |
|
255 +#include "memcache.h" |
|
256 #endif |
|
257 |
|
258 #ifdef SUPPORT_CRYPTEQ |
|
259 @@ -113,6 +117,7 @@ |
|
260 US"length", |
|
261 US"lookup", |
|
262 US"map", |
|
263 + US"memcache", |
|
264 US"nhash", |
|
265 US"perl", |
|
266 US"prvs", |
|
267 @@ -135,6 +140,7 @@ |
|
268 EITEM_LENGTH, |
|
269 EITEM_LOOKUP, |
|
270 EITEM_MAP, |
|
271 + EITEM_MEMCACHE, |
|
272 EITEM_NHASH, |
|
273 EITEM_PERL, |
|
274 EITEM_PRVS, |
|
275 @@ -4980,6 +4986,316 @@ |
|
276 } |
|
277 } |
|
278 #endif /* EXPAND_DLFUNC */ |
|
279 + |
|
280 + case EITEM_MEMCACHE: |
|
281 + #ifndef SUPPORT_MEMCACHE |
|
282 + expand_string_message = US"\"${memcache\" encountered, but this facility " |
|
283 + "is not included in this binary"; |
|
284 + goto EXPAND_FAILED; |
|
285 + |
|
286 + #else /* SUPPORT_MEMCACHE */ |
|
287 + /* we dont use flags right now - should we? */ |
|
288 + |
|
289 + { |
|
290 + int i_memcache_expiration = -1, i_memcache_timeout = -1, i_memcache_servers = -1; |
|
291 + int memcache_operation; |
|
292 + int memcache_min_args = 2, memcache_max_args = 6; |
|
293 + uschar *sub_arg[memcache_max_args]; |
|
294 + |
|
295 + uschar *memcache_key; |
|
296 + uschar *memcache_hostlist_name = MEMCACHE_HOSTLIST_NAME; |
|
297 + tree_node *memcache_hosts_node; |
|
298 + uschar *memcache_hosts_string = NULL, *memcache_op_string; |
|
299 + uschar *memcache_host; |
|
300 + int memcache_hostlist_separator = 0; |
|
301 + int i_memcache_server = 0; |
|
302 + int n_memcache_servers = 0; |
|
303 + |
|
304 + uschar *memcache_request; |
|
305 + int memcache_request_len; |
|
306 + |
|
307 + uschar *memcache_readsocket_result; |
|
308 + |
|
309 + if ((expand_forbid & RDO_MEMCACHE) != 0) |
|
310 + { |
|
311 + expand_string_message = US"memcache storage/retrieval are not permitted"; |
|
312 + goto EXPAND_FAILED; |
|
313 + } |
|
314 + |
|
315 + /* read up to 6 args */ |
|
316 + switch(read_subs(sub_arg, memcache_max_args, memcache_min_args, &s, skipping, TRUE, US"memcache")) |
|
317 + { |
|
318 + case 1: goto EXPAND_FAILED_CURLY; |
|
319 + case 2: |
|
320 + case 3: goto EXPAND_FAILED; |
|
321 + } |
|
322 + |
|
323 + /* read_subs sets only the first unset arg to NULL; we set *all* |
|
324 + * remaining unset args to NULL to avoid segfaults when trying to |
|
325 + * access them later */ |
|
326 + int i = memcache_min_args; |
|
327 + while (i < memcache_max_args - 1) |
|
328 + { |
|
329 + if (sub_arg[i++] == NULL) sub_arg[i] = NULL; |
|
330 + } |
|
331 + |
|
332 + /* validate operation and set argument indices */ |
|
333 + memcache_op_string = string_sprintf(US"%S", sub_arg[0]); |
|
334 + if (Ustrcmp(memcache_op_string, US"set") == 0) |
|
335 + { |
|
336 + i_memcache_expiration = 3; i_memcache_timeout = 4; i_memcache_servers = 5; memcache_operation = MEMCACHE_OP_SET; |
|
337 + } |
|
338 + else if (Ustrcmp(memcache_op_string, US"get") == 0) |
|
339 + { |
|
340 + i_memcache_timeout = 2; i_memcache_servers = 3; memcache_operation = MEMCACHE_OP_GET; |
|
341 + } |
|
342 + else |
|
343 + { |
|
344 + expand_string_message = string_sprintf(US"invalid memcache operation '%s'", |
|
345 + sub_arg[0]); |
|
346 + goto EXPAND_FAILED; |
|
347 + } |
|
348 + |
|
349 + /* encode key */ |
|
350 + memcache_key = memcache_encode_key(sub_arg[1]); |
|
351 + if (memcache_key == NULL) |
|
352 + { |
|
353 + if (memcache_errno == E_MEMCACHE_KEY_TOO_LONG) |
|
354 + { |
|
355 + expand_string_message = string_sprintf(US"memcache key '%s' is longer than 250 characters (or will be if invalid chars are urlencoded)", sub_arg[1]); |
|
356 + } |
|
357 + else |
|
358 + { |
|
359 + expand_string_message = string_sprintf(US"memcache: unknown error while urlencoding key"); |
|
360 + } |
|
361 + goto EXPAND_FAILED; |
|
362 + } |
|
363 + |
|
364 + DEBUG(D_memcache) |
|
365 + { |
|
366 + debug_printf("sub_arg[1]: '"); |
|
367 + int i = 0; |
|
368 + while (*(sub_arg[1] + i) != 0) |
|
369 + { |
|
370 + debug_printf("%c%c%c", |
|
371 + ' ', |
|
372 + hex_digits[*(sub_arg[1] + i) >> 4], |
|
373 + hex_digits[*(sub_arg[1] + i) & 0xf]); |
|
374 + i++; |
|
375 + } |
|
376 + debug_printf("'\nmemcache key: %s\n", memcache_key); |
|
377 + } |
|
378 + |
|
379 + /* expiration */ |
|
380 + if (i_memcache_expiration >=0 && sub_arg[i_memcache_expiration] != NULL) |
|
381 + { |
|
382 + memcache_expiration = readconf_readtime(sub_arg[i_memcache_expiration], 0, FALSE); |
|
383 + if (memcache_expiration < 0) |
|
384 + { |
|
385 + expand_string_message = string_sprintf(US"bad time value %s", |
|
386 + sub_arg[i_memcache_expiration]); |
|
387 + goto EXPAND_FAILED; |
|
388 + } |
|
389 + } |
|
390 + |
|
391 + DEBUG(D_memcache) |
|
392 + { |
|
393 + debug_printf("memcache expiration: %d\n", memcache_expiration); |
|
394 + } |
|
395 + |
|
396 + /* timeout */ |
|
397 + if (i_memcache_timeout >=0 && sub_arg[i_memcache_timeout] != NULL) |
|
398 + { |
|
399 + memcache_timeout = readconf_readtime(sub_arg[i_memcache_timeout], 0, FALSE); |
|
400 + if (memcache_timeout < 0) |
|
401 + { |
|
402 + expand_string_message = string_sprintf(US"bad time value %s", |
|
403 + sub_arg[i_memcache_timeout]); |
|
404 + goto EXPAND_FAILED; |
|
405 + } |
|
406 + } |
|
407 + |
|
408 + /* now that we have an expiration time we should be able to build the request string */ |
|
409 + if (memcache_operation == MEMCACHE_OP_SET) |
|
410 + { |
|
411 + uschar* data = sub_arg[2]; |
|
412 + int data_len = Ustrlen(data); |
|
413 + |
|
414 + /* writeop key flags expiration size-in-bytes [noreply] */ |
|
415 + uschar *cmd = string_sprintf(US"%s %s %d %d %d %s", |
|
416 + US"set", |
|
417 + memcache_key, |
|
418 + 0, |
|
419 + memcache_expiration, |
|
420 + data_len, |
|
421 + US""); |
|
422 + int cmd_len = Ustrlen(cmd); |
|
423 + |
|
424 + memcache_request = |
|
425 + memcache_escape_for_expansion(string_sprintf(US"%s\r\n%s\r\n", cmd, |
|
426 + data)); |
|
427 + memcache_request_len = Ustrlen(memcache_request); |
|
428 + } |
|
429 + else if (memcache_operation == MEMCACHE_OP_GET) |
|
430 + { |
|
431 + memcache_request = memcache_escape_for_expansion(string_sprintf( |
|
432 + US"%s %s\r\n", US"get", memcache_key)); |
|
433 + memcache_request_len = Ustrlen(memcache_request); |
|
434 + } |
|
435 + else |
|
436 + { |
|
437 + expand_string_message = string_sprintf(US"invalid memcache operation: '%s'", memcache_op_string); |
|
438 + goto EXPAND_FAILED; |
|
439 + } |
|
440 + |
|
441 + if (memcache_request == NULL) |
|
442 + { |
|
443 + expand_string_message = memcache_errno == E_MEMCACHE_REQUEST_TOO_LONG |
|
444 + ? US"memcache request too long" : US"memcache unknown error"; |
|
445 + goto EXPAND_FAILED; |
|
446 + } |
|
447 + |
|
448 + /* memcache servers - we expect either a named list or just a list */ |
|
449 + if (sub_arg[i_memcache_servers] != NULL) |
|
450 + { |
|
451 + if (sub_arg[i_memcache_servers][0] == '+') |
|
452 + { |
|
453 + memcache_hostlist_name = sub_arg[i_memcache_servers] + sizeof(sub_arg[i_memcache_servers][0]); |
|
454 + } |
|
455 + else |
|
456 + { |
|
457 + memcache_hosts_string = sub_arg[i_memcache_servers]; |
|
458 + } |
|
459 + } |
|
460 + |
|
461 + /* get the string of the named hostlist if we dont have one by now */ |
|
462 + if (memcache_hosts_string == NULL) |
|
463 + { |
|
464 + if (NULL == (memcache_hosts_node = tree_search(hostlist_anchor, memcache_hostlist_name))) |
|
465 + { |
|
466 + expand_string_message = string_sprintf(US"named list '%s' not found", |
|
467 + sub_arg[0]); |
|
468 + goto EXPAND_FAILED; |
|
469 + } |
|
470 + memcache_hosts_string = ((namedlist_block *) memcache_hosts_node->data.ptr)->string; |
|
471 + } |
|
472 + |
|
473 + /* talk to server(s) unless we are skipping */ |
|
474 + if (!skipping) |
|
475 + { |
|
476 + BOOL memcache_connections_failed = TRUE; |
|
477 + BOOL memcache_requests_failed = TRUE; |
|
478 + uschar *memcache_server_name, *memcache_port_name; |
|
479 + while (NULL != (memcache_server_name = |
|
480 + string_nextinlist(&memcache_hosts_string, |
|
481 + &memcache_hostlist_separator, NULL, 0)) && |
|
482 + memcache_requests_failed) |
|
483 + { |
|
484 + |
|
485 + memcache_port_name = Ustrrchr(memcache_server_name, ':'); |
|
486 + |
|
487 + /* Sort out the port */ |
|
488 + if (memcache_port_name == NULL) |
|
489 + { |
|
490 + memcache_port_name = MEMCACHE_DEFAULT_PORT; |
|
491 + } |
|
492 + else |
|
493 + { |
|
494 + *memcache_port_name++ = 0; /* Terminate server name */ |
|
495 + } |
|
496 + |
|
497 + uschar *memcache_server_readsocket = |
|
498 + string_sprintf("${readsocket{inet:%s:%s}{%s}{%ds}}", |
|
499 + memcache_server_name, |
|
500 + memcache_port_name, |
|
501 + memcache_request, |
|
502 + memcache_timeout); |
|
503 + |
|
504 + DEBUG(D_memcache) |
|
505 + { |
|
506 + debug_printf("readsocket expansion item: '%s'\n", memcache_server_readsocket); |
|
507 + } |
|
508 + |
|
509 + memcache_readsocket_result = expand_string(memcache_server_readsocket); |
|
510 + |
|
511 + if (memcache_readsocket_result != NULL) |
|
512 + { |
|
513 + |
|
514 + memcache_connections_failed = FALSE; |
|
515 + |
|
516 + if (memcache_operation == MEMCACHE_OP_GET |
|
517 + && 0 == Ustrncmp(memcache_readsocket_result, US"VALUE ", Ustrlen(US"VALUE "))) |
|
518 + { |
|
519 + |
|
520 + memcache_requests_failed = FALSE; |
|
521 + |
|
522 + /* we tried to use expand_gettokened but it segfaults and we |
|
523 + didnt find out why */ |
|
524 + uschar *memcache_value_size = memcache_readsocket_result; |
|
525 + int i; |
|
526 + for (i = 0; i < 3; i++) { memcache_value_size = Ustrchr(memcache_value_size, ' ') + 1; } |
|
527 + |
|
528 + lookup_value = strstr(memcache_readsocket_result, "\r\n") + Ustrlen("\r\n"); |
|
529 + lookup_value[ |
|
530 + Ustrtol(memcache_value_size, |
|
531 + NULL, |
|
532 + 0)] = 0; |
|
533 + |
|
534 + DEBUG(D_memcache) { debug_printf("lookup_value: '%s'\n", lookup_value); } |
|
535 + |
|
536 + } |
|
537 + else if (memcache_operation == MEMCACHE_OP_SET |
|
538 + && 0 == Ustrcmp(memcache_readsocket_result, US"STORED\r\n")) |
|
539 + { |
|
540 + memcache_requests_failed = FALSE; |
|
541 + } |
|
542 + else |
|
543 + { |
|
544 + /* something went wrong - try something else */ |
|
545 + DEBUG(D_memcache) |
|
546 + { |
|
547 + debug_printf("memcache operation '%s' failed: '%s'\n", |
|
548 + memcache_op_string, |
|
549 + memcache_readsocket_result); |
|
550 + } |
|
551 + } |
|
552 + |
|
553 + if (!memcache_requests_failed) |
|
554 + { |
|
555 + yield = string_cat(yield, |
|
556 + &size, |
|
557 + &ptr, |
|
558 + US"yes", |
|
559 + Ustrlen(US"yes")); |
|
560 + } |
|
561 + |
|
562 + } |
|
563 + |
|
564 + } |
|
565 + |
|
566 + if (memcache_connections_failed) |
|
567 + { |
|
568 + /* expansion failed for last server */ |
|
569 + expand_string_message = |
|
570 + string_sprintf(US"all memcache servers failed - last error was: '%s'", |
|
571 + string_copy(expand_string_message)); |
|
572 + goto EXPAND_FAILED; |
|
573 + } |
|
574 + |
|
575 + if (memcache_requests_failed) |
|
576 + { |
|
577 + /* none of the memcache operations returned the expected result */ |
|
578 + yield = string_cat(yield, |
|
579 + &size, |
|
580 + &ptr, |
|
581 + US"no", |
|
582 + Ustrlen(US"no")); |
|
583 + } |
|
584 + |
|
585 + } |
|
586 + continue; |
|
587 + } |
|
588 + #endif /* SUPPORT_MEMCACHE */ |
|
589 } |
|
590 |
|
591 /* Control reaches here if the name is not recognized as one of the more |
|
592 diff -r 938614e333a1 src/globals.c |
|
593 --- a/src/globals.c Fri Aug 14 08:39:52 2009 +0200 |
|
594 +++ b/src/globals.c Fri Aug 21 11:48:06 2009 +0200 |
|
595 @@ -448,6 +448,7 @@ |
|
596 { US"load", D_load }, |
|
597 { US"local_scan", D_local_scan }, |
|
598 { US"lookup", D_lookup }, |
|
599 + { US"memcache", D_memcache }, |
|
600 { US"memory", D_memory }, |
|
601 { US"pid", D_pid }, |
|
602 { US"process_info", D_process_info }, |
|
603 @@ -757,6 +758,10 @@ |
|
604 #endif |
|
605 int max_received_linelength= 0; |
|
606 int max_username_length = 0; |
|
607 +#ifdef SUPPORT_MEMCACHE |
|
608 +int memcache_expiration = 0; |
|
609 +int memcache_timeout = 3; |
|
610 +#endif |
|
611 int message_age = 0; |
|
612 uschar *message_body = NULL; |
|
613 uschar *message_body_end = NULL; |
|
614 diff -r 938614e333a1 src/globals.h |
|
615 --- a/src/globals.h Fri Aug 14 08:39:52 2009 +0200 |
|
616 +++ b/src/globals.h Fri Aug 21 11:48:06 2009 +0200 |
|
617 @@ -441,6 +441,10 @@ |
|
618 #endif |
|
619 extern int max_received_linelength;/* What it says */ |
|
620 extern int max_username_length; /* For systems with broken getpwnam() */ |
|
621 +#ifdef SUPPORT_MEMCACHE |
|
622 +extern int memcache_expiration; |
|
623 +extern int memcache_timeout; |
|
624 +#endif |
|
625 extern int message_age; /* In seconds */ |
|
626 extern uschar *message_body; /* Start of message body for filter */ |
|
627 extern uschar *message_body_end; /* End of message body for filter */ |
|
628 diff -r 938614e333a1 src/macros.h |
|
629 --- a/src/macros.h Fri Aug 14 08:39:52 2009 +0200 |
|
630 +++ b/src/macros.h Fri Aug 21 11:48:06 2009 +0200 |
|
631 @@ -313,19 +313,20 @@ |
|
632 #define D_load 0x00008000 |
|
633 #define D_lookup 0x00010000 |
|
634 #define D_memory 0x00020000 |
|
635 -#define D_pid 0x00040000 |
|
636 -#define D_process_info 0x00080000 |
|
637 -#define D_queue_run 0x00100000 |
|
638 -#define D_receive 0x00200000 |
|
639 -#define D_resolver 0x00400000 |
|
640 -#define D_retry 0x00800000 |
|
641 -#define D_rewrite 0x01000000 |
|
642 -#define D_route 0x02000000 |
|
643 -#define D_timestamp 0x04000000 |
|
644 -#define D_tls 0x08000000 |
|
645 -#define D_transport 0x10000000 |
|
646 -#define D_uid 0x20000000 |
|
647 -#define D_verify 0x40000000 |
|
648 +#define D_memcache 0x00040000 |
|
649 +#define D_pid 0x00080000 |
|
650 +#define D_process_info 0x00100000 |
|
651 +#define D_queue_run 0x00200000 |
|
652 +#define D_receive 0x00400000 |
|
653 +#define D_resolver 0x00800000 |
|
654 +#define D_retry 0x01000000 |
|
655 +#define D_rewrite 0x02000000 |
|
656 +#define D_route 0x04000000 |
|
657 +#define D_timestamp 0x08000000 |
|
658 +#define D_tls 0x10000000 |
|
659 +#define D_transport 0x20000000 |
|
660 +#define D_uid 0x40000000 |
|
661 +#define D_verify 0x80000000 |
|
662 |
|
663 /* The D_all value must always have all bits set, as it is recognized specially |
|
664 by the function that decodes debug and log selectors. This is to enable it to |
|
665 @@ -515,21 +516,21 @@ |
|
666 #define RDO_INCLUDE 0x00000100 /* Forbid :include: */ |
|
667 #define RDO_LOG 0x00000200 /* Forbid "log" */ |
|
668 #define RDO_LOOKUP 0x00000400 /* Forbid "lookup" in expansion in filter */ |
|
669 -#define RDO_PERL 0x00000800 /* Forbid "perl" in expansion in filter */ |
|
670 -#define RDO_READFILE 0x00001000 /* Forbid "readfile" in exp in filter */ |
|
671 -#define RDO_READSOCK 0x00002000 /* Forbid "readsocket" in exp in filter */ |
|
672 -#define RDO_RUN 0x00004000 /* Forbid "run" in expansion in filter */ |
|
673 -#define RDO_DLFUNC 0x00008000 /* Forbid "dlfunc" in expansion in filter */ |
|
674 -#define RDO_REALLOG 0x00010000 /* Really do log (not testing/verifying) */ |
|
675 -#define RDO_REWRITE 0x00020000 /* Rewrite generated addresses */ |
|
676 -#define RDO_EXIM_FILTER 0x00040000 /* Forbid Exim filters */ |
|
677 -#define RDO_SIEVE_FILTER 0x00080000 /* Forbid Sieve filters */ |
|
678 -#define RDO_PREPEND_HOME 0x00100000 /* Prepend $home to relative paths in Exim filter save commands */ |
|
679 - |
|
680 +#define RDO_MEMCACHE 0x00000800 /* Forbid "memcache" in expansion in filter */ |
|
681 +#define RDO_PERL 0x00001000 /* Forbid "perl" in expansion in filter */ |
|
682 +#define RDO_READFILE 0x00002000 /* Forbid "readfile" in exp in filter */ |
|
683 +#define RDO_READSOCK 0x00004000 /* Forbid "readsocket" in exp in filter */ |
|
684 +#define RDO_RUN 0x00008000 /* Forbid "run" in expansion in filter */ |
|
685 +#define RDO_DLFUNC 0x00010000 /* Forbid "dlfunc" in expansion in filter */ |
|
686 +#define RDO_REALLOG 0x00020000 /* Really do log (not testing/verifying) */ |
|
687 +#define RDO_REWRITE 0x00040000 /* Rewrite generated addresses */ |
|
688 +#define RDO_EXIM_FILTER 0x00080000 /* Forbid Exim filters */ |
|
689 +#define RDO_SIEVE_FILTER 0x00100000 /* Forbid Sieve filters */ |
|
690 +#define RDO_PREPEND_HOME 0x00200000 /* Prepend $home to relative paths in Exim filter save commands */ |
|
691 /* This is the set that apply to expansions in filters */ |
|
692 |
|
693 #define RDO_FILTER_EXPANSIONS \ |
|
694 - (RDO_EXISTS|RDO_LOOKUP|RDO_PERL|RDO_READFILE|RDO_READSOCK|RDO_RUN|RDO_DLFUNC) |
|
695 + (RDO_EXISTS|RDO_LOOKUP|RDO_MEMCACHE|RDO_PERL|RDO_READFILE|RDO_READSOCK|RDO_RUN|RDO_DLFUNC) |
|
696 |
|
697 /* As well as the RDO bits themselves, we need the bit numbers in order to |
|
698 access (most of) the individual bits as separate options. This could be |
|
699 @@ -537,8 +538,9 @@ |
|
700 |
|
701 enum { RDON_BLACKHOLE, RDON_DEFER, RDON_EACCES, RDON_ENOTDIR, RDON_EXISTS, |
|
702 RDON_FAIL, RDON_FILTER, RDON_FREEZE, RDON_INCLUDE, RDON_LOG, RDON_LOOKUP, |
|
703 - RDON_PERL, RDON_READFILE, RDON_READSOCK, RDON_RUN, RDON_DLFUNC, RDON_REALLOG, |
|
704 - RDON_REWRITE, RDON_EXIM_FILTER, RDON_SIEVE_FILTER, RDON_PREPEND_HOME }; |
|
705 + RDON_MEMCACHE, RDON_PERL, RDON_READFILE, RDON_READSOCK, RDON_RUN, |
|
706 + RDON_DLFUNC, RDON_REALLOG, RDON_REWRITE, RDON_EXIM_FILTER, RDON_SIEVE_FILTER, |
|
707 + RDON_PREPEND_HOME }; |
|
708 |
|
709 /* Results of filter or forward file processing. Some are only from a filter; |
|
710 some are only from a forward file. */ |
|
711 diff -r 938614e333a1 src/memcache.c |
|
712 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 |
|
713 +++ b/src/memcache.c Fri Aug 21 11:48:06 2009 +0200 |
|
714 @@ -0,0 +1,101 @@ |
|
715 + |
|
716 +#include "exim.h" |
|
717 +#include "memcache.h" |
|
718 + |
|
719 +#ifdef SUPPORT_MEMCACHE |
|
720 + |
|
721 +int memcache_errno = E_MEMCACHE_OK; |
|
722 + |
|
723 +/* escape special chars for expansion (required as long as we are using the |
|
724 + * 'readsocket' expansion instead of connecting and talking to the memcache |
|
725 + * server ourselves) */ |
|
726 + |
|
727 +uschar * |
|
728 +memcache_escape_for_expansion(uschar *s) |
|
729 +{ |
|
730 + |
|
731 +int i = 0; |
|
732 +int slen = 2 * Ustrlen(s); |
|
733 + |
|
734 +/* we set this limit rather low - this function is supposed to go away anyway |
|
735 + * */ |
|
736 +if (slen > 1022) |
|
737 + { |
|
738 + memcache_errno = E_MEMCACHE_REQUEST_TOO_LONG; |
|
739 + return NULL; |
|
740 + } |
|
741 + |
|
742 +uschar *e = store_get(slen + 2); |
|
743 + |
|
744 +while (TRUE) |
|
745 + { |
|
746 + if (*s == '\\' || *s == '$' || *s == '}' || *s == '\n' || *s == '\r' || *s == '\t') |
|
747 + { |
|
748 + e[i++] = '\\'; |
|
749 + if (*s == '\n') e[i++] = 'n'; |
|
750 + else if (*s == '\r') e[i++] = 'r'; |
|
751 + else if (*s == '\t') e[i++] = 't'; |
|
752 + else e[i++] = *s; |
|
753 + } |
|
754 + else |
|
755 + { |
|
756 + e[i++] = *s; |
|
757 + } |
|
758 + |
|
759 + if (*s == 0) |
|
760 + { |
|
761 + return e; |
|
762 + } |
|
763 + |
|
764 + s++; |
|
765 + |
|
766 + } |
|
767 + |
|
768 + |
|
769 +} |
|
770 + |
|
771 +/* urlencode the key because it may not contain any whitespace or control |
|
772 + * characters; returns encoded string or NULL if the resulting key will be |
|
773 + * longer than 250 bytes; memcache_errno will be set in that case */ |
|
774 + |
|
775 +uschar * |
|
776 +memcache_encode_key(uschar *key) |
|
777 +{ |
|
778 + |
|
779 +int i = 0; |
|
780 +int qlen = 3 * Ustrlen(key); |
|
781 + |
|
782 +uschar *q = store_get((qlen < MEMCACHE_MAX_KEY_SIZE ? qlen : |
|
783 + MEMCACHE_MAX_KEY_SIZE) + 3); |
|
784 + |
|
785 +while (TRUE) |
|
786 + { |
|
787 + if ((iscntrl(*key) && *key != 0) || isspace(*key) || *key == '%') |
|
788 + { |
|
789 + q[i++] = '%'; |
|
790 + q[i++] = hex_digits[*key >> 4]; |
|
791 + q[i++] = hex_digits[*key & 0xf]; |
|
792 + } |
|
793 + else |
|
794 + { |
|
795 + q[i++] = *key; |
|
796 + } |
|
797 + |
|
798 + if (*key == 0) |
|
799 + { |
|
800 + return q; |
|
801 + } |
|
802 + |
|
803 + if (i > MEMCACHE_MAX_KEY_SIZE) |
|
804 + { |
|
805 + memcache_errno = E_MEMCACHE_KEY_TOO_LONG; |
|
806 + return NULL; |
|
807 + } |
|
808 + |
|
809 + key++; |
|
810 + |
|
811 + } |
|
812 + |
|
813 +} |
|
814 + |
|
815 +#endif /* SUPPORT_MEMCACHE */ |
|
816 diff -r 938614e333a1 src/memcache.h |
|
817 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 |
|
818 +++ b/src/memcache.h Fri Aug 21 11:48:06 2009 +0200 |
|
819 @@ -0,0 +1,15 @@ |
|
820 +#ifndef MEMCACHE_H |
|
821 +#define MEMCACHE_H |
|
822 + |
|
823 +#define MEMCACHE_HOSTLIST_NAME US"memcache_servers" |
|
824 +#define MEMCACHE_DEFAULT_PORT US"11211" |
|
825 +#define MEMCACHE_MAX_KEY_SIZE 250 |
|
826 + |
|
827 +enum { MEMCACHE_OP_SET, MEMCACHE_OP_GET }; |
|
828 +enum { E_MEMCACHE_OK, E_MEMCACHE_KEY_TOO_LONG, E_MEMCACHE_REQUEST_TOO_LONG }; |
|
829 + |
|
830 +extern uschar *memcache_encode_key(uschar *); |
|
831 +extern uschar *memcache_escape_for_expansion(uschar *); |
|
832 +extern int memcache_errno; |
|
833 + |
|
834 +#endif |
|
835 diff -r 938614e333a1 src/readconf.c |
|
836 --- a/src/readconf.c Fri Aug 14 08:39:52 2009 +0200 |
|
837 +++ b/src/readconf.c Fri Aug 21 11:48:06 2009 +0200 |
|
838 @@ -265,6 +265,10 @@ |
|
839 { "log_timezone", opt_bool, &log_timezone }, |
|
840 { "lookup_open_max", opt_int, &lookup_open_max }, |
|
841 { "max_username_length", opt_int, &max_username_length }, |
|
842 +#ifdef SUPPORT_MEMCACHE |
|
843 + { "memcache_expiration", opt_time, &memcache_expiration }, |
|
844 + { "memcache_timeout", opt_time, &memcache_timeout }, |
|
845 +#endif |
|
846 { "message_body_newlines", opt_bool, &message_body_newlines }, |
|
847 { "message_body_visible", opt_mkint, &message_body_visible }, |
|
848 { "message_id_header_domain", opt_stringptr, &message_id_domain }, |
|