exim4/4.69/memcache-support-upstream.patch
changeset 35 00eb34bfe348
parent 34 87b508932fa3
equal deleted inserted replaced
34:87b508932fa3 35:00eb34bfe348
     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 },