Logo Search packages:      
Sourcecode: dcc version File versions  Download package

dccproc.c

/* Distributed Checksum Clearinghouse server
 *
 * report a message for such as procmail
 *
 * Copyright (c) 2005 by Rhyolite Software
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND RHYOLITE SOFTWARE DISCLAIMS ALL
 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL RHYOLITE SOFTWARE
 * BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 *
 * Rhyolite Software DCC 1.2.74-1.88 $Revision$
 */

#include "dcc_ck.h"
#include "dcc_xhdr.h"
#include "dcc_paths.h"
#include "dcc_heap_debug.h"
#include <signal.h>                 /* for Linux and SunOS*/
#ifndef DCC_WIN32
#include <arpa/inet.h>
#endif


static DCC_EMSG dcc_emsg;

static const char *homedir;
static const char *mapfile_nm = DCC_MAP_NM_DEF;

static const char *logdir;
static DCC_PATH log_nm;
static int lfd = -1;
static u_char logging = 1;          /* 0=no log, 1=have file, 2=used it */
static u_int honor;

static const char *tmpdir;
static DCC_PATH tfile_nm;
static int tfd = -1;
static u_char tfd_rewound;
static int hdrs_len, body_len;
static u_char seen_hdr;

static int exit_code = EX_NOUSER;
static DCC_TGTS targets = 1;
static u_char targets_set;
static int total_hdrs, cr_hdrs;

static const char* white_nm;
static const char *ifile_nm = "stdin", *ofile_nm = "stdout";
static FILE *ifile, *ofile;

static DCC_CLNT_CTXT *ctxt;
static char xhdr[sizeof(DCC_XHDR_START)+sizeof(DCC_BRAND)+1];
static int xhdr_len;
static u_char add_xhdr;             /* add instead of replace header */
static u_char dcc_query_only;
static u_char cksums_only;          /* output only checksums */
static u_char x_dcc_only;           /* output only the X-DCC header */
static u_char fake_envelope;        /* fake envelope log lines */
static u_char std_received;         /* first Received: line is standard */

static DCC_GOT_CKS cks;
static DCC_CKS_WTGTS wtgts;
static char clnt_name_buf[MAXHOSTNAMELEN];      /* SMPT client */
static char clnt_str_buf[INET6_ADDRSTRLEN];
static const char *clnt_str;
static struct in6_addr clnt_addr;

static char env_from_buf[DCC_HDR_CK_MAX+1];
static const char *env_from;

static char mail_host[MAXHOSTNAMELEN];

static DCC_HEADER_BUF header;

static char early_error_buf[MAXHOSTNAMELEN*4];
static int early_error_len;

static u_char get_hdr(char *, int);
static void twrite(const void *, int);
static int tread(void *, int);
static void scopy(int, u_char);
static void log_write(const void *, int);
static void log_print(const char *, ...) PATTRIB(1,2);
static void log_print_ctxt(void *, const char *, ...) PATTRIB(2,3);
#define LOG_CAPTION(s) log_write((s), sizeof(s)-1);
#define LOG_EOL() LOG_CAPTION("\n")
static void log_fin(void);
static void log_ck(void *, const void *, u_int);
static void dccproc_error_msg(const char *, ...) PATTRIB(1,2);
static void sigterm(int);


static void NRATTRIB
usage(void)
{
      dcc_logbad(EX_USAGE,
               "usage: [-VdAQCHER]  [-h homedir] [-m map] [-w whiteclnt]"
               " [-T tmpdir]\n"
               "   [-a IP-address] [-f env_from] [-t targets]\n"
               "   [-c type,[log-thold,][spam-thold]] [-g [not-]type]"
               " [-S header]\n"
               "   [-i infile] [-o outfile] [-l logdir]"
               " [-B dnsbl-option] [-L ltype,facility.level]");
}



