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

encwords.c

/* Encwords.c - Handling of MIME encoded-words for af
   Copyright (C) 1996 - 2002 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 "af.h"
#include "atom.h"
#include "mime.h"
#include "enclist.h"
#include "keyseq.h"
#include "functions.h"
#include "variable.h"
#include STRING_HDR

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

#ifndef lint
static char *RcsId = "$Id: encwords.c,v 1.3 2002/08/21 23:54:48 malc Exp $";
#endif /* ! lint */

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

extern char *xmalloc(), *xrealloc(), *xstrdup();
extern char *vstrcat(), *get_vtext(), *atext();
extern char *avalue(), *fold_header();
extern char *qp_encode(), *qp_decode();
extern char *b64_encode(), *b64_decode();
extern int strcasecmp(), mklower(), is_header();
extern int viewable_charset();
extern size_t qp_encoded_len();
extern void free(), afree();
extern ATOM *aappend(), *ainsert(), *amerge();
extern ATOM *acut(), *adelete(), *adiscard();
extern ATOM *aprev(), *afind(), *asearch();

/* Local function declarations */

static char *recode_header(), *recode_header_line();
static char *recode_text(), *word_charset();
static char *charset_directive();
static char *encode_word(), *decode_word();
static int is_8bit_word(), is_fake_word();
static int contains_excluded_chars();
static ATOM *encode_atoms(), *decode_atoms();
static ATOM *encoded_atoms(), *decoded_atom();
static ATOM *insert_directive(), *delete_directive();
static ATOM *fix_directives(), *last_to_encode();
static ATOM *split_qp_word(), *split_b64_word();

