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

appendlet.c

/*
 *    Copyright 1988 by Rayan S. Zachariassen, all rights reserved.
 *    This will be free software, but only when it is finished.
 *    Copyright 1991-2003 by Matti Aarnio -- modifications, including MIME
 */

#include "smtp.h"

#define ALARM_BLOCKSIZE 2000 /* Not alarm() thing, but more for reports.. */


/*
 * appendlet - append letter to file pointed at by fd
 */

int
appendlet(SS, dp, convertmode, CT)
      SmtpState *SS;
      struct ctldesc *dp;
      CONVERTMODE convertmode;
      struct ct_data *CT;
{
      /* `convertmode' controls the behaviour of the message conversion:
           _CONVERT_NONE (0): send as is
           _CONVERT_QP   (1): Convert 8-bit chars to QUOTED-PRINTABLE
           _CONVERT_MULTIPARTQP (2): Convert substructures to QP
           _CONVERT_8BIT (3): Convert QP-encoded chars to 8-bit
           _CONVERT_UNKNOWN (4): Turn message to charset=UNKNOWN-8BIT, Q-P..
       */

      register int i, rc;
      int lastwasnl = 0;
      int ct_boundary_len = 999999;

      volatile int bufferfull = 0;
      char iobuf[ZBUFSIZ];
      Sfio_t *mfp = NULL;

      if (CT && CT->boundary)
        ct_boundary_len = strlen(CT->boundary);

      SS->state  = 1;
      SS->column = -1;
      SS->alarmcnt = ALARM_BLOCKSIZE;
      SS->lastch = '\n'; /* WriteMIMELine() can decode-QP and then the
                        "lastwasnl" is no longer valid .. */

      /* AlarmJmp wraps the  appendlet()  to an all encompasing
         wrapping which breaks out of the lower levels if the
         timer does not get reset.. */

      gotalarm = 0;


#define MFPCLOSE  if (mfp != NULL) {                        \
                    zsfsetfd(mfp,-1);     /* keep the fd      */  \
                    sfclose(mfp);         /* close the stream */  \
                  }

      if (statusreport)
        report(SS,"DATA %d/%d (%d%%)",
               SS->hsize, SS->msize, (SS->hsize*100+SS->msize/2)/SS->msize);
#if 0 /* No, we aren't SYNCin now.. */
      sfsync(SS->smtpfp);
      if (gotalarm) {
        sprintf(SS->remotemsg,"smtp; 500 (msgbuffer write timeout!  DATA %d/%d [%d%%])",
              SS->hsize, SS->msize, (SS->hsize*100+SS->msize/2)/SS->msize);
        return EX_IOERR;
      }