int NRATTRIB
main(int argc, char **argv)
{
      char buf[20*1024];            /* at least DCC_HDR_CK_MAX*2 */
      u_char log_tgts_set = 0;
      u_char ask_result;
      char *p, *p2;
      u_long l;
      int error, blen, i;

      /* because stderr is often mixed with stdout and effectively
       * invisible, also complain to syslog */
      dcc_syslog_init(1, argv[0], 0);
      dcc_init_tholds();

      /* we must be SUID to read and write the system's common connection
       * parameter memory mapped file.  We also need to read the common
       * local white list and write the mmap()'ed hash file */
      dcc_init_priv();

      ofile = stdout;
      ifile = stdin;
      while ((i = getopt(argc, argv,
                     "VdAQCHERh:m:w:T:a:f:g:S:t:x:c:i:o:l:B:L:"))!=EOF) {
            switch (i) {
            case 'V':
                  fprintf(stderr, DCC_VERSION"\n");
                  exit(EX_OK);
                  break;

            case 'd':
                  ++dcc_clnt_debug;
                  break;

            case 'A':
                  add_xhdr = 1;
                  break;

            case 'Q':
                  dcc_query_only = 1;
                  break;

            case 'C':
                  cksums_only = 1;
                  break;

            case 'H':
                  x_dcc_only = 1;
                  break;

            case 'E':
                  fake_envelope = 1;
                  break;

            case 'R':
                  std_received = 1;
                  break;

            case 'h':
                  homedir = optarg;
                  break;

            case 'm':
                  mapfile_nm = optarg;
                  break;

            case 'w':
                  white_nm = optarg;
                  break;

            case 'T':
                  tmpdir = optarg;
                  break;

            case 'a':
                  dcc_host_lock();
                  if (!dcc_get_host(optarg, 1, &error)) {
                        dccproc_error_msg("\"-a %s\": %s",
                                      optarg, DCC_HSTRERROR(error));
                  } else {
                        clnt_addr = dcc_hostaddrs[0].ipv6.sin6_addr;
                        dcc_get_ipv6_ck(&cks, &clnt_addr);
                        clnt_str = optarg;
                  }
                  dcc_host_unlock();
                  break;

            case 'f':
                  env_from = optarg;
                  dcc_get_cks(&cks, DCC_CK_ENV_FROM, env_from, 1);
                  break;

            case 'g':         /* honor not-spam "counts" */
                  dcc_parse_honor(optarg);
                  break;

            case 'S':
                  if (!dcc_add_sub_hdr(dcc_emsg, optarg))
                        dcc_logbad(EX_USAGE, "%s", dcc_emsg);
                  break;

            case 't':
                  if (!strcasecmp(optarg, "many")) {
                        targets = DCC_TGTS_TOO_MANY;
                        targets_set = 1;
                  } else {
                        l = strtoul(optarg, &p, 0);
                        if (*p != '\0' || l > 1000) {
                              dccproc_error_msg("invalid count"
                                          " \"%s\"", optarg);
                        } else {
                              targets = l;
                              targets_set = 1;
                        }
                  }
                  break;

            case 'x':
                  l = strtoul(optarg, &p, 0);
                  if (*p != '\0') {
                        dccproc_error_msg("invalid exit code \"%s\"",
                                      optarg);
                  } else {
                        exit_code = l;
                  }
                  break;

            case 'c':
                  if (dcc_parse_tholds('c', optarg))
                        log_tgts_set = 1;
                  break;

            case 'i':
                  /* open the input file now, before changing to the
                   * home DCC directory */
                  ifile_nm = optarg;
                  ifile = fopen(ifile_nm, "r");
                  if (!ifile)
                        dcc_logbad(EX_USAGE,
                                 "bad input file \"%s\": %s",
                                 ifile_nm, ERROR_STR());
                  break;

            case 'o':
                  /* open the output file now, before changing to the
                   * home DCC directory */
                  ofile_nm = optarg;
                  ofile = fopen(ofile_nm, "w");
                  if (!ofile)
                        dcc_logbad(EX_USAGE,
                                 "bad output file \"%s\": %s",
                                 ofile_nm, ERROR_STR());
                  break;

            case 'l':
                  logdir = optarg;
                  break;

            case 'B':
                  if (!dcc_parse_dnsbl(dcc_emsg, optarg, 0))
                        dcc_logbad(EX_USAGE, "%s", dcc_emsg);
                  break;

#ifndef DCC_WIN32
            case 'L':
                  dcc_parse_log_opt(optarg);
                  break;
#endif

            default:
                  usage();
            }
      }
      if (argc != optind)
            usage();

#ifdef SIGHUP
      signal(SIGHUP, sigterm);
#endif
      signal(SIGTERM, sigterm);
      signal(SIGINT, sigterm);

      dcc_clnt_unthread_init();

      dcc_cdhome(0, homedir);

      if (logdir)
            dcc_log_init(0, logdir);
      if (!dcc_have_logdir) {
            if (log_tgts_set)
                  dccproc_error_msg("log thresholds set with -c"
                                " but no -l directory");
            logging = 0;
      } else {
            lfd = dcc_log_open(0, log_nm, sizeof(log_nm));
            if (lfd < 0)
                  logging = 0;
      }
      if (!tmpdir)
            tmpdir = logging ? dcc_logdir : _PATH_TMP;
      if (fake_envelope && lfd >= 0) {
            struct tm tm;
            char date_buf[40];

            strftime(date_buf, sizeof(date_buf), DCC_LOG_DATE_FMT,
                   dcc_localtime(time(0), &tm));
            log_print(DCC_LOG_DATE_PAT"\n", date_buf);
            if (early_error_len)
                  log_write(early_error_buf, early_error_len);
      }

      if (!targets_set) {
            targets = dcc_query_only ? 0 : 1;
      } else if (targets == 0) {
            dcc_query_only = 1;
      } else if (dcc_query_only) {
            dcc_error_msg("\"-t %s\" is incompatible with \"-Q\"",
                        dcc_cnt2str(buf, sizeof(buf), targets, 0));
            targets = 0;
      }

#ifdef DCC_WIN32
      /* Apparently WIN32 lacks /dev/null */
#else
      /* Close STDERR to keep it from being mixed with the message,
       * unless we are not going to output the message.
       * Ensure that stderr and file descriptor 2 are open to something
       * to prevent surprises from busybody libraries. */
      if (!dcc_clnt_debug && !cksums_only && !x_dcc_only) {
            int fd = open(_PATH_DEVNULL, O_WRONLY, 0);
            if (fd < 0) {
                  dcc_error_msg("open("_PATH_DEVNULL"): %s",
                              ERROR_STR());
            } else if (0 > dup2(fd, STDERR_FILENO)) {
                  dcc_error_msg("dup2("_PATH_DEVNULL",2): %s",
                              ERROR_STR());
            }
      }
#endif

      /* start a connection to a DCC server */
      ctxt = dcc_clnt_init(dcc_emsg, 0, mapfile_nm, DCC_CLNT_FG_NONE);
      if (!ctxt) {
            dccproc_error_msg("%s", dcc_emsg);
      } else {
            xhdr_len = dcc_xhdr_start(xhdr, sizeof(xhdr));
      }

      /* get ready for the body checksums before the headers so that
       * we can notice the MIME separator */
      dcc_ck_body_init(&cks);
      dcc_dnsbl_init(&cks, 0, 0, 0);

      if (logdir || (!cksums_only && !x_dcc_only)) {
            i = strlen(tmpdir);
            tfd = dcc_mkstemp(dcc_emsg, tfile_nm, sizeof(tfile_nm),
                          tmpdir,
                          ((i > 0 && tmpdir[i-1] != '/')
                           ? "/dccproc." : "dccproc."),
                          1, 0, 0);
            if (tfd < 0)
                  dcc_logbad(EX_IOERR, "%s", dcc_emsg);
      }

      /* get the headers */
      for (;;) {
            /* stop at the separator between the body and headers */
            if (!get_hdr(buf, sizeof(buf)))
                  break;

#define GET_HDR_CK(h,t) {                                   \
                  if (!CSTRCMP(buf, h)) {                   \
                        dcc_get_cks(&cks,DCC_CK_##t, &buf[STRZ(h)], 1);\
                        seen_hdr = 1;                       \
                        continue;}}
            GET_HDR_CK(DCC_XHDR_TYPE_FROM":", FROM);
            GET_HDR_CK(DCC_XHDR_TYPE_MESSAGE_ID":", MESSAGE_ID);
#undef GET_HDR_CK

            if (!CSTRCMP(buf, "Return-Path:")) {
                  if (cks.sums[DCC_CK_ENV_FROM].type == DCC_CK_INVALID) {
                        BUFCPY(env_from_buf,
                               &buf[STRZ("Return-Path:")]);
                        env_from = env_from_buf;
                        dcc_get_cks(&cks, DCC_CK_ENV_FROM, env_from, 1);
                  }
                  seen_hdr = 1;
                  continue;
            }

            /* notice UNIX From_ line */
            if (!seen_hdr
                && !strncmp(buf, "From ", STRZ("From "))) {
                  p = &buf[STRZ("From ")];
                  p += strspn(p, " ");
                  p2 = strchr(p, ' ');
                  if (p2 != 0) {
                        if (cks.sums[DCC_CK_ENV_FROM].type
                            == DCC_CK_INVALID) {
                              if (p2 > p+sizeof(env_from_buf))
                                  p2 = p+sizeof(env_from_buf);
                              memcpy(env_from_buf, p, p2-p);
                              env_from_buf[p2-p] = '\0';
                              env_from = env_from_buf;
                              dcc_get_cks(&cks, DCC_CK_ENV_FROM,
                                        env_from, 1);
                        }
                        seen_hdr = 1;
                        continue;
                  }
            }

            if (!CSTRCMP(buf, DCC_XHDR_TYPE_RECEIVED":")) {
                  p = &buf[sizeof(DCC_XHDR_TYPE_RECEIVED":")-1];

                  /* compute checksum of the last Received: header */
                  dcc_get_cks(&cks, DCC_CK_RECEIVED, p, 1);

                  /* pick IP address out of first Received: header */
                  if (std_received
                      && cks.sums[DCC_CK_IP].type == DCC_CK_INVALID
                      && 0 != (p2 = strchr(p, '('))
                      && 0 != (p = strchr(++p2, '['))
                      && (i = strspn(++p, ".:abcdefABCDEF0123456789")) > 6
                      && i < INET6_ADDRSTRLEN
                      && p[i] == ']') {
                        p[i] = '\0';
                        if (dcc_get_str_ip_ck(&cks, p, &clnt_addr)) {
                              if (!DCC_INET_NTOP(AF_INET6, &clnt_addr,
                                          clnt_str_buf,
                                          sizeof(clnt_str_buf)))
                                  strcpy(clnt_str_buf, p);
                              clnt_str = clnt_str_buf;

                              i = p-p2-1;
                              if (i > ISZ(clnt_name_buf)-1)
                                  i = ISZ(clnt_name_buf)-1;
                              while (i > 0
                                     && (p2[i-1] == ' '
                                       || p2[i-1] == '\t'))
                                  --i;
                              if (i > 0) {
                                  memcpy(clnt_name_buf, p2, i);
                                  clnt_name_buf[i] = '\0';
                              } else {
                                  snprintf(clnt_name_buf,
                                         sizeof(clnt_name_buf),
                                         "[%s]", p);
                              }
                        }
                  }
                  std_received = 0;
                  seen_hdr = 1;
                  continue;
            }

            /* Notice MIME multipart boundary definitions */
            dcc_ck_mime_hdr(&cks, buf, 0);

            if (dcc_ck_get_sub(&cks, buf, 0))
                  seen_hdr = 1;

            /* notice any sort of header */
            if (!seen_hdr) {
                  for (p = buf; ; ++p) {
                        if (*p == ':') {
                              seen_hdr = 1;
                              break;
                        }
                        if (*p <= ' ' || *p >= 0x7f)
                              break;
                  }
            }
      }
      /* Create a checksum for a null Message-ID header if there
       * was no Message-ID header.  */
      if (cks.sums[DCC_CK_MESSAGE_ID].type != DCC_CK_MESSAGE_ID)
            dcc_get_cks(&cks, DCC_CK_MESSAGE_ID, "", 0);

      /* Check DNS blacklists for STMP client and envelope sender
       * before collecting the body to avoid wasting time DNS resolving
       * URLs if the envelope answers the question.  Much of the DNS
       * work for the envelope has probably already been done. */
      if (cks.sums[DCC_CK_IP].type != DCC_CK_INVALID)
            dcc_sender_dnsbl(cks.dnsbl, &clnt_addr);
      if (env_from
          && (p = strchr(env_from, '@')) != 0) {
            ++p;
            p2 = strchr(p, '>');
            i = sizeof(mail_host);
            if (p2)
                  i = min(i, p2-p);
            STRLIMCPY(mail_host, i, p);
            if (strchr(mail_host, ';') || strchr(mail_host, '@')
                || strchr(mail_host, ','))
                  mail_host[0] = '\0';
      }
      if (mail_host[0] != '\0') {
            dcc_ck_get_sub(&cks, "mail_host", mail_host);
            dcc_mail_host_dnsbl(cks.dnsbl, mail_host);
      }

      /* collect the body */
      do {
            blen = fread(buf, 1, sizeof(buf), ifile);
            if (blen != sizeof(buf)) {
                  if (ferror(ifile))
                        dcc_logbad(EX_DATAERR, "fgets(%s): %s",
                                 ifile_nm, ERROR_STR());
                  if (!blen)
                        break;
            }

            twrite(buf, blen);
            body_len += blen;
            dcc_ck_body(&cks, buf, blen);
      } while (!feof(ifile));
      fclose(ifile);

      dcc_ck_body_fin(&cks);

      /* compute the X-DCC header and exitcode by asking our local
       * whitelist and then the DCC server */
      dcc_wf_init(&cmn_wf, 0, 0);
      if (!ctxt) {
            ask_result= 0;
      } else {
            ask_result = dcc_white_ask(dcc_emsg, &cmn_wf, ctxt,
                                 &header, &honor,
                                 white_nm, &cks, wtgts,
                                 dcc_query_only ? 0 : targets);
            if (!ask_result)
                  dccproc_error_msg("%s", dcc_emsg);
      }

      if (fake_envelope && lfd >= 0) {
            if (clnt_str) {
                  LOG_CAPTION(DCC_XHDR_TYPE_IP": ");
                  log_write(clnt_name_buf, strlen(clnt_name_buf));
                  LOG_CAPTION(" ");
                  log_write(clnt_str, strlen(clnt_str));
                  LOG_EOL();
            }
            if (env_from) {
                  LOG_CAPTION(DCC_XHDR_TYPE_ENV_FROM": ");
                  log_write(env_from, strlen(env_from));
#if 0
                  /* this would be nice, but might break compatibility */
                  log_print("  mail_host=%s", mail_host);
#endif
                  LOG_EOL();
            }
            LOG_EOL();
      }

      /* copy the headers to the log file and the output */
      scopy(hdrs_len, 1);

      /* emit the X-DCC header */
      if (ask_result
          && header.buf[0] != '\0') {
            if (ofile)
                  fwrite(header.buf, header.used, 1, ofile);
            log_write(header.buf, header.used);
            if (cr_hdrs > total_hdrs/2) {
                  /* end the X-DCC header with "\r\n" if at least
                   * half of the header lines ended that way */
                  fputs("\r\n", ofile);
                  LOG_CAPTION("\r\n");
            } else {
                  /* otherwise use only "\n" */
                  fputs("\n", ofile);
                  LOG_CAPTION("\n");
            }
      }

      /* emit body */
      scopy(body_len, 1);

      LOG_CAPTION(DCC_LOG_MSG_SEP);
      if (dcc_dnsbl_log_print(cks.dnsbl, log_print_ctxt, 0))
            honor |= DCC_HONOR_LOGIT;
      dcc_print_cks(0, &cks, wtgts, log_ck);

      if (ofile && fclose(ofile)) {
            dcc_logbad(EX_IOERR, "fclose(%s): %s",
                     ofile_nm, ERROR_STR());
      }

      log_fin();
      exit((honor & (DCC_HONOR_SRVR_ISSPAM | DCC_HONOR_LOCAL_ISSPAM)) != 0
           ? exit_code
           : EX_OK);
#ifdef DCC_WIN32
      return 0;
#endif
}