/****************************************************************************/
char *encode_header(name, text, flags)
char *name, *text;
int flags;
{
      /* Encode any 8-bit words in a header line */

      static char *buf = NULL;

      /* Free any old return buffer */

      if (buf != NULL) {
            free(buf);
      }

      /* Encode the header and return it */

      buf = recode_header(name, text, FALSE, flags);
      return(buf);
}
/****************************************************************************/
char *encode_header_line(line, flags)
char *line;
int flags;
{
      /* Encode any 8-bit words in a header line */

      static char *buf = NULL;

      /* Free any old return buffer */

      if (buf != NULL) {
            free(buf);
      }

      /* Encode the header line and return it */

      buf = recode_header_line(line, FALSE, flags);
      return(buf);
}
/****************************************************************************/
char *encode_text(text, excludes)
char *text, *excludes;
{
      /* Encode any 8-bit words in text */

      static char *buf = NULL;

      /* Free any old return buffer */

      if (buf != NULL) {
            free(buf);
      }

      /* Encode the text and return it */

      buf = recode_text(text, FALSE, FALSE, excludes);
      return(buf);
}
/****************************************************************************/
char *decode_header(name, text, flags)
char *name, *text;
int flags;
{
      /* Decode any encoded-words in a header line */

      static char *buf = NULL;

      /* Free any old return buffer */

      if (buf != NULL) {
            free(buf);
      }

      /* Decode the header and return it */

      buf = recode_header(name, text, TRUE, flags);
      return(buf);
}
/****************************************************************************/
char *decode_header_line(line, flags)
char *line;
int flags;
{
      /* Decode any 8-bit words in a header line */

      static char *buf = NULL;

      /* Free any old return buffer */

      if (buf != NULL) {
            free(buf);
      }

      /* Decode the header line and return it */

      buf = recode_header_line(line, TRUE, flags);
      return(buf);
}
/****************************************************************************/
char *decode_text(text, show)
char *text;
int show;
{
      /* Decode any valid encoded-words in text */

      static char *buf = NULL;

      /* Free any old return buffer */

      if (buf != NULL) {
            free(buf);
      }

      /* Decode the text and return it */

      buf = recode_text(text, TRUE, show, NULL);
      return(buf);
}
/****************************************************************************/
static char *recode_header(name, text, decode, flags)
char *name, *text;
int decode, flags;
{
      /* Encode or decode any 8-bit words in a header line */

      char *otext, *rtext, *ftext;
      ATOM *(*tokeniser)() = wtokenise;
      ATOM *(*recoder)() = recode_all;
      ATOM *alist;
      ENCODING *enc;

      /* First check how we should recode the header */

      for (enc = header_encodings; enc->header != NULL; enc++) {
            /* Is this the header we're recoding? */

            if (!strcasecmp(name, enc->header)) {
                  tokeniser = enc->tokeniser;
                  recoder = enc->recoder;
                  break;
            }
      }

      /* Tokenise the text according to syntax */

      if ((alist = tokeniser(text)) == NULL) {
            /* Can't recode if it won't tokenise */

            return((text != NULL) ? xstrdup(text) : NULL);
      }

      /* Save the original text */

      otext = atext(NULL, alist, (flags & WR_UNFOLD)
                  ? AC_UNFOLD : AC_TRIM);

      /* Now recode the atom list */

      alist = recoder(alist, decode, flags & WR_SHOW);

      /* And generate the text of the recoded header */

      rtext = atext(NULL, alist, (flags & WR_UNFOLD)
                  ? AC_UNFOLD : AC_TRIM);

      /* Refold the header to fit in 72 columns */

      if ((flags & WR_FOLD) && strcmp(rtext, otext)) {
            /* Refold the header to fix recoded text layout */

            ftext = fold_header(name, rtext, TRUE, tokeniser == tokenise);
            free(rtext);
            rtext = ftext;
      }

      /* Clean up and return the recoded header */

      free(otext);
      afree(alist);
      return(rtext);
}
/****************************************************************************/
static char *recode_header_line(line, decode, flags)
char *line;
int decode, flags;
{
      /* Return the encoded or decoded header line */

      char *rtext, *name, *text, *htext;

      /* Find the header's text */

      if (!is_header(line) || (text = strchr(line, ':')) == NULL) {
            /* This isn't a header at all */

            return((line != NULL) ? xstrdup(line) : NULL);
      }
      text++;

      /* Extract the name from the line */

      name = xmalloc(text - line + 1);
      (void) strncpy(name, line, text - line);
      name[text - line] = '\0';

      /* Now recode the header as required */

      rtext = recode_header(name, text, decode, flags);

      /* And form the recoded header line */

      htext = vstrcat(name, " ", rtext, "\n", NULL);
      free(name);
      free(rtext);

      /* Now return the recoded header line */

      return(htext);
}
/****************************************************************************/
static char *recode_text(text, decode, show, excludes)
char *text, *excludes;
int decode, show;
{
      /* Encode or decode any 8-bit words in a header line */

      char *rtext;
      ATOM *alist;

      /* Split the text into an atom list */

      if ((alist = wtokenise(text)) == NULL) {
            /* No text to decode */

            return((text != NULL) ? xstrdup(text) : text);
      }

      /* Now recode the atoms in the list */

      alist = (decode) ? decode_atoms(alist, alist, NULL, show)
            : encode_atoms(alist, alist, NULL, excludes);

      /* And generate the recoded text */

      rtext = atext(NULL, alist, AC_NONE);

      /* Clean up and return the recoded text */

      afree(alist);
      return(rtext);
}
/****************************************************************************/
static ATOM *recode_all(alist, decode, show)
ATOM *alist;
int decode, show;
{
      /* Recode any 8-bit words in the atom list */

      alist = (decode) ? decode_atoms(alist, alist, NULL, show)
            : encode_atoms(alist, alist, NULL, EW_NOEWORD);

      /* Now recode any comments in the list */

      alist = recode_comments(alist, decode, show);

      /* And return the list */

      return(alist);
}
/****************************************************************************/
static ATOM *recode_addresses(alist, decode, show)
ATOM *alist;
int decode, show;
{
      /* Recode any 8-bit words in phrases in the list */

      ATOM *start = alist, *end;

      /* Find and recode each phrase in the list */

      while (start != NULL) {
            /* Find the next atom or quoted string in the list */

            while (start != NULL && start->type != AT_ATOM &&
                   (decode || start->type != AT_QSTRING)) {
                  /* Try the next atom in the list or skip <...> */

                  start = (start->type == AT_LANGLEB) ?
                        asearch(start, AT_RANGLEB) : start->next;
            }

            /* Default the end atom to the next in the list */

            end = (start != NULL) ? start->next : NULL;

            /* Find the end of the possible phrase */

            while (end != NULL &&
                   (end->type == AT_ATOM || end->type == AT_QSTRING
                  || end->type == AT_WS || end->type == AT_COMMENT)) {
                  /* Move end on to the next atom */

                  end = end->next;
            }

            /* Now recode the atoms if they make up a phrase */

            if (end != NULL &&
                (end->type == AT_LANGLEB || end->type == AT_COLON)) {
                  /* Recode the atoms as required */

                  alist = (decode) ?
                        decode_atoms(alist, start, end, show) :
                        encode_atoms(alist, start, end, EW_NOPHRASE);
            }

            /* Now update start as required */

            start = end;
      }

      /* Now recode all the comments in the list */

      alist = recode_comments(alist, decode, show);
      return(alist);
}
/****************************************************************************/
static ATOM *recode_phrases(alist, decode, show)
ATOM *alist;
int decode, show;
{
      /* Recode any 8-bit words in phrases in the list */

      ATOM *start = alist, *end;

      /* Find and recode each phrase in the list */

      while (start != NULL) {
            /* Find the next atom or quoted string in the list */

            while (start != NULL && start->type != AT_ATOM &&
                   (decode || start->type != AT_QSTRING)) {
                  /* Try the next atom in the list or skip <...> */

                  start = (start->type == AT_LANGLEB) ?
                        asearch(start, AT_RANGLEB) : start->next;
            }

            /* Default the end atom to the next in the list */

            end = (start != NULL) ? start->next : NULL;

            /* Find the end of the phrase */

            while (end != NULL &&
                   (end->type == AT_ATOM || end->type == AT_QSTRING
                  || end->type == AT_WS || end->type == AT_COMMENT)) {
                  /* Move end on to the next atom */

                  end = end->next;
            }

            /* Now recode the atoms in the phrase */

            alist = (decode) ? decode_atoms(alist, start, end, show)
                  : encode_atoms(alist, start, end, EW_NOPHRASE);

            /* And update start as required */

            start = end;
      }

      /* Now recode all the comments in the list */

      alist = recode_comments(alist, decode, show);
      return(alist);
}
/****************************************************************************/
static ATOM *recode_comments(alist, decode, show)
ATOM *alist;
int decode, show;
{
      /* Recode any 8-bit words in comments in the list */

      char *rtext;
      ATOM *atom, *clist;

      /* Loop over the atoms in the list looking for comments */

      for (atom = alist; atom != NULL; atom = atom->next) {
            /* Recode this atom if it's a valid comment */

            if (atom->type == AT_COMMENT &&
                (clist = cttokenise(avalue(atom))) != NULL) {
                  /* Recode the atoms of the comment */

                  clist = (decode) ?
                        decode_atoms(clist, clist, NULL, show) :
                        encode_atoms(clist, clist, NULL,
                                   EW_NOCOMMENT);

                  /* Recurse to decode any nested comments */

                  clist = recode_comments(clist, decode, show);

                  /* Generate the recoded text */

                  rtext = atext(NULL, clist, AC_NONE);
                  afree(clist);

                  /* And set the atom's recoded text */

                  free(atom->text);
                  atom->text = vstrcat("(", rtext, ")", NULL);
                  free(rtext);
            }
      }

      /* Now return the updated atom list */

      return(alist);
}
/****************************************************************************/
static ATOM *encode_atoms(alist, start, end, excludes)
ATOM *alist, *start, *end;
char *excludes;
{
      /* Encode any 8-bit words between start and end */

      static char *fallback = FALLBACK_CHARSET;
      char *cset, *dcset, *wcset, *enc;
      int nonascii, base64;
      ATOM *atom, *prev, *next;
      ATOM *last, *encoded;

      /* Default the character set and encoding for encoded-words */

      cset = xstrdup(get_vtext(V_CHARSET));
      enc = get_vtext(V_ENCODING);
      base64 = (!strcasecmp(enc, BASE64));

      /* Decode any encoded words and canonicalise charset directives */

      prev = aprev(alist, start);
      alist = decode_atoms(alist, start, end, FALSE);
      start = (prev != NULL) ? prev->next : alist;
      alist = fix_directives(alist, start, end, cset);
      start = (prev != NULL) ? prev->next : alist;

      /* Loop over all the atoms in the list */

      for (atom = start; atom != end; atom = atom->next) {
            /* Check for a character set directive */

            if ((dcset = charset_directive(atom)) != NULL) {
                  /* Update the current character set */

                  free(cset);
                  cset = xstrdup(dcset);

                  /* Delete the atom from the list */

                  next = afind(atom->next);
                  alist = delete_directive(alist, atom);
                  atom = next;
            }

            /* Does this atom need encoding? */

            if ((nonascii = is_8bit_word(atom)) || is_fake_word(atom)
                || contains_excluded_chars(atom, excludes)) {
                  /* Find the last atom to merge into one word */

                  last = last_to_encode(alist, atom, end, &nonascii);

                  /* Now extract the atoms from the list */

                  next = last->next;
                  alist = acut(alist, atom, next);

                  /* Determine the charset for this encoded-word */

                  wcset = (nonascii) ? (!strcasecmp(cset, US_ASCII))
                        ? fallback : cset : US_ASCII;

                  /* And replace them with the encoded atoms */

                  encoded = encoded_atoms(atom, wcset, base64,
                                    excludes);
                  alist = amerge(alist, next, encoded);
                  afree(atom);

                  /* Now update the current atom */

                  atom = aprev(alist, next);
            }
      }

      /* Free the charset and return the updated atom list */

      free(cset);
      return(alist);
}
/****************************************************************************/
static ATOM *decode_atoms(alist, start, end, show)
ATOM *alist, *start, *end;
int show;
{
      /* Decode any encoded-words between start and end */

      char *cset, *lcset;
      int direct = FALSE;
      ATOM *atom, *decoded;
      ATOM *last = NULL;

      /* Default the last character set */

      lcset = xstrdup(get_vtext(V_CHARSET));

      /* Loop over all the atoms in the list */

      for (atom = start; atom != end; atom = atom->next) {
            /* Is this atom an encoded-word? */

            if ((cset = word_charset(atom)) != NULL) {
                  /* Do we have consecutive encoded-words? */

                  if (last != NULL && afind(last->next) == atom) {
                        /* Strip white space between the words */

                        alist = adelete(alist, last->next, atom);
                  }

                  /* Check if the character sets match */

                  if (strcmp(cset, lcset) && strcmp(cset, US_ASCII) &&
                      (direct || !show || !viewable_charset(cset))) {
                        /* Insert a character-set directive */

                        alist = insert_directive(alist, atom, cset);
                        direct = TRUE;
                  }

                  /* Now decode the word and replace the atom */

                  decoded = decoded_atom(atom);
                  alist = amerge(alist, atom, decoded);
                  atom = aprev(alist, atom);
                  alist = adiscard(alist, atom->next);

                  /* Now update the last charset and encoded-word */

                  if (strcasecmp(cset, US_ASCII)) {
                        free(lcset);
                        lcset = xstrdup(cset);
                  }
                  last = atom;
            }
      }

      /* Free any last charset */

      if (lcset != NULL) {
            free(lcset);
      }

      /* And return the updated atom list */

      return(alist);
}
/****************************************************************************/
static int is_8bit_word(atom)
ATOM *atom;
{
      /* Return TRUE if word contains 8-bit characters */

      char *c;

      /* Now check for 8-bit characters in the word */

      for (c = atom->text; *c != '\0'; c++) {
            /* Is this an 8-bit character? */

            if (!isascii(*c)) {
                  return(TRUE);
            }
      }

      /* The word doesn't contain 8-bit characters */

      return(FALSE);
}
/****************************************************************************/
static int is_fake_word(atom)
ATOM *atom;
{
      /* Return TRUE if atom could be mistaken for an encoded-word */

      size_t len;

      /* Get the length of the atom */

      len = strlen(atom->text);

      /* And return whether it looks like an encoded-word */

      return(atom->type == AT_ATOM && len >= 4 &&
             *(atom->text) == EW_TERMINATOR &&
             *(atom->text + 1) == EW_DELIMITER &&
             *(atom->text + len - 2) == EW_DELIMITER &&
             *(atom->text + len - 1) == EW_TERMINATOR);
}
/****************************************************************************/
static int contains_excluded_chars(atom, excludes)
ATOM *atom;
char *excludes;
{
      /* Return TRUE if atom contains excluded characters */

      char *c;

      /* Look for an excluded character in the atom's text */

      for (c = atom->text; atom->type == AT_ATOM && *c != '\0'; c++) {
            /* Is this an excluded character? */

            if (strchr(EW_NOENCODE, *c) == NULL &&
                strchr(excludes, *c) != NULL) {
                  /* This is an excluded character */

                  return(TRUE);
            }
      }

      /* No excluded characters in the text */

      return(FALSE);
}
/****************************************************************************/
static char *charset_directive(atom)
ATOM *atom;
{
      /* Return the charset directed by atom, or NULL if none */

      static char *buf = NULL;
      size_t len;

      /* Check that the atom is a comment or atom */

      if (atom->type != AT_ATOM && atom->type != AT_COMMENT) {
            /* This atom can't be a charset directive */

            return(NULL);
      }

      /* Check if the atom might be a charset directive */

      if ((len = strlen(atom->text)) < CD_MIN_LEN ||
          strncmp(atom->text, CD_START, CD_TERM_LEN) ||
          strcmp(atom->text + len - CD_TERM_LEN, CD_END)) {
            /* This isn't a charset directive */

            return(NULL);
      }
          
      /* Free any old return buffer */

      if (buf != NULL) {
            free(buf);
      }

      /* Extract the charset from the directive */

      len -= (CD_TERM_LEN * 2);
      buf = xmalloc(len + 1);
      (void) strncpy(buf, atom->text + CD_TERM_LEN, len);
      buf[len] = '\0';

      /* Now return the charset */

      return(buf);
}
/****************************************************************************/
static ATOM *insert_directive(alist, where, cset)
ATOM *alist, *where;
char *cset;
{
      /* Insert a charset directive for cset before where */

      char *text;
      ATOM *prev;

      /* Check if the atom before where is white space */

      if ((prev = aprev(alist, where)) != NULL && prev->type != AT_WS) {
            /* Insert a space before the directive */

            alist = ainsert(alist, where, " ", AT_WS);
      }

      /* Now insert the charset directive as a comment */

      text = vstrcat("(**", cset, "**)", NULL);
      alist = ainsert(alist, where, text, AT_COMMENT);
      free(text);

      /* Insert a space after the directive */

      alist = ainsert(alist, where, " ", AT_WS);

      /* And return the updated atom list */

      return(alist);
}
/****************************************************************************/
static ATOM *delete_directive(alist, atom)
ATOM *alist, *atom;
{
      /* Delete the charset directive at atom from alist */

      ATOM *prev;

      /* Check if the atom before the directive is white space */

      if ((prev = aprev(alist, atom)) != NULL && prev->type != AT_WS) {
            /* Insert a space in place of the directive */

            alist = ainsert(alist, atom, " ", AT_WS);
      }

      /* Now delete the directive and any trailing space */

      alist = adelete(alist, atom, afind(atom->next));

      /* And return the updated atom list */

      return(alist);
}
/****************************************************************************/
static ATOM *fix_directives(alist, start, end, cset)
ATOM *alist, *start, *end;
char *cset;
{
      /* Find the last consecutive word to encode after atom */

      char *dcset, *acset, *lcset;
      ATOM *atom, *next;

      /* Initially the default charset is active */

      acset = xstrdup(cset);
      lcset = xstrdup(cset);

      /* Loop over atoms from start up to end */

      for (atom = start; atom != end; atom = atom->next) {
            /* Is this atom a charset directive? */

            while ((dcset = charset_directive(atom)) != NULL) {
                  /* Update the active character set */

                  free(acset);
                  acset = xstrdup(dcset);

                  /* Delete the directive from the list */

                  next = afind(atom->next);
                  alist = delete_directive(alist, atom);

                  /* And update the current atom */

                  atom = next;
            }

            /* Is this atom an 8bit word to be encoded? */

            if (atom != NULL && is_8bit_word(atom) &&
                strcmp(acset, lcset) && strcmp(acset, US_ASCII)) {
                  /* Insert a charset directive before the atom */

                  alist = insert_directive(alist, atom, acset);

                  /* And save the last atom's charset */

                  free(lcset);
                  lcset = xstrdup(acset);
            }
      }

      /* Free the charsets */

      free(acset);
      free(lcset);

      /* Return the updated atom list */

      return(alist);
}
/****************************************************************************/
static ATOM *last_to_encode(alist, first, end, nonascii)
ATOM *alist, *first, *end;
int *nonascii;
{
      /* Find the last consecutive word to encode after atom */

      int seen_8bit = FALSE;
      int atom_8bit, encode;
      size_t encoded_len;
      size_t unencoded_len = 0;
      ATOM *atom, *last = first;

      /* Initialise the last atom and nonascii flag */

      last = first;
      *nonascii = is_8bit_word(last);

      /* Initialise the encoded text length */

      encoded_len = strlen(first->text);

      /* Loop over atoms after first up to end */

      for (atom = first->next; atom != end && 
           (atom->type == AT_ATOM && charset_directive(atom) == NULL
            || atom->type == AT_QSTRING || atom->type == AT_WS);
           atom = atom->next) {
            /* Is this another encoded-word */

            encode = ((atom_8bit = is_8bit_word(atom))
                    || is_fake_word(atom));

            /* Update the encoded and unencoded length of the word */

            encoded_len += (encode) ? strlen(atom->text) : 0;
            unencoded_len += (!encode) ? strlen(atom->text) : 0;

            /* And update last and the assorted status flags */

            last = (encode && encoded_len >= unencoded_len ||
                  IS_PHRASE(atom) && last->next == atom) ? atom : last;
            seen_8bit = (seen_8bit || atom_8bit);
            *nonascii = (*nonascii || seen_8bit &&
                       encoded_len >= unencoded_len);
      }

      /* Check if we have consecutive encoded-words to deal with */

      if (atom != end && charset_directive(atom) != NULL
          && afind(last->next) == atom) {
            /* Insert a space and add it to the word */

            alist = ainsert(alist, last->next, " ", AT_WS);
            last = last->next;
      }

      /* Return the last encoded-word in the list */

      return(last);
}
/****************************************************************************/
static ATOM *encoded_atoms(alist, cset, base64, excludes)
ATOM *alist;
char *cset, *excludes;
int base64;
{
      /*
       * Form one or more encoded-words by merging the text of
       * the atoms in alist into as few encoded-words as the
       * length limit will allow.  Returns an atom list containing
       * the encoded-words.
       */

      char *text;
      ATOM *encoded = NULL;

      /* Concatenate the text of the atoms */

      text = atext(NULL, alist, AC_NONE);

      /* Now split the word down into atoms */

      encoded = (base64) ? split_b64_word(text, cset)
            : split_qp_word(text, cset, excludes);
         
      /* Clean up and return the encoded-word */

      free(text);
      return(encoded);
}
/****************************************************************************/
static ATOM *decoded_atom(atom)
ATOM *atom;
{
      /*
       * Form one or more atoms by decoding atom, and splitting the
       * resulting text into word-based tokens.  Returns an atom list
       * containing the decoded text.
       */

      char *dtext;
      ATOM *decoded = NULL;

      /* First decode the text of the atom */

      dtext = decode_word(atom->text);

      /* And form a new list from the decoded text */

      decoded = wtokenise(dtext);
      free(dtext);

      /* Now return the decoded list */

      return(decoded);
}
/****************************************************************************/
static ATOM *split_qp_word(text, cset, excludes)
char *text, *cset, *excludes;
{
      /* Actually encode text into one or more encoded-words */

      char *word, *eword, *c;
      size_t format_len, len;
      size_t encoded_len;
      ATOM *encoded = NULL;

      /* Get the length of an encoded-word's format */

      encoded_len = format_len = strlen(cset) + EW_FORMLEN;

      /* Now split a quoted-printable word if it's too long */

      for (c = text; *c != '\0' && format_len < EW_MAX_LEN; c++) {
            /* How long is this character when encoded? */

            len = qp_encoded_len(*c, excludes, FALSE);

            /* Do we need to create another encoded-word? */

            if (encoded_len + len > EW_MAX_LEN) {
                  /* Split the part to encode out of the string */

                  word = xstrdup(text);
                  word[c - text] = '\0';
                  text = c;

                  /* Now form an encoded-word from the text */

                  eword = encode_word(word, cset, FALSE, excludes);
                  encoded = aappend(encoded, eword, AT_ATOM);
                  encoded = aappend(encoded, " ", AT_WS);

                  /* Reset the length of the encoded word */

                  encoded_len = format_len;

                  /* And clean up */

                  free(eword);
            }

            /* Now update the length of the encoded-word */

            encoded_len += len;
      }

      /* Now add an encoded-word from the remaining text */

      eword = encode_word(text, cset, FALSE, excludes);
      encoded = aappend(encoded, eword, AT_ATOM);

      /* Clean up and return the encoded text */

      free(eword);
      return(encoded);
}
/****************************************************************************/
static ATOM *split_b64_word(text, cset)
char *text, *cset;
{
      /* Actually encode text into one or more base64 encoded-words */

      char *word, *eword;
      size_t format_len;
      size_t len_to_encode;
      ATOM *encoded = NULL;

      /* Get the length of an encoded-word's format */

      format_len = strlen(cset) + EW_FORMLEN;

      /* Now split the word if it's too long */

      while (format_len + B64_ELENGTH <= EW_MAX_LEN
             && B64_ENCODED_LEN(strlen(text)) > EW_MAX_LEN) {
            /* How much of the text can we encode this time? */

            len_to_encode = B64_DECODED_LEN(EW_MAX_LEN - format_len);

            /* Split the part to encode out of the string */

            word = xstrdup(text);
            word[len_to_encode] = '\0';
            text += len_to_encode;

            /* Now add an encoded-word from the text */

            eword = encode_word(word, cset, TRUE, NULL);
            encoded = aappend(encoded, eword, AT_ATOM);
            encoded = aappend(encoded, " ", AT_WS);

            /* And clean up */

            free(word);
            free(eword);
      }

      /* Now add an encoded-word from the remaining text */

      eword = encode_word(text, cset, TRUE, NULL);
      encoded = aappend(encoded, eword, AT_ATOM);

      /* Clean up and return the encoded text */

      free(eword);
      return(encoded);
}
/****************************************************************************/
static char *word_charset(atom)
ATOM *atom;
{
      /*
       * Return the charset of text in a static buffer, or NULL if
       * text isn't a valid encoded-word.
       */

      static char *buf = NULL;
      char *query1, *query2, *query3;
      char *text, *enc, *c;

      /* Extract the text of the atom */

      text = atom->text;

      /* Find the question marks in the encoded-word */

      if (*text != EW_TERMINATOR || *(text + 1) != EW_DELIMITER ||
          (query1 = strchr(text + 2, EW_DELIMITER)) == NULL ||
          (query2 = strchr(query1 + 1, EW_DELIMITER)) == NULL ||
          (query3 = strchr(query2 + 1, EW_DELIMITER)) == NULL ||
          *(query3 + 1) != EW_TERMINATOR || *(query3 + 2) != '\0') {
            /* Not a valid encoded-word */

            return(NULL);
      }

      /* Free any old return buffer */

      if (buf != NULL) {
            free(buf);
      }

      /* Extract the charset from the encoded-word */

      buf = xmalloc(query1 - text - 1);
      (void) strncpy(buf, text + 2, query1 - text - 2);
      buf[query1 - text - 2] = '\0';

      /* Extract the encoding from the encoded-word */

      enc = xmalloc(query2 - query1);
      (void) strncpy(enc, query1 + 1, query2 - query1 - 1);
      enc[query2 - query1 - 1] = '\0';

      /* And check the encoding is valid */

      if (strcasecmp(enc, EW_QP) && strcasecmp(enc, EW_B64)) {
            /* Not a valid encoding for the word */

            free(enc);
            return(NULL);
      }

      /* And ensure the charset is in lower case */

      for (c = buf; *c != '\0'; c++) {
            *c = mklower(*c);
      }

      /* Now return the charset */

      return(buf);
}
/****************************************************************************/
static char *encode_word(word, cset, base64, excludes)
char *word, *cset, *excludes;
int base64;
{
      /* Encode a word from a mail header in rfc1522 form */

      char *etext, *space, *eword;

      /* Generate the encoded version of the text */

      etext = (base64) ? b64_encode(word, strlen(word), TRUE)
            : qp_encode(word, excludes);

      /* Convert spaces to _ in the encoded text */

      for (space = strchr(etext, EW_SPACE); space != NULL;
           space = strchr(space + 1, EW_SPACE)) {
            /* Convert this space to an underscore */

            *space = EW_USCORE;
      }

      /* Now allocate and fill the encoded word */

      eword = xmalloc(strlen(cset) + strlen(etext) + EW_FORMLEN + 1);
      (void) sprintf(eword, "=?%s?%s?%s?=", cset,
                   (base64) ? EW_B64 : EW_QP, etext);

      /* Clean up and return the encoded-word */

      free(etext);
      return(eword);
}
/****************************************************************************/
static char *decode_word(word)
char *word;
{
      /* Decode an rfc1522 encoded word from a mail header */

      char *query1, *query2, *query3;
      char *enc, *text, *uscore;
      char *dword, *d;
      size_t len = 0;

      /* Find the question marks in the encoded-word */

      query1 = strchr(word + 2, EW_DELIMITER);
      query2 = strchr(query1 + 1, EW_DELIMITER);
      query3 = strchr(query2 + 1, EW_DELIMITER);

      /* Extract the encoding from the encoded-word */

      enc = xmalloc(query2 - query1);
      (void) strncpy(enc, query1 + 1, query2 - query1 - 1);
      enc[query2 - query1 - 1] = '\0';

      /* Extract the encoded text from the encoded-word */

      text = xmalloc(query3 - query2);
      (void) strncpy(text, query2 + 1, query3 - query2 - 1);
      text[query3 - query2 - 1] = '\0';

      /* And store the length of the text */

      len = query3 - query2 - 1;

      /* Convert _ to space in the encoded text */

      for (uscore = strchr(text, EW_USCORE); uscore != NULL;
           uscore = strchr(uscore + 1, EW_USCORE)) {
            /* Convert this underscore to a space */

            *uscore = EW_SPACE;
      }

      /* Generate the decoded version of the text */

      dword = (!strcasecmp(enc, EW_QP)) ? qp_decode(text, &len)
            : b64_decode(text, &len, TRUE);

      /* Convert null characters to '?' in the word */

      for (d = dword; d < dword - len; d++) {
            /* Convert a null to a ? */

            *d = (*d) ? *d : '?';
      }

      /* Clean up and return the decoded word */

      free(enc);
      free(text);
      return(dword);
}
/****************************************************************************/

Generated by  Doxygen 1.6.0   Back to index