#endif


      if (ta_use_mmap <= 0) {
        /* Using MALLOC()ed memory block */

        /* Makeing sure we are properly positioned
           at the begin of the message body */

        if (lseek(dp->msgfd, (off_t)dp->msgbodyoffset, SEEK_SET) < 0L)
          warning("Cannot seek to message body! (%m)", (char *)NULL);

        lastwasnl = 1;  /* we are guaranteed to have a \n after
                           the header */

        if (convertmode == _CONVERT_NONE) {
          bufferfull = 0;

          if (readalready > 0) {
            i = readalready;

            lastwasnl = (dp->let_buffer[i-1] == '\n');
            rc = writebuf(SS, dp->let_buffer, i);

            if (statusreport)
            report(SS,"DATA %d/%d (%d%%)",
                   SS->hsize, SS->msize, (SS->hsize*100+SS->msize/2)/SS->msize);
            /* We NEVER get timeouts here.. We get anything else.. */
            if (rc != i) {
            goto write_error_processing;
            }
            goto lastch_processing;
          }

          for (;;) {
            /* Optimization:  If the buffer has stuff in it due to
             earlier read in some of the check algorithms, we use
             it straight away: */
            i = read(dp->msgfd, (void*)(dp->let_buffer), dp->let_buffer_size);
            if (i == 0) /*EOF*/
            break;
            if (i < 0) {
            if (errno == EINTR || errno == EAGAIN)
              continue; /* Restart in good POSIX style.. */
            strcpy(SS->remotemsg,
                   "smtp; 500 (Read error from message file!?)");
            return EX_IOERR;
            }
            readalready = i;
            ++bufferfull;

            lastwasnl = (dp->let_buffer[i-1] == '\n');
            rc = writebuf(SS, dp->let_buffer, i);
            if (statusreport)
            report(SS,"DATA %d/%d (%d%%)",
                   SS->hsize, SS->msize, (SS->hsize*100+SS->msize/2)/SS->msize);
            /* We NEVER get timeouts here.. We get anything else.. */
            if (rc != i) {

            write_error_processing:;

            if (gotalarm) {
              sprintf(SS->remotemsg,"smtp; 500 (msgbuffer write timeout!  DATA %d/%d [%d%%])",
                    SS->hsize, SS->msize, (SS->hsize*100+SS->msize/2)/SS->msize);
              return EX_IOERR;
            }
            sprintf(SS->remotemsg,
                  "smtp; 500 (msgbuffer write IO-error[1]! [%s] DATA %d/%d [%d%%])",
                  strerror(errno),
                  SS->hsize, SS->msize, (SS->hsize*100+SS->msize/2)/SS->msize);
            return EX_IOERR;
            }
          } /* .. end for() */

          if (bufferfull > 1)
            readalready = 0;  /* More than one bufferfull read.. */

          /* End of "NO CONVERSIONS" mode, then ... */

        } else {

          /* ... various esoteric conversion modes:
             We are better to feed writemimeline() with
             LINES instead of blocks of data.. */

          /* Classical way to read in things, one line at the time! */

          mfp = sfnew(NULL, iobuf, sizeof(iobuf), dp->msgfd, SF_READ);
          readalready = 0;

          /* we are assuming to be positioned properly
             at the start of the message body */
          lastwasnl = 0;
          for (;;) {

            i = csfgets((void*)(dp->let_buffer), dp->let_buffer_size, mfp);
            if (i <= 0)
            break; /* EOF, or such.. */

            /* It MAY be malformed -- if it has a ZBUFSIZ*8 length
             line in it, IT CAN'T BE STANDARD CONFORMANT MIME  :-/      */

            lastwasnl = (dp->let_buffer[i-1] == '\n');

            /* XX: Detect multiparts !! */
            if (CT && CT->boundary /* defined at all! */ &&
              dp->let_buffer[0] == '-' &&
              dp->let_buffer[1] == '-' &&
              i > ct_boundary_len &&
              memcmp(dp->let_buffer+2, CT->boundary, ct_boundary_len)==0){
            /* Begin/intermediate/end boundary line of something */
            
            /* XXX: Multipart part-switching line detected ? */

            }

            /* Ok, write the line -- decoding QP can alter
             the "lastwasnl" */

            rc = writemimeline(SS, dp->let_buffer, i, convertmode);

            /* We NEVER get timeouts here.. We get anything else.. */
            if (rc != i) {
            sprintf(SS->remotemsg,
                  "500 (msgbuffer write IO-error[2]! [%s] DATA %d/%d [%d%%])",
                  strerror(errno),
                  SS->hsize, SS->msize, (SS->hsize*100+SS->msize/2)/SS->msize);
            MFPCLOSE;
            return EX_IOERR;
            }
          } /* End of line loop */

          if (i == EOF && !sfeof(mfp)) {
            strcpy(SS->remotemsg, "500 (Read error from message file!?)");
            MFPCLOSE;
            return EX_IOERR;
          }
          MFPCLOSE;

        } /* ... end of conversion modes */

      } else { /* WITH MMAP()ED MEMORY BLOCK */

        if (convertmode == _CONVERT_NONE) {
          const char *p = dp->let_buffer + dp->msgbodyoffset;
          i = dp->let_end - dp->let_buffer - dp->msgbodyoffset;

          lastwasnl = (p[i-1] == '\n');
          rc = writebuf(SS, p, i);
          if (statusreport)
            report(SS,"DATA %d/%d (%d%%)",
                 SS->hsize, SS->msize, (SS->hsize*100+SS->msize/2)/SS->msize);
          /* We NEVER get timeouts here.. We get anything else.. */
          if (rc != i) {
            if (gotalarm) {
            sprintf(SS->remotemsg,"smtp; 500 (msgbuffer write timeout!  DATA %d/%d [%d%%])",
                  SS->hsize, SS->msize, (SS->hsize*100+SS->msize/2)/SS->msize);
            return EX_IOERR;
            }
            sprintf(SS->remotemsg,
                  "smtp; 500 (msgbuffer write IO-error[1]! [%s] DATA %d/%d [%d%%])",
                  strerror(errno),
                  SS->hsize, SS->msize, (SS->hsize*100+SS->msize/2)/SS->msize);
            return EX_IOERR;
          }
          /* End of "NO CONVERSIONS" mode, then ... */

        } else {

          /* ... various esoteric conversion modes:
             We are better to feed writemimeline() with
             LINES instead of blocks of data.. */

          const char *s = dp->let_buffer + dp->msgbodyoffset;

          /* we are assuming to be positioned properly
             at the start of the message body */
          lastwasnl = 0;
          for (;;) {

            const char *p = s, *s2 = s;
            if (s >= dp->let_end) break; /* EOF */
            i = 0;
            while (s2 < dp->let_end && *s2 != '\n')
            ++s2, ++i;
            if ((lastwasnl = (*s2 == '\n')))
            ++s2, ++i;
            s = s2;
            
            /* XX: Detect multiparts !! */
            if (CT && CT->boundary /* defined at all! */ &&
              p[0] == '-' &&
              p[1] == '-' &&
              i > ct_boundary_len &&
              memcmp(p+2, CT->boundary, ct_boundary_len)==0) {
            /* Begin/intermediate/end boundary line of something */
            
            /* XXX: Multipart part-switching line detected ? */

            }

            /* Ok, write the line -- decoding QP can alter the "lastwasnl" */
            rc = writemimeline(SS, p, i, convertmode);

            /* We NEVER get timeouts here.. We get anything else.. */
            if (rc != i) {
            sprintf(SS->remotemsg,
                  "500 (msgbuffer write IO-error[2]! [%s] DATA %d/%d [%d%%])",
                  strerror(errno),
                  SS->hsize, SS->msize, (SS->hsize*100+SS->msize/2)/SS->msize);
            return EX_IOERR;
            }
            s = s2; /* Advance one linefull.. */
          
          } /* End of line loop */
          
        } /* ... end of conversion modes */
      }

 lastch_processing:;

      /* we must make sure the last thing we transmit is a CRLF sequence */
      if (!lastwasnl || SS->lastch != '\n') {
        if (writebuf(SS, "\n", 1) != 1) {
          if (gotalarm) {
            sprintf(SS->remotemsg,"500 (msgbuffer write timeout!  DATA %d/%d [%d%%])",
                  SS->hsize, SS->msize, (SS->hsize*100+SS->msize/2)/SS->msize);
            return EX_IOERR;
          }
          sprintf(SS->remotemsg,
                "500 (msgbuffer write IO-error[3]! [%s] DATA %d/%d [%d%%])",
                strerror(errno),
                SS->hsize, SS->msize, (SS->hsize*100+SS->msize/2)/SS->msize);
          if (bufferfull > 1) readalready = 0;
          return EX_IOERR;
        }
      }

      if (bufferfull > 1)     /* not all in memory, need to reread */
        readalready = 0;
