コード例 #1
0
ファイル: spool_in.c プロジェクト: digideskio/exim
static BOOL
read_nonrecipients_tree(tree_node **connect, FILE *f, uschar *buffer,
  int buffer_size)
{
tree_node *node;
int n = Ustrlen(buffer);
BOOL right = buffer[1] == 'Y';

if (n < 5) return FALSE;    /* malformed line */
buffer[n-1] = 0;            /* Remove \n */
node = store_get(sizeof(tree_node) + n - 3);
*connect = node;
Ustrcpy(node->name, buffer + 3);
node->data.ptr = NULL;

if (buffer[0] == 'Y')
  {
  if (Ufgets(buffer, buffer_size, f) == NULL ||
    !read_nonrecipients_tree(&node->left, f, buffer, buffer_size))
      return FALSE;
  }
else node->left = NULL;

if (right)
  {
  if (Ufgets(buffer, buffer_size, f) == NULL ||
    !read_nonrecipients_tree(&node->right, f, buffer, buffer_size))
      return FALSE;
  }
else node->right = NULL;

(void) count_below(*connect);
return TRUE;
}
コード例 #2
0
ファイル: em_menu.c プロジェクト: Exim/exim
static void
msglogAction(Widget w, XtPointer client_data, XtPointer call_data)
{
int i;
Widget text = text_create(US client_data, text_depth);
uschar * fname = NULL;
FILE * f = NULL;

w = w;      /* Keep picky compilers happy */
call_data = call_data;

/* End up with the split version, so message looks right when non-exist */

for (i = 0; i < (spool_is_split ? 2:1); i++)
  {
  message_subdir[0] = i != 0 ? (US client_data)[5] : 0;
  fname = spool_fname(US"msglog", message_subdir, US client_data, US"");
  if ((f = fopen(CS fname, "r")))
    break;
  }

if (!f)
  text_showf(text, "%s: %s\n", fname, strerror(errno));
else
  {
  uschar buffer[256];
  while (Ufgets(buffer, sizeof(buffer), f) != NULL) text_show(text, buffer);
  fclose(f);
  }
}
コード例 #3
0
ファイル: em_menu.c プロジェクト: Exim/exim
static void
bodyAction(Widget w, XtPointer client_data, XtPointer call_data)
{
int i;
Widget text = text_create(US client_data, text_depth);
FILE *f = NULL;

w = w;      /* Keep picky compilers happy */
call_data = call_data;

for (i = 0; i < (spool_is_split? 2:1); i++)
  {
  uschar * fname;
  message_subdir[0] = i != 0 ? (US client_data)[5] : 0;
  fname = spool_fname(US"input", message_subdir, US client_data, US"-D");
  if ((f = fopen(CS fname, "r")))
    break;
  }

if (f == NULL)
  text_showf(text, "Failed to open file: %s\n", strerror(errno));
else
  {
  uschar buffer[256];
  int count = 0;

  while (Ufgets(buffer, sizeof(buffer), f) != NULL)
    {
    text_show(text, buffer);
    count += Ustrlen(buffer);
    if (count > body_max)
      {
      text_show(text, US"\n*** Message length exceeds BODY_MAX ***\n");
      break;
      }
    }
  fclose(f);
  }
}
コード例 #4
0
ファイル: autoreply.c プロジェクト: ArthasZRZ/exim
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);
    }
  }