/* get the next header line */
static  u_char                      /* 1=have one, 0=end of headers */
get_hdr(char *buf, int buflen)
{
      u_char copying;
      int hlen, llen, c;
      const char *line;

      hlen = 0;
      copying = 0;
      for (;;) {
            line = fgets(&buf[hlen], buflen-hlen, ifile);
            if (!line) {
                  if (ferror(ifile))
                        dcc_logbad(EX_DATAERR, "fgets(%s): %s",
                                 ifile_nm, ERROR_STR());
                  else
                        dcc_logbad(EX_DATAERR, "missing message body");
            }
            llen = strlen(line);

            /* delete our X-DCC header */
            if (hlen == 0
                && !add_xhdr && xhdr_len != 0
                && llen > xhdr_len
                && buf[xhdr_len] == ':'
                && !strncasecmp(buf, xhdr, xhdr_len)) {
                  seen_hdr = 1;
                  copying = 2;
            }

            /* copy to temporary file so we can emit the message
             * after we add the X-DCC header */
            if (copying != 2) {
                  twrite(&buf[hlen], llen);
                  if (copying != 1) {
                        /* stop on blank line */
                        if (buf[0] == '\n'
                            || (buf[1] == '\n' && buf[0] == '\r')) {
                              if (!seen_hdr)
                                  dcc_logbad(EX_DATAERR,
                                       "missing SMTP header lines");
                              body_len = llen;
                              return 0;
                        }
                        copying = 1;
                  }
                  hdrs_len += llen;
            }

            /* do not crash on too-long headers */
            hlen += llen;
            if (buf[hlen-1] != '\n') {
                  /* truncate headers with lines too big for our buffer */
                  if (hlen > DCC_HDR_CK_MAX) {
                        /* but know if they end with "\r\n" */
                        if (buf[hlen-1] == '\r')
                              buf[DCC_HDR_CK_MAX-1] = '\r';
                        hlen = DCC_HDR_CK_MAX;
                  }
                  continue;
            }

            /* get the next character after the end-of-line to see if
             * the next line is a continuation */
            c = getc(ifile);
            if (c != EOF)
                  ungetc(c, ifile);
            if (c != ' ' && c != '\t') {
                  /* not a continuation, so stop */
                  ++total_hdrs;
                  /* notice if this line ended with "\r\n" */
                  if (hlen > 1 && buf[hlen-2] == '\r')
                        ++cr_hdrs;

                  if (copying != 2) {
                        if (hlen > DCC_HDR_CK_MAX)
                              buf[DCC_HDR_CK_MAX-1] = '\0';
                        return 1;
                  }

                  /* at the end of our X-DCC header, look for another */
                  copying = 0;
                  hlen = 0;
            }
      }
}



