"Fossies" - the Fresh Open Source Software Archive

Member "mod_sendmail-1.1.0/mod_sendmail.c" (17 Apr 2014, 25820 Bytes) of package /linux/www/apache_httpd_modules/mod_sendmail-1.1.0.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "mod_sendmail.c" see the Fossies "Dox" file reference documentation.

    1 /* Licensed to the Apache Software Foundation (ASF) under one or more
    2  * contributor license agreements.  See the NOTICE file distributed with
    3  * this work for additional information regarding copyright ownership.
    4  * The ASF licenses this file to You under the Apache License, Version 2.0
    5  * (the "License"); you may not use this file except in compliance with
    6  * the License.  You may obtain a copy of the License at
    7  *
    8  *     http://www.apache.org/licenses/LICENSE-2.0
    9  *
   10  * Unless required by applicable law or agreed to in writing, software
   11  * distributed under the License is distributed on an "AS IS" BASIS,
   12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   13  * See the License for the specific language governing permissions and
   14  * limitations under the License.
   15  */
   16 
   17 /*
   18  * Provides a restful interface to gate email messages through to sendmail.
   19  *
   20  * A return mechanism is provided to report whether messages have been
   21  * delivered, using VERP and DSN.
   22  *
   23  *
   24  */
   25 #include <apr_lib.h>
   26 #include <apr_sha1.h>
   27 #include <apr_strings.h>
   28 #include <apr_hash.h>
   29 #include <apr_uuid.h>
   30 
   31 #include "httpd.h"
   32 #include "http_config.h"
   33 #include "http_core.h"
   34 #include "http_log.h"
   35 #include "http_protocol.h"
   36 #include "http_request.h"
   37 #include "util_script.h"
   38 
   39 #if AP_MODULE_MAGIC_AT_LEAST(20071108,10)
   40 #include "ap_expr.h"
   41 #endif
   42 
   43 #define DEFAULT_HASH_TIMEOUT 86400
   44 #define DEFAULT_SALT_LENGTH 4
   45 #define DEFAULT_PROGNAME "/usr/lib/sendmail"
   46 
   47 typedef struct
   48 {
   49     const char *progname;
   50     int progname_set;
   51     apr_array_header_t *args;
   52     const char *location;
   53     int location_set;
   54 #if AP_MODULE_MAGIC_AT_LEAST(20071108,10)
   55     ap_expr_info_t *dsn_location;
   56 #else
   57     const char *dsn_location;
   58 #endif
   59     int dsn_location_set;
   60     const char *sendermail;
   61     int sendermail_set;
   62     const char *sendername;
   63     int sendername_set;
   64 } sendmail_config_rec;
   65 
   66 typedef struct
   67 {
   68     request_rec *r;
   69     apr_file_t *file;
   70     int found;
   71 } sendmail_array_rec;
   72 
   73 static void *create_sendmail_dir_config(apr_pool_t *p, char *d)
   74 {
   75     sendmail_config_rec *conf = apr_pcalloc(p, sizeof(sendmail_config_rec));
   76 
   77     conf->progname = DEFAULT_PROGNAME;
   78     conf->args = apr_array_make(p, 8, sizeof(const char * const *));
   79     conf->location = NULL;
   80 
   81     return conf;
   82 }
   83 
   84 static void *merge_sendmail_dir_config(apr_pool_t *p, void *basev, void *addv)
   85 {
   86     sendmail_config_rec *new = (sendmail_config_rec *) apr_pcalloc(p,
   87             sizeof(sendmail_config_rec));
   88     sendmail_config_rec *add = (sendmail_config_rec *) addv;
   89     sendmail_config_rec *base = (sendmail_config_rec *) basev;
   90 
   91     new->progname = (add->progname_set == 0) ? base->progname : add->progname;
   92     new->progname_set = add->progname_set || base->progname_set;
   93     new->args = apr_array_append(p, add->args, base->args);
   94     new->location = (add->location_set == 0) ? base->location : add->location;
   95     new->location_set = add->location_set || base->location_set;
   96     new->dsn_location = (add->dsn_location_set == 0) ? base->dsn_location
   97             : add->dsn_location;
   98     new->dsn_location_set = add->dsn_location_set || base->dsn_location_set;
   99     new->sendermail = (add->sendermail_set == 0) ? base->sendermail
  100             : add->sendermail;
  101     new->sendermail_set = add->sendermail_set || base->sendermail_set;
  102     new->sendername = (add->sendername_set == 0) ? base->sendername
  103             : add->sendername;
  104     new->sendername_set = add->sendername_set || base->sendername_set;
  105 
  106     return new;
  107 }
  108 
  109 static const char *set_progname(cmd_parms *cmd, void *dconf, const char *arg)
  110 {
  111     sendmail_config_rec *conf = dconf;
  112 
  113     conf->progname = arg;
  114     conf->progname_set = 1;
  115 
  116     return NULL;
  117 }
  118 
  119 static const char *set_args(cmd_parms *cmd, void *dconf, const char *arg)
  120 {
  121     sendmail_config_rec *conf = dconf;
  122 #if AP_MODULE_MAGIC_AT_LEAST(20071108,10)
  123     ap_expr_info_t **array = apr_array_push(conf->args);
  124     const char *err;
  125 
  126     *array = ap_expr_parse_cmd(cmd, arg, AP_EXPR_FLAG_STRING_RESULT,
  127                                &err, NULL);
  128     if (err) {
  129         return apr_psprintf(cmd->pool,
  130                             "Could not parse SendmailArgument expression '%s': %s",
  131                             arg, err);
  132     }
  133 
  134 #else
  135     const char **array = apr_array_push(conf->args);
  136 
  137     *array = arg;
  138 #endif
  139 
  140     return NULL;
  141 }
  142 
  143 static const char *set_location(cmd_parms *cmd, void *dconf, const char *arg)
  144 {
  145     sendmail_config_rec *conf = dconf;
  146     conf->location = arg;
  147     conf->location_set = 1;
  148 
  149     return NULL;
  150 }
  151 
  152 static const char *set_dsn_location(cmd_parms *cmd, void *dconf,
  153         const char *arg)
  154 {
  155     sendmail_config_rec *conf = dconf;
  156 #if AP_MODULE_MAGIC_AT_LEAST(20071108,10)
  157     const char *err;
  158 
  159     conf->dsn_location = ap_expr_parse_cmd(cmd, arg, AP_EXPR_FLAG_STRING_RESULT,
  160                                         &err, NULL);
  161     if (err) {
  162         return apr_psprintf(cmd->pool,
  163                             "Could not parse SendmailDSNLocation expression '%s': %s",
  164                             arg, err);
  165     }
  166 
  167 #else
  168     conf->dsn_location = arg;
  169 #endif
  170     conf->dsn_location_set = 1;
  171 
  172     return NULL;
  173 }
  174 
  175 static const char *set_sender_mail(cmd_parms *cmd, void *dconf, const char *arg)
  176 {
  177     sendmail_config_rec *conf = dconf;
  178 
  179     conf->sendermail = arg;
  180     conf->sendermail_set = 1;
  181 
  182     return NULL;
  183 }
  184 
  185 static const char *set_sender_name(cmd_parms *cmd, void *dconf, const char *arg)
  186 {
  187     sendmail_config_rec *conf = dconf;
  188 
  189     conf->sendername = arg;
  190     conf->sendername_set = 1;
  191 
  192     return NULL;
  193 }
  194 
  195 static const command_rec sendmail_cmds[] =
  196 {
  197 AP_INIT_TAKE1("SendmailName",
  198         set_progname, NULL, RSRC_CONF | ACCESS_CONF,
  199         "Set to the path and name of the sendmail binary."),
  200 AP_INIT_ITERATE(
  201         "SendmailArguments", set_args, NULL, RSRC_CONF | ACCESS_CONF,
  202         "Set to the arguments to pass to the sendmail binary."),
  203 AP_INIT_TAKE1("SendmailLocation",
  204         set_location, NULL, RSRC_CONF | ACCESS_CONF,
  205         "Set to the location of the sendmail service."),
  206 AP_INIT_TAKE1("SendmailDSNLocation",
  207         set_dsn_location, NULL, RSRC_CONF | ACCESS_CONF,
  208         "Set to the location of the delivery status notification service."),
  209 AP_INIT_TAKE1("SendmailSenderMail",
  210         set_sender_mail, NULL, RSRC_CONF | ACCESS_CONF,
  211         "Set to the name of the variable for the from address."),
  212 AP_INIT_TAKE1("SendmailSenderName",
  213         set_sender_name, NULL, RSRC_CONF | ACCESS_CONF,
  214         "Set to the name of the variable for the from name."),
  215 { NULL } };
  216 
  217 module AP_MODULE_DECLARE_DATA sendmail_module;
  218 
  219 static void log_message(request_rec *r, apr_status_t status,
  220         const char *message)
  221 {
  222     apr_table_setn(r->notes, "error-notes", apr_pstrcat(r->pool,
  223             "The sendmail gateway could not accept the mail: ", ap_escape_html(
  224                     r->pool, message), NULL));
  225 
  226     /* Allow "error-notes" string to be printed by ap_send_error_response() */
  227     apr_table_setn(r->notes, "verbose-error-to", "*");
  228 
  229     ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, "%s", message);
  230 }
  231 
  232 static int contains_do(void *rec, const char *key, const char *value)
  233 {
  234     sendmail_array_rec *arr = (sendmail_array_rec *) rec;
  235     arr->found = 1;
  236     return 1;
  237 }
  238 
  239 static int header_do(void *rec, const char *key, const char *value)
  240 {
  241     sendmail_array_rec *arr = (sendmail_array_rec *) rec;
  242     apr_size_t len;
  243 
  244     len = apr_file_printf(arr->file, "%s: %s" CRLF, key, value);
  245 
  246     if (len <= 0) {
  247         ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_SUCCESS, arr->r,
  248                 "error writing header to sendmail");
  249         return 0;
  250     }
  251 
  252     return 1;
  253 }
  254 
  255 static int pipe_to_sendmail(request_rec *r, sendmail_config_rec *conf,
  256         const char *message_id)
  257 {
  258 
  259     const char **buf;
  260     const char * const *args;
  261     const char * const *env;
  262     apr_proc_t *procnew;
  263     apr_status_t status;
  264     apr_procattr_t *procattr;
  265     apr_bucket_brigade *bb;
  266     sendmail_array_rec arr;
  267     int seen_eos, child_stopped_reading = 0, rv, i;
  268     const char *data, *from, *sender;
  269     apr_size_t len;
  270     int exitcode;
  271     apr_exit_why_e exitwhy;
  272     static const char
  273             *strip_headers[] =
  274             { "Cache-Control", "Connection", "Pragma", "Trailer",
  275                     "Transfer-Encoding", "Upgrade", "Warning", "Accept",
  276                     "Accept-Charset", "Accept-Encoding", "Accept-Language",
  277                     "Authorization", "Expect", "Host", "If-Match",
  278                     "If-Modified-Since", "If-None-Match", "If-Range",
  279                     "If-Unmodified-Since", "Max-Forwards",
  280                     "Proxy-Authorization", "Range", "Referer", "TE",
  281                     "User-Agent", NULL };
  282     apr_table_t *headers = apr_table_copy(r->pool, r->headers_in);
  283     char date_str[APR_RFC822_DATE_LEN];
  284 
  285     /* remove hop by hop headers */
  286     for (i = 0; strip_headers[i]; i++) {
  287         apr_table_unset(headers, strip_headers[i]);
  288     }
  289 
  290     if (!conf->progname) {
  291 
  292         /* sendmail binary not specified, bail out */
  293         log_message(r, APR_SUCCESS, "sendmail binary name not specified");
  294 
  295         return HTTP_INTERNAL_SERVER_ERROR;
  296 
  297     }
  298 
  299     if (((status = apr_procattr_create(&procattr, r->pool)) != APR_SUCCESS)
  300             || ((status = apr_procattr_io_set(procattr, APR_CHILD_BLOCK,
  301                     APR_CHILD_BLOCK, APR_CHILD_BLOCK)) != APR_SUCCESS)
  302             || ((status = apr_procattr_dir_set(procattr, ap_make_dirstr_parent(
  303                     r->pool, conf->progname))) != APR_SUCCESS) || ((status
  304             = apr_procattr_cmdtype_set(procattr, APR_PROGRAM_ENV))
  305             != APR_SUCCESS) ||
  306 
  307     ((status = apr_procattr_detach_set(procattr, 0)) != APR_SUCCESS)
  308             || ((status = apr_procattr_addrspace_set(procattr, 0))
  309                     != APR_SUCCESS)) {
  310 
  311         /* Something bad happened, tell the world. */
  312         log_message(r, status, apr_psprintf(r->pool,
  313                 "couldn't set child process attributes: %s", conf->progname));
  314 
  315         return HTTP_INTERNAL_SERVER_ERROR;
  316 
  317     }
  318 
  319     env = (const char * const *) ap_create_environment(r->pool,
  320             r->subprocess_env);
  321 
  322     procnew = apr_pcalloc(r->pool, sizeof(*procnew));
  323 
  324     buf = apr_pcalloc(r->pool, sizeof(char *) * (conf->args->nelts + 2));
  325     args = memcpy(buf, &conf->progname, sizeof(char *));
  326 #if AP_MODULE_MAGIC_AT_LEAST(20071108,10)
  327     for (i = 0; i < conf->args->nelts; i++) {
  328         const char *err;
  329         *(buf + i + 1) = ap_expr_str_exec(r,
  330                             conf->dsn_location, &err);
  331         if (err) {
  332             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02339)
  333                           "Can't evaluate SendmailArgument expression: %s", err);
  334             return HTTP_INTERNAL_SERVER_ERROR;
  335         }
  336     }
  337 #else
  338     memcpy(buf + 1, conf->args->elts, sizeof(char *)
  339             * conf->args->nelts);
  340 #endif
  341 
  342     status = apr_proc_create(procnew, conf->progname, args, env, procattr,
  343             r->pool);
  344     if (status != APR_SUCCESS) {
  345 
  346         log_message(r, status, apr_psprintf(r->pool, "Could not crank up '%s'",
  347                 conf->progname));
  348 
  349         return HTTP_INTERNAL_SERVER_ERROR;
  350 
  351     }
  352 
  353     apr_pool_note_subprocess(r->pool, procnew, APR_KILL_AFTER_TIMEOUT);
  354 
  355     if (!procnew->out) {
  356 
  357         log_message(r, APR_EBADF,
  358                 "Timeout could not be set reading request entity data");
  359 
  360         return HTTP_INTERNAL_SERVER_ERROR;
  361 
  362     }
  363     apr_file_pipe_timeout_set(procnew->out, r->server->timeout);
  364 
  365     if (!procnew->in) {
  366 
  367         log_message(r, APR_EBADF,
  368                 "Timeout could not be set reading request entity data");
  369 
  370         return HTTP_INTERNAL_SERVER_ERROR;
  371 
  372     }
  373     apr_file_pipe_timeout_set(procnew->in, r->server->timeout);
  374 
  375     if (!procnew->err) {
  376 
  377         log_message(r, APR_EBADF,
  378                 "Timeout could not be set reading request entity data");
  379 
  380         return HTTP_INTERNAL_SERVER_ERROR;
  381 
  382     }
  383     apr_file_pipe_timeout_set(procnew->err, r->server->timeout);
  384 
  385     arr.r = r;
  386     arr.file = procnew->in;
  387     arr.found = 0;
  388 
  389     /* sanity check - no to or cc, bail out */
  390     rv = apr_table_do(contains_do, &arr, headers, "To", "CC", NULL);
  391     if (!arr.found) {
  392 
  393         log_message(r, APR_SUCCESS,
  394                 "No 'To' or 'CC' was present in the request");
  395 
  396         return HTTP_BAD_REQUEST;
  397     }
  398 
  399     sender = apr_table_get(headers, "Sender");
  400     if (conf->sendermail) {
  401         const char *mail = apr_table_get(r->subprocess_env, conf->sendermail);
  402         const char *name = conf->sendername ? apr_table_get(r->subprocess_env,
  403                 conf->sendername) : NULL;
  404 
  405         if (!mail) {
  406 
  407             log_message(
  408                     r,
  409                     APR_SUCCESS,
  410                     apr_psprintf(
  411                             r->pool,
  412                             "No '%s' was present in the request subprocess environment",
  413                             conf->sendermail));
  414 
  415             return HTTP_BAD_REQUEST;
  416         }
  417 
  418         if (name && strchr(name, ',')) {
  419             name = apr_psprintf(r->pool, "\"%s\"", name);
  420         }
  421 
  422         if (name) {
  423             sender = apr_psprintf(r->pool, "%s <%s>", name, mail);
  424         }
  425         else {
  426             sender = mail;
  427         }
  428         apr_table_setn(headers, "Sender", sender);
  429 
  430     }
  431 
  432     /* when sender and from are the same, collapse to only have from */
  433     from = apr_table_get(headers, "From");
  434     if (sender && from) {
  435         if (!strcmp(sender, from)) {
  436             apr_table_unset(headers, "Sender");
  437         }
  438     }
  439 
  440     /* sanity check - did we have a from? */
  441     if (!from) {
  442 
  443         log_message(r, APR_SUCCESS, "No 'From' was present in the request");
  444 
  445         return HTTP_BAD_REQUEST;
  446     }
  447 
  448     /* add a received header */
  449     ap_get_remote_host(r->connection, r->per_dir_config, REMOTE_DOUBLE_REV,
  450             NULL);
  451     apr_rfc822_date(date_str, apr_time_now());
  452 #if AP_MODULE_MAGIC_AT_LEAST(20111125,0)
  453     header_do(&arr, "Received", apr_pstrcat(r->pool, "from [",
  454             r->useragent_ip, "] (",
  455             strcmp(r->useragent_ip, r->connection->client_ip) ||
  456                     !r->connection->remote_host ? r->connection->remote_host
  457                     : r->useragent_ip, " [", r->useragent_ip,
  458             "])\n\t", r->user ? "(Authenticated sender: " : "",
  459             r->user ? r->user : "", r->user ? ")\n\t" : "", "by ",
  460             r->server->server_hostname ? r->server->server_hostname
  461                     : r->connection->local_ip, " (mod_sendmail)\n\tfor ", from,
  462             "; ", date_str, NULL));
  463 #else
  464     header_do(&arr, "Received", apr_pstrcat(r->pool, "from [",
  465             r->connection->remote_ip, "] (",
  466             r->connection->remote_host ? r->connection->remote_host
  467                     : r->connection->remote_ip, " [", r->connection->remote_ip,
  468             "])\n\t", r->user ? "(Authenticated sender: " : "",
  469             r->user ? r->user : "", r->user ? ")\n\t" : "", "by ",
  470             r->server->server_hostname ? r->server->server_hostname
  471                     : r->connection->local_ip, " (mod_sendmail)\n\tfor ", from,
  472             "; ", date_str, NULL));
  473 #endif
  474 
  475     /* message-id from the URL */
  476     if (message_id) {
  477         apr_table_set(headers, "Message-ID", message_id);
  478     }
  479 
  480     /* message-id missing? add our own */
  481     if (!message_id && !(message_id = apr_table_get(headers, "Message-ID"))) {
  482         char buffer[APR_UUID_FORMATTED_LENGTH + 1];
  483         apr_uuid_t uuid;
  484         apr_uuid_get(&uuid);
  485         apr_uuid_format(buffer, &uuid);
  486 
  487         message_id = apr_psprintf(r->pool, "<%s@%s>", buffer,
  488                 r->server->server_hostname);
  489 
  490         len = apr_file_printf(procnew->in, "Message-ID: %s" CRLF, message_id);
  491 
  492         if (len <= 0) {
  493             /* silly script stopped reading, soak up remaining message */
  494             child_stopped_reading = 1;
  495 
  496             log_message(r, status, "Sendmail stopped reading message, aborting");
  497 
  498         }
  499 
  500     }
  501 
  502     /* pass headers to sendmail */
  503     rv = apr_table_do(header_do, &arr, headers, NULL);
  504     if (!rv) {
  505         /* we expect the error to be logged already */
  506         return HTTP_INTERNAL_SERVER_ERROR;
  507     }
  508 
  509     len = apr_file_printf(procnew->in, CRLF);
  510 
  511     if (len <= 0) {
  512         /* silly script stopped reading, soak up remaining message */
  513         child_stopped_reading = 1;
  514 
  515         log_message(r, status, "Sendmail stopped reading message, aborting");
  516 
  517     }
  518 
  519     bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
  520     seen_eos = 0;
  521     do {
  522         apr_bucket *bucket;
  523 
  524         status = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES,
  525                 APR_BLOCK_READ, HUGE_STRING_LEN);
  526 
  527         if (status != APR_SUCCESS) {
  528             if (APR_STATUS_IS_TIMEUP(status)) {
  529                 log_message(r, status,
  530                         "Timeout during reading request entity data");
  531                 return HTTP_REQUEST_TIME_OUT;
  532             }
  533             log_message(r, status, "Error reading request entity data");
  534             return HTTP_INTERNAL_SERVER_ERROR;
  535         }
  536 
  537         for (bucket = APR_BRIGADE_FIRST(bb); bucket != APR_BRIGADE_SENTINEL(bb); bucket
  538                 = APR_BUCKET_NEXT(bucket)) {
  539 
  540             if (APR_BUCKET_IS_EOS(bucket)) {
  541                 seen_eos = 1;
  542                 break;
  543             }
  544 
  545             /* We can't do much with this. */
  546             if (APR_BUCKET_IS_FLUSH(bucket)) {
  547                 continue;
  548             }
  549 
  550             /* If the child stopped, we still must read to EOS. */
  551             if (child_stopped_reading) {
  552                 continue;
  553             }
  554 
  555             /* read */
  556             apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ);
  557 
  558             /* Keep writing data to the child until done or too much time
  559              * elapses with no progress or an error occurs.
  560              */
  561             status = apr_file_write_full(procnew->in, data, len, NULL);
  562 
  563             if (status != APR_SUCCESS) {
  564 
  565                 /* silly script stopped reading, soak up remaining message */
  566                 child_stopped_reading = 1;
  567 
  568                 log_message(r, status,
  569                         "Sendmail stopped reading message, aborting");
  570 
  571             }
  572 
  573         }
  574         apr_brigade_cleanup(bb);
  575     } while (!seen_eos);
  576 
  577     /* Is this flush really needed? */
  578     apr_file_flush(procnew->in);
  579     apr_file_close(procnew->in);
  580 
  581     status = APR_SUCCESS;
  582     while (APR_SUCCESS == status) {
  583         char err[MAX_STRING_LEN];
  584 
  585         status = apr_file_read_full(procnew->err, err, sizeof(err), &len);
  586 
  587         if (status == APR_SUCCESS && len > 0) {
  588             log_message(r, status, apr_psprintf(r->pool, "%s: %s",
  589                     conf->progname, apr_pstrmemdup(r->pool, err, len)));
  590         }
  591 
  592     }
  593 
  594     /* how did sendmail do? */
  595     apr_proc_wait(procnew, &exitcode, &exitwhy, APR_WAIT);
  596     if (exitcode || APR_PROC_EXIT != exitwhy) {
  597         log_message(
  598                 r,
  599                 APR_SUCCESS,
  600                 apr_psprintf(
  601                         r->pool,
  602                         "%s %s with code %d",
  603                         conf->progname,
  604                         APR_PROC_EXIT == exitwhy ? "exited normally"
  605                                 : APR_PROC_SIGNAL == exitwhy ? "exited due to a signal"
  606                                         : APR_PROC_SIGNAL_CORE == exitwhy ? "exited and dumped a core file"
  607                                                 : "exited", exitcode));
  608         return HTTP_INTERNAL_SERVER_ERROR;
  609     }
  610 
  611     /* did the client bail out? */
  612     if (child_stopped_reading) {
  613         return HTTP_INTERNAL_SERVER_ERROR;
  614     }
  615 
  616     /* add a Location header to the message status */
  617     if (conf->dsn_location) {
  618 #if AP_MODULE_MAGIC_AT_LEAST(20071108,10)
  619         const char *err;
  620         const char *dsn_location = ap_expr_str_exec(r,
  621                             conf->dsn_location, &err);
  622         if (!err) {
  623             apr_table_set(r->headers_out, "Location", apr_pstrcat(r->pool,
  624                     dsn_location, "/", ap_escape_path_segment(r->pool,
  625                             message_id), "/", NULL));
  626         }
  627         else {
  628             ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02339)
  629                           "Can't evaluate SendmailDSNLocation expression: %s", err);
  630             return HTTP_INTERNAL_SERVER_ERROR;
  631         }
  632 #else
  633         apr_table_set(r->headers_out, "Location", apr_pstrcat(r->pool,
  634                 conf->dsn_location, "/", ap_escape_path_segment(r->pool,
  635                         message_id), "/", NULL));
  636 #endif
  637         return HTTP_SEE_OTHER;
  638     }
  639 
  640     return HTTP_OK;
  641 }
  642 
  643 static int wadl_description(request_rec *r, sendmail_config_rec *conf)
  644 {
  645 
  646     ap_set_content_type(r, "application/vnd.sun.wadl+xml");
  647 
  648     ap_rprintf(
  649             r,
  650             "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
  651                 "<ns1:application xmlns:ns1=\"http://wadl.dev.java.net/2009/02\"\n"
  652                 "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n"
  653                 "xsi:schemaLocation=\"http://wadl.dev.java.net/2009/02 file:wadl.xsd\">\n"
  654                 "<ns1:resources base=\"%s\">\n"
  655                 "<ns1:resource path=\"/\">\n"
  656                 "<ns1:method name=\"POST\" id=\"sendmail\">\n"
  657                 "<ns1:request>\n"
  658                 "<ns1:param name=\"Message-Id\" type=\"xsi:string\" style=\"header\" required=\"false\">\n"
  659                 "<ns1:doc>If the caller wants to specify an explicit Message-Id, the caller should\n"
  660                 "pass it as a Message-Id header. If no header is provided, a message id will be\n"
  661                 "generated automatically, and returned in the Location header.</ns1:doc>\n"
  662                 "</ns1:param>\n"
  663                 "<ns1:param name=\"To\" type=\"xsi:string\" style=\"header\" required=\"true\">\n"
  664                 "<ns1:doc>The recipient of the message. Can be specified more than once to send\n"
  665                 "to more than one recipient.</ns1:doc>\n"
  666                 "</ns1:param>\n"
  667                 "<ns1:param name=\"CC\" type=\"xsi:string\" style=\"header\" required=\"false\">\n"
  668                 "<ns1:doc>The CC recipient of the message. Can be specified more than once to\n"
  669                 "send to more than one CC recipient.</ns1:doc>\n"
  670                 "</ns1:param>\n"
  671                 "</ns1:request>\n"
  672                 "<ns1:response status=\"500\">\n"
  673                 "<ns1:representation mediaType=\"text/html\">\n"
  674                 "<ns1:doc>If an internal error occurred while trying to send the mail, 500\n"
  675                 "Internal Server Error will be returned, and the server error log will contain\n"
  676                 "full details of the error.</ns1:doc>\n"
  677                 "</ns1:representation>\n"
  678                 "</ns1:response>\n"
  679                 "<ns1:response status=\"408\">\n"
  680                 "<ns1:representation mediaType=\"text/html\">\n"
  681                 "<ns1:doc>If an attempt to read from the caller times out, 408 Request Timed Out\n"
  682                 "will be returned.</ns1:doc>\n"
  683                 "</ns1:representation>\n"
  684                 "</ns1:response>\n"
  685                 "<ns1:response status=\"400\">\n"
  686                 "<ns1:representation mediaType=\"text/html\">\n"
  687                 "<ns1:doc>If no To or CC header was provided with the request, 400 Bad Request\n"
  688                 "will be returned.</ns1:doc>\n"
  689                 "</ns1:representation>\n"
  690                 "</ns1:response>\n"
  691                 "<ns1:response status=\"303\">\n"
  692                 "<ns1:doc>If mail sending is successful, and a processdsn URL has been defined,\n"
  693                 "303 See Other will be returned.</ns1:doc>\n"
  694                 "<ns1:representation mediaType=\"text/html\">\n"
  695                 "<ns1:param name=\"Location\" type=\"xsi:string\" style=\"header\">\n"
  696                 "<ns1:doc>The URL of the processdsn service where status of the message can\n"
  697                 "be tracked.</ns1:doc>\n"
  698                 "</ns1:param>\n"
  699                 "</ns1:representation>\n"
  700                 "</ns1:response>\n"
  701                 "<ns1:response status=\"200\">\n"
  702                 "<ns1:representation mediaType=\"text/html\">\n"
  703                 "<ns1:doc>If mail sending is successful, and a processdsn URL has not been defined,\n"
  704                 "200 OK will be returned.</ns1:doc>\n"
  705                 "</ns1:representation>\n"
  706                 "</ns1:response>\n"
  707                 "</ns1:method>\n"
  708                 "</ns1:resource>\n"
  709                 "</ns1:resources>\n"
  710                 "</ns1:application>\n", conf->location ? conf->location
  711                     : apr_pstrcat(r->pool, ap_http_scheme(r), "://",
  712                             r->server->server_hostname, r->uri, NULL));
  713 
  714     return OK;
  715 }
  716 
  717 static int sendmail_handler(request_rec *r)
  718 {
  719 
  720     sendmail_config_rec *conf = ap_get_module_config(r->per_dir_config,
  721             &sendmail_module);
  722 
  723     if (!conf) {
  724         return DECLINED;
  725     }
  726 
  727     if (strcmp(r->handler, "sendmail")) {
  728         return DECLINED;
  729     }
  730 
  731     /* A POST to / should send the email, OPTIONS should return the WADL */
  732     ap_allow_methods(r, 1, "POST", "OPTIONS", NULL);
  733     if (!strcmp(r->method, "POST")) {
  734         return pipe_to_sendmail(r, conf, NULL);
  735     }
  736     else if (!strcmp(r->method, "OPTIONS")) {
  737         return wadl_description(r, conf);
  738     }
  739     else {
  740         return HTTP_METHOD_NOT_ALLOWED;
  741     }
  742 
  743 }
  744 
  745 static void register_hooks(apr_pool_t *p)
  746 {
  747     ap_hook_handler(sendmail_handler, NULL, NULL, APR_HOOK_MIDDLE);
  748 }
  749 
  750 module AP_MODULE_DECLARE_DATA sendmail_module =
  751 {
  752     STANDARD20_MODULE_STUFF,
  753     create_sendmail_dir_config,  /* dir config creater */
  754     merge_sendmail_dir_config, /* dir merger --- default is to override */
  755     NULL, /* server config */
  756     NULL, /* merge server config */
  757     sendmail_cmds, /* command apr_table_t */
  758     register_hooks /* register hooks */
  759 };