#if 0
      if (sfsync(SS->smtpfp) != 0) {
        return EX_IOERR;
      }
#endif

      return EX_OK;
}


#ifdef DO_CHUNKING

extern int ssputc __(( SmtpState *, int, Sfio_t * ));

int
ssputc(SS, ch, fp)
     SmtpState *SS;
     int ch;
     Sfio_t *fp;
{
  if (SS->chunkbuf == NULL) {
    if (sferror(fp)) return EOF;
    if (sfputc(fp, ch) < 0) return EOF;
    return 0;
  }
  if (SS->chunksize >= CHUNK_MAX_SIZE) {
    if (bdat_flush(SS, 0) != EX_OK) /* Not yet the last one! */
      return EOF;
  }
  if (SS->chunksize >= SS->chunkspace) {
    SS->chunkspace <<= 1; /* Double the size */
    SS->chunkbuf = realloc(SS->chunkbuf, SS->chunkspace);
    if (SS->chunkbuf == NULL)
      return EOF;
  }
  SS->chunkbuf[SS->chunksize] = ch;
  SS->chunksize += 1;
  return 0;
}

#else

#define ssputc(SS,ch,fp) sfputc((fp),(ch))

#endif


#if 0
# define VLSFPRINTF(x) if(SS->verboselog)fprintf x
#else
# define VLSFPRINTF(x)
#endif
/*
 * Writebuf() is like write(), except all '\n' are converted to "\r\n"
 * (CRLF), and the sequence "\n." is converted to "\r\n..".
 * writebuf() has a cousin: writemimeline(), which does some more esoteric
 * conversions on flight..
 */
