Exemple #1
0
void *
lsearch_open(uschar *filename, uschar **errmsg)
{
FILE *f = Ufopen(filename, "rb");
if (f == NULL)
  {
  int save_errno = errno;
  *errmsg = string_open_failed(errno, "%s for linear search", filename);
  errno = save_errno;
  return NULL;
  }
return f;
}
Exemple #2
0
Fichier : json.c Projet : Exim/exim
static void *
json_open(uschar *filename, uschar **errmsg)
{
FILE * f;

json_set_alloc_funcs(json_malloc, json_free);

if (!(f = Ufopen(filename, "rb")))
  {
  int save_errno = errno;
  *errmsg = string_open_failed(errno, "%s for json search", filename);
  errno = save_errno;
  return NULL;
  }
return f;
}
Exemple #3
0
BOOL
autoreply_transport_entry(
  transport_instance *tblock,      /* data for this instantiation */
  address_item *addr)              /* address we are working on */
{
int fd, pid, rc;
int cache_fd = -1;
int log_fd = -1;
int cache_size = 0;
int add_size = 0;
EXIM_DB *dbm_file = NULL;
BOOL file_expand, return_message;
uschar *from, *reply_to, *to, *cc, *bcc, *subject, *headers, *text, *file;
uschar *logfile, *oncelog;
uschar *cache_buff = NULL;
uschar *cache_time = NULL;
uschar *message_id = NULL;
header_line *h;
time_t now = time(NULL);
time_t once_repeat_sec = 0;
FILE *f;
FILE *ff = NULL;

autoreply_transport_options_block *ob =
  (autoreply_transport_options_block *)(tblock->options_block);

DEBUG(D_transport) debug_printf("%s transport entered\n", tblock->name);

/* Set up for the good case */

addr->transport_return = OK;
addr->basic_errno = 0;

/* If the address is pointing to a reply block, then take all the data
from that block. It has typically been set up by a mail filter processing
router. Otherwise, the data must be supplied by this transport, and
it has to be expanded here. */

if (addr->reply != NULL)
  {
  DEBUG(D_transport) debug_printf("taking data from address\n");
  from = addr->reply->from;
  reply_to = addr->reply->reply_to;
  to = addr->reply->to;
  cc = addr->reply->cc;
  bcc = addr->reply->bcc;
  subject = addr->reply->subject;
  headers = addr->reply->headers;
  text = addr->reply->text;
  file = addr->reply->file;
  logfile = addr->reply->logfile;
  oncelog = addr->reply->oncelog;
  once_repeat_sec = addr->reply->once_repeat;
  file_expand = addr->reply->file_expand;
  expand_forbid = addr->reply->expand_forbid;
  return_message = addr->reply->return_message;
  }
else
  {
  uschar *oncerepeat = ob->once_repeat;

  DEBUG(D_transport) debug_printf("taking data from transport\n");
  from = ob->from;
  reply_to = ob->reply_to;
  to = ob->to;
  cc = ob->cc;
  bcc = ob->bcc;
  subject = ob->subject;
  headers = ob->headers;
  text = ob->text;
  file = ob->file;
  logfile = ob->logfile;
  oncelog = ob->oncelog;
  file_expand = ob->file_expand;
  return_message = ob->return_message;

  if ((from  != NULL &&
        (from = checkexpand(from, addr, tblock->name, cke_hdr)) == NULL) ||
      (reply_to    != NULL &&
        (reply_to = checkexpand(reply_to, addr, tblock->name, cke_hdr)) == NULL) ||
      (to    != NULL &&
        (to = checkexpand(to, addr, tblock->name, cke_hdr)) == NULL) ||
      (cc    != NULL &&
        (cc = checkexpand(cc, addr, tblock->name, cke_hdr)) == NULL) ||
      (bcc   != NULL &&
        (bcc = checkexpand(bcc, addr, tblock->name, cke_hdr)) == NULL) ||
      (subject   != NULL &&
        (subject = checkexpand(subject, addr, tblock->name, cke_hdr)) == NULL) ||
      (headers != NULL &&
        (headers = checkexpand(headers, addr, tblock->name, cke_text)) == NULL) ||
      (text  != NULL &&
        (text = checkexpand(text, addr, tblock->name, cke_text)) == NULL) ||
      (file  != NULL &&
        (file = checkexpand(file, addr, tblock->name, cke_file)) == NULL) ||
      (logfile != NULL &&
        (logfile = checkexpand(logfile, addr, tblock->name, cke_file)) == NULL) ||
      (oncelog != NULL &&
        (oncelog = checkexpand(oncelog, addr, tblock->name, cke_file)) == NULL) ||
      (oncerepeat != NULL &&
        (oncerepeat = checkexpand(oncerepeat, addr, tblock->name, cke_file)) == NULL))
    return FALSE;

  if (oncerepeat != NULL)
    {
    once_repeat_sec = readconf_readtime(oncerepeat, 0, FALSE);
    if (once_repeat_sec < 0)
      {
      addr->transport_return = FAIL;
      addr->message = string_sprintf("Invalid time value \"%s\" for "
        "\"once_repeat\" in %s transport", oncerepeat, tblock->name);
      return FALSE;
      }
    }
  }

/* If the never_mail option is set, we have to scan all the recipients and
remove those that match. */

if (ob->never_mail != NULL)
  {
  uschar *never_mail = expand_string(ob->never_mail);

  if (never_mail == NULL)
    {
    addr->transport_return = FAIL;
    addr->message = string_sprintf("Failed to expand \"%s\" for "
      "\"never_mail\" in %s transport", ob->never_mail, tblock->name);
    return FALSE;
    }

  if (to != NULL) check_never_mail(&to, never_mail);
  if (cc != NULL) check_never_mail(&cc, never_mail);
  if (bcc != NULL) check_never_mail(&bcc, never_mail);

  if (to == NULL && cc == NULL && bcc == NULL)
    {
    DEBUG(D_transport)
      debug_printf("*** all recipients removed by never_mail\n");
    return OK;
    }
  }

/* If the -N option is set, can't do any more. */

if (dont_deliver)
  {
  DEBUG(D_transport)
    debug_printf("*** delivery by %s transport bypassed by -N option\n",
      tblock->name);
  return FALSE;
  }


/* If the oncelog field is set, we send want to send only one message to the
given recipient(s). This works only on the "To" field. If there is no "To"
field, the message is always sent. If the To: field contains more than one
recipient, the effect might not be quite as envisaged. If once_file_size is
set, instead of a dbm file, we use a regular file containing a circular buffer
recipient cache. */

if (oncelog != NULL && *oncelog != 0 && to != NULL)
  {
  time_t then = 0;

  /* Handle fixed-size cache file. */

  if (ob->once_file_size > 0)
    {
    uschar *p;
    struct stat statbuf;
    cache_fd = Uopen(oncelog, O_CREAT|O_RDWR, ob->mode);

    if (cache_fd < 0 || fstat(cache_fd, &statbuf) != 0)
      {
      addr->transport_return = DEFER;
      addr->message = string_sprintf("Failed to %s \"once\" file %s when "
        "sending message from %s transport: %s",
        (cache_fd < 0)? "open" : "stat", oncelog, tblock->name,
          strerror(errno));
      goto END_OFF;
      }

    /* Get store in the temporary pool and read the entire file into it. We get
    an amount of store that is big enough to add the new entry on the end if we
    need to do that. */

    cache_size = statbuf.st_size;
    add_size = sizeof(time_t) + Ustrlen(to) + 1;
    cache_buff = store_get(cache_size + add_size);

    if (read(cache_fd, cache_buff, cache_size) != cache_size)
      {
      addr->transport_return = DEFER;
      addr->basic_errno = errno;
      addr->message = US"error while reading \"once\" file";
      goto END_OFF;
      }

    DEBUG(D_transport) debug_printf("%d bytes read from %s\n", cache_size, oncelog);

    /* Scan the data for this recipient. Each entry in the file starts with
    a time_t sized time value, followed by the address, followed by a binary
    zero. If we find a match, put the time into "then", and the place where it
    was found into "cache_time". Otherwise, "then" is left at zero. */

    p = cache_buff;
    while (p < cache_buff + cache_size)
      {
      uschar *s = p + sizeof(time_t);
      uschar *nextp = s + Ustrlen(s) + 1;
      if (Ustrcmp(to, s) == 0)
        {
        memcpy(&then, p, sizeof(time_t));
        cache_time = p;
        break;
        }
      p = nextp;
      }
    }

  /* Use a DBM file for the list of previous recipients. */

  else
    {
    EXIM_DATUM key_datum, result_datum;
    EXIM_DBOPEN(oncelog, O_RDWR|O_CREAT, ob->mode, &dbm_file);
    if (dbm_file == NULL)
      {
      addr->transport_return = DEFER;
      addr->message = string_sprintf("Failed to open %s file %s when sending "
        "message from %s transport: %s", EXIM_DBTYPE, oncelog, tblock->name,
        strerror(errno));
      goto END_OFF;
      }

    EXIM_DATUM_INIT(key_datum);        /* Some DBM libraries need datums */
    EXIM_DATUM_INIT(result_datum);     /* to be cleared */
    EXIM_DATUM_DATA(key_datum) = CS to;
    EXIM_DATUM_SIZE(key_datum) = Ustrlen(to) + 1;

    if (EXIM_DBGET(dbm_file, key_datum, result_datum))
      {
      /* If the datum size is that of a binary time, we are in the new world
      where messages are sent periodically. Otherwise the file is an old one,
      where the datum was filled with a tod_log time, which is assumed to be
      different in size. For that, only one message is ever sent. This change
      introduced at Exim 3.00. In a couple of years' time the test on the size
      can be abolished. */

      if (EXIM_DATUM_SIZE(result_datum) == sizeof(time_t))
        {
        memcpy(&then, EXIM_DATUM_DATA(result_datum), sizeof(time_t));
        }
      else then = now;
      }
    }

  /* Either "then" is set zero, if no message has yet been sent, or it
  is set to the time of the last sending. */

  if (then != 0 && (once_repeat_sec <= 0 || now - then < once_repeat_sec))
    {
    DEBUG(D_transport) debug_printf("message previously sent to %s%s\n", to,
      (once_repeat_sec > 0)? " and repeat time not reached" : "");
    log_fd = Uopen(logfile, O_WRONLY|O_APPEND|O_CREAT, ob->mode);
    if (log_fd >= 0)
      {
      uschar *ptr = log_buffer;
      sprintf(CS ptr, "%s\n  previously sent to %.200s\n", tod_stamp(tod_log), to);
      while(*ptr) ptr++;
      if(write(log_fd, log_buffer, ptr - log_buffer) != ptr-log_buffer
        || close(log_fd))
        DEBUG(D_transport) debug_printf("Problem writing log file %s for %s "
          "transport\n", logfile, tblock->name);
      }
    goto END_OFF;
    }

  DEBUG(D_transport) debug_printf("%s %s\n", (then <= 0)?
    "no previous message sent to" : "repeat time reached for", to);
  }

/* We are going to send a message. Ensure any requested file is available. */

if (file != NULL)
  {
  ff = Ufopen(file, "rb");
  if (ff == NULL && !ob->file_optional)
    {
    addr->transport_return = DEFER;
    addr->message = string_sprintf("Failed to open file %s when sending "
      "message from %s transport: %s", file, tblock->name, strerror(errno));
    return FALSE;
    }
  }

/* Make a subprocess to send the message */

pid = child_open_exim(&fd);

/* Creation of child failed; defer this delivery. */

if (pid < 0)
  {
  addr->transport_return = DEFER;
  addr->message = string_sprintf("Failed to create child process to send "
    "message from %s transport: %s", tblock->name, strerror(errno));
  DEBUG(D_transport) debug_printf("%s\n", addr->message);
  return FALSE;
  }

/* Create the message to be sent - recipients are taken from the headers,
as the -t option is used. The "headers" stuff *must* be last in case there
are newlines in it which might, if placed earlier, screw up other headers. */

f = fdopen(fd, "wb");

if (from != NULL) fprintf(f, "From: %s\n", from);
if (reply_to != NULL) fprintf(f, "Reply-To: %s\n", reply_to);
if (to != NULL) fprintf(f, "To: %s\n", to);
if (cc != NULL) fprintf(f, "Cc: %s\n", cc);
if (bcc != NULL) fprintf(f, "Bcc: %s\n", bcc);
if (subject != NULL) fprintf(f, "Subject: %s\n", subject);

/* Generate In-Reply-To from the message_id header; there should
always be one, but code defensively. */

for (h = header_list; h != NULL; h = h->next)
  if (h->type == htype_id) break;

if (h != NULL)
  {
  message_id = Ustrchr(h->text, ':') + 1;
  while (isspace(*message_id)) message_id++;
  fprintf(f, "In-Reply-To: %s", message_id);
  }

/* Generate a References header if there is at least one of Message-ID:,
References:, or In-Reply-To: (see RFC 2822). */

for (h = header_list; h != NULL; h = h->next)
  if (h->type != htype_old && strncmpic(US"References:", h->text, 11) == 0)
    break;

if (h == NULL)
  for (h = header_list; h != NULL; h = h->next)
    if (h->type != htype_old && strncmpic(US"In-Reply-To:", h->text, 12) == 0)
      break;

/* We limit the total length of references.  Although there is no fixed
limit, some systems do not like headers growing beyond recognition.
Keep the first message ID for the thread root and the last few for
the position inside the thread, up to a maximum of 12 altogether. */

if (h != NULL || message_id != NULL)
  {
  fprintf(f, "References:");
  if (h != NULL)
    {
    uschar *s, *id, *error;
    uschar *referenced_ids[12];
    int reference_count = 0;
    int i;

    s = Ustrchr(h->text, ':') + 1;
    parse_allow_group = FALSE;
    while (*s != 0 && (s = parse_message_id(s, &id, &error)) != NULL)
      {
      if (reference_count == sizeof(referenced_ids)/sizeof(uschar *))
        {
        memmove(referenced_ids + 1, referenced_ids + 2,
           sizeof(referenced_ids) - 2*sizeof(uschar *));
        referenced_ids[reference_count - 1] = id;
        }
      else referenced_ids[reference_count++] = id;
      }
    for (i = 0; i < reference_count; ++i) fprintf(f, " %s", referenced_ids[i]);
    }

  /* The message id will have a newline on the end of it. */

  if (message_id != NULL) fprintf(f, " %s", message_id);
    else fprintf(f, "\n");
  }

/* Add an Auto-Submitted: header */

fprintf(f, "Auto-Submitted: auto-replied\n");

/* Add any specially requested headers */

if (headers != NULL) fprintf(f, "%s\n", headers);
fprintf(f, "\n");

if (text != NULL)
  {
  fprintf(f, "%s", CS text);
  if (text[Ustrlen(text)-1] != '\n') fprintf(f, "\n");
  }

if (ff != NULL)
  {
  while (Ufgets(big_buffer, big_buffer_size, ff) != NULL)
    {
    if (file_expand)
      {
      uschar *s = expand_string(big_buffer);
      DEBUG(D_transport)
        {
        if (s == NULL)
          debug_printf("error while expanding line from file:\n  %s\n  %s\n",
            big_buffer, expand_string_message);
        }
      fprintf(f, "%s", (s == NULL)? CS big_buffer : CS s);
      }
    else fprintf(f, "%s", CS big_buffer);
    }
  }
Exemple #4
0
int dcc_process(uschar **listptr) {
  int sep = 0;
  uschar *list = *listptr;
  FILE *data_file;
  uschar *dcc_daemon_ip = US"";
  uschar *dcc_default_ip_option = US"127.0.0.1";
  uschar *dcc_ip_option = US"";
  uschar *dcc_helo_option = US"localhost";
  uschar *dcc_reject_message = US"Rejected by DCC";
  uschar *xtra_hdrs = NULL;

  /* from local_scan */
  int i, j, k, c, retval, sockfd, resp, line;
  unsigned int portnr;
  struct sockaddr_un  serv_addr;
  struct sockaddr_in  serv_addr_in;
  struct hostent *ipaddress;
  uschar sockpath[128];
  uschar sockip[40], client_ip[40];
  uschar opts[128];
  uschar rcpt[128], from[128];
  uschar sendbuf[4096];
  uschar recvbuf[4096];
  uschar dcc_return_text[1024];
  uschar mbox_path[1024];
  uschar message_subdir[2];
  struct header_line *dcchdr;
  uschar *dcc_acl_options;
  uschar dcc_acl_options_buffer[10];
  uschar dcc_xtra_hdrs[1024];

  /* grep 1st option */
  if ((dcc_acl_options = string_nextinlist(&list, &sep,
                                           dcc_acl_options_buffer,
                                           sizeof(dcc_acl_options_buffer))) != NULL)
  {
    /* parse 1st option */
    if ( (strcmpic(dcc_acl_options,US"false") == 0) ||
         (Ustrcmp(dcc_acl_options,"0") == 0) ) {
      /* explicitly no matching */
      return FAIL;
    };

    /* special cases (match anything except empty) */
    if ( (strcmpic(dcc_acl_options,US"true") == 0) ||
         (Ustrcmp(dcc_acl_options,"*") == 0) ||
         (Ustrcmp(dcc_acl_options,"1") == 0) ) {
      dcc_acl_options = dcc_acl_options;
    };
  }
  else {
    /* empty means "don't match anything" */
    return FAIL;
  };

  sep = 0;

  /* if we scanned this message last time, just return */
  if ( dcc_ok )
      return dcc_rc;

  /* open the spooled body */
  message_subdir[1] = '\0';
  for (i = 0; i < 2; i++) {
    message_subdir[0] = (split_spool_directory == (i == 0))? message_id[5] : 0;
    sprintf(CS mbox_path, "%s/input/%s/%s-D", spool_directory, message_subdir, message_id);
    data_file = Ufopen(mbox_path,"rb");
    if (data_file != NULL)
      break;
  };

  if (data_file == NULL) {
    /* error while spooling */
    log_write(0, LOG_MAIN|LOG_PANIC,
           "dcc acl condition: error while opening spool file");
    return DEFER;
  };

  /* Initialize the variables */

  bzero(sockip,sizeof(sockip));
  if (dccifd_address) {
    if (dccifd_address[0] == '/')
      Ustrncpy(sockpath, dccifd_address, sizeof(sockpath));
    else
      if( sscanf(CS dccifd_address, "%s %u", sockip, &portnr) != 2) {
        log_write(0, LOG_MAIN,
          "dcc acl condition: warning - invalid dccifd address: '%s'", dccifd_address);
        (void)fclose(data_file);
        return DEFER;
      }
  }

  /* opts is what we send as dccifd options - see man dccifd */
  /* We don't support any other option than 'header' so just copy that */
  bzero(opts,sizeof(opts));
  Ustrncpy(opts, "header", sizeof(opts)-1);
  Ustrncpy(client_ip, dcc_ip_option, sizeof(client_ip)-1);
  /* If the dcc_client_ip is not provided use the
   * sender_host_address or 127.0.0.1 if it is NULL */
  DEBUG(D_acl)
    debug_printf("my_ip_option = %s - client_ip = %s - sender_host_address = %s\n", dcc_ip_option, client_ip, sender_host_address);
  if(!(Ustrcmp(client_ip, ""))){
    /* Do we have a sender_host_address or is it NULL? */
    if(sender_host_address){
      Ustrncpy(client_ip, sender_host_address, sizeof(client_ip)-1);
    } else {
      /* sender_host_address is NULL which means it comes from localhost */
      Ustrncpy(client_ip, dcc_default_ip_option, sizeof(client_ip)-1);
    }
  }
  DEBUG(D_acl)
    debug_printf("Client IP: %s\n", client_ip);
  Ustrncpy(sockip, dcc_daemon_ip, sizeof(sockip)-1);
  /* strncat(opts, my_request, strlen(my_request)); */
  Ustrcat(opts, "\n");
  Ustrncat(opts, client_ip, sizeof(opts)-Ustrlen(opts)-1);
  Ustrncat(opts, "\nHELO ", sizeof(opts)-Ustrlen(opts)-1);
  Ustrncat(opts, dcc_helo_option, sizeof(opts)-Ustrlen(opts)-2);
  Ustrcat(opts, "\n");

  /* initialize the other variables */
  dcchdr = header_list;
  /* we set the default return value to DEFER */
  retval = DEFER;

  bzero(sendbuf,sizeof(sendbuf));
  bzero(dcc_header_str,sizeof(dcc_header_str));
  bzero(rcpt,sizeof(rcpt));
  bzero(from,sizeof(from));

  /* send a null return path as "<>". */
  if (Ustrlen(sender_address) > 0)
    Ustrncpy(from, sender_address, sizeof(from));
  else
    Ustrncpy(from, "<>", sizeof(from));
  Ustrncat(from, "\n", sizeof(from)-Ustrlen(from)-1);

  /**************************************
   * Now creating the socket connection *
   **************************************/

  /* If there is a dcc_daemon_ip, we use a tcp socket, otherwise a UNIX socket */
  if(Ustrcmp(sockip, "")){
    ipaddress = gethostbyname((char *)sockip);
    bzero((char *) &serv_addr_in, sizeof(serv_addr_in));
    serv_addr_in.sin_family = AF_INET;
    bcopy((char *)ipaddress->h_addr, (char *)&serv_addr_in.sin_addr.s_addr, ipaddress->h_length);
    serv_addr_in.sin_port = htons(portnr);
    if ((sockfd = socket(AF_INET, SOCK_STREAM,0)) < 0){
      DEBUG(D_acl)
        debug_printf("Creating socket failed: %s\n", strerror(errno));
      log_write(0,LOG_REJECT,"Creating socket failed: %s\n", strerror(errno));
      /* if we cannot create the socket, defer the mail */
      (void)fclose(data_file);
      return retval;
    }
    /* Now connecting the socket (INET) */
    if (connect(sockfd, (struct sockaddr *)&serv_addr_in, sizeof(serv_addr_in)) < 0){
      DEBUG(D_acl)
        debug_printf("Connecting socket failed: %s\n", strerror(errno));
      log_write(0,LOG_REJECT,"Connecting socket failed: %s\n", strerror(errno));
      /* if we cannot contact the socket, defer the mail */
      (void)fclose(data_file);
      return retval;
    }
  } else {
    /* connecting to the dccifd UNIX socket */
    bzero((char *)&serv_addr,sizeof(serv_addr));
    serv_addr.sun_family = AF_UNIX;
    Ustrcpy(serv_addr.sun_path, sockpath);
    if ((sockfd = socket(AF_UNIX, SOCK_STREAM,0)) < 0){
      DEBUG(D_acl)
        debug_printf("Creating socket failed: %s\n", strerror(errno));
      log_write(0,LOG_REJECT,"Creating socket failed: %s\n", strerror(errno));
      /* if we cannot create the socket, defer the mail */
      (void)fclose(data_file);
      return retval;
    }
    /* Now connecting the socket (UNIX) */
    if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0){
      DEBUG(D_acl)
                            debug_printf("Connecting socket failed: %s\n", strerror(errno));
      log_write(0,LOG_REJECT,"Connecting socket failed: %s\n", strerror(errno));
      /* if we cannot contact the socket, defer the mail */
      (void)fclose(data_file);
      return retval;
    }
  }
  /* the socket is open, now send the options to dccifd*/
  DEBUG(D_acl)
    debug_printf("\n---------------------------\nSocket opened; now sending input\n-----------------\n");
  /* First, fill in the input buffer */
  Ustrncpy(sendbuf, opts, sizeof(sendbuf));
  Ustrncat(sendbuf, from, sizeof(sendbuf)-Ustrlen(sendbuf)-1);

  DEBUG(D_acl)
  {
    debug_printf("opts = %s\nsender = %s\nrcpt count = %d\n", opts, from, recipients_count);
    debug_printf("Sending options:\n****************************\n");
  }

  /* let's send each of the recipients to dccifd */
  for (i = 0; i < recipients_count; i++){
    DEBUG(D_acl)
      debug_printf("recipient = %s\n",recipients_list[i].address);
    if(Ustrlen(sendbuf) + Ustrlen(recipients_list[i].address) > sizeof(sendbuf))
    {
      DEBUG(D_acl)
        debug_printf("Writing buffer: %s\n", sendbuf);
      flushbuffer(sockfd, sendbuf);
      bzero(sendbuf, sizeof(sendbuf));
    }
    Ustrncat(sendbuf, recipients_list[i].address, sizeof(sendbuf)-Ustrlen(sendbuf)-1);
    Ustrncat(sendbuf, "\r\n", sizeof(sendbuf)-Ustrlen(sendbuf)-1);
  }
  /* send a blank line between options and message */
  Ustrncat(sendbuf, "\n", sizeof(sendbuf)-Ustrlen(sendbuf)-1);
  /* Now we send the input buffer */
  DEBUG(D_acl)
    debug_printf("%s\n****************************\n", sendbuf);
  flushbuffer(sockfd, sendbuf);

  /* now send the message */
  /* Clear the input buffer */
  bzero(sendbuf, sizeof(sendbuf));
  /* First send the headers */
  /* Now send the headers */
  DEBUG(D_acl)
    debug_printf("Sending headers:\n****************************\n");
  Ustrncpy(sendbuf, dcchdr->text, sizeof(sendbuf)-2);
  while((dcchdr=dcchdr->next)) {
    if(dcchdr->slen > sizeof(sendbuf)-2) {
      /* The size of the header is bigger than the size of
       * the input buffer, so split it up in smaller parts. */
       flushbuffer(sockfd, sendbuf);
       bzero(sendbuf, sizeof(sendbuf));
       j = 0;
       while(j < dcchdr->slen)
       {
        for(i = 0; i < sizeof(sendbuf)-2; i++) {
          sendbuf[i] = dcchdr->text[j];
          j++;
        }
        flushbuffer(sockfd, sendbuf);
        bzero(sendbuf, sizeof(sendbuf));
       }
    } else if(Ustrlen(sendbuf) + dcchdr->slen > sizeof(sendbuf)-2) {
      flushbuffer(sockfd, sendbuf);
      bzero(sendbuf, sizeof(sendbuf));
      Ustrncpy(sendbuf, dcchdr->text, sizeof(sendbuf)-2);
    } else {
      Ustrncat(sendbuf, dcchdr->text, sizeof(sendbuf)-Ustrlen(sendbuf)-2);
    }
  }

  /* a blank line seperates header from body */
  Ustrncat(sendbuf, "\n", sizeof(sendbuf)-Ustrlen(sendbuf)-1);
  flushbuffer(sockfd, sendbuf);
  DEBUG(D_acl)
    debug_printf("\n****************************\n%s", sendbuf);

  /* Clear the input buffer */
  bzero(sendbuf, sizeof(sendbuf));

  /* now send the body */
  DEBUG(D_acl)
    debug_printf("Writing body:\n****************************\n");
  (void)fseek(data_file, SPOOL_DATA_START_OFFSET, SEEK_SET);
  while((fread(sendbuf, 1, sizeof(sendbuf)-1, data_file)) > 0) {
    flushbuffer(sockfd, sendbuf);
    bzero(sendbuf, sizeof(sendbuf));
  }
  DEBUG(D_acl)
    debug_printf("\n****************************\n");

  /* shutdown() the socket */
  if(shutdown(sockfd, 1) < 0){
    DEBUG(D_acl)
      debug_printf("Couldn't shutdown socket: %s\n", strerror(errno));
    log_write(0,LOG_MAIN,"Couldn't shutdown socket: %s\n", strerror(errno));
    /* If there is a problem with the shutdown()
     * defer the mail. */
    (void)fclose(data_file);
    return retval;
  }
  DEBUG(D_acl)
    debug_printf("\n-------------------------\nInput sent.\n-------------------------\n");

    /********************************
   * receiving output from dccifd *
   ********************************/
  DEBUG(D_acl)
    debug_printf("\n-------------------------------------\nNow receiving output from server\n-----------------------------------\n");

  /******************************************************************
   * We should get 3 lines:                                         *
   * 1/ First line is overall result: either 'A' for Accept,        *
   *    'R' for Reject, 'S' for accept Some recipients or           *
   *    'T' for a Temporary error.                                  *
   * 2/ Second line contains the list of Accepted/Rejected          *
   *    recipients in the form AARRA (A = accepted, R = rejected).  *
   * 3/ Third line contains the X-DCC header.                       *
   ******************************************************************/

  line = 1;    /* we start at the first line of the output */
  j = 0;       /* will be used as index for the recipients list */
  k = 0;       /* initializing the index of the X-DCC header: dcc_header_str[k] */

  /* Let's read from the socket until there's nothing left to read */
  bzero(recvbuf, sizeof(recvbuf));
  while((resp = read(sockfd, recvbuf, sizeof(recvbuf)-1)) > 0) {
    /* How much did we get from the socket */
    c = Ustrlen(recvbuf) + 1;
    DEBUG(D_acl)
      debug_printf("Length of the output buffer is: %d\nOutput buffer is:\n------------\n%s\n-----------\n", c, recvbuf);

    /* Now let's read each character and see what we've got */
    for(i = 0; i < c; i++) {
      /* First check if we reached the end of the line and
       * then increment the line counter */
      if(recvbuf[i] == '\n') {
        line++;
      }
      else {
        /* The first character of the first line is the
         * overall response. If there's another character
         * on that line it is not correct. */
        if(line == 1) {
          if(i == 0) {
            /* Now get the value and set the
             * return value accordingly */
            if(recvbuf[i] == 'A') {
              DEBUG(D_acl)
                debug_printf("Overall result = A\treturning OK\n");
              Ustrcpy(dcc_return_text, "Mail accepted by DCC");
              dcc_result = US"A";
              retval = OK;
            }
            else if(recvbuf[i] == 'R') {
              DEBUG(D_acl)
                debug_printf("Overall result = R\treturning FAIL\n");
              dcc_result = US"R";
              retval = FAIL;
              if(sender_host_name) {
                log_write(0, LOG_MAIN, "H=%s [%s] F=<%s>: rejected by DCC", sender_host_name, sender_host_address, sender_address);
              }
              else {
                log_write(0, LOG_MAIN, "H=[%s] F=<%s>: rejected by DCC", sender_host_address, sender_address);
              }
              Ustrncpy(dcc_return_text, dcc_reject_message, Ustrlen(dcc_reject_message) + 1);
            }
            else if(recvbuf[i] == 'S') {
              DEBUG(D_acl)
                debug_printf("Overall result  = S\treturning OK\n");
              Ustrcpy(dcc_return_text, "Not all recipients accepted by DCC");
              /* Since we're in an ACL we want a global result
               * so we accept for all */
              dcc_result = US"A";
              retval = OK;
            }
            else if(recvbuf[i] == 'G') {
              DEBUG(D_acl)
                debug_printf("Overall result  = G\treturning FAIL\n");
              Ustrcpy(dcc_return_text, "Greylisted by DCC");
              dcc_result = US"G";
              retval = FAIL;
            }
            else if(recvbuf[i] == 'T') {
              DEBUG(D_acl)
                debug_printf("Overall result = T\treturning DEFER\n");
              retval = DEFER;
              log_write(0,LOG_MAIN,"Temporary error with DCC: %s\n", recvbuf);
              Ustrcpy(dcc_return_text, "Temporary error with DCC");
              dcc_result = US"T";
            }
            else {
              DEBUG(D_acl)
                debug_printf("Overall result = something else\treturning DEFER\n");
              retval = DEFER;
              log_write(0,LOG_MAIN,"Unknown DCC response: %s\n", recvbuf);
              Ustrcpy(dcc_return_text, "Unknown DCC response");
              dcc_result = US"T";
            }
          }
          else {
          /* We're on the first line but not on the first character,
           * there must be something wrong. */
            DEBUG(D_acl)
              debug_printf("Line = %d but i = %d != 0  character is %c - This is wrong!\n", line, i, recvbuf[i]);
              log_write(0,LOG_MAIN,"Wrong header from DCC, output is %s\n", recvbuf);
          }
        }
        else if(line == 2) {
          /* On the second line we get a list of
           * answers for each recipient. We don't care about
           * it because we're in an acl and take the
           * global result. */
        }
        else if(line > 2) {
          /* The third and following lines are the X-DCC header,
           * so we store it in dcc_header_str. */
          /* check if we don't get more than we can handle */
          if(k < sizeof(dcc_header_str)) { 
            dcc_header_str[k] = recvbuf[i];
            k++;
          }
          else {
            DEBUG(D_acl)
              debug_printf("We got more output than we can store in the X-DCC header. Truncating at 120 characters.\n");
          }
        }
        else {
          /* Wrong line number. There must be a problem with the output. */
          DEBUG(D_acl)
            debug_printf("Wrong line number in output. Line number is %d\n", line);
        }
      }
    }
    /* we reinitialize the output buffer before we read again */
    bzero(recvbuf,sizeof(recvbuf));
  }
  /* We have read everything from the socket */

  /* We need to terminate the X-DCC header with a '\n' character. This needs to be k-1
   * since dcc_header_str[k] contains '\0'. */
  dcc_header_str[k-1] = '\n';

  /* Now let's sum up what we've got. */
  DEBUG(D_acl)
    debug_printf("\n--------------------------\nOverall result = %d\nX-DCC header: %sReturn message: %s\ndcc_result: %s\n", retval, dcc_header_str, dcc_return_text, dcc_result);

  /* We only add the X-DCC header if it starts with X-DCC */
  if(!(Ustrncmp(dcc_header_str, "X-DCC", 5))){
    dcc_header = dcc_header_str;
    if(dcc_direct_add_header) {
      header_add(' ' , "%s", dcc_header_str);
  /* since the MIME ACL already writes the .eml file to disk without DCC Header we've to erase it */
      unspool_mbox();
    }
  }
  else {
    DEBUG(D_acl)
      debug_printf("Wrong format of the X-DCC header: %s\n", dcc_header_str);
  }

  /* check if we should add additional headers passed in acl_m_dcc_add_header */
  if(dcc_direct_add_header) {
    if (((xtra_hdrs = expand_string(US"$acl_m_dcc_add_header")) != NULL) && (xtra_hdrs[0] != '\0')) {
      Ustrncpy(dcc_xtra_hdrs, xtra_hdrs, sizeof(dcc_xtra_hdrs) - 2);
      if (dcc_xtra_hdrs[Ustrlen(dcc_xtra_hdrs)-1] != '\n')
        Ustrcat(dcc_xtra_hdrs, "\n");
      header_add(' ', "%s", dcc_xtra_hdrs);
      DEBUG(D_acl)
        debug_printf("adding additional headers in $acl_m_dcc_add_header: %s", dcc_xtra_hdrs);
    }
  }

  dcc_ok = 1;
  /* Now return to exim main process */
  DEBUG(D_acl)
    debug_printf("Before returning to exim main process:\nreturn_text = %s - retval = %d\ndcc_result = %s\n", dcc_return_text, retval, dcc_result);

  (void)fclose(data_file);
  dcc_rc = retval;
  return dcc_rc;
}
Exemple #5
0
int
spool_read_header(uschar *name, BOOL read_headers, BOOL subdir_set)
{
FILE *f = NULL;
int n;
int rcount = 0;
long int uid, gid;
BOOL inheader = FALSE;
uschar *p;

/* Reset all the global variables to their default values. However, there is
one exception. DO NOT change the default value of dont_deliver, because it may
be forced by an external setting. */

acl_var_c = acl_var_m = NULL;
authenticated_id = NULL;
authenticated_sender = NULL;
allow_unqualified_recipient = FALSE;
allow_unqualified_sender = FALSE;
body_linecount = 0;
body_zerocount = 0;
deliver_firsttime = FALSE;
deliver_freeze = FALSE;
deliver_frozen_at = 0;
deliver_manual_thaw = FALSE;
/* dont_deliver must NOT be reset */
header_list = header_last = NULL;
host_lookup_deferred = FALSE;
host_lookup_failed = FALSE;
interface_address = NULL;
interface_port = 0;
local_error_message = FALSE;
local_scan_data = NULL;
max_received_linelength = 0;
message_linecount = 0;
received_protocol = NULL;
received_count = 0;
recipients_list = NULL;
sender_address = NULL;
sender_fullhost = NULL;
sender_helo_name = NULL;
sender_host_address = NULL;
sender_host_name = NULL;
sender_host_port = 0;
sender_host_authenticated = NULL;
sender_ident = NULL;
sender_local = FALSE;
sender_set_untrusted = FALSE;
smtp_active_hostname = primary_hostname;
tree_nonrecipients = NULL;

#ifdef EXPERIMENTAL_BRIGHTMAIL
bmi_run = 0;
bmi_verdicts = NULL;
#endif

#ifndef DISABLE_DKIM
dkim_signers = NULL;
dkim_disable_verify = FALSE;
dkim_collect_input = FALSE;
#endif

#ifdef SUPPORT_TLS
tls_in.certificate_verified = FALSE;
# ifdef EXPERIMENTAL_DANE
tls_in.dane_verified = FALSE;
# endif
tls_in.cipher = NULL;
# ifndef COMPILE_UTILITY	/* tls support fns not built in */
tls_free_cert(&tls_in.ourcert);
tls_free_cert(&tls_in.peercert);
# endif
tls_in.peerdn = NULL;
tls_in.sni = NULL;
tls_in.ocsp = OCSP_NOT_REQ;
#endif

#ifdef WITH_CONTENT_SCAN
spam_bar = NULL;
spam_score = NULL;
spam_score_int = NULL;
#endif

#if defined(SUPPORT_I18N) && !defined(COMPILE_UTILITY)
message_smtputf8 = FALSE;
message_utf8_downconvert = 0;
#endif

dsn_ret = 0;
dsn_envid = NULL;

/* Generate the full name and open the file. If message_subdir is already
set, just look in the given directory. Otherwise, look in both the split
and unsplit directories, as for the data file above. */

for (n = 0; n < 2; n++)
  {
  if (!subdir_set)
    message_subdir[0] = (split_spool_directory == (n == 0))? name[5] : 0;
  sprintf(CS big_buffer, "%s/input/%s/%s", spool_directory, message_subdir,
    name);
  f = Ufopen(big_buffer, "rb");
  if (f != NULL) break;
  if (n != 0 || subdir_set || errno != ENOENT) return spool_read_notopen;
  }

errno = 0;

#ifndef COMPILE_UTILITY
DEBUG(D_deliver) debug_printf("reading spool file %s\n", name);
#endif  /* COMPILE_UTILITY */

/* The first line of a spool file contains the message id followed by -H (i.e.
the file name), in order to make the file self-identifying. */

if (Ufgets(big_buffer, big_buffer_size, f) == NULL) goto SPOOL_READ_ERROR;
if (Ustrlen(big_buffer) != MESSAGE_ID_LENGTH + 3 ||
    Ustrncmp(big_buffer, name, MESSAGE_ID_LENGTH + 2) != 0)
  goto SPOOL_FORMAT_ERROR;

/* The next three lines in the header file are in a fixed format. The first
contains the login, uid, and gid of the user who caused the file to be written.
There are known cases where a negative gid is used, so we allow for both
negative uids and gids. The second contains the mail address of the message's
sender, enclosed in <>. The third contains the time the message was received,
and the number of warning messages for delivery delays that have been sent. */

if (Ufgets(big_buffer, big_buffer_size, f) == NULL) goto SPOOL_READ_ERROR;

p = big_buffer + Ustrlen(big_buffer);
while (p > big_buffer && isspace(p[-1])) p--;
*p = 0;
if (!isdigit(p[-1])) goto SPOOL_FORMAT_ERROR;
while (p > big_buffer && (isdigit(p[-1]) || '-' == p[-1])) p--;
gid = Uatoi(p);
if (p <= big_buffer || *(--p) != ' ') goto SPOOL_FORMAT_ERROR;
*p = 0;
if (!isdigit(p[-1])) goto SPOOL_FORMAT_ERROR;
while (p > big_buffer && (isdigit(p[-1]) || '-' == p[-1])) p--;
uid = Uatoi(p);
if (p <= big_buffer || *(--p) != ' ') goto SPOOL_FORMAT_ERROR;
*p = 0;

originator_login = string_copy(big_buffer);
originator_uid = (uid_t)uid;
originator_gid = (gid_t)gid;

/* envelope from */
if (Ufgets(big_buffer, big_buffer_size, f) == NULL) goto SPOOL_READ_ERROR;
n = Ustrlen(big_buffer);
if (n < 3 || big_buffer[0] != '<' || big_buffer[n-2] != '>')
  goto SPOOL_FORMAT_ERROR;

sender_address = store_get(n-2);
Ustrncpy(sender_address, big_buffer+1, n-3);
sender_address[n-3] = 0;

/* time */
if (Ufgets(big_buffer, big_buffer_size, f) == NULL) goto SPOOL_READ_ERROR;
if (sscanf(CS big_buffer, "%d %d", &received_time, &warning_count) != 2)
  goto SPOOL_FORMAT_ERROR;

message_age = time(NULL) - received_time;

#ifndef COMPILE_UTILITY
DEBUG(D_deliver) debug_printf("user=%s uid=%ld gid=%ld sender=%s\n",
  originator_login, (long int)originator_uid, (long int)originator_gid,
  sender_address);
#endif  /* COMPILE_UTILITY */

/* Now there may be a number of optional lines, each starting with "-". If you
add a new setting here, make sure you set the default above.

Because there are now quite a number of different possibilities, we use a
switch on the first character to avoid too many failing tests. Thanks to Nico
Erfurth for the patch that implemented this. I have made it even more efficient
by not re-scanning the first two characters.

To allow new versions of Exim that add additional flags to interwork with older
versions that do not understand them, just ignore any lines starting with "-"
that we don't recognize. Otherwise it wouldn't be possible to back off a new
version that left new-style flags written on the spool. */

p = big_buffer + 2;
for (;;)
  {
  int len;
  if (Ufgets(big_buffer, big_buffer_size, f) == NULL) goto SPOOL_READ_ERROR;
  if (big_buffer[0] != '-') break;
  while (  (len = Ustrlen(big_buffer)) == big_buffer_size-1
	&& big_buffer[len-1] != '\n'
	)
    {	/* buffer not big enough for line; certs make this possible */
    uschar * buf;
    if (big_buffer_size >= BIG_BUFFER_SIZE*4) goto SPOOL_READ_ERROR;
    buf = store_get_perm(big_buffer_size *= 2);
    memcpy(buf, big_buffer, --len);
    big_buffer = buf;
    if (Ufgets(big_buffer+len, big_buffer_size-len, f) == NULL)
      goto SPOOL_READ_ERROR;
    }
  big_buffer[len-1] = 0;

  switch(big_buffer[1])
    {
    case 'a':

    /* Nowadays we use "-aclc" and "-aclm" for the different types of ACL
    variable, because Exim allows any number of them, with arbitrary names.
    The line in the spool file is "-acl[cm] <name> <length>". The name excludes
    the c or m. */

    if (Ustrncmp(p, "clc ", 4) == 0 ||
        Ustrncmp(p, "clm ", 4) == 0)
      {
      uschar *name, *endptr;
      int count;
      tree_node *node;
      endptr = Ustrchr(big_buffer + 6, ' ');
      if (endptr == NULL) goto SPOOL_FORMAT_ERROR;
      name = string_sprintf("%c%.*s", big_buffer[4], endptr - big_buffer - 6,
        big_buffer + 6);
      if (sscanf(CS endptr, " %d", &count) != 1) goto SPOOL_FORMAT_ERROR;
      node = acl_var_create(name);
      node->data.ptr = store_get(count + 1);
      if (fread(node->data.ptr, 1, count+1, f) < count) goto SPOOL_READ_ERROR;
      ((uschar*)node->data.ptr)[count] = 0;
      }

    else if (Ustrcmp(p, "llow_unqualified_recipient") == 0)
      allow_unqualified_recipient = TRUE;
    else if (Ustrcmp(p, "llow_unqualified_sender") == 0)
      allow_unqualified_sender = TRUE;

    else if (Ustrncmp(p, "uth_id", 6) == 0)
      authenticated_id = string_copy(big_buffer + 9);
    else if (Ustrncmp(p, "uth_sender", 10) == 0)
      authenticated_sender = string_copy(big_buffer + 13);
    else if (Ustrncmp(p, "ctive_hostname", 14) == 0)
      smtp_active_hostname = string_copy(big_buffer + 17);

    /* For long-term backward compatibility, we recognize "-acl", which was
    used before the number of ACL variables changed from 10 to 20. This was
    before the subsequent change to an arbitrary number of named variables.
    This code is retained so that upgrades from very old versions can still
    handle old-format spool files. The value given after "-acl" is a number
    that is 0-9 for connection variables, and 10-19 for message variables. */

    else if (Ustrncmp(p, "cl ", 3) == 0)
      {
      int index, count;
      uschar name[20];   /* Need plenty of space for %d format */
      tree_node *node;
      if (  sscanf(CS big_buffer + 5, "%d %d", &index, &count) != 2
	 || index >= 20
         )
        goto SPOOL_FORMAT_ERROR;
      if (index < 10)
        (void) string_format(name, sizeof(name), "%c%d", 'c', index);
      else
        (void) string_format(name, sizeof(name), "%c%d", 'm', index - 10);
      node = acl_var_create(name);
      node->data.ptr = store_get(count + 1);
      if (fread(node->data.ptr, 1, count+1, f) < count) goto SPOOL_READ_ERROR;
      ((uschar*)node->data.ptr)[count] = 0;
      }
    break;

    case 'b':
    if (Ustrncmp(p, "ody_linecount", 13) == 0)
      body_linecount = Uatoi(big_buffer + 15);
    else if (Ustrncmp(p, "ody_zerocount", 13) == 0)
      body_zerocount = Uatoi(big_buffer + 15);
#ifdef EXPERIMENTAL_BRIGHTMAIL
    else if (Ustrncmp(p, "mi_verdicts ", 12) == 0)
      bmi_verdicts = string_copy(big_buffer + 14);
#endif
    break;

    case 'd':
    if (Ustrcmp(p, "eliver_firsttime") == 0)
      deliver_firsttime = TRUE;
    /* Check if the dsn flags have been set in the header file */
    else if (Ustrncmp(p, "sn_ret", 6) == 0)
      dsn_ret= atoi(CS big_buffer + 8);
    else if (Ustrncmp(p, "sn_envid", 8) == 0)
      dsn_envid = string_copy(big_buffer + 11);
    break;

    case 'f':
    if (Ustrncmp(p, "rozen", 5) == 0)
      {
      deliver_freeze = TRUE;
      if (sscanf(CS big_buffer+7, TIME_T_FMT, &deliver_frozen_at) != 1)
	goto SPOOL_READ_ERROR;
      }
    break;

    case 'h':
    if (Ustrcmp(p, "ost_lookup_deferred") == 0)
      host_lookup_deferred = TRUE;
    else if (Ustrcmp(p, "ost_lookup_failed") == 0)
      host_lookup_failed = TRUE;
    else if (Ustrncmp(p, "ost_auth", 8) == 0)
      sender_host_authenticated = string_copy(big_buffer + 11);
    else if (Ustrncmp(p, "ost_name", 8) == 0)
      sender_host_name = string_copy(big_buffer + 11);
    else if (Ustrncmp(p, "elo_name", 8) == 0)
      sender_helo_name = string_copy(big_buffer + 11);

    /* We now record the port number after the address, separated by a
    dot. For compatibility during upgrading, do nothing if there
    isn't a value (it gets left at zero). */

    else if (Ustrncmp(p, "ost_address", 11) == 0)
      {
      sender_host_port = host_address_extract_port(big_buffer + 14);
      sender_host_address = string_copy(big_buffer + 14);
      }
    break;

    case 'i':
    if (Ustrncmp(p, "nterface_address", 16) == 0)
      {
      interface_port = host_address_extract_port(big_buffer + 19);
      interface_address = string_copy(big_buffer + 19);
      }
    else if (Ustrncmp(p, "dent", 4) == 0)
      sender_ident = string_copy(big_buffer + 7);
    break;

    case 'l':
    if (Ustrcmp(p, "ocal") == 0) sender_local = TRUE;
    else if (Ustrcmp(big_buffer, "-localerror") == 0)
      local_error_message = TRUE;
    else if (Ustrncmp(p, "ocal_scan ", 10) == 0)
      local_scan_data = string_copy(big_buffer + 12);
    break;

    case 'm':
    if (Ustrcmp(p, "anual_thaw") == 0) deliver_manual_thaw = TRUE;
    else if (Ustrncmp(p, "ax_received_linelength", 22) == 0)
      max_received_linelength = Uatoi(big_buffer + 24);
    break;

    case 'N':
    if (*p == 0) dont_deliver = TRUE;   /* -N */
    break;

    case 'r':
    if (Ustrncmp(p, "eceived_protocol", 16) == 0)
      received_protocol = string_copy(big_buffer + 19);
    break;

    case 's':
    if (Ustrncmp(p, "ender_set_untrusted", 19) == 0)
      sender_set_untrusted = TRUE;
#ifdef WITH_CONTENT_SCAN
    else if (Ustrncmp(p, "pam_bar ", 8) == 0)
      spam_bar = string_copy(big_buffer + 10);
    else if (Ustrncmp(p, "pam_score ", 10) == 0)
      spam_score = string_copy(big_buffer + 12);
    else if (Ustrncmp(p, "pam_score_int ", 14) == 0)
      spam_score_int = string_copy(big_buffer + 16);
#endif
#if defined(SUPPORT_I18N) && !defined(COMPILE_UTILITY)
    else if (Ustrncmp(p, "mtputf8", 7) == 0)
      message_smtputf8 = TRUE;
#endif
    break;

#ifdef SUPPORT_TLS
    case 't':
    if (Ustrncmp(p, "ls_certificate_verified", 23) == 0)
      tls_in.certificate_verified = TRUE;
    else if (Ustrncmp(p, "ls_cipher", 9) == 0)
      tls_in.cipher = string_copy(big_buffer + 12);
# ifndef COMPILE_UTILITY	/* tls support fns not built in */
    else if (Ustrncmp(p, "ls_ourcert", 10) == 0)
      (void) tls_import_cert(big_buffer + 13, &tls_in.ourcert);
    else if (Ustrncmp(p, "ls_peercert", 11) == 0)
      (void) tls_import_cert(big_buffer + 14, &tls_in.peercert);
# endif
    else if (Ustrncmp(p, "ls_peerdn", 9) == 0)
      tls_in.peerdn = string_unprinting(string_copy(big_buffer + 12));
    else if (Ustrncmp(p, "ls_sni", 6) == 0)
      tls_in.sni = string_unprinting(string_copy(big_buffer + 9));
    else if (Ustrncmp(p, "ls_ocsp", 7) == 0)
      tls_in.ocsp = big_buffer[10] - '0';
    break;
#endif

#if defined(SUPPORT_I18N) && !defined(COMPILE_UTILITY)
    case 'u':
    if (Ustrncmp(p, "tf8_downcvt", 11) == 0)
      message_utf8_downconvert = 1;
    else if (Ustrncmp(p, "tf8_optdowncvt", 15) == 0)
      message_utf8_downconvert = -1;
    break;
#endif

    default:    /* Present because some compilers complain if all */
    break;      /* possibilities are not covered. */
    }
  }

/* Build sender_fullhost if required */

#ifndef COMPILE_UTILITY
host_build_sender_fullhost();
#endif  /* COMPILE_UTILITY */

#ifndef COMPILE_UTILITY
DEBUG(D_deliver)
  debug_printf("sender_local=%d ident=%s\n", sender_local,
    (sender_ident == NULL)? US"unset" : sender_ident);
#endif  /* COMPILE_UTILITY */

/* We now have the tree of addresses NOT to deliver to, or a line
containing "XX", indicating no tree. */

if (Ustrncmp(big_buffer, "XX\n", 3) != 0 &&
  !read_nonrecipients_tree(&tree_nonrecipients, f, big_buffer, big_buffer_size))
    goto SPOOL_FORMAT_ERROR;

#ifndef COMPILE_UTILITY
DEBUG(D_deliver)
  {
  debug_printf("Non-recipients:\n");
  debug_print_tree(tree_nonrecipients);
  }
#endif  /* COMPILE_UTILITY */

/* After reading the tree, the next line has not yet been read into the
buffer. It contains the count of recipients which follow on separate lines. */

if (Ufgets(big_buffer, big_buffer_size, f) == NULL) goto SPOOL_READ_ERROR;
if (sscanf(CS big_buffer, "%d", &rcount) != 1) goto SPOOL_FORMAT_ERROR;

#ifndef COMPILE_UTILITY
DEBUG(D_deliver) debug_printf("recipients_count=%d\n", rcount);
#endif  /* COMPILE_UTILITY */

recipients_list_max = rcount;
recipients_list = store_get(rcount * sizeof(recipient_item));

for (recipients_count = 0; recipients_count < rcount; recipients_count++)
  {
  int nn;
  int pno = -1;
  int dsn_flags = 0;
  uschar *orcpt = NULL;
  uschar *errors_to = NULL;
  uschar *p;

  if (Ufgets(big_buffer, big_buffer_size, f) == NULL) goto SPOOL_READ_ERROR;
  nn = Ustrlen(big_buffer);
  if (nn < 2) goto SPOOL_FORMAT_ERROR;

  /* Remove the newline; this terminates the address if there is no additional
  data on the line. */

  p = big_buffer + nn - 1;
  *p-- = 0;

  /* Look back from the end of the line for digits and special terminators.
  Since an address must end with a domain, we can tell that extra data is
  present by the presence of the terminator, which is always some character
  that cannot exist in a domain. (If I'd thought of the need for additional
  data early on, I'd have put it at the start, with the address at the end. As
  it is, we have to operate backwards. Addresses are permitted to contain
  spaces, you see.)

  This code has to cope with various versions of this data that have evolved
  over time. In all cases, the line might just contain an address, with no
  additional data. Otherwise, the possibilities are as follows:

  Exim 3 type:       <address><space><digits>,<digits>,<digits>

    The second set of digits is the parent number for one_time addresses. The
    other values were remnants of earlier experiments that were abandoned.

  Exim 4 first type: <address><space><digits>

    The digits are the parent number for one_time addresses.

  Exim 4 new type:   <address><space><data>#<type bits>

    The type bits indicate what the contents of the data are.

    Bit 01 indicates that, reading from right to left, the data
      ends with <errors_to address><space><len>,<pno> where pno is
      the parent number for one_time addresses, and len is the length
      of the errors_to address (zero meaning none).

    Bit 02 indicates that, again reading from right to left, the data continues
     with orcpt len(orcpt),dsn_flags
   */

  while (isdigit(*p)) p--;

  /* Handle Exim 3 spool files */

  if (*p == ',')
    {
    int dummy;
    while (isdigit(*(--p)) || *p == ',');
    if (*p == ' ')
      {
      *p++ = 0;
      (void)sscanf(CS p, "%d,%d", &dummy, &pno);
      }
    }

  /* Handle early Exim 4 spool files */

  else if (*p == ' ')
    {
    *p++ = 0;
    (void)sscanf(CS p, "%d", &pno);
    }

  /* Handle current format Exim 4 spool files */

  else if (*p == '#')
    {
    int flags;

#if !defined (COMPILE_UTILITY)
    DEBUG(D_deliver) debug_printf("**** SPOOL_IN - Exim 4 standard format spoolfile\n");
#endif

    (void)sscanf(CS p+1, "%d", &flags);

    if ((flags & 0x01) != 0)      /* one_time data exists */
      {
      int len;
      while (isdigit(*(--p)) || *p == ',' || *p == '-');
      (void)sscanf(CS p+1, "%d,%d", &len, &pno);
      *p = 0;
      if (len > 0)
        {
        p -= len;
        errors_to = string_copy(p);
        }
      }

    *(--p) = 0;   /* Terminate address */
    if ((flags & 0x02) != 0)      /* one_time data exists */
      {
      int len;
      while (isdigit(*(--p)) || *p == ',' || *p == '-');
      (void)sscanf(CS p+1, "%d,%d", &len, &dsn_flags);
      *p = 0;
      if (len > 0)
        {
        p -= len;
        orcpt = string_copy(p);
        }
      }

    *(--p) = 0;   /* Terminate address */
    }
#if !defined(COMPILE_UTILITY)
  else
    { DEBUG(D_deliver) debug_printf("**** SPOOL_IN - No additional fields\n"); }

  if ((orcpt != NULL) || (dsn_flags != 0))
    {
    DEBUG(D_deliver) debug_printf("**** SPOOL_IN - address: |%s| orcpt: |%s| dsn_flags: %d\n",
      big_buffer, orcpt, dsn_flags);
    }
  if (errors_to != NULL)
    {
    DEBUG(D_deliver) debug_printf("**** SPOOL_IN - address: |%s| errorsto: |%s|\n",
      big_buffer, errors_to);
    }
#endif

  recipients_list[recipients_count].address = string_copy(big_buffer);
  recipients_list[recipients_count].pno = pno;
  recipients_list[recipients_count].errors_to = errors_to;
  recipients_list[recipients_count].orcpt = orcpt;
  recipients_list[recipients_count].dsn_flags = dsn_flags;
  }

/* The remainder of the spool header file contains the headers for the message,
separated off from the previous data by a blank line. Each header is preceded
by a count of its length and either a certain letter (for various identified
headers), space (for a miscellaneous live header) or an asterisk (for a header
that has been rewritten). Count the Received: headers. We read the headers
always, in order to check on the format of the file, but only create a header
list if requested to do so. */

inheader = TRUE;
if (Ufgets(big_buffer, big_buffer_size, f) == NULL) goto SPOOL_READ_ERROR;
if (big_buffer[0] != '\n') goto SPOOL_FORMAT_ERROR;

while ((n = fgetc(f)) != EOF)
  {
  header_line *h;
  uschar flag[4];
  int i;

  if (!isdigit(n)) goto SPOOL_FORMAT_ERROR;
  if(ungetc(n, f) == EOF  ||  fscanf(f, "%d%c ", &n, flag) == EOF)
    goto SPOOL_READ_ERROR;
  if (flag[0] != '*') message_size += n;  /* Omit non-transmitted headers */

  if (read_headers)
    {
    h = store_get(sizeof(header_line));
    h->next = NULL;
    h->type = flag[0];
    h->slen = n;
    h->text = store_get(n+1);

    if (h->type == htype_received) received_count++;

    if (header_list == NULL) header_list = h;
      else header_last->next = h;
    header_last = h;

    for (i = 0; i < n; i++)
      {
      int c = fgetc(f);
      if (c == 0 || c == EOF) goto SPOOL_FORMAT_ERROR;
      if (c == '\n' && h->type != htype_old) message_linecount++;
      h->text[i] = c;
      }
    h->text[i] = 0;
    }

  /* Not requiring header data, just skip through the bytes */

  else for (i = 0; i < n; i++)
    {
    int c = fgetc(f);
    if (c == 0 || c == EOF) goto SPOOL_FORMAT_ERROR;
    }
  }

/* We have successfully read the data in the header file. Update the message
line count by adding the body linecount to the header linecount. Close the file
and give a positive response. */

#ifndef COMPILE_UTILITY
DEBUG(D_deliver) debug_printf("body_linecount=%d message_linecount=%d\n",
  body_linecount, message_linecount);
#endif  /* COMPILE_UTILITY */

message_linecount += body_linecount;

fclose(f);
return spool_read_OK;


/* There was an error reading the spool or there was missing data,
or there was a format error. A "read error" with no errno means an
unexpected EOF, which we treat as a format error. */

SPOOL_READ_ERROR:
if (errno != 0)
  {
  n = errno;

#ifndef COMPILE_UTILITY
  DEBUG(D_any) debug_printf("Error while reading spool file %s\n", name);
#endif  /* COMPILE_UTILITY */

  fclose(f);
  errno = n;
  return inheader? spool_read_hdrerror : spool_read_enverror;
  }

SPOOL_FORMAT_ERROR:

#ifndef COMPILE_UTILITY
DEBUG(D_any) debug_printf("Format error in spool file %s\n", name);
#endif  /* COMPILE_UTILITY */

fclose(f);
errno = ERRNO_SPOOLFORMAT;
return inheader? spool_read_hdrerror : spool_read_enverror;
}
Exemple #6
0
Fichier : rda.c Projet : toddr/exim
static uschar *
rda_get_file_contents(redirect_block *rdata, int options, uschar **error,
  int *yield)
{
FILE *fwd;
uschar *filebuf;
uschar *filename = rdata->string;
BOOL uid_ok = !rdata->check_owner;
BOOL gid_ok = !rdata->check_group;
struct stat statbuf;

/* Attempt to open the file. If it appears not to exist, check up on the
containing directory by statting it. If the directory does not exist, we treat
this situation as an error (which will cause delivery to defer); otherwise we
pass back FF_NONEXIST, which causes the redirect router to decline.

However, if the ignore_enotdir option is set (to ignore "something on the
path is not a directory" errors), the right behaviour seems to be not to do the
directory test. */

fwd = Ufopen(filename, "rb");
if (fwd == NULL)
  {
  switch(errno)
    {
    case ENOENT:          /* File does not exist */
    DEBUG(D_route) debug_printf("%s does not exist\n%schecking parent directory\n",
      filename,
      ((options & RDO_ENOTDIR) != 0)? "ignore_enotdir set => skip " : "");
    *yield = (((options & RDO_ENOTDIR) != 0) ||
              rda_exists(filename, error) == FILE_NOT_EXIST)?
      FF_NONEXIST : FF_ERROR;
    return NULL;

    case ENOTDIR:         /* Something on the path isn't a directory */
    if ((options & RDO_ENOTDIR) == 0) goto DEFAULT_ERROR;
    DEBUG(D_route) debug_printf("non-directory on path %s: file assumed not to "
      "exist\n", filename);
    *yield = FF_NONEXIST;
    return NULL;

    case EACCES:           /* Permission denied */
    if ((options & RDO_EACCES) == 0) goto DEFAULT_ERROR;
    DEBUG(D_route) debug_printf("permission denied for %s: file assumed not to "
      "exist\n", filename);
    *yield = FF_NONEXIST;
    return NULL;

    DEFAULT_ERROR:
    default:
    *error = string_open_failed(errno, "%s", filename);
    *yield = FF_ERROR;
    return NULL;
    }
  }

/* Check that we have a regular file. */

if (fstat(fileno(fwd), &statbuf) != 0)
  {
  *error = string_sprintf("failed to stat %s: %s", filename, strerror(errno));
  goto ERROR_RETURN;
  }

if ((statbuf.st_mode & S_IFMT) != S_IFREG)
  {
  *error = string_sprintf("%s is not a regular file", filename);
  goto ERROR_RETURN;
  }

/* Check for unwanted mode bits */

if ((statbuf.st_mode & rdata->modemask) != 0)
  {
  *error = string_sprintf("bad mode (0%o) for %s: 0%o bit(s) unexpected",
    statbuf.st_mode, filename, statbuf.st_mode & rdata->modemask);
  goto ERROR_RETURN;
  }

/* Check the file owner and file group if required to do so. */

if (!uid_ok)
  {
  if (rdata->pw != NULL && statbuf.st_uid == rdata->pw->pw_uid)
    uid_ok = TRUE;
  else if (rdata->owners != NULL)
    {
    int i;
    for (i = 1; i <= (int)(rdata->owners[0]); i++)
      if (rdata->owners[i] == statbuf.st_uid) { uid_ok = TRUE; break; }
    }
  }

if (!gid_ok)
  {
  if (rdata->pw != NULL && statbuf.st_gid == rdata->pw->pw_gid)
    gid_ok = TRUE;
  else if (rdata->owngroups != NULL)
    {
    int i;
    for (i = 1; i <= (int)(rdata->owngroups[0]); i++)
      if (rdata->owngroups[i] == statbuf.st_gid) { gid_ok = TRUE; break; }
    }
  }

if (!uid_ok || !gid_ok)
  {
  *error = string_sprintf("bad %s for %s", uid_ok? "group" : "owner", filename);
  goto ERROR_RETURN;
  }

/* Put an upper limit on the size of the file, just to stop silly people
feeding in ridiculously large files, which can easily be created by making
files that have holes in them. */

if (statbuf.st_size > MAX_FILTER_SIZE)
  {
  *error = string_sprintf("%s is too big (max %d)", filename, MAX_FILTER_SIZE);
  goto ERROR_RETURN;
  }

/* Read the file in one go in order to minimize the time we have it open. */

filebuf = store_get(statbuf.st_size + 1);

if (fread(filebuf, 1, statbuf.st_size, fwd) != statbuf.st_size)
  {
  *error = string_sprintf("error while reading %s: %s",
    filename, strerror(errno));
  goto ERROR_RETURN;
  }
filebuf[statbuf.st_size] = 0;

DEBUG(D_route)
  debug_printf(OFF_T_FMT " bytes read from %s\n", statbuf.st_size, filename);

(void)fclose(fwd);
return filebuf;

/* Return an error: the string is already set up. */

ERROR_RETURN:
*yield = FF_ERROR;
(void)fclose(fwd);
return NULL;
}