static void
twrite(const void *buf, int len)
{
      int i;

      if (tfd < 0)
            return;

      if (tfd_rewound)
            dcc_logbad(EX_SOFTWARE, "writing to rewound temp file");

      i = write(tfd, buf, len);
      if (i != len) {
            if (i < 0)
                  dcc_logbad(EX_IOERR, "write(%s,%d): %s",
                           tfile_nm, len, ERROR_STR());
            else
                  dcc_logbad(EX_IOERR, "write(%s,%d)=%d",
                           tfile_nm, len, i);
      }
}



static int
tread(void *buf, int len)
{
      int i;
      int serrno;

      i = read(tfd, buf, len);
      if (i <= 0) {
            serrno = errno;
            close(tfd);
            tfd = -1;
            if (i < 0)
                  dcc_logbad(EX_IOERR, "read(%s,%d): %s",
                           tfile_nm, len, ERROR_STR1(serrno));
      }
      return i;
}



/* copy some of the temporary file to the output */
static void
scopy(int total_len,                /* copy this much of temporary file */
      u_char complain)              /* 1=ok to complain about problems */
{
      char buf[BUFSIZ];
      int serrno, len, i;

      if (tfd < 0)
            return;

      /* if the temporary file has not been rewound,
       * then rewind it now */
      if (!tfd_rewound) {
            tfd_rewound = 1;
            if (0 > lseek(tfd, 0, SEEK_SET)) {
                  serrno = errno;
                  close(tfd);
                  tfd = -1;
                  if (complain)
                        dcc_logbad(EX_IOERR, "rewind(%s): %s",
                                 tfile_nm, ERROR_STR1(serrno));
            }
      }

      while (total_len > 0) {
            len = sizeof(buf);
            if (len > total_len) {
                  len = total_len;
            }
            len = tread(buf, len);
            if (len == 0) {
                  close(tfd);
                  tfd = -1;
                  if (complain)
                        dcc_logbad(EX_IOERR, "premature end of %s",
                                 tfile_nm);
                  return;
            }

            log_write(buf, len);

            if (ofile && (!cksums_only && !x_dcc_only)) {
                  i = fwrite(buf, 1, len, ofile);
                  if (i != len) {
                        serrno = errno;
                        close(tfd);
                        tfd = -1;
                        if (complain) {
                              if (feof(ofile))
                                  dcc_logbad(EX_IOERR,
                                           "premature end of %s",
                                           ofile_nm);
                              else
                                  dcc_logbad(EX_IOERR,
                                           "fwrite(%s): %s",
                                           ofile_nm,
                                           ERROR_STR1(serrno));
                        }
                        return;
                  }
            }

            total_len -= len;
      }
}



