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

encoding.c

/* Encoding.c - MIME encoding and decoding for af.
   Copyright (C) 1996 - 2003 Malc Arnold.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */


#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include "af.h"
#include "mime.h"
#include "keyseq.h"
#include "functions.h"
#include "variable.h"
#include STRING_HDR

/****************************************************************************/
/* RCS info */

#ifndef lint
static char *RcsId = "$Id: encoding.c,v 1.5 2003/10/27 23:16:20 malc Exp $";
#endif /* ! lint */

/****************************************************************************/
/* Global function declarations */

extern char *xmalloc(), *xrealloc();
extern char *xstrdup(), *vstrcat();
extern char *get_line(), *get_binline();
extern int strcasecmp(), strncasecmp();
extern void free(), typeout(), typebin();
extern void change_text(), free_text();
extern TEXTLINE *add_text(), *add_bintext();
extern TEXTLINE *copy_text();
extern KEYSEQ *make_seq();

/* Local function declarations */

static char *qp_encode_line(), *b64_encode_line();
static char *b64_encode_final(), *b64_decode_line();
static char *b64_decode_final(), *uue_encode_line();
static char *uue_encode_final(), *uue_decode_line();
static char *find_newline();
static TEXTLINE *qp_encode_list(), *qp_decode_list();
static TEXTLINE *b64_encode_list(), *b64_decode_list();
static TEXTLINE *uue_encode_list(), *uue_decode_list();

/****************************************************************************/
/* Import the system error number */

extern int errno;

/****************************************************************************/
/* Import the user quit flag from commands.c */

extern int user_quit;

/****************************************************************************/
/* The characters used as hex and base64 digits */

static char *hex_digits = "0123456789ABCDEF";
static char *b64_digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZ\
abcdefghijklmnopqrstuvwxyz0123456789+/";