int
writebuf(SS, buf, len)
      SmtpState *SS;
      const char *buf;
      int len;
{
      Sfio_t *fp = SS->smtpfp;
      register const char *cp;
      register int n;
      register int state;
      int alarmcnt;

      state  = SS->state;
      alarmcnt = SS->alarmcnt;
      for (cp = buf, n = len; n > 0 && !gotalarm; --n, ++cp) {
        register char c = (*cp) & 0xFF;
        ++SS->hsize;

        if (--alarmcnt <= 0) {
          alarmcnt = ALARM_BLOCKSIZE;
          /* sfsync(fp); */

          if (statusreport)
            report(SS,"DATA %d/%d (%d%%)",
                 SS->hsize, SS->msize,
                 (SS->hsize*100+SS->msize/2)/SS->msize);
        }

        if (state && c != '\n') {
          state = 0;
          if (c == '.' && !SS->chunking) {
            if (ssputc(SS, c, fp) == EOF || ssputc(SS, c, fp) == EOF) {
            time(&endtime);
            notary_setxdelay((int)(endtime-starttime));
            notaryreport(NULL,FAILED,"5.4.2 (body write error, 1)",
                       "smtp; 500 (body write error, 1)");
            strcpy(SS->remotemsg, "write error 1");
            return EOF;
            }
            VLSFPRINTF((SS->verboselog,".."));
          } else {
            if (ssputc(SS, c, fp) == EOF) {
            time(&endtime);
            notary_setxdelay((int)(endtime-starttime));
            notaryreport(NULL,FAILED,"5.4.2 (body write error, 2)",
                       "smtp; 500 (body write error, 2)");
            strcpy(SS->remotemsg, "write error 2");
            return EOF;
            }
            VLSFPRINTF((SS->verboselog,"%c",c));
          }
        } else if (c == '\n') {
          if (ssputc(SS, '\r', fp) == EOF || ssputc(SS, c, fp) == EOF) {
            time(&endtime);
            notary_setxdelay((int)(endtime-starttime));
            notaryreport(NULL,FAILED,"5.4.2 (body write error, 3)",
                     "smtp; 500 (body write error, 3)");
            strcpy(SS->remotemsg, "write error 3");
            return EOF;
          }
          VLSFPRINTF((SS->verboselog,"\r\n"));
          state = 1;
        } else {
          if (ssputc(SS, c, fp) == EOF) {
            time(&endtime);
            notary_setxdelay((int)(endtime-starttime));
            notaryreport(NULL,FAILED,"5.4.2 (body write error, 4)",
                     "smtp; 500 (body write error, 4)");
            strcpy(SS->remotemsg, "write error 4");
            return EOF;
          }
          VLSFPRINTF((SS->verboselog,"%c",c));
        }
      }
      SS->state    = state;
      SS->alarmcnt = alarmcnt;
      return len;
}