コード例 #5
0
ファイル: spool_in.c プロジェクト: digideskio/exim
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;
}
コード例 #6
0
ファイル: em_log.c プロジェクト: fanf2/exim
void read_log(void)
{
struct stat statdata;
uschar buffer[log_buffer_len];

/* If log is not yet open, skip all of this. */

if (LOG != NULL)
  {
  fseek(LOG, log_position, SEEK_SET);

  while (Ufgets(buffer, log_buffer_len, LOG) != NULL)
    {
    uschar *id;
    uschar *p = buffer;
    void *reset_point;
    int length = Ustrlen(buffer);
    int i;

    /* Skip totally blank lines (paranoia: there shouldn't be any) */

    while (*p == ' ' || *p == '\t') p++;
    if (*p == '\n') continue;

    /* We should now have a complete log entry in the buffer; check
    it for various regular expression matches and take appropriate
    action. Get the current store point so we can reset to it. */

    reset_point = store_get(0);

    /* First, update any stripchart data values, noting that the zeroth
    stripchart is the queue length, which is handled elsewhere, and the
    1st may the a size monitor. */

    for (i = stripchart_varstart; i < stripchart_number; i++)
      {
      if (pcre_exec(stripchart_regex[i], NULL, CS buffer, length, 0, PCRE_EOPT,
            NULL, 0) >= 0)
        stripchart_total[i]++;
      }

    /* Munge the log entry and display shortened form on one line.
    We omit the date and show only the time. Remove any time zone offset.
    Take note of the presence of [pid]. */

    if (pcre_exec(yyyymmdd_regex,NULL,CS buffer,length,0,PCRE_EOPT,NULL,0) >= 0)
      {
      int pidlength = 0;
      if ((buffer[20] == '+' || buffer[20] == '-') &&
          isdigit(buffer[21]) && buffer[25] == ' ')
        memmove(buffer + 20, buffer + 26, Ustrlen(buffer + 26) + 1);
      if (buffer[20] == '[')
        {
        while (Ustrchr("[]0123456789", buffer[20+pidlength++]) != NULL);
        }
      id = string_copyn(buffer + 20 + pidlength, MESSAGE_ID_LENGTH);
      show_log("%s", buffer+11);
      }
    else
      {
      id = US"";
      show_log("%s", buffer);
      }

    /* Deal with frozen and unfrozen messages */

    if (strstric(buffer, US"frozen", FALSE) != NULL)
      {
      queue_item *qq = find_queue(id, queue_noop, 0);
      if (qq != NULL)
        {
        if (strstric(buffer, US"unfrozen", FALSE) != NULL)
          qq->frozen = FALSE;
        else qq->frozen = TRUE;
        }
      }

    /* Notice defer messages, and add the destination if it
    isn't already on the list for this message, with a pointer
    to the parent if we can. */

    if ((p = Ustrstr(buffer, "==")) != NULL)
      {
      queue_item *qq = find_queue(id, queue_noop, 0);
      if (qq != NULL)
        {
        dest_item *d;
        uschar *q, *r;
        p += 2;
        while (isspace(*p)) p++;
        q = p;
        while (*p != 0 && !isspace(*p))
          {
          if (*p++ != '\"') continue;
          while (*p != 0)
            {
            if (*p == '\\') p += 2;
              else if (*p++ == '\"') break;
            }
          }
        *p++ = 0;
        if ((r = strstric(q, qualify_domain, FALSE)) != NULL &&
          *(--r) == '@') *r = 0;

        /* If we already have this destination, as tested case-insensitively,
        do not add it to the destinations list. */

        d = find_dest(qq, q, dest_add, TRUE);

        if (d->parent == NULL)
          {
          while (isspace(*p)) p++;
          if (*p == '<')
            {
            dest_item *dd;
            q = ++p;
            while (*p != 0 && *p != '>') p++;
            *p = 0;
            if ((p = strstric(q, qualify_domain, FALSE)) != NULL &&
              *(--p) == '@') *p = 0;
            dd = find_dest(qq, q, dest_noop, FALSE);
            if (dd != NULL && dd != d) d->parent = dd;
            }
          }
        }
      }

    store_reset(reset_point);
    }
  }


/* We have to detect when the log file is changed, and switch to the new file.
In practice, for non-datestamped files, this means that some deliveries might
go unrecorded, since they'll be written to the old file, but this usually
happens in the middle of the night, and I don't think the hassle of keeping
track of two log files is worth it.

First we check the datestamped name of the log file if necessary; if it is
different to the file we currently have open, go for the new file. As happens
in Exim itself, we leave in the following inode check, even when datestamping
because it does no harm and will cope should a file actually be renamed for
some reason.

The test for a changed log file is to look up the inode of the file by name and
compare it with the saved inode of the file we currently are processing. This
accords with the usual interpretation of POSIX and other Unix specs that imply
"one file, one inode". However, it appears that on some Digital systems, if an
open file is unlinked, a new file may be created with the same inode while the
old file remains in existence. This can happen if the old log file is renamed,
processed in some way, and then deleted. To work round this, also test for a
link count of zero on the currently open file. */

if (log_datestamping)
  {
  uschar log_file_wanted[256];
  string_format(log_file_wanted, sizeof(log_file_wanted), CS log_file);
  if (Ustrcmp(log_file_wanted, log_file_open) != 0)
    {
    if (LOG != NULL)
      {
      fclose(LOG);
      LOG = NULL;
      }
    Ustrcpy(log_file_open, log_file_wanted);
    }
  }

if (LOG == NULL ||
    (fstat(fileno(LOG), &statdata) == 0 && statdata.st_nlink == 0) ||
    (Ustat(log_file, &statdata) == 0 && log_inode != statdata.st_ino))
  {
  FILE *TEST;

  /* Experiment shows that sometimes you can't immediately open
  the new log file - presumably immediately after the old one
  is renamed and before the new one exists. Therefore do a
  trial open first to be sure. */

  if ((TEST = fopen(CS log_file_open, "r")) != NULL)
    {
    if (LOG != NULL) fclose(LOG);
    LOG = TEST;
    fstat(fileno(LOG), &statdata);
    log_inode = statdata.st_ino;
    }
  }

/* Save the position we have got to in the log. */

if (LOG != NULL) log_position = ftell(LOG);
}
コード例 #7
0
ファイル: dbfn.c プロジェクト: mosqutip/EECS395-27
int
main(int argc, char **cargv)
{
open_db dbblock[8];
int max_db = sizeof(dbblock)/sizeof(open_db);
int current = -1;
int showtime = 0;
int i;
dbdata_wait *dbwait = NULL;
uschar **argv = USS cargv;
uschar buffer[256];
uschar structbuffer[1024];

if (argc != 2)
  {
  printf("Usage: test_dbfn directory\n");
  printf("The subdirectory called \"db\" in the given directory is used for\n");
  printf("the files used in this test program.\n");
  return 1;
  }

/* Initialize */

spool_directory = argv[1];
debug_selector = D_all - D_memory;
debug_file = stderr;
big_buffer = malloc(big_buffer_size);

for (i = 0; i < max_db; i++) dbblock[i].dbptr = NULL;

printf("\nExim's db functions tester: interface type is %s\n", EXIM_DBTYPE);
printf("DBM library: ");

#ifdef DB_VERSION_STRING
printf("Berkeley DB: %s\n", DB_VERSION_STRING);
#elif defined(BTREEVERSION) && defined(HASHVERSION)
  #ifdef USE_DB
  printf("probably Berkeley DB version 1.8x (native mode)\n");
  #else
  printf("probably Berkeley DB version 1.8x (compatibility mode)\n");
  #endif
#elif defined(_DBM_RDONLY) || defined(dbm_dirfno)
printf("probably ndbm\n");
#elif defined(USE_TDB)
printf("using tdb\n");
#else
  #ifdef USE_GDBM
  printf("probably GDBM (native mode)\n");
  #else
  printf("probably GDBM (compatibility mode)\n");
  #endif
#endif

/* Test the functions */

printf("\nTest the functions\n> ");

while (Ufgets(buffer, 256, stdin) != NULL)
  {
  int len = Ustrlen(buffer);
  int count = 1;
  clock_t start = 1;
  clock_t stop = 0;
  uschar *cmd = buffer;
  while (len > 0 && isspace((uschar)buffer[len-1])) len--;
  buffer[len] = 0;

  if (isdigit((uschar)*cmd))
    {
    count = Uatoi(cmd);
    while (isdigit((uschar)*cmd)) cmd++;
    while (isspace((uschar)*cmd)) cmd++;
    }

  if (Ustrncmp(cmd, "open", 4) == 0)
    {
    int i;
    open_db *odb;
    uschar *s = cmd + 4;
    while (isspace((uschar)*s)) s++;

    for (i = 0; i < max_db; i++)
      if (dbblock[i].dbptr == NULL) break;

    if (i >= max_db)
      {
      printf("Too many open databases\n> ");
      continue;
      }

    start = clock();
    odb = dbfn_open(s, O_RDWR, dbblock + i, TRUE);
    stop = clock();

    if (odb != NULL)
      {
      current = i;
      printf("opened %d\n", current);
      }
    /* Other error cases will have written messages */
    else if (errno == ENOENT)
      {
      printf("open failed: %s%s\n", strerror(errno),
        #ifdef USE_DB
        " (or other Berkeley DB error)"
        #else
        ""
        #endif
        );
      }
    }

  else if (Ustrncmp(cmd, "write", 5) == 0)
    {
    int rc = 0;
    uschar *key = cmd + 5;
    uschar *data;

    if (current < 0)
      {
      printf("No current database\n");
      continue;
      }

    while (isspace((uschar)*key)) key++;
    data = key;
    while (*data != 0 && !isspace((uschar)*data)) data++;
    *data++ = 0;
    while (isspace((uschar)*data)) data++;

    dbwait = (dbdata_wait *)(&structbuffer);
    Ustrcpy(dbwait->text, data);

    start = clock();
    while (count-- > 0)
      rc = dbfn_write(dbblock + current, key, dbwait,
        Ustrlen(data) + sizeof(dbdata_wait));
    stop = clock();
    if (rc != 0) printf("Failed: %s\n", strerror(errno));
    }

  else if (Ustrncmp(cmd, "read", 4) == 0)
    {
    uschar *key = cmd + 4;
    if (current < 0)
      {
      printf("No current database\n");
      continue;
      }
    while (isspace((uschar)*key)) key++;
    start = clock();
    while (count-- > 0)
      dbwait = (dbdata_wait *)dbfn_read_with_length(dbblock+ current, key, NULL);
    stop = clock();
    printf("%s\n", (dbwait == NULL)? "<not found>" : CS dbwait->text);
    }

  else if (Ustrncmp(cmd, "delete", 6) == 0)
    {
    uschar *key = cmd + 6;
    if (current < 0)
      {
      printf("No current database\n");
      continue;
      }
    while (isspace((uschar)*key)) key++;
    dbfn_delete(dbblock + current, key);
    }

  else if (Ustrncmp(cmd, "scan", 4) == 0)
    {
    EXIM_CURSOR *cursor;
    BOOL startflag = TRUE;
    uschar *key;
    uschar keybuffer[256];
    if (current < 0)
      {
      printf("No current database\n");
      continue;
      }
    start = clock();
    while ((key = dbfn_scan(dbblock + current, startflag, &cursor)) != NULL)
      {
      startflag = FALSE;
      Ustrcpy(keybuffer, key);
      dbwait = (dbdata_wait *)dbfn_read_with_length(dbblock + current,
        keybuffer, NULL);
      printf("%s: %s\n", keybuffer, dbwait->text);
      }
    stop = clock();
    printf("End of scan\n");
    }

  else if (Ustrncmp(cmd, "close", 5) == 0)
    {
    uschar *s = cmd + 5;
    while (isspace((uschar)*s)) s++;
    i = Uatoi(s);
    if (i >= max_db || dbblock[i].dbptr == NULL) printf("Not open\n"); else
      {
      start = clock();
      dbfn_close(dbblock + i);
      stop = clock();
      dbblock[i].dbptr = NULL;
      if (i == current) current = -1;
      }
    }

  else if (Ustrncmp(cmd, "file", 4) == 0)
    {
    uschar *s = cmd + 4;
    while (isspace((uschar)*s)) s++;
    i = Uatoi(s);
    if (i >= max_db || dbblock[i].dbptr == NULL) printf("Not open\n");
      else current = i;
    }

  else if (Ustrncmp(cmd, "time", 4) == 0)
    {
    showtime = ~showtime;
    printf("Timing %s\n", showtime? "on" : "off");
    }

  else if (Ustrcmp(cmd, "q") == 0 || Ustrncmp(cmd, "quit", 4) == 0) break;

  else if (Ustrncmp(cmd, "help", 4) == 0)
    {
    printf("close  [<number>]              close file [<number>]\n");
    printf("delete <key>                   remove record from current file\n");
    printf("file   <number>                make file <number> current\n");
    printf("open   <name>                  open db file\n");
    printf("q[uit]                         exit program\n");
    printf("read   <key>                   read record from current file\n");
    printf("scan                           scan current file\n");
    printf("time                           time display on/off\n");
    printf("write  <key> <rest-of-line>    write record to current file\n");
    }

  else printf("Eh?\n");

  if (showtime && stop >= start)
    printf("start=%d stop=%d difference=%d\n", (int)start, (int)stop,
     (int)(stop - start));

  printf("> ");
  }

for (i = 0; i < max_db; i++)
  {
  if (dbblock[i].dbptr != NULL)
    {
    printf("\nClosing %d", i);
    dbfn_close(dbblock + i);
    }
  }

printf("\n");
return 0;
}
コード例 #8
0
ファイル: exim_dbutil.c プロジェクト: hillu/exim
int main(int argc, char **cargv)
{
int dbdata_type;
uschar **argv = USS cargv;
uschar buffer[256];
uschar name[256];
void *reset_point = store_get(0);

name[0] = 0;  /* No name set */

/* Sort out the database type, verify what we are working on and then process
user requests */

dbdata_type = check_args(argc, argv, US"fixdb", US"");
printf("Modifying Exim hints database %s/db/%s\n", argv[1], argv[2]);

for(;;)
  {
  open_db dbblock;
  open_db *dbm;
  void *record;
  dbdata_retry *retry;
  dbdata_wait *wait;
  dbdata_callout_cache *callout;
  dbdata_ratelimit *ratelimit;
  int i, oldlength;
  uschar *t;
  uschar field[256], value[256];

  store_reset(reset_point);

  printf("> ");
  if (Ufgets(buffer, 256, stdin) == NULL) break;

  buffer[Ustrlen(buffer)-1] = 0;
  field[0] = value[0] = 0;

  /* If the buffer contains just one digit, or just consists of "d", use the
  previous name for an update. */

  if ((isdigit((uschar)buffer[0]) && (buffer[1] == ' ' || buffer[1] == '\0'))
       || Ustrcmp(buffer, "d") == 0)
    {
    if (name[0] == 0)
      {
      printf("No previous record name is set\n");
      continue;
      }
    (void)sscanf(CS buffer, "%s %s", field, value);
    }
  else
    {
    name[0] = 0;
    (void)sscanf(CS buffer, "%s %s %s", name, field, value);
    }

  /* Handle an update request */

  if (field[0] != 0)
    {
    int verify = 1;
    dbm = dbfn_open(argv[1], argv[2], O_RDWR, &dbblock);
    if (dbm == NULL) continue;

    if (Ustrcmp(field, "d") == 0)
      {
      if (value[0] != 0) printf("unexpected value after \"d\"\n");
        else printf("%s\n", (dbfn_delete(dbm, name) < 0)?
          "not found" : "deleted");
      dbfn_close(dbm);
      continue;
      }

    else if (isdigit((uschar)field[0]))
      {
      int fieldno = Uatoi(field);
      if (value[0] == 0)
        {
        printf("value missing\n");
        dbfn_close(dbm);
        continue;
        }
      else
        {
        record = dbfn_read_with_length(dbm, name, &oldlength);
        if (record == NULL) printf("not found\n"); else
          {
          time_t tt;
          int length = 0;     /* Stops compiler warning */

          switch(dbdata_type)
            {
            case type_retry:
            retry = (dbdata_retry *)record;
            length = sizeof(dbdata_retry) + Ustrlen(retry->text);

            switch(fieldno)
              {
              case 0:
              retry->basic_errno = Uatoi(value);
              break;

              case 1:
              retry->more_errno = Uatoi(value);
              break;

              case 2:
              if ((tt = read_time(value)) > 0) retry->first_failed = tt;
                else printf("bad time value\n");
              break;

              case 3:
              if ((tt = read_time(value)) > 0) retry->last_try = tt;
                else printf("bad time value\n");
              break;

              case 4:
              if ((tt = read_time(value)) > 0) retry->next_try = tt;
                else printf("bad time value\n");
              break;

              case 5:
              if (Ustrcmp(value, "yes") == 0) retry->expired = TRUE;
              else if (Ustrcmp(value, "no") == 0) retry->expired = FALSE;
              else printf("\"yes\" or \"no\" expected=n");
              break;

              default:
              printf("unknown field number\n");
              verify = 0;
              break;
              }
            break;

            case type_wait:
            printf("Can't change contents of wait database record\n");
            break;

            case type_misc:
            printf("Can't change contents of misc database record\n");
            break;

            case type_callout:
            callout = (dbdata_callout_cache *)record;
            length = sizeof(dbdata_callout_cache);
            switch(fieldno)
              {
              case 0:
              callout->result = Uatoi(value);
              break;

              case 1:
              callout->postmaster_result = Uatoi(value);
              break;

              case 2:
              callout->random_result = Uatoi(value);
              break;

              default:
              printf("unknown field number\n");
              verify = 0;
              break;
              }
            break;

            case type_ratelimit:
            ratelimit = (dbdata_ratelimit *)record;
            length = sizeof(dbdata_ratelimit);
            switch(fieldno)
              {
              case 0:
              if ((tt = read_time(value)) > 0) ratelimit->time_stamp = tt;
                else printf("bad time value\n");
              break;

              case 1:
              ratelimit->time_usec = Uatoi(value);
              break;

              case 2:
              ratelimit->rate = Ustrtod(value, NULL);
              break;

              default:
              printf("unknown field number\n");
              verify = 0;
              break;
              }
            break;
            }

          dbfn_write(dbm, name, record, length);
          }
        }
      }

    else
      {
      printf("field number or d expected\n");
      verify = 0;
      }

    dbfn_close(dbm);
    if (!verify) continue;
    }

  /* The "name" q causes an exit */

  else if (Ustrcmp(name, "q") == 0) return 0;

  /* Handle a read request, or verify after an update. */

  dbm = dbfn_open(argv[1], argv[2], O_RDONLY, &dbblock);
  if (dbm == NULL) continue;

  record = dbfn_read_with_length(dbm, name, &oldlength);
  if (record == NULL)
    {
    printf("record %s not found\n", name);
    name[0] = 0;
    }
  else
    {
    int count_bad = 0;
    printf("%s\n", CS print_time(((dbdata_generic *)record)->time_stamp));
    switch(dbdata_type)
      {
      case type_retry:
      retry = (dbdata_retry *)record;
      printf("0 error number: %d %s\n", retry->basic_errno, retry->text);
      printf("1 extra data:   %d\n", retry->more_errno);
      printf("2 first failed: %s\n", print_time(retry->first_failed));
      printf("3 last try:     %s\n", print_time(retry->last_try));
      printf("4 next try:     %s\n", print_time(retry->next_try));
      printf("5 expired:      %s\n", (retry->expired)? "yes" : "no");
      break;

      case type_wait:
      wait = (dbdata_wait *)record;
      t = wait->text;
      printf("Sequence: %d\n", wait->sequence);
      if (wait->count > WAIT_NAME_MAX)
        {
        printf("**** Data corrupted: count=%d=0x%x max=%d ****\n", wait->count,
          wait->count, WAIT_NAME_MAX);
        wait->count = WAIT_NAME_MAX;
        count_bad = 1;
        }
      for (i = 1; i <= wait->count; i++)
        {
        Ustrncpy(value, t, MESSAGE_ID_LENGTH);
        value[MESSAGE_ID_LENGTH] = 0;
        if (count_bad && value[0] == 0) break;
        if (Ustrlen(value) != MESSAGE_ID_LENGTH ||
            Ustrspn(value, "0123456789"
                          "abcdefghijklmnopqrstuvwxyz"
                          "ABCDEFGHIJKLMNOPQRSTUVWXYZ-") != MESSAGE_ID_LENGTH)
          {
          int j;
          printf("\n**** Data corrupted: bad character in message id ****\n");
          for (j = 0; j < MESSAGE_ID_LENGTH; j++)
            printf("%02x ", value[j]);
          printf("\n");
          break;
          }
        printf("%s ", value);
        t += MESSAGE_ID_LENGTH;
        }
      printf("\n");
      break;

      case type_misc:
      break;

      case type_callout:
      callout = (dbdata_callout_cache *)record;
      printf("0 callout:    %s (%d)\n", print_cache(callout->result),
          callout->result);
      if (oldlength > sizeof(dbdata_callout_cache_address))
        {
        printf("1 postmaster: %s (%d)\n", print_cache(callout->postmaster_result),
            callout->postmaster_result);
        printf("2 random:     %s (%d)\n", print_cache(callout->random_result),
            callout->random_result);
        }
      break;

      case type_ratelimit:
      ratelimit = (dbdata_ratelimit *)record;
      printf("0 time stamp:  %s\n", print_time(ratelimit->time_stamp));
      printf("1 fract. time: .%06d\n", ratelimit->time_usec);
      printf("2 sender rate: % .3f\n", ratelimit->rate);
      break;
      }
    }

  /* The database is closed after each request */

  dbfn_close(dbm);
  }

printf("\n");
return 0;
}
コード例 #9
0
ファイル: lsearch.c プロジェクト: KMU-embedded/mosbench-ext
static int
internal_lsearch_find(void *handle, uschar *filename, uschar *keystring,
  int length, uschar **result, uschar **errmsg, int type)
{
FILE *f = (FILE *)handle;
BOOL last_was_eol = TRUE;
BOOL this_is_eol = TRUE;
int old_pool = store_pool;
void *reset_point = NULL;
uschar buffer[4096];

/* Wildcard searches may use up some store, because of expansions. We don't
want them to fill up our search store. What we do is set the pool to the main
pool and get a point to reset to later. Wildcard searches could also issue
lookups, but internal_search_find will take care of that, and the cache will be
safely stored in the search pool again. */

if(type == LSEARCH_WILD || type == LSEARCH_NWILD)
  {
  store_pool = POOL_MAIN;
  reset_point = store_get(0);
  }

filename = filename;  /* Keep picky compilers happy */
errmsg = errmsg;

rewind(f);
for (last_was_eol = TRUE;
     Ufgets(buffer, sizeof(buffer), f) != NULL;
     last_was_eol = this_is_eol)
  {
  int ptr, size;
  int p = Ustrlen(buffer);
  int linekeylength;
  BOOL this_is_comment;
  uschar *yield;
  uschar *s = buffer;

  /* Check whether this the final segment of a line. If it follows an
  incomplete part-line, skip it. */

  this_is_eol = p > 0 && buffer[p-1] == '\n';
  if (!last_was_eol) continue;

  /* We now have the start of a physical line. If this is a final line segment,
  remove trailing white space. */

  if (this_is_eol)
    {
    while (p > 0 && isspace((uschar)buffer[p-1])) p--;
    buffer[p] = 0;
    }

  /* If the buffer is empty it might be (a) a complete empty line, or (b) the
  start of a line that begins with so much white space that it doesn't all fit
  in the buffer. In both cases we want to skip the entire physical line.

  If the buffer begins with # it is a comment line; if it begins with white
  space it is a logical continuation; again, we want to skip the entire
  physical line. */

  if (buffer[0] == 0 || buffer[0] == '#' || isspace(buffer[0])) continue;

  /* We assume that they key will fit in the buffer. If the key starts with ",
  read it as a quoted string. We don't use string_dequote() because that uses
  new store for the result, and we may be doing this many times in a long file.
  We know that the dequoted string must be shorter than the original, because
  we are removing the quotes, and also any escape sequences always turn two or
  more characters into one character. Therefore, we can store the new string in
  the same buffer. */

  if (*s == '\"')
    {
    uschar *t = s++;
    while (*s != 0 && *s != '\"')
      {
      if (*s == '\\') *t++ = string_interpret_escape(&s);
        else *t++ = *s;
      s++;
      }
    if (*s != 0) s++;               /* Past terminating " */
    linekeylength = t - buffer;
    }

  /* Otherwise it is terminated by a colon or white space */

  else
    {
    while (*s != 0 && *s != ':' && !isspace(*s)) s++;
    linekeylength = s - buffer;
    }

  /* The matching test depends on which kind of lsearch we are doing */

  switch(type)
    {
    /* A plain lsearch treats each key as a literal */

    case LSEARCH_PLAIN:
    if (linekeylength != length || strncmpic(buffer, keystring, length) != 0)
      continue;
    break;      /* Key matched */

    /* A wild lsearch treats each key as a possible wildcarded string; no
    expansion is done for nwildlsearch. */

    case LSEARCH_WILD:
    case LSEARCH_NWILD:
      {
      int rc;
      int save = buffer[linekeylength];
      uschar *list = buffer;
      buffer[linekeylength] = 0;
      rc = match_isinlist(keystring,
        &list,
        UCHAR_MAX+1,              /* Single-item list */
        NULL,                     /* No anchor */
        NULL,                     /* No caching */
        MCL_STRING + ((type == LSEARCH_WILD)? 0:MCL_NOEXPAND),
        TRUE,                     /* Caseless */
        NULL);
      buffer[linekeylength] = save;
      if (rc == FAIL) continue;
      if (rc == DEFER) return DEFER;
      }

    /* The key has matched. If the search involved a regular expression, it
    might have caused numerical variables to be set. However, their values will
    be in the wrong storage pool for external use. Copying them to the standard
    pool is not feasible because of the caching of lookup results - a repeated
    lookup will not match the regular expression again. Therefore, we flatten
    all numeric variables at this point. */

    expand_nmax = -1;
    break;

    /* Compare an ip address against a list of network/ip addresses. We have to
    allow for the "*" case specially. */

    case LSEARCH_IP:
    if (linekeylength == 1 && buffer[0] == '*')
      {
      if (length != 1 || keystring[0] != '*') continue;
      }
    else if (length == 1 && keystring[0] == '*') continue;
    else
      {
      int maskoffset;
      int save = buffer[linekeylength];
      buffer[linekeylength] = 0;
      if (string_is_ip_address(buffer, &maskoffset) == 0 ||
          !host_is_in_net(keystring, buffer, maskoffset)) continue;
      buffer[linekeylength] = save;
      }
    break;      /* Key matched */
    }

  /* The key has matched. Skip spaces after the key, and allow an optional
  colon after the spaces. This is an odd specification, but it's for
  compatibility. */

  while (isspace((uschar)*s)) s++;
  if (*s == ':')
    {
    s++;
    while (isspace((uschar)*s)) s++;
    }

  /* Reset dynamic store, if we need to, and revert to the search pool */

  if (reset_point != NULL)
    {
    store_reset(reset_point);
    store_pool = old_pool;
    }

  /* Now we want to build the result string to contain the data. There can be
  two kinds of continuation: (a) the physical line may not all have fitted into
  the buffer, and (b) there may be logical continuation lines, for which we
  must convert all leading white space into a single blank.

  Initialize, and copy the first segment of data. */

  this_is_comment = FALSE;
  size = 100;
  ptr = 0;
  yield = store_get(size);
  if (*s != 0)
    yield = string_cat(yield, &size, &ptr, s, Ustrlen(s));

  /* Now handle continuations */

  for (last_was_eol = this_is_eol;
       Ufgets(buffer, sizeof(buffer), f) != NULL;
       last_was_eol = this_is_eol)
    {
    s = buffer;
    p = Ustrlen(buffer);
    this_is_eol = p > 0 && buffer[p-1] == '\n';

    /* Remove trailing white space from a physical line end */

    if (this_is_eol)
      {
      while (p > 0 && isspace((uschar)buffer[p-1])) p--;
      buffer[p] = 0;
      }

    /* If this is not a physical line continuation, skip it entirely if it's
    empty or starts with #. Otherwise, break the loop if it doesn't start with
    white space. Otherwise, replace leading white space with a single blank. */

    if (last_was_eol)
      {
      this_is_comment = (this_is_comment || (buffer[0] == 0 || buffer[0] == '#'));
      if (this_is_comment) continue;
      if (!isspace((uschar)buffer[0])) break;
      while (isspace((uschar)*s)) s++;
      *(--s) = ' ';
      }
    if (this_is_comment) continue;

    /* Join a physical or logical line continuation onto the result string. */

    yield = string_cat(yield, &size, &ptr, s, Ustrlen(s));
    }

  yield[ptr] = 0;
  store_reset(yield + ptr + 1);
  *result = yield;
  return OK;
  }

/* Reset dynamic store, if we need to */

if (reset_point != NULL)
  {
  store_reset(reset_point);
  store_pool = old_pool;
  }

return FAIL;
}
コード例 #10
0
ファイル: read.c プロジェクト: nigelm/xfpt
uschar *
read_nextline(void)
{
int len;
uschar *p, *q;

/* Handle a dot line that terminated a paragraph, or a .nonl line */

if (next_line != NULL)
  {
  uschar *yield = next_line;
  next_line = NULL;
  return yield;
  }

/* Handle a line off the stack */

if (popto == 0)
  {
  pushstr *ps = pushed;
  if (ps == NULL) error(12); else
    {
    popto = -1;
    (void)sprintf(CS inbuffer, "%s\n", ps->string);
    pushed = ps->next;
    free(ps);
    return inbuffer;
    }
  }

/* Handle a line off the stack when there is a matching line at the top or
below for the given letter. When we reach the matching line, stop popping. The
value of popto is set greater than zero only when it is known that there's a
matching line. */

if (popto > 0)
  {
  pushstr *ps = pushed;
  if (ps->letter == popto) popto = -1;
  (void)sprintf(CS inbuffer, "%s\n", ps->string);
  pushed = ps->next;
  free(ps);
  return inbuffer;
  }

/* Get the next line from the current macro or the current file. We need a loop
for handling the ends of macros and files. First check for having previously
reached the end of the input. */

if (from_type_ptr < 0) return NULL;

for (;;)
  {
  if (from_type[from_type_ptr] == FROM_MACRO)
    {
    if (macrocurrent->nextline == NULL)
      {
      macroexe *temp = macrocurrent;
      macrocurrent = macrocurrent->prev;
      free(temp);
      }
    else
      {
      read_process_macroline(macrocurrent->nextline->string, inbuffer);
      macrocurrent->nextline = macrocurrent->nextline->next;
      break;
      }
    }

  /* When reading from a file, handle continuation lines, but only within the
  single file. */

  else
    {
    if (Ufgets(inbuffer, INBUFFSIZE, istack->file) == NULL)
      {
      istackstr *prev = istack->prev;
      fclose(istack->file);
      free(istack);
      istack = prev;
      }
    else
      {
      istack->linenumber++;

      q = inbuffer;
      len = Ustrlen(q);

      for (;;)
        {
        p = q + len;
        while (p > q && isspace(p[-1])) p--;

        if (p - q < 3 || Ustrncmp(p - 3, "&&&", 3) != 0) break;

        q = p - 3;
        *q = 0;

        if (istack == NULL ||
            Ufgets(q, INBUFFSIZE - (q - inbuffer), istack->file) == NULL)
          break;

        istack->linenumber++;
        p = q;
        while (*p == ' ' || *p == '\t') p++;
        len = Ustrlen(p);
        if (p > q) memmove(q, p, len + 1);
        }

      break;
      }
    }

  /* We get here if the end of a macro or a file was reached. The appropriate
  chain has been popped. Back up the stack of input types before the loop
  repeats. When we reach the end of the stack, we have reached the end of all
  the input. */

  if (--from_type_ptr < 0) return NULL;
  }

return inbuffer;
}