static void
log_write(const void *buf, int len)
{
      int i;

      if (lfd < 0)
            return;

      i = write(lfd, buf, len);
      if (i == len) {
            logging = 2;
      } else {
            dcc_error_msg("write(log %s): %s", log_nm, ERROR_STR());
            close(lfd);
            lfd = -1;
            logging = 0;
            log_nm[0] = '\0';
      }
}



static void
vlog_print(const char *p, va_list args)
{
      char logbuf[MAXHOSTNAMELEN*2];

      if (lfd < 0)
            return;
      vsnprintf(logbuf, sizeof(logbuf), p, args);
      log_write(logbuf, strlen(logbuf));
}



static void
log_print(const char *p, ...)
{
      va_list args;

      va_start(args, p);
      vlog_print(p, args);
      va_end(args);
}



static void
log_print_ctxt(void *ctxtp UATTRIB, const char *p, ...)
{
      va_list args;

      va_start(args, p);
      vlog_print(p, args);
      va_end(args);
}



static void
log_fin(void)
{
      if (log_nm[0] == '\0')
            return;

      /* Close before renaming to accomodate WIN32 foolishness.
       * Assuming dcc_mkstemp() works properly, there is no race */
      close(lfd);
      lfd = -1;
      if (honor & DCC_HONOR_LOGIT) {
            dcc_log_keep(0, log_nm, sizeof(log_nm));
      } else {
            unlink(log_nm);
            log_nm[0] = '\0';
      }
}