int
writemimeline(SS, buf, len, convertmode)
      SmtpState *SS;
      const char *buf;
      int len;
      CONVERTMODE convertmode;
{
      Sfio_t *fp = SS->smtpfp;
      register const char *cp;
      register int n;
      char *i2h = "0123456789ABCDEF";
      int qp_chrs = 0;
      int qp_val = 0;
      int qp_conv;
      int column;
      int alarmcnt;

      /* `convertmode' controls the behaviour of the message conversion:
           _CONVERT_NONE (0): send as is
           _CONVERT_QP   (1): Convert 8-bit chars to QUOTED-PRINTABLE
           _CONVERT_MULTIPARTQP (2): Convert substructures to QP
           _CONVERT_8BIT (3): Convert QP-encoded chars to 8-bit
           _CONVERT_UNKNOWN (4): Turn message to charset=UNKNOWN-8BIT, Q-P..
       */

      alarmcnt = SS->alarmcnt;
      column   = SS->column;

      qp_conv = (convertmode == _CONVERT_QP ||
               convertmode == _CONVERT_UNKNOWN);

      if (buf == NULL) {            /* magic initialization */
        /* No magics here.. we are linemode.. */
        return 0;
      }
      SS->lastch = -1;
      for (cp = buf, n = len; n > 0; --n, ++cp) {
        register int c = (*cp) & 0xFF;
        ++column;
        ++SS->hsize;

        if (--alarmcnt <= 0) {
          alarmcnt = ALARM_BLOCKSIZE;
          /* sfsync(fp); */

          if (statusreport)
            report(SS,"DATA %d/%d (%d%%)",
                 SS->hsize, SS->msize,
                 (SS->hsize*100+SS->msize/2)/SS->msize);
        }

        if (convertmode == _CONVERT_8BIT) {
          if (c == '=' && qp_chrs == 0) {
            qp_val = 0;
            qp_chrs = 2;
            continue;
          }
          if (qp_chrs != 0) {
            if (c == ' ' || c == '\t' || c == '\n' || c == '\r') {
            n = 0;            /* We have the line-end wrapper mode */
            continue;   /* It should NEVER be present except at
                           the end of the line, thus we are safe
                           to do this ? */
            }
            --column;
            if ((c >= '0' && c <= '9') ||
              (c >= 'a' && c <= 'f') ||
              (c >= 'A' && c <= 'F')) {
            /* The first char was HEX digit, assume the second one
               to be also, and convert all three (=XX) to be a char
               of given value.. */
            if (c >= 'a') c -= ('a' - 'A');
            if (c > '9') c -= ('A' - '9' - 1);
            qp_val <<= 4;
            qp_val |= (c & 0x0F);
            }
            --qp_chrs;
            if (qp_chrs == 0)
            c = qp_val;
            else
            continue;
          }
          SS->lastch = c;
        } else if (qp_conv) {
          if (column > 70 && c != '\n') {
            ssputc(SS, '=',  fp);
            ssputc(SS, '\r', fp);
            ssputc(SS, '\n', fp);
            SS->lastch = '\n';
            VLSFPRINTF((SS->verboselog,"=\r\n"));
            column = 0;
          }
          /* Trailing SPACE/TAB ? */
          if (n < 3 && (c == ' ' || c == '\t')) {
            ssputc(SS, '=', fp);
            ssputc(SS, i2h[(c >> 4) & 15], fp);
            ssputc(SS, i2h[(c)      & 15], fp);
            SS->lastch = i2h[(c) & 15];
            column += 2;
            VLSFPRINTF((SS->verboselog,"=%02X",c));
            continue;
          }
          /* Any other char which needs quoting ? */
          if (c == '='  ||  c > 126 ||
            (column == 0 && (c == 'F' || c == '.')) ||
            (c != '\n' && c != '\t' && c < 32)) {

            ssputc(SS, '=', fp);
            ssputc(SS, i2h[(c >> 4) & 15], fp);
            ssputc(SS, i2h[(c)      & 15], fp);
            SS->lastch = i2h[(c) & 15];
            column += 2;
            VLSFPRINTF((SS->verboselog,"=%02X",c));
            continue;
          }
        } /* .... end convertmode   */

        if (column == 0 && c == '.' && !SS->chunking) {
          if (ssputc(SS, c, fp) == EOF) {
            time(&endtime);
            notary_setxdelay((int)(endtime-starttime));
            notaryreport(NULL,FAILED,"5.4.2 (body write error, 5)",
                     "smtp; 500 (body write error, 5)");
            strcpy(SS->remotemsg, "write error 5");
            return EOF;
          }
          VLSFPRINTF((SS->verboselog,".."));
        }

        if (c == '\n') {
          if (ssputc(SS, '\r', fp) == EOF) {
            time(&endtime);
            notary_setxdelay((int)(endtime-starttime));
            notaryreport(NULL,FAILED,"5.4.2 (body write error, 6)",
                     "smtp; 500 (body write error, 6)");
            strcpy(SS->remotemsg, "write error 6");
            return EOF;
          }
          VLSFPRINTF((SS->verboselog,"\r"));
          column = -1;
        }
        if (ssputc(SS, c, fp) == EOF) {
          time(&endtime);
          notary_setxdelay((int)(endtime-starttime));
          notaryreport(NULL,FAILED,"5.4.2 (body write error, 7)",
                   "smtp; 500 (body write error, 7)");
          strcpy(SS->remotemsg, "write error 7");
          return EOF;
        }
        SS->lastch = c;
        VLSFPRINTF((SS->verboselog,"%c",c));

      }
      SS->column   = column;
      SS->alarmcnt = alarmcnt;
      return len;
}


