-/* $Cambridge: exim/src/src/transport.c,v 1.22 2008/09/30 09:20:31 tom Exp $ */
-
/*************************************************
* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2007 */
+/* Copyright (c) University of Cambridge 1995 - 2012 */
/* See the file NOTICE for conditions of use and distribution. */
/* General functions concerned with transportation, and generic options for all
(void *)(offsetof(transport_instance, envelope_to_add)) },
{ "group", opt_expand_gid|opt_public,
(void *)offsetof(transport_instance, gid) },
- { "headers_add", opt_stringptr|opt_public,
+ { "headers_add", opt_stringptr|opt_public|opt_rep_str,
(void *)offsetof(transport_instance, add_headers) },
{ "headers_only", opt_bool|opt_public,
(void *)offsetof(transport_instance, headers_only) },
- { "headers_remove", opt_stringptr|opt_public,
+ { "headers_remove", opt_stringptr|opt_public|opt_rep_str,
(void *)offsetof(transport_instance, remove_headers) },
{ "headers_rewrite", opt_rewrite|opt_public,
(void *)offsetof(transport_instance, headers_rewrite) },
if (transport_write_timeout <= 0) /* No timeout wanted */
{
#ifdef SUPPORT_TLS
- if (tls_active == fd) rc = tls_write(block, len); else
+ if (tls_out.active == fd) rc = tls_write(FALSE, block, len); else
#endif
rc = write(fd, block, len);
save_errno = errno;
{
alarm(local_timeout);
#ifdef SUPPORT_TLS
- if (tls_active == fd) rc = tls_write(block, len); else
+ if (tls_out.active == fd) rc = tls_write(FALSE, block, len); else
#endif
rc = write(fd, block, len);
save_errno = errno;
*/
BOOL
-transport_write_string(int fd, char *format, ...)
+transport_write_string(int fd, const char *format, ...)
{
va_list ap;
va_start(ap, format);
if (use_crlf) *chunk_ptr++ = '\r';
*chunk_ptr++ = '\n';
+ transport_newlines++;
/* The check_string test (formerly "from hack") replaces the specific
string at the start of a line with an escape string (e.g. "From " becomes
}
}
- /* Finished with the check string */
-
- nl_check_length = nl_escape_length = 0;
-
/* A read error on the body will have left len == -1 and errno set. */
if (len != 0) return FALSE;
+ }
- /* If requested, add a terminating "." line (SMTP output). */
+/* Finished with the check string */
- if ((options & topt_end_dot) != 0 && !write_chunk(fd, US".\n", 2, use_crlf))
- return FALSE;
- }
+nl_check_length = nl_escape_length = 0;
+
+/* If requested, add a terminating "." line (SMTP output). */
+
+if ((options & topt_end_dot) != 0 && !write_chunk(fd, US".\n", 2, use_crlf))
+ return FALSE;
/* Write out any remaining data in the buffer before returning. */
}
-#if (defined EXPERIMENTAL_DOMAINKEYS) || (defined EXPERIMENTAL_DKIM)
+#ifndef DISABLE_DKIM
/***************************************************************************************************
* External interface to write the message, while signing it with DKIM and/or Domainkeys *
0/false => send anyway
uschar *dkim_sign_headers DKIM: List of headers that should be included in signature
generation
- uschar *dk_private_key Domainkeys: The private key to use (filename or plain data)
- uschar *dk_domain Domainkeys: Override domain (normally NULL)
- uschar *dk_selector Domainkeys: The selector to use.
- uschar *dk_canon Domainkeys: The canonalization scheme to use, "simple" or "nofws"
- uschar *dk_headers Domainkeys: Colon-separated header list to include in the signing
- process.
- uschar *dk_strict Domainkeys: What to do if signing fails: 1/true => throw error
- 0/false => send anyway
Returns: TRUE on success; FALSE (with errno) for any failure
*/
int size_limit, uschar *add_headers, uschar *remove_headers,
uschar *check_string, uschar *escape_string, rewrite_rule *rewrite_rules,
int rewrite_existflags, uschar *dkim_private_key, uschar *dkim_domain,
- uschar *dkim_selector, uschar *dkim_canon, uschar *dkim_strict, uschar *dkim_sign_headers,
- uschar *dk_private_key, uschar *dk_domain, uschar *dk_selector, uschar *dk_canon,
- uschar *dk_headers, uschar *dk_strict
+ uschar *dkim_selector, uschar *dkim_canon, uschar *dkim_strict, uschar *dkim_sign_headers
)
{
int dkim_fd;
int sread = 0;
int wwritten = 0;
uschar *dkim_signature = NULL;
- uschar *dk_signature = NULL;
off_t size = 0;
- if ( !( ((dkim_private_key != NULL) && (dkim_domain != NULL) && (dkim_selector != NULL)) ||
- ((dk_private_key != NULL) && (dk_selector != NULL)) ) ) {
- /* If we can sign with neither method, just call the original function. */
+ if (!( ((dkim_private_key != NULL) && (dkim_domain != NULL) && (dkim_selector != NULL)) )) {
+ /* If we can't sign, just call the original function. */
return transport_write_message(addr, fd, options,
size_limit, add_headers, remove_headers,
check_string, escape_string, rewrite_rules,
goto CLEANUP;
}
-
- #ifdef EXPERIMENTAL_DKIM
if ( (dkim_private_key != NULL) && (dkim_domain != NULL) && (dkim_selector != NULL) ) {
/* Rewind file and feed it to the goats^W DKIM lib */
lseek(dkim_fd, 0, SEEK_SET);
if (dkim_strict_result != NULL) {
if ( (strcmpic(dkim_strict,US"1") == 0) ||
(strcmpic(dkim_strict,US"true") == 0) ) {
- save_errno = errno;
+ /* Set errno to something halfway meaningful */
+ save_errno = EACCES;
+ log_write(0, LOG_MAIN, "DKIM: message could not be signed, and dkim_strict is set. Deferring message delivery.");
rc = FALSE;
goto CLEANUP;
}
int siglen = Ustrlen(dkim_signature);
while(siglen > 0) {
#ifdef SUPPORT_TLS
- if (tls_active == fd) wwritten = tls_write(dkim_signature, siglen); else
+ if (tls_out.active == fd) wwritten = tls_write(FALSE, dkim_signature, siglen); else
#endif
wwritten = write(fd,dkim_signature,siglen);
if (wwritten == -1) {
}
}
}
- #endif
-
- #ifdef EXPERIMENTAL_DOMAINKEYS
- if ( (dk_private_key != NULL) && (dk_selector != NULL) ) {
- /* Rewind file and feed it to the goats^W DK lib */
- lseek(dkim_fd, 0, SEEK_SET);
- dk_signature = dk_exim_sign(dkim_fd,
- dk_private_key,
- dk_domain,
- dk_selector,
- dk_canon);
- if (dk_signature == NULL) {
- if (dk_strict != NULL) {
- uschar *dk_strict_result = expand_string(dk_strict);
- if (dk_strict_result != NULL) {
- if ( (strcmpic(dk_strict,US"1") == 0) ||
- (strcmpic(dk_strict,US"true") == 0) ) {
- save_errno = errno;
- rc = FALSE;
- goto CLEANUP;
- }
- }
- }
- }
- else {
- int siglen = Ustrlen(dk_signature);
- while(siglen > 0) {
- #ifdef SUPPORT_TLS
- if (tls_active == fd) wwritten = tls_write(dk_signature, siglen); else
- #endif
- wwritten = write(fd,dk_signature,siglen);
- if (wwritten == -1) {
- /* error, bail out */
- save_errno = errno;
- rc = FALSE;
- goto CLEANUP;
- }
- siglen -= wwritten;
- dk_signature += wwritten;
- }
- }
- }
- #endif
/* Fetch file positition (the size) */
size = lseek(dkim_fd,0,SEEK_CUR);
to the socket. However only if we don't use TLS,
in which case theres another layer of indirection
before the data finally hits the socket. */
- if (tls_active != fd)
+ if (tls_out.active != fd)
{
ssize_t copied = 0;
off_t offset = 0;
/* write the chunk */
DKIM_WRITE:
#ifdef SUPPORT_TLS
- if (tls_active == fd) wwritten = tls_write(US p, sread); else
+ if (tls_out.active == fd) wwritten = tls_write(FALSE, US p, sread); else
#endif
wwritten = write(fd,p,sread);
if (wwritten == -1)
CLEANUP:
/* unlink -K file */
(void)close(dkim_fd);
- //Uunlink(dkim_spool_name);
+ Uunlink(dkim_spool_name);
errno = save_errno;
return rc;
}
+
#endif
size_limit, add_headers, remove_headers, NULL, NULL,
rewrite_rules, rewrite_existflags);
save_errno = errno;
- (void)write(pfd[pipe_write], (void *)&rc, sizeof(BOOL));
- (void)write(pfd[pipe_write], (void *)&save_errno, sizeof(int));
- (void)write(pfd[pipe_write], (void *)&(addr->more_errno), sizeof(int));
+ if ( write(pfd[pipe_write], (void *)&rc, sizeof(BOOL))
+ != sizeof(BOOL)
+ || write(pfd[pipe_write], (void *)&save_errno, sizeof(int))
+ != sizeof(int)
+ || write(pfd[pipe_write], (void *)&(addr->more_errno), sizeof(int))
+ != sizeof(int)
+ )
+ rc = FALSE; /* compiler quietening */
_exit(0);
}
save_errno = errno;
if (rc == 0)
{
BOOL ok;
- (void)read(pfd[pipe_read], (void *)&ok, sizeof(BOOL));
+ int dummy = read(pfd[pipe_read], (void *)&ok, sizeof(BOOL));
if (!ok)
{
- (void)read(pfd[pipe_read], (void *)&save_errno, sizeof(int));
- (void)read(pfd[pipe_read], (void *)&(addr->more_errno), sizeof(int));
+ dummy = read(pfd[pipe_read], (void *)&save_errno, sizeof(int));
+ dummy = read(pfd[pipe_read], (void *)&(addr->more_errno), sizeof(int));
yield = FALSE;
}
}
memmove(argv + i + 1 + additional, argv + i + 1,
(argcount - i)*sizeof(uschar *));
- for (ad = addr; ad != NULL; ad = ad->next) argv[i++] = ad->address;
+ for (ad = addr; ad != NULL; ad = ad->next) {
+ argv[i++] = ad->address;
+ argcount++;
+ }
+
+ /* Subtract one since we replace $pipe_addresses */
+ argcount--;
+ i--;
+ }
+
+ /* Handle special case of $address_pipe when af_force_command is set */
+
+ else if (addr != NULL && testflag(addr,af_force_command) &&
+ (Ustrcmp(argv[i], "$address_pipe") == 0 ||
+ Ustrcmp(argv[i], "${address_pipe}") == 0))
+ {
+ int address_pipe_i;
+ int address_pipe_argcount = 0;
+ int address_pipe_max_args;
+ uschar **address_pipe_argv;
+
+ /* We can never have more then the argv we will be loading into */
+ address_pipe_max_args = max_args - argcount + 1;
+
+ DEBUG(D_transport)
+ debug_printf("address_pipe_max_args=%d\n", address_pipe_max_args);
+
+ /* We allocate an additional for (uschar *)0 */
+ address_pipe_argv = store_get((address_pipe_max_args+1)*sizeof(uschar *));
+
+ /* +1 because addr->local_part[0] == '|' since af_force_command is set */
+ s = expand_string(addr->local_part + 1);
+
+ if (s == NULL || *s == '\0')
+ {
+ addr->transport_return = FAIL;
+ addr->message = string_sprintf("Expansion of \"%s\" "
+ "from command \"%s\" in %s failed: %s",
+ (addr->local_part + 1), cmd, etext, expand_string_message);
+ return FALSE;
+ }
+
+ while (isspace(*s)) s++; /* strip leading space */
+
+ while (*s != 0 && address_pipe_argcount < address_pipe_max_args)
+ {
+ if (*s == '\'')
+ {
+ ss = s + 1;
+ while (*ss != 0 && *ss != '\'') ss++;
+ address_pipe_argv[address_pipe_argcount++] = ss = store_get(ss - s++);
+ while (*s != 0 && *s != '\'') *ss++ = *s++;
+ if (*s != 0) s++;
+ *ss++ = 0;
+ }
+ else address_pipe_argv[address_pipe_argcount++] = string_dequote(&s);
+ while (isspace(*s)) s++; /* strip space after arg */
+ }
+
+ address_pipe_argv[address_pipe_argcount] = (uschar *)0;
+
+ /* If *s != 0 we have run out of argument slots. */
+ if (*s != 0)
+ {
+ uschar *msg = string_sprintf("Too many arguments in $address_pipe "
+ "\"%s\" in %s", addr->local_part + 1, etext);
+ if (addr != NULL)
+ {
+ addr->transport_return = FAIL;
+ addr->message = msg;
+ }
+ else *errptr = msg;
+ return FALSE;
+ }
+
+ /* address_pipe_argcount - 1
+ * because we are replacing $address_pipe in the argument list
+ * with the first thing it expands to */
+ if (argcount + address_pipe_argcount - 1 > max_args)
+ {
+ addr->transport_return = FAIL;
+ addr->message = string_sprintf("Too many arguments to command "
+ "\"%s\" after expanding $address_pipe in %s", cmd, etext);
+ return FALSE;
+ }
+
+ /* If we are not just able to replace the slot that contained
+ * $address_pipe (address_pipe_argcount == 1)
+ * We have to move the existing argv by address_pipe_argcount - 1
+ * Visually if address_pipe_argcount == 2:
+ * [argv 0][argv 1][argv 2($address_pipe)][argv 3][0]
+ * [argv 0][argv 1][ap_arg0][ap_arg1][old argv 3][0]
+ */
+ if (address_pipe_argcount > 1)
+ memmove(
+ /* current position + additonal args */
+ argv + i + address_pipe_argcount,
+ /* current position + 1 (for the (uschar *)0 at the end) */
+ argv + i + 1,
+ /* -1 for the (uschar *)0 at the end)*/
+ (argcount - i)*sizeof(uschar *)
+ );
+
+ /* Now we fill in the slots we just moved argv out of
+ * [argv 0][argv 1][argv 2=pipeargv[0]][argv 3=pipeargv[1]][old argv 3][0]
+ */
+ for (address_pipe_i = 0;
+ address_pipe_argv[address_pipe_i] != (uschar *)0;
+ address_pipe_i++)
+ {
+ argv[i++] = address_pipe_argv[address_pipe_i];
+ argcount++;
+ }
+
+ /* Subtract one since we replace $address_pipe */
+ argcount--;
i--;
}