/****************************************************************************/
int encoding_needed(text)
TEXTLINE *text;
{
      /* Return whether the text in the list needs encoding */

      char *c;
      int needed = ENCODE_NONE;
      int no_chars = 0, no_8bit = 0;
      TEXTLINE *line;

      /* Loop over each line in the list */

      for (line = text; line != NULL; line = line->next) {
            /* Check if the line contains non-ascii characters */

            for (c = line->line; c < line->line + line->len; c++) {
                  /* Check if this character isn't ascii */

                  needed |= (!isascii(*c)) ? ENCODE_8BIT : 0;
                  needed |= (*c == '\0') ? ENCODE_BINARY : 0;

                  /* Check if we have a trailing space in the line */

                  needed |= (isascii(*c) && (*c == ' ' || *c == '\t')
                           && *(c + 1) == '\n') ? ENCODE_SPACES : 0;

                  /* Update the character counts */

                  no_chars++;
                  no_8bit += (isascii(*c) && isprint(*c)) ? 0 : 1;
            }

            /* Check if the line's so long it needs encoding */

            needed |= (line->len > SMTP_MAX_LINE_LEN) ? ENCODE_SMTP : 0;
            needed |= (line->len > QP_LINE_LENGTH) ? ENCODE_FOLD : 0;
      }

      /* Set the encoding hint if required */

      if (needed != ENCODE_NONE && no_8bit > no_chars / 6) {
            /* It'll be cheaper to encode this in base64 */

            needed |= ENCODE_BASE64;
      }

      /* Return what encoding is needed */

      return(needed);
}
/****************************************************************************/
TEXTLINE *encode_text_list(text, encoding, textual)
TEXTLINE *text;
char *encoding;
int textual;
{
      /* Return an encoded copy of the text list */

      if (encoding != NULL && !strcasecmp(encoding, QUOTED_PRINTABLE)) {
            /* Return the quoted-printable encoded text */

            return(qp_encode_list(text));
      } else if (encoding != NULL && !strcasecmp(encoding, BASE64)) {
            /* Return the base64 encoded text */

            return(b64_encode_list(text, textual));
      } else if (encoding != NULL
               && (!strcasecmp(encoding, X_UUE)
                   || !strcasecmp(encoding, X_UUENCODE))) {
            /* Write the uuencoded text */

            return(uue_encode_list(text, textual));
      } else {
            /* Return the text of the file as is */

            return(copy_text(text));
      }
      /*NOTREACHED*/
}
/****************************************************************************/
TEXTLINE *decode_text_list(text, encoding, textual)
TEXTLINE *text;
char *encoding;
int textual;
{
      /* Return the decoded text held in the text list */

      if (encoding != NULL && !strcasecmp(encoding, QUOTED_PRINTABLE)) {
            /* Return the quoted-printable decoded text */

            return(qp_decode_list(text));
      } else if (encoding != NULL && !strcasecmp(encoding, BASE64)) {
            /* Return the base64-decoded text */

            return(b64_decode_list(text, textual));
      } else if (encoding != NULL
               && (!strcasecmp(encoding, X_UUE)
                   || !strcasecmp(encoding, X_UUENCODE))) {
            /* Return the uudecoded text */

            return(uue_decode_list(text, textual));
      } else {
            /* Just copy the text as is */

            return(copy_text(text));
      }
      /*NOTREACHED*/
}
/****************************************************************************/
int write_decoded_text(text, fp, encoding, delim, prefix, textual)
TEXTLINE *text;
FILE *fp;
char *encoding, *delim, *prefix;
int textual;
{
      /*
       * Write the decoded text of the list to fp,  Use encoding to
       * check how to decode the text.
       *
       * If delim and prefix are non-null, then we must quote any
       * instances of delim in the text of the message.
       *
       * Textual implies that we may need CRLF conversion in base64
       * encoded text.
       */
      
      int prefix_line;
      TEXTLINE *decoded_text, *line;

      /* First decode the text of the list */

      decoded_text = decode_text_list(text, encoding, textual);

      /* Loop over the text of the list */

      for (line = decoded_text; line != NULL; line = line->next) {
            /* Will we need to prefix this line? */

            prefix_line = (delim != NULL && prefix != NULL
                         && !strncmp(line->line, delim, strlen(delim)));

            /* Now write the prefix and decoded text to the file */

            if (prefix_line && fputs(prefix, fp) == EOF
                 || fwrite(line->line, 1, line->len, fp) < line->len) {
                  /* Error writing the decoded text */

                  return(errno);
            }
      }

      /* Free the decoded text and return success */

      free_text(decoded_text);
      return(0);
}
/****************************************************************************/
void show_decoded_text(text, encoding, textual)
TEXTLINE *text;
char *encoding;
int textual;
{
      /* Display the decoded text of the list via typeout */

      TEXTLINE *decoded_text, *line;

      /* First decode the text of the list */

      decoded_text = decode_text_list(text, encoding, textual);

      /* Loop over the text of the list */

      for (line = decoded_text; line != NULL; line = line->next) {
            /* Display the text via typeout */

            typebin(line->line, line->len);
      }

      /* Free the decoded text and return */

      free_text(decoded_text);
      return;
}
/****************************************************************************/
char *qp_encode(text, encode_chars)
char *text, *encode_chars;
{
      /* Return the 8bit text encoded in the quoted-printable encoding */

      return(qp_encode_line(text, strlen(text), encode_chars, FALSE));
}
/****************************************************************************/
static TEXTLINE *qp_encode_list(text)
TEXTLINE *text;
{
      /* Return a quoted-printable encoded copy of the text list */

      char *eline, *newline, *next;
      TEXTLINE *line, *encoded_text = NULL;

      /* Loop over the text encoding and writing each line */

      for (line = text; line != NULL; line = line->next) {
            /* Get the encoded text of the line */

            eline = qp_encode_line(line->line, line->len, NULL, TRUE);

            /* Add the encoded text lines to the output */

            while (eline != NULL) {
                  /* Find the first newline in the line */

                  if ((newline = strchr(eline, '\n')) != NULL) {
                        /* Now check if it's the end of the line */

                        next = (*(newline + 1) != '\0')
                              ? xstrdup(newline + 1) : NULL;
                        *(newline + 1) = '\0';

                        /* Reallocate the space for the line */
                  
                        eline = xrealloc(eline, newline - eline + 1);
                  } else {
                        /* We're at the end of the lines */

                        next = NULL;
                  }

                  /* Add the line to the list and move on */

                  encoded_text = add_text(encoded_text, eline);
                  eline = next;
            }
      }

      /* Return the encoded text of the file */

      return(encoded_text);
}
/****************************************************************************/
size_t qp_encoded_len(c, encode_chars, eol)
char c, *encode_chars;
int eol;
{
      /* Return the length of character c when QP encoded */

      return((c == QP_ESC_CHAR || !isascii(c)
            || (!isgraph(c) && c != '\n'
                && (c != '\t' && c != ' ' || eol))
            || (encode_chars != NULL &&
                strchr(encode_chars, c) != NULL)) ? QP_ESC_LEN : 1);
}
/****************************************************************************/
static char *qp_encode_line(text, len, encode_chars, soft_breaks)
char *text, *encode_chars;
size_t len;
int soft_breaks;
{
      /* Encode 8bit text in the quoted-printable encoding */

      char *etext = NULL, *e, *p;
      unsigned char x1, x2;
      int col = 0, eol, encode;

      /* Allocate the encoded text buffer to the maximum possible size */

      e = etext = xmalloc(len * 4 + 1);

      /* Set a pointer into the text */

      p = text;

      /* Loop over the text, encoding as required */

      while (p < text + len) {
            /* Are we at the end of the line? */

            eol = (*p == '\n' || *(p + 1) == '\n');

            /* Do we need to encode this character? */

            encode = (*p == QP_ESC_CHAR || !isascii(*p)
                    || !isgraph(*p) && *p != '\n' &&
                    (*p != '\t' && *p != ' ' || eol)
                    || encode_chars != NULL &&
                    strchr(encode_chars, *p) != NULL);
            col += (encode) ? QP_ESC_LEN : 1;

            /* Check if we need a soft carriage-return yet */

            if (soft_breaks && !eol && col > QP_LINE_LENGTH) {
                  /* Add a soft line break to the line */

                  (void) strcpy(e, QP_LINE_BREAK);
                  e += strlen(QP_LINE_BREAK);
                  col = (encode) ? QP_ESC_LEN : 1;
            }

            /* Output the character and update the length */

            if (encode) {
                  /* We need to encode this character */

                  x1 = hex_digits[((unsigned char) *p) / 16];
                  x2 = hex_digits[((unsigned char) *p) % 16];
                  (void) sprintf(e, "%c%c%c", QP_ESC_CHAR, x1, x2);
                  e += QP_ESC_LEN;
            } else {
                  /* This character is valid as it stands */

                  *e++ = *p;
            }

            /* And move on to the next character */

            p++;
      }
      *e = '\0';

      /* Reallocate and return the encoded text */

      etext = xrealloc(etext, strlen(etext) + 1);
      return(etext);
}
/****************************************************************************/
char *qp_decode(text, len)
char *text;
size_t *len;
{
      /* Decode and return a single line in quoted-printable encoding */

      char *dtext, *d;
      char *etext, *p;
      int newlines = 0;
      int spaces = 0;
      int x1, x2;

      /* Copy the encoded text into an allocated buffer */

      etext = xmalloc(*len + 1);
      (void) memcpy(etext, text, *len);

      /* Trim trailing white space off the encoded line */

      for (p = etext + *len; p > etext &&
           (*(p - 1) == ' ' || *(p - 1) == '\t'
            || *(p - 1) == '\n'); p--) {
            /* Update the newline and space counts */

            newlines += (*(p - 1) == '\n') ? 1 : 0;
            spaces++;
      }

      /* Update the line length and restore newlines */

      *len -= (newlines) ? spaces : 0;
      while (newlines--) {
            *(etext + (*len)++) = '\n';
      }

      /* Allocate the decoded text buffer */

      d = dtext = xmalloc(*len + 1);

      /* Set a pointer into the text */

      p = etext;

      /* Loop over the text, decoding as required */

      while (p < etext + *len) {
            /* Does the character start an escape sequence? */

            if (*p == QP_ESC_CHAR && *len - (p - etext) >= QP_ESC_LEN
                && (x1 = QP_VALUE(*(p + 1))) >= 0
                && (x2 = QP_VALUE(*(p + 2))) >= 0) {
                  /* Decode the escape sequence */

                  *d++ = x1 * 16 + x2;
                  p += QP_ESC_LEN;
            } else if (*p == QP_ESC_CHAR && *(p + 1) == '\n') {
                  /* We can discard soft line breaks */

                  p += strlen(QP_LINE_BREAK);
            } else {
                  /* This character is ok as is */

                  *d++ = *p++;
            }
      }
      *d = '\0';

      /* Free the encoded text */

      free(etext);

      /* Reallocate and return the decoded string */

      *len = (d - dtext);
      dtext = xrealloc(dtext, *len + 1);
      return(dtext);
}
/****************************************************************************/
static TEXTLINE *qp_decode_list(text)
TEXTLINE *text;
{
      /* Return a quoted-printable decoded copy of the text list */

      char *buf = NULL, *dtext;
      size_t buflen = 0, len = 0;
      TEXTLINE *line, *decoded_text = NULL;

      /* Loop over each line in the list */

      for (line = text; !user_quit && line != NULL; line = line->next) {
            /* Get the decoded form of the text */

            len = line->len;
            dtext = qp_decode(line->line, &len);

            /* Prepend any buffer to the text */

            if (buf != NULL) {
                  /* Prepend the buffer to the text */

                  buf = xrealloc(buf, buflen + len + 1);
                  (void) memcpy(buf + buflen, dtext, len);
                  *(buf + buflen + len) = '\0';

                  /* Update the decoded text and clear the buffer */

                  dtext = buf;
                  len += buflen;
                  buf = NULL;
                  buflen = 0;
            }

            /* Do we have a full line to add to the list? */

            if (*(dtext + len - 1) == '\n') {
                  /* Add the line to the list */

                  decoded_text = add_bintext(decoded_text, dtext, len);
            } else {
                  /* Save any partial line in the buffer */

                  buf = dtext;
                  buflen = len;
            }
      }

      /* Write any final, incomplete decoded line */

      if (buf != NULL) {
            decoded_text = add_bintext(decoded_text, buf, buflen);
      }

      /* And return the decoded text */

      return(decoded_text);
}
/****************************************************************************/
char *b64_encode(text, len, textual)
char *text;
size_t len;
int textual;
{
      /*
       * Encode text with the base 64 MIME encoding, or clean up
       * after encoding if text is NULL.
       */

      char *etext, *ctext, *p, *q;
      char values[B64_ELENGTH];
      int nvalues = 0, col = -1;

      /* Encode the text, including any trailing characters */

      etext = b64_encode_line(text, len, textual, values, &nvalues, &col);
      etext = b64_encode_final(etext, values, nvalues, col);

      /* Remove any newlines from the base 64 output */

      q = ctext = xmalloc(strlen(etext) + 1);
      for (p = etext; *p != '\0'; p++) {
            if (!isspace(*p)) {
                  *q++ = *p;
            }
      }
      *q = '\0';

      /* Clean up and return the encoded text */

      free(etext);
      return(ctext);
}
/****************************************************************************/
static TEXTLINE *b64_encode_list(text, textual)
TEXTLINE *text;
int textual;
{
      /* Return a base64 encoded copy of the text list */

      char *buf = NULL, *etext;
      char *newline, *next;
      char values[B64_ELENGTH];
      int nvalues = 0, col = 0;
      TEXTLINE *line, *encoded_text = NULL;

      /* Loop over the file encoding each block */

      for (line = text; line != NULL; line = line->next) {
            /* Get the encoded form of the text */

            etext = b64_encode_line(line->line, line->len, textual,
                              values, &nvalues, &col);

            /* Prepend any buffer to the text */

            if (buf != NULL) {
                  /* Prepend the buffer and clear it */

                  buf = xrealloc(buf, strlen(buf) + strlen(etext) + 1);
                  (void) strcat(buf, etext);
                  etext = buf;
                  buf = NULL;
            }

            /* Do we have any full lines to add to the list? */

            while (etext != NULL &&
                   (newline = strchr(etext, '\n')) != NULL) {
                  /* Cut the line out of the text */

                  next = (*(newline + 1) != '\0')
                        ? xstrdup(newline + 1) : NULL;
                  *(newline + 1) = '\0';
                  etext = xrealloc(etext, newline - etext + 2);

                  /* Add the line to the list */

                  encoded_text = add_text(encoded_text, etext);
                  etext = next;
            }

            /* Save any partial line in the buffer */

            buf = etext;
      }

      /* Write any final encoded characters and return the text */

      buf = b64_encode_final(buf, values, nvalues, col);
      encoded_text = add_text(encoded_text, buf);
      return(encoded_text);
}
/****************************************************************************/
static char *b64_encode_line(line, len, textual, values, nvalues, col)
char *line, *values;
int len, textual, *nvalues, *col;
{
      /* Encode len with the base 64 MIME encoding */

      char *etext, *e, *c, *v;
      int canonicalised = FALSE;

      /* Allocate the encoded text buffer */

      e = etext = xmalloc(len * 2 + 1);

      /* Set up a pointer to the values array */

      v = values;

      /* Loop over the text, encoding as required */

      c = line;
      while (c < line + len) {
            /* Get the next three characters */

            while (*nvalues < B64_TLENGTH && c < line + len) {
                  /* Do need to canonicalise a line break? */

                  canonicalised = (textual && !canonicalised &&
                               *c == '\n' &&
                               (c == line || *(c - 1) != '\r'));

                  /* Copy the character or a CR into the output */

                  v[(*nvalues)++] = (canonicalised) ? '\r' : *c++;
            }

            /* Give up if we don't have enough characters */

            if (*nvalues < B64_TLENGTH) {
                  break;
            }

            /* Insert a line break if required */

            if ((*col + B64_ELENGTH) > B64_LINE_LENGTH) {
                  *e++ = '\n';
                  *col = 0;
            }

            /* Set the next four output characters */

            *e++ = b64_digits[(v[0] & 0xFC) >> 2];
            *e++ = b64_digits[(v[0] & 0x03) << 4 | (v[1] & 0xF0) >> 4];
            *e++ = b64_digits[(v[1] & 0x0F) << 2 | (v[2] & 0xC0) >> 6];
            *e++ = b64_digits[(v[2] & 0x3F)];

            /* Update the column and values counters */

            if (*col >= 0) {
                  *col += B64_ELENGTH;
            }
            *nvalues = 0;
      }
      *e = '\0';

      /* Now return the encoded text */

      return(etext);
}
/****************************************************************************/
static char *b64_encode_final(etext, values, nvalues, col)
char *etext, *values;
int nvalues, col;
{
      /* Encode any partial characters at the end of base64 text */

      char *e, *v;
      int len;

      /* There's no work to do if there are no values */

      if (!nvalues && col > 0) {
            /* Append a newline to the text */

            etext = xrealloc(etext, strlen(etext) + 2);
            (void) strcat(etext, "\n");

            /* And return the encoded text */

            return(etext);
      } else if (!nvalues) {
            /* Just return the encoded text */

            return(etext);
      }

      /* (Re)allocate the encoded text buffer */

      len = (etext != NULL) ? strlen(etext) : 0;
      etext = (etext == NULL) ? xmalloc(B64_ELENGTH + 3) :
            xrealloc(etext, strlen(etext) + B64_ELENGTH + 3);
      e = etext + len;

      /* Set up a pointer to the values array */

      v = values;

      /* Insert a line break if required */

      if ((col + B64_ELENGTH) >= B64_LINE_LENGTH) {
            *e++ = '\n';
      }

      /* Initialise the second value if required */

      v[1] = (nvalues > 1) ? v[1] : '\0';

      /* Now add the characters to the text */

      *e++ = b64_digits[(v[0] & 0xFC) >> 2];
      *e++ = b64_digits[(v[0] & 0x03) << 4 | (v[1] & 0xF0) >> 4];

      /* Handle the second character if we have one */

      *e++ = (nvalues > 1) ? b64_digits[(v[1] & 0x0F) << 2] : B64_PAD_CHAR;

      /* Add the final padding and newline */

      (void) sprintf(e, "%c\n", B64_PAD_CHAR);

      /* Reallocate and return the final string */

      etext = xrealloc(etext, strlen(etext) + 1);
      return(etext);
}
/****************************************************************************/
char *b64_decode(text, len, textual)
char *text;
size_t *len;
int textual;
{
      /* Decode and return a single line in base64 encoding */

      char *dtext = NULL;
      char values[B64_ELENGTH + 2];
      int nvalues = 0;

      /* Decode the main part of the line and any final characters */

      dtext = b64_decode_line(text, len, textual, values, &nvalues);
      dtext = b64_decode_final(dtext, len, textual, values, nvalues);

      /* And return the decoded text */

      return(dtext);
}
/****************************************************************************/
static TEXTLINE *b64_decode_list(text, textual)
TEXTLINE *text;
int textual;
{
      /* Return a base64 decoded copy of the text list */

      char *buf = NULL, *dtext;
      char *newline, *next;
      char values[B64_ELENGTH + 2];
      int nvalues = 0;
      size_t buflen = 0, len, nextlen;
      TEXTLINE *line, *decoded_text = NULL;

      /* Loop over the file decoding each block */

      for (line = text; line != NULL; line = line->next) {
            /* Get the decoded form of the text */

            len = line->len;
            dtext = b64_decode_line(line->line, &len, textual,
                              values, &nvalues);

            /* Append any final text if required */

            if (line->next == NULL) {
                  /* Append any final text to this line */

                  dtext = b64_decode_final(dtext, &len, textual,
                                     values, nvalues);
            }

            /* Prepend any buffer to the text */

            if (buf != NULL) {
                  /* Prepend the buffer and clear it */

                  buf = xrealloc(buf, buflen + len + 1);
                  (void) memcpy(buf + buflen, dtext, len);
                  *(buf + buflen + len) = '\0';
                  dtext = buf;
                  len += buflen;
                  buf = NULL;
                  buflen = 0;
            }

            /* Do we have any full lines to add to the list? */

            while (dtext != NULL &&
                   (newline = find_newline(dtext, len)) != NULL) {
                  /* Copy any text after the newline */

                  if (newline - dtext < len) {
                        nextlen = len - ((newline + 1) - dtext);
                        next = xmalloc(nextlen + 1);
                        (void) memcpy(next, newline + 1, nextlen);
                        *(next + nextlen) = '\0';
                  } else {
                        next = NULL;
                        nextlen = 0;
                  }

                  /* Cut the text out of the line */

                  *(newline + 1) = '\0';
                  len = (newline - dtext) + 1;
                  dtext = xrealloc(dtext, len + 1);
                  *(dtext + len) = '\0';

                  /* Add the line to the list */

                  decoded_text = add_bintext(decoded_text, dtext, len);
                  dtext = next;
                  len = nextlen;
            }

            /* Save any partial line in the buffer */

            buf = dtext;
            buflen = (buf != NULL) ? len : 0;
      }

      /* Write any final, incomplete decoded line */

      if (buf != NULL) {
            decoded_text = add_bintext(decoded_text, buf, buflen);
      }

      /* And return the decoded text */

      return(decoded_text);
}
/****************************************************************************/
static char *b64_decode_line(text, len, textual, values, nvalues)
char *text, *values;
size_t *len;
int textual, *nvalues;
{
      /*
       * Decode and return a single line in base64 encoding.  Values
       * contains a pointer to any partial base 64 word in the
       * previous line decoded.
       */

      char *dtext, *p, *v, *d;
      int x, d1, d2, d3, d4;

      /* Allocate the decoded text buffer */

      d = dtext = xmalloc(*len + 1);

      /* Set up a pointer to the values array */

      v = values;

      /* Set up a pointer to the start of the text */

      p = text;

      /* Loop over the text, decoding as required */

      while (p < text + *len) {
            /* Is this a valid base 64 character? */

            if ((x = B64_VALUE(*p)) >= 0) {
                  /* Add this character to the current set */

                  v[(*nvalues)++] = x;
            }

            /* Do we have a full set of characters? */

            if (*nvalues >= B64_ELENGTH + 2) {
                  /* Extract the next four output characters */

                  d1 = v[0] << 2 | (v[1] & 0x30) >> 4;
                  d2 = (v[1] & 0x0F) << 4 | (v[2] & 0x3C) >> 2;
                  d3 = (v[2] & 0x03) << 6 | v[3];
                  d4 = v[4] << 2 | (v[5] & 0x30) >> 4;

                  /* Append the characters, stripping textual CRs */

                  if (!textual || d1 != '\r' || d2 != '\n') {
                        *d++ = d1;
                  }
                  if (!textual || d2 != '\r' || d3 != '\n') {
                        *d++ = d2;
                  }
                  if (!textual || d3 != '\r' || d4 != '\n') {
                        *d++ = d3;
                  }

                  /* Shift the values and update the count */

                  v[0] = v[4];
                  v[1] = v[5];
                  *nvalues = 2;
            }

            /* And move on to the next character */

            p++;
      }
      *d = '\0';

      /* Reallocate and return the decoded text */

      *len = d - dtext;
      dtext = xrealloc(dtext, *len + 1);
      return(dtext);
}
/****************************************************************************/
static char *b64_decode_final(dtext, len, textual, values, nvalues)
char *dtext, *values;
size_t *len;
int textual, nvalues;
{
      /*
       * Decode values, a partial base64 word, and append the
       * decoded text to dtext, returning the updated dtext.
       */

      char *v, *d;
      int d1, d2, d3;

      /* There's no work to do if there are no values */

      if (nvalues < 2) {
            return(dtext);
      }

      /* Reallocate the decoded text buffer */

      dtext = xrealloc(dtext, *len + 4);
      d = dtext + *len;

      /* Set up a pointer to the values array */

      v = values;

      /* Initialise the third and fourth values */

      v[2] = (nvalues > 2) ? v[2] : 0;
      v[3] = (nvalues > 3) ? v[3] : 0;

      /* Extract the next three output characters */

      d1 = v[0] << 2 | (v[1] & 0x30) >> 4;
      d2 = (v[1] & 0x0F) << 4 | (v[2] & 0x3C) >> 2;
      d3 = (v[2] & 0x03) << 6 | v[3];

      /* Append the characters, stripping textual CRs */

      if (!textual || d1 != '\r' || d2 != '\n') {
            *d++ = d1;
      }
      if (nvalues > 2 && (!textual || d2 != '\r' || d3 != '\n')) {
            *d++ = d2;
      }
      if (nvalues > 3) {
            *d++ = d3;
      }
      *d = '\0';

      /* Reallocate and return the updated text */

      *len = (d - dtext);
      dtext = xrealloc(dtext, *len + 1);
      return(dtext);
}
/****************************************************************************/
static TEXTLINE *uue_encode_list(text, textual)
TEXTLINE *text;
int textual;
{
      /* Return a uuencoded copy of the text list */

      char *rtext = NULL, *etext;
      char *newline, *next;
      size_t rlen = 0;
      TEXTLINE *line, *encoded_text = NULL;

      /* First add the begin block to the output */

      etext = vstrcat(UUE_BEGIN, "644 message\n", NULL);
      encoded_text = add_text(encoded_text, etext);
      
      /* Loop over the list encoding and writing each line */

      for (line = text; line != NULL; line = line->next) {
            /* Get the encoded form of the text */

            etext = uue_encode_line(&rtext, &rlen, line->line, line->len);

            /* Do we have any full lines to add to the list? */

            while (etext != NULL &&
                   (newline = strchr(etext, '\n')) != NULL) {
                  /* Cut the line out of the text */

                  next = (*(newline + 1) != '\0')
                        ? xstrdup(newline + 1) : NULL;
                  *(newline + 1) = '\0';
                  etext = xrealloc(etext, newline - etext + 1);

                  /* Add the line to the list */

                  encoded_text = add_text(encoded_text, etext);
                  etext = next;
            }
      }

      /* Write any final encoded characters and return the text */

      etext = uue_encode_final(rtext, rlen);
      encoded_text = add_text(encoded_text, etext);
      encoded_text = add_text(encoded_text, xstrdup(UUE_END));
      return(encoded_text);
}
/****************************************************************************/
static char *uue_encode_line(rline, rlen, line, len)
char **rline, *line;
int *rlen, len;
{
      /* Encode len with the obsolete UUENCODE encoding */

      char v[UUE_TLENGTH], *c, *e = NULL;
      char *mline, *etext = NULL;
      int nlines, nchars = 0;
      int l, nvalues = 0;

      /* Allocate a single line for the input */

      c = mline = xmalloc(*rlen + len + 1);

      /* Copy the remaining and new lines into the merged line */

      if (*rline != NULL) {
            (void) memcpy(mline, *rline, *rlen);
      }
      (void) memcpy(mline + *rlen, line, len);
      *(mline + *rlen + len) = '\0';

      /* Zero the length of the remaining line */

      len += *rlen;
      *rlen = 0;

      /* Free any old remaining line */

      if (*rline != NULL) {
            free(*rline);
            *rline = NULL;
      }

      /* How many full lines can we encode from this line? */

      if (nlines = (len / UUE_LINE_LEN)) {
            /* Allocate the encoded text buffer */

            e = etext = xmalloc(nlines * (UUE_LINE_LEN * 4 / 3 + 2) + 1);
      }

      /* Loop over each line of output in the input buffer */

      for (l = 0; l < nlines; l++) {
            /* Set the count position at the start of the line */

            *e++ = UUE_LINE_LEN + UUE_BASE_CHAR;

            /* Loop over the text, encoding as required */

            while (nchars < UUE_LINE_LEN) {
                  /* Get the next three characters */

                  while (nvalues < UUE_TLENGTH) {
                        /* Copy the character into the output */

                        v[nvalues++] = *c++;
                  }

                  /* Set the next four output characters */

                  *e++ = ((v[0] & 0xFC) >> 2) + UUE_BASE_CHAR;
                  *e++ = ((v[0] & 0x03) << 4 |
                        (v[1] & 0xF0) >> 4) + UUE_BASE_CHAR;
                  *e++ = ((v[1] & 0x0F) << 2 |
                        (v[2] & 0xC0) >> 6) + UUE_BASE_CHAR;
                  *e++ = (v[2] & 0x3F) + UUE_BASE_CHAR;

                  /* Update the value and character counts */

                  nchars += UUE_TLENGTH;
                  nvalues = 0;
            }

            /* Add a newline and terminate the text */

            *e++ = '\n';
            *e = '\0';

            /* And reset the number of characters */

            nchars = 0;
      }

      /* Now store any remaining text */

      if (len > UUE_LINE_LEN * nlines) {
            /* Copy the remaining characters across */

            *rlen = len - UUE_LINE_LEN * nlines;
            *rline = xmalloc(*rlen + 1);
            (void) memcpy(*rline, mline + UUE_LINE_LEN * nlines, *rlen);
            *(*rline + *rlen) = '\0';
      }

      /* Clean up and return the encoded text */

      free(mline);
      return(etext);
}
/****************************************************************************/
static char *uue_encode_final(line, len)
char *line;
int len;
{
      /* Encode a partial line at the end of UUENCODED text */

      char v[UUE_TLENGTH], *e, *c;
      char *etext = NULL;
      int nchars = 0, nvalues = 0;

      /* Allocate the encoded text buffer */

      e = etext = xmalloc(((len - 1) / 3 + 1) * 4 + strlen(UUE_END) + 5);

      /* Set the count position at the start of the line */

      *e++ = len + UUE_BASE_CHAR;

      /* Loop over the text, encoding as required */

      c = line;
      while (nchars < len) {
            /* Get the next three characters */

            while (nvalues < UUE_TLENGTH) {
                  /* Copy the character into the output */

                  v[(nvalues)++] = (nchars < len) ? *c++ : 0;
            }

            /* Set the next four output characters */

            *e++ = ((v[0] & 0xFC) >> 2) + UUE_BASE_CHAR;
            *e++ = ((v[0] & 0x03) << 4 |
                  (v[1] & 0xF0) >> 4) + UUE_BASE_CHAR;
            *e++ = ((v[1] & 0x0F) << 2 |
                  (v[2] & 0xC0) >> 6) + UUE_BASE_CHAR;
            *e++ = (v[2] & 0x3F) + UUE_BASE_CHAR;

            /* Update the value and character counts */

            nchars += UUE_TLENGTH;
            nvalues = 0;
      }
      *e++ = '\n';

      /* Ensure the message ends with a blank line */

      if (len > 0) {
            /* Add a blank line to the message */

            *e++ = UUE_BASE_CHAR;
            *e++ = '\n';
      }

      /* Add the end-of-data marker */

      (void) strcpy(e, UUE_END);

      /* Clean up and return the encoded text */

      free(line);
      return(etext);
}
/****************************************************************************/
static TEXTLINE *uue_decode_list(text)
TEXTLINE *text;
{
      /* Return a uue decoded copy of the text list */

      char *buf = NULL, *dtext;
      char *newline, *next;
      int blank = FALSE;
      size_t buflen = 0, len, nextlen;
      TEXTLINE *line, *decoded_text = NULL;

      /* Find the begin line in the list */

      line = text;
      while (line != NULL &&
             strncasecmp(line->line, UUE_BEGIN, strlen(UUE_BEGIN))) {
            /* Move on to the next line */

            line = line->next;
      }

      /* Skip any begin line from the file */

      line = (line != NULL) ? line->next : line;

      /* Loop over the file decoding each block */

      while (line != NULL && (!blank || strcmp(line->line, UUE_END))) {
            /* Get the decoded form of the text */

            len = line->len;
            dtext = uue_decode_line(line->line, &len);

            /* Check if the line's blank */

            blank = (!len);

            /* Prepend any buffer to the text */

            if (buf != NULL) {
                  /* Prepend the buffer and clear it */

                  buf = xrealloc(buf, buflen + len + 1);
                  (void) memcpy(buf + buflen, dtext, len);
                  *(buf + buflen + len) = '\0';
                  dtext = buf;
                  len += buflen;
                  buf = NULL;
                  buflen = 0;
            }

            /* Do we have any full lines to add to the list? */

            while (dtext != NULL &&
                   (newline = find_newline(dtext, len)) != NULL) {
                  /* Copy any text after the newline */

                  if (newline - dtext < len) {
                        nextlen = len - ((newline + 1) - dtext);
                        next = xmalloc(nextlen + 1);
                        (void) memcpy(next, newline + 1, nextlen);
                        *(next + nextlen) = '\0';
                  } else {
                        next = NULL;
                        nextlen = 0;
                  }

                  /* Cut the text out of the line */

                  *(newline + 1) = '\0';
                  len = (newline - dtext) + 1;
                  dtext = xrealloc(dtext, len + 1);
                  *(dtext + len) = '\0';

                  /* Add the line to the list */

                  decoded_text = add_bintext(decoded_text, dtext, len);
                  dtext = next;
                  len = nextlen;
            }

            /* Save any partial line in the buffer */

            buf = dtext;
            buflen = (buf != NULL) ? len : 0;

            /* And move on to the next line */

            line = line->next;
      }

      /* Write any final, incomplete decoded line */

      if (buf != NULL) {
            decoded_text = add_bintext(decoded_text, buf, buflen);
      }

      /* And return the decoded text */

      return(decoded_text);
}
/****************************************************************************/
static char *uue_decode_line(text, len)
char *text;
size_t *len;
{
      /* Decode and return a single line in UUENCODE encoding */

      char *dtext, *d, *p;
      char v[UUE_ELENGTH];
      int nchars, nvalues = 0;

      /* Extract the number of characters from the line */

      nchars = (*len > 0) ? (*text - UUE_BASE_CHAR) : 0;
      nchars = (nchars < 0 || nchars >= UUE_MAX_VALUE) ? 0 : nchars;

      /* Allocate the decoded text buffer */

      d = dtext = xmalloc(nchars + 1);

      /* Set up a pointer to the encoded text */

      p = text + 1;

      /* Loop over the text, decoding as required */

      while (nchars > 0 && p < text + *len) {
            /* Is this a valid uuencode character? */

            if (*p >= UUE_BASE_CHAR && *p <=
                UUE_BASE_CHAR + UUE_MAX_VALUE) {
                  /* Add this character to the current set */

                  v[nvalues++] = (*p - UUE_BASE_CHAR);
            }

            /* Do we have a full set of characters? */

            if (nvalues >= UUE_ELENGTH) {
                  /* Set the next three output characters */

                  *d++ = v[0] << 2 | (v[1] & 0x30) >> 4;
                  *d++ = (v[1] & 0x0F) << 4 | (v[2] & 0x3C) >> 2;
                  *d++ = (v[2] & 0x03) << 6 | v[3];

                  /* And update the value and char counts */

                  nvalues = 0;
                  nchars -= UUE_TLENGTH;
            }

            /* Move on to the next character */

            p++;
      }

      /* Set the base length of the decoded text */

      d += (nchars < 0) ? nchars : 0;
      *len = (d - dtext);

      /* Handle any final trailing characters */

      if (nchars > 0 && nvalues > 0) {
            /* Initialise the third value in the array */

            v[2] = (nvalues > 2) ? v[2] : 0;

            /* Now add any final characters to the text */

            *d++ = v[0] << 2 | (v[1] & 0x30) >> 4;
            *d++ = (v[1] & 0x0F) << 4 | (v[2] & 0x3C) >> 2;

            /* And update the length count */

            *len += (nvalues - 1);
      }
      *d = '\0';

      /* Reallocate and return the decoded text */

      dtext = xrealloc(dtext, *len + 1);
      return(dtext);
}
/****************************************************************************/
static char *find_newline(text, len)
char *text;
size_t len;
{
      /* Return the position of the first newline in the binary text */

      char *p;

      for (p = text; p < text + len; p++) {
            /* Is this the newline? */

            if (*p == '\n') {
                  return(p);
            }
      }

      /* No newline found */

      return(NULL);
}
/****************************************************************************/

Generated by  Doxygen 1.6.0   Back to index