static void
log_ck(void *arg UATTRIB, const void *buf, u_int buf_len)
{
      if (cksums_only && ofile)
            fputs(buf, ofile);
      log_write(buf, buf_len);
}



static void
dccproc_error_msg(const char *p, ...)
{
      va_list args;
      int i;

      if (lfd >= 0) {
            va_start(args, p);
            dcc_verror_msg(p, args);
            va_end(args);

            if (tfd_rewound)
                  log_write("\n", 1);
            va_start(args, p);
            vlog_print(p, args);
            va_end(args);
            if (!tfd_rewound)
                  log_write("\n\n", 2);

      } else {
            va_start(args, p);
            dcc_verror_msg(p, args);
            va_end(args);
            if (early_error_len < ISZ(early_error_buf)-1) {
                  va_start(args, p);
                  i = vsnprintf(&early_error_buf[early_error_len],
                              ISZ(early_error_buf)-early_error_len-1,
                              p, args);
                  va_end(args);
                  if (i > ISZ(early_error_buf)-early_error_len-1)
                        i = ISZ(early_error_buf)-early_error_len-1;
                  early_error_len += i;
                  early_error_buf[early_error_len++] = '\n';
            }
      }

      honor |= DCC_HONOR_LOGIT;
}



/* things are so sick that we must bail out */
void NRATTRIB
dcc_logbad(int ex_code UATTRIB, const char *p, ...)
{
      char buf[BUFSIZ];
      va_list args;
      size_t len;

      if (*p >= ' ' && !tfd_rewound) {
            va_start(args, p);
            dcc_vfatal_msg(p, args);
            va_end(args);

            honor |= DCC_HONOR_LOGIT;
            if (logging > 1)
                  log_write("\n", 1);
            va_start(args, p);
            vlog_print(p, args);
            va_end(args);
            log_write("\n\n", 2);
            p = 0;
      }

      /* copy first from the temporary file and then the input
       * to try to ensure that we don't lose mail */
      scopy(INT_MAX, 0);
      if (ifile && ofile && !cksums_only && !x_dcc_only) {
            for (;;) {
                  len = fread(buf, 1, sizeof(buf), ifile);
                  if (!len)
                        break;
                  log_write(buf, len);
                  if (len != fwrite(buf, 1, len, ofile))
                        break;
            }
      }

      if (p && *p >= ' ') {
            va_start(args, p);
            dcc_vfatal_msg(p, args);
            va_end(args);

            log_write("\n\n", 2);
            va_start(args, p);
            vlog_print(p, args);
            va_end(args);
            log_write("\n", 1);
      }
      log_fin();

      exit(0);                /* don't tell procmail to reject mail */
}



/* watch for fatal signals */
static void
sigterm(int sig)
{
      log_fin();
      exit(-sig);
}

Generated by  Doxygen 1.6.0   Back to index