/* When data is clean 7-BIT, do:  *flag_ptr = (*flag_ptr) << 1  */
int 
check_7bit_cleanness(dp)
struct ctldesc *dp;
{
      if (ta_use_mmap > 0) {

      /* With MMAP()ed spool file this is sweet and simple.. */

      const register char *s = dp->let_buffer + dp->msgbodyoffset;
      while (s < dp->let_end)
        if (128 & *s)
          return 0;
        else
          ++s;
      return 1;

      } else {


      register int i;
      register int bufferfull;
      int lastwasnl;
      off_t mfd_pos;
      int mfd = dp->msgfd;

      /* can we use cache of message body data ? */
      if (readalready != 0) {
        for (i=0; i<readalready; ++i)
          if (128 & (dp->let_buffer[i]))
            return 0;         /* Not clean ! */
      }

      /* make sure we are properly positioned at the start
         of the message body */
      bufferfull = 0;

      mfd_pos = lseek(mfd, (off_t)dp->msgbodyoffset, SEEK_SET);
      
      while (1) {
        i = read(mfd, (void*)(dp->let_buffer), dp->let_buffer_size);
        if (i == 0)
          break;
        if (i < 0) {
          /* ERROR ?!?!? */
          if (errno == EINTR)
            continue; /* Hickup... */
          readalready = 0;
          return 0;
        }
        lastwasnl = (dp->let_buffer[i-1] == '\n');
        readalready = i;
        bufferfull++;
        for (i = 0; i < readalready; ++i)
          if (128 & (dp->let_buffer[i])) {
            lseek(mfd, mfd_pos, SEEK_SET);
            readalready = 0;
            return 0;         /* Not clean ! */
          }
      }
      /* Got to EOF, and still it is clean 7-BIT! */
      lseek(mfd, mfd_pos, SEEK_SET);
      if (bufferfull > 1)     /* not all in memory, need to reread */
        readalready = 0;

      /* Set indication! */
      return 1;
      }
}

Generated by  Doxygen 1.6.0   Back to index