/*****************************************************************************/
#ifdef COMMENTS_WITH_COMMENTS
/*
                                  compose.c

All things involved in the composition and sending of a mail message.

When sending a message, if there are soyMAIL attachment files that have been
checked for inclusion a "multipart/mixed" MIME message is generated.  Each
attachment with a "text/.." content-type is quoted-printable encoded in a
separate part.  Attachments with non-text content are provided as base-64
encoded parts.  If there is a message body this is appropriately encoded and
supplied as the first part with a content-type of "text/plain".

If there are no attachments files present or checked the message body is
appropriately encoded and made into an in-line MIME message with a plain-text
content-type.

If any one of the address fields (To, CC or BCC) contain a non-VMS address the
SMTP Message Transport Agent is used to send the message.  If the address
fields only contain what looks like VMS Mail addresses the message is sent via
VMS callable Mail.


HTML EDITING
------------
This rather neat capability is provided using stand-alone, JavaScript-driven,
HTML text editors that can be attached to the composition text area.  soyMAIL
has some hooks that recognise and support such editors, and when used to
compose a message the content is made into quoted-printable encoded HTML for
transmission.

In soyMAIL versions earlier than v1.8.0 the HTML editor was an independent
element, installed and configured by the site.  With v1.8.0 and later a version
of TinyMCE is provided within the soyMAIL archive and supported out-of-the-box. 
HTML editing is unavailable by default but can simply enabled with

  [html-editor-load]  *

TinyMCE Version 4.0.8 has been tested as working in this environment.  Version
3.n is no supported (though may continue to work with some site tweaking). 
Another version and/or TinyMCE installation is also possible by placing
JavaScript in the [html-editor-load] configuration directive.  YMMV.


EXAMPLE FOR TINYMCE
-------------------
A functional configuration for TinyMCE 4.n.

  [html-editor-load]
  <script type="text/javascript"
          src="/tinymce/js/tinymce/tinymce.min.js"></script>
  <script type="text/javascript">

  tinyMCE.init({
     mode : "exact",
     elements : "textarea#compose_text",
     theme : "modern"
  });

  function htmlEditorContent() {
   if (typeof tinyMCE == 'undefined') return '';
   return tinyMCE.get('compose_text').getContent();
  }

  function htmlEditorLoad() { }
  </script>

This of course requires a mapping rule to provide access to the TinyMCE
installation.

  pass /tinymce/* /$1$disk/tinymce/*


TINYMCE AND ODS-2
-----------------
TinyMCE v4.n really needs EFS and an ODS-5 volume for installation (file names
with multiple periods, etc.)  It CAN BE MADE TO WORK with non-EFS using some
mapping rules to cause soyMAIL to munge the URIs to be non-EFS (and non-EFS
UNZIP) compliant.  Basically forbidden periods are transmogrified into
underscores, so that a file such as "tinymce.min.js" is requested from the
server as "tinymce_min.js".  It adds considerable overhead on an already
performance-challenged platform.  YMMV (again).

The WASD mapping rules would end up something like:

  redirect  /cgi-bin/soymail/* /cgiplus-bin/soymail/*?
  redirect  /soymail/-/*  /cgiplus-bin/soymail/soymail/_/*
  pass      /soymail/_/*  /wasd_root/src/soymail/*
  set /cgi*/soymail* map=once

Converts directory name periods to underscores, and file names with multiple
periods to file names with trailing single periods and replaced underscores. 


CHARACTER SETS
--------------
Composition is normally using the language file character set ([lang_charset]). 
When supported by browser and platform it is possible to compose messages using
other character sets.  This is (manually) configured in user options or
globally configured by the soyMAIL administrator.  The user option and soyMAIL
configuration directive (same for both) [compose-charsets] takes a string
containing comma and/or white-space separated ISO language names.

The following example shows configuration for Arabic, Cyrillic, Greek and
Hebrew.  The language file character set is always displayed as a first item in
the selector list, after which are the explicitly configured items.

  [compose-charsets]  Arabic=<ISO-8859-6,Cyrillic=ISO-8859-5,
  Greek=ISO-8859-7,Hebrew=<ISO-8859-8

The directionality flag of this configuration will also be used with message
display to correctly set display direction (right-to-left as required).

As the use of the ISO names is somewhat deprecated a more realistic
configuration might be:

  [compose-charsets]  Arabic=<UTF-8,Hebrew=<UTF-8

Which really just sets the directionality right-to-left for the modern
character set encoding UTF-8.

When composing a new message the character set MUST be selected BEFORE entering
any message text.  Once text is present character set selection is disabled. 
This appears to be necessary due to browser handling of requests POSTed using
one character set and processed using another.  Replied-to messages inherit the
character set of the original.  Directionality will be correctly established if
that matches one of the [charsets-rtl] configured character sets (see
MESSAGE.C).

For TinyMCE HTML editor directionality include in the initialisation:

     plugins: "directionality",
     theme_advanced_buttons3_add: "ltr,rtl",

For FCKeditor directionality - you're on your own!


COPYRIGHT
---------
Copyright (C) 2005-2025 Mark G.Daniel
This program, comes with ABSOLUTELY NO WARRANTY.
This is free software, and you are welcome to redistribute it under the
conditions of the GNU GENERAL PUBLIC LICENSE, version 3, or any later version.


VERSION HISTORY
---------------
20-JUL-2025  MGD  better unintended window closure control using
                    window.add/removeEventListener('beforeunload',...);
04-OCT-2023  MGD  ComposePageRequest() bugfix; charset setting
18-AUG-2023  MGD  ComposePreviewMessage() add font-family, font-size
11-JUL-2022  MGD  ComposeCharsetAlert()
12-MAY-2019  MGD  ComposePreviewMessage()
25-SEP-2015  MGD  Confirm[x] enabling confirm-before-send
13-OCT-2013  MGD  HTML editor now TinyMCE 4.n (FCKeditor deprecated)
                  bugfix; ComposeEditCols() ComposeEditRows() -1
25-FEB-2010  MGD  ComposePage() UTF-8 encode signature when required
10-DEC-2007  MGD  ComposePageRequest() improve generation of references header
01-NOV-2008  MGD  support for RFC "In-Reply-To:" and "References:"
11-AUG-2008  MGD  ComposeSelfAddress() use login alias if configured
25-JUL-2008  MGD  ComposeSigFileLoad() allows [SMTP-from] and [SMTP-reply-to]
                    directives to specify the "From:" and  "Reply-to:" message
                    header fields (as well as adding any signature of course)
20-JUL-2008  MGD  composition character set and text directionality
15-JUL-2008  MGD  ComposePage() JavaScript support for searching contacts list
                    tested against Firefox 2 & 3, MSIE 6 & 7, Opera 9.5
09-JUL-2008  MGD  [compose][^] (new window) from compose page always results
                    in a fresh compose page ([compose] 'regenerates' the page)
05-JUL-2008  MGD  ComposePageRequest() ?mailto: query string
02-JUL-2008  MGD  ComposeSelfAddress() allow for empty [smtp-from]
27-JUN-2008  MGD  bugfix; JavaScript confirmSend() use htmlEditorContent()
                    to determine whether an HTML editor has content or not
                    (NOTE that the initialisation JavaScript has changed!)
14-JUN-2008  MGD  "From:" composition field (enabled by [compose-user-from])
                  increase the column granularity of the compose text area
07-NOV-2007  MGD  bugfix; ComposeMime() obtain and propagate request ID and
                    hash from state data if unavailable from request data
03-NOV-2007  MGD  support multiple signature files
30-SEP-2007  MGD  ComposeSendClose() returns to the message listing when
                    it's not a child window ([send-close] is always active)
04-AUG-2007  MGD  bugfix; ComposePage() charset
13-APR-2007  MGD  ComposeMime() refine (or at least fiddle with) the
                    originating "Received:" header format
22-MAR-2007  MGD  ComposeBodyTextMassage() refine eol space handling
12-FEB-2007  MGD  draft maintenance now performed by DRAFT.C module
21-OCT-2006  MGD  ComposeSendMessage() if 'delete all attachments' checkbox 
                    delete all attachments after successful send
20-JUL-2006  MGD  rework reply-all address compilation
                  try to make font width allowances for monospace
19-JUL-2006  MGD  bugfix; ComposeSendMessage() check reply-to for VMS address
                  bugfix; ComposePage() HTML-escape the hidden signature
                  bugfix; ComposeTextMassage() retain quote-char as a string
06-JUL-2006  MGD  ComposeCopySelf() to refine interactions between VMS mail
                    addresses, [smtp-default-host], and copy-self, etc.
01-JUL-2006  MGD  add [forward]ing (from message read page) of attachments
                    with ComposeReplyAttachList(), ComposeForwardAttachList()
                    and changes to ComposeMime() to add the forwarded parts
24-JUN-2006  MGD  significant enhancements to JavaScript functionality
15-JUN-2006  MGD  ComposePage() improve layout and resizing of addressing
12-JUN-2006  MGD  ComposeRfcDate() to derive RFC822 "Date:" from client
                    to better represent message origination date/time
25-APR-2006  MGD  reply-to/all to reply-to: if available otherwise from:
                  ComposePage() do not infinitely increase the number of
                    'RE:' or 'FW:' in subject lines
14-APR-2006  MGD  bugfix; ComposeSelfAddress() size for calloc()
04-APR-2006  MGD  add [send-close] and ComposeSendClose()
15-MAR-2006  MGD  use form field TABINDEX attribute to control focus
13-MAR-2006  MGD  bugfix; ComposeSelfAddress() 'len' initialization
11-MAR-2006  MGD  move input focus to the field most appropriate to the stage
                    of message composition, defaulting to the message text
                  bugfix; ComposeSelfAddress() with rationalization of 'self' 
                    address generation and using this for the "From:" header
25-FEB-2006  MGD  ComposePageRequest() support VMS copy-send/forward/reply
                  ComposeSendMessage() expand MX_.. logical names that
                    can be used to generate the "From:" address
                  ComposePage() [signature] button only if the user has a
                    signature file specified
14-FEB-2006  MGD  when used from the message page the main menu [compose]
                    button should provide a blank message, when used from the
                    compose page it should propagate any in-composition message
09-FEB-2006  MGD  bugfix; default charset should be ISO-8859-1 not IS0-8859-1
                    (actually went to using the macro SOY_DEFAULT_CHARSET)
06-FEB-2006  MGD  modified [reply-to] option to [smtp-from] and in conjunction
                    with [personal-name] have ComposePageRequest() generate a
                    "From:" header more in line with TCPIP$SMTP_FROM behaviour
                  add explicit [reply-to] button and field so that messages
                    can individually have a reply-to set against them
                  ComposeMime() add client traceability information to
                    "Received:" header field
01-FEB-2005  MGD  initial
*/
#endif /* COMMENTS_WITH_COMMENTS */
/*****************************************************************************/

#ifdef SOYMAIL_VMS_V7
#undef _VMS_V6_SOURCE
#define _VMS_V6_SOURCE
#undef __VMS_VER
#define __VMS_VER 70000000
#undef __CRTL_VER
#define __CRTL_VER 70000000
#endif

/* standard C header files */
#include <ctype.h>
#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <types.h>
#include <unixio.h>

/* VMS related header files */
#include <descrip.h>
#include <mailmsgdef.h>
#include <rmsdef.h>
#include <ssdef.h>
#include <stsdef.h>

/* application header file */
#include "soymail.h"
#include "address.h"
#include "attach.h"
#include "callmail.h"
#include "cgilib.h"
#include "compose.h"
#include "config.h"
#include "draft.h"
#include "html.h"
#include "mainmenu.h"
#include "mta.h"
#include "other.h"
#include "options.h"
#include "request.h"
#include "message.h"
#include "sendmail.h"

#define FI_LI "COMPOSE", __LINE__

/* global storage */

/* prototypes */

int sys$gettim (__unknown_params);
int lib$cvt_vectim(__unknown_params);

/* can't get the include dependencies settled (this'll do in the interim) */
int ComposeWrapAt (int, USER_OPTIONS*);
int ComposeEditCols (int, USER_OPTIONS*);
int ComposeEditRows (int, USER_OPTIONS*);

/* external storage */

extern BOOL  Debug,
             WatchEnabled;

extern int  CgiPlusUsageCount,
            VmsVersion;

extern char  *CgiEnvironmentPtr,
             *CgiLib__soyMAILhack;

extern char  CurrentVmsTimeString[],
             DocType[],
             LocalHostName[],
             SoftwareCopy[],
             SoftwareEnv[],
             SoftwareId[],
             SoftwareVn[],
             SoyMailQueryVersion[];

extern  CONFIG_DATA SoyMailConfig;
extern VMS_MAIL_USER VmsMailUser;

/*****************************************************************************/
/*
Assemble the request data related to this message composition.
*/

BOOL ComposePageRequest (REQUEST_DATA *rdptr)

{
#define MAIL$M_REPLIED 0x02

   BOOL  FreshComposePage,
         MsgReply, MsgReplyAll,
         MsgForward, MsgEditAsNew;
   int  len;
   char  *cptr, *sptr, *zptr,
         *CcPtr, *BccPtr, *ReplyToPtr;
   COMPOSE_DATA  *cdptr;
   MIME_DATA  *mdptr;
   USER_OPTIONS  *uoptr;
   REQUEST_DATA  *sdptr;
   VMS_MAIL_MSG  *vmptr;
   VMS_MAIL_USER  *muptr;

   /*********/
   /* begin */
   /*********/

   if (WatchEnabled) WatchThis (FI_LI, "ComposePageRequest()");

   /* a bit of sanity checking */
   if (!rdptr->PrivateAccess) ErrorExit (SS$_BUGCHECK, FI_LI);

   muptr = &VmsMailUser;
   vmptr = &muptr->VmsMailMsg;
   sdptr = (REQUEST_DATA*)rdptr->StateDataPtr;
   uoptr = &rdptr->UserOptions;
   cdptr = &rdptr->ComposeData;

   if (SoyMailConfig.VmsOccluded)
   {
      /* no relevant VMS options to be used in message composition */
      muptr->VmsMailCopyForward =
         muptr->VmsMailCopyReply =  
         muptr->VmsMailCopySend = 0;
      muptr->VmsMailPersonalNameLength = 0;
      muptr->VmsMailPersonalName[0] = '\0';
      muptr->VmsMailSigFile[0] = '\0';
      muptr->VmsMailSigFileLength = 0;
   }

   /* this also is used as the "from:" address */
   ComposeSelfAddress (rdptr);

   CGIVARNULL (cptr, "FORM_MAIN_BTN");
   if (cptr && *cptr == '^')
      FreshComposePage = TRUE;
   else
      FreshComposePage = FALSE;

   if (!strncmp (rdptr->CgiQueryStringPtr, "mailto:", 7))
   {
      /***********/
      /* mailto: */
      /***********/

      if (muptr->VmsMailCopySend) ComposeCopySelf (rdptr);

      cdptr->ToPtr = cptr =  CgiLibVeeMemCalloc (strlen(rdptr->CgiQueryStringPtr));
      if (!cptr) ErrorExit (vaxc$errno, FI_LI);
      strcpy (cptr, rdptr->CgiQueryStringPtr+7);
      CgiLibUrlDecode (cptr);
      while (*cptr && *cptr != '?' && *cptr != '&') cptr++;
      *cptr = '\0';

      cdptr->EditRows = ComposeEditRows (-1, uoptr);
      cdptr->EditCols = ComposeEditCols (-1, uoptr);
      cdptr->WrapAt = ComposeWrapAt (-1, uoptr);

      if (muptr->VmsMailCopySend) ComposeCopySelf (rdptr);

      rdptr->FieldFocusPtr =
         "document.getElementById(\'compose_subj\').focus();";

      StatusMessage (FI_LI, 0, "%s %s",
                     LangFor("compose"), LangFor("page_opened"));
   }
   else
   if (rdptr->PrevPageIdent == PAGE_COMPOSE && !FreshComposePage)
   {
      /*************************/
      /* from the compose page */
      /*************************/

      /* propagate selected items from (any) previous request */
      cdptr->AlreadySent = sdptr->ComposeData.AlreadySent;
      cdptr->ContentTypeIs = sdptr->ComposeData.ContentTypeIs;
      cdptr->CopiedSelf = sdptr->ComposeData.CopiedSelf;
      cdptr->EditorType = sdptr->ComposeData.EditorType;
      cdptr->MsgForward = sdptr->ComposeData.MsgForward;
      cdptr->MsgReply = sdptr->ComposeData.MsgReply;

      if (SoyMailConfig.ComposeUserFrom > 0)
      {
         /* only when enabled (1) not when display (-1) */
         CGIVARNULL (cdptr->FromPtr, "FORM_COMPOSE_FROM");
         if (!cdptr->FromPtr) cdptr->FromPtr = cdptr->SelfPtr;
         /* ensure it's not empty (or effectively empty) */
         for (cptr = cdptr->FromPtr; *cptr && isspace(*cptr); cptr++);
         if (!*cptr) cdptr->FromPtr = cdptr->SelfPtr;
      }

      CGIVAR (cdptr->ToPtr, "FORM_COMPOSE_TO");
      CGIVARNULL (CcPtr, "FORM_COMPOSE_CC");
      if (!(cdptr->CcPtr = CcPtr)) cdptr->CcPtr = "";
      CGIVARNULL (BccPtr, "FORM_COMPOSE_BCC");
      if (!(cdptr->BccPtr = BccPtr)) cdptr->BccPtr = "";
      CGIVARNULL (ReplyToPtr, "FORM_COMPOSE_REPLY_TO");
      if (!(cdptr->ReplyToPtr = ReplyToPtr)) cdptr->ReplyToPtr = "";
      CGIVARNULL (cdptr->InReplyToPtr, "FORM_COMPOSE_IN_REPLY_TO");
      CGIVARNULL (cdptr->ReferencesPtr, "FORM_COMPOSE_REFERENCES");
      CGIVAR (cdptr->SubjPtr, "FORM_COMPOSE_SUBJ");
      CGIVAR (cdptr->TextPtr, "FORM_COMPOSE_TEXT");

      CGIVARNULL (cptr, "FORM_COMPOSE_SEND_CONFIRM");
      if (cptr) cdptr->ConfirmBeforeSend = (*cptr == '1'); 

      strcpy (cdptr->DraftCreated, sdptr->ComposeData.DraftCreated);
      cdptr->PrevDraftHash = sdptr->ComposeData.DraftHash;
      cdptr->DraftHash = DraftHash (cdptr);

      CGIVARNULL (cptr, "FORM_COMPOSE_EDIT_ROWS");
      if (cptr) cdptr->EditRows = atoi(cptr);
      CGIVARNULL (cptr, "FORM_COMPOSE_BTN_ROWS");
      if (cptr)
      {
         while (*cptr && isspace(*cptr)) cptr++;
         if ((cdptr->EditRows = ComposeEditRows (atoi(cptr), uoptr)) < 0)
            cdptr->EditRows = ComposeEditRows (-1, uoptr);
      }

      CGIVARNULL (cptr, "FORM_COMPOSE_EDIT_COLS");
      if (cptr) cdptr->EditCols = atoi(cptr); 
      CGIVARNULL (cptr, "FORM_COMPOSE_BTN_COLS");
      if (cptr)
      {
         while (*cptr && isspace(*cptr)) cptr++;
         if ((cdptr->EditCols = ComposeEditCols (atoi(cptr), uoptr)) < 0)
            cdptr->EditCols = ComposeEditCols (-1, uoptr);
      }

      CGIVARNULL (cptr, "FORM_COMPOSE_WRAP_AT");
      if (cptr) cdptr->WrapAt = atoi(cptr);
      CGIVARNULL (cptr, "FORM_COMPOSE_BTN_WRAP");
      if (cptr)
      {
         while (*cptr && !isdigit(*cptr)) cptr++;
         cdptr->WrapAt = ComposeWrapAt (atoi(cptr), uoptr);
      }

      CGIVARNULL (cptr, "FORM_CONTACT_ADDR");
      if (cptr) while (*cptr && isspace(*cptr)) cptr++;
      if (cptr && *cptr) cdptr->ContactListPtr = cptr;

      CGIVARNULL (cptr, "FORM_COMPOSE_BTN");
      if (cptr)
      {
         if (LangSame ("compose_to", cptr))
         {
            if (cptr = cdptr->ContactListPtr)
            {
               cptr = ComposeCheckForSelf (rdptr, cptr);
               cdptr->ToPtr = AddressListAppend (cdptr->ToPtr, cptr);
               cptr = AddressListMassage (cptr, TRUE);
               StatusMessage (FI_LI, 0, "%s %s: %s",
                              LangFor("to"), LangFor("compose_add"),
                              HTML_ESCAPE(cptr));
            }
            else
               StatusMessage (FI_LI, 1, LangFor("compose_add_none"));
         }
         else
         if (LangSame ("compose_cc", cptr))
         {
            if (cptr = cdptr->ContactListPtr)
            {
               cptr = ComposeCheckForSelf (rdptr, cptr);
               cdptr->CcPtr = AddressListAppend (cdptr->CcPtr, cptr);
               cptr = AddressListMassage (cptr, TRUE);
               StatusMessage (FI_LI, 0, "%s %s: %s",
                              LangFor("cc"), LangFor("compose_add"),
                              HTML_ESCAPE(cptr));
            }
            else
            if (CcPtr && !CcPtr[0])
            {
               cdptr->ShowCcAddr = FALSE;
               StatusMessage (FI_LI, 0, "%s %s%s",
                              LangFor("compose"), LangFor("page_opened"),
                              ComposeCharsetAlert(cdptr->CharSetPtr));
            }
            else
            if (!CcPtr)
            {
               cdptr->ShowCcAddr = TRUE;
               rdptr->FieldFocusPtr = "document.getElementById(\'compose_cc\').focus();";
               StatusMessage (FI_LI, 0, "%s %s%s",
                              LangFor("compose"), LangFor("page_opened"),
                              ComposeCharsetAlert(cdptr->CharSetPtr));
            }
            else
               StatusMessage (FI_LI, 1, LangFor("compose_add_none"));
         }
         else
         if (LangSame ("compose_bcc", cptr))
         {
            if (cptr = cdptr->ContactListPtr)
            {
               cptr = ComposeCheckForSelf (rdptr, cptr);
               if (AddressIsRfc (cptr))
               {
                  cdptr->BccPtr = AddressListAppend (cdptr->BccPtr, cptr);
                  cptr = AddressListMassage (cptr, TRUE);
                  StatusMessage (FI_LI, 0, "%s %s: %s",
                                 LangFor("bcc"), LangFor("compose_add"),
                                 HTML_ESCAPE(cptr));
               }
               else
                  StatusMessage (FI_LI, 1, "%s: %s",
                                 LangFor("compose_bcc_vms_address"),
                                 HTML_ESCAPE(cptr));
            }
            else
            if (BccPtr && !BccPtr[0])
            {
               cdptr->ShowBccAddr = FALSE;
               StatusMessage (FI_LI, 0, "%s %s%s",
                              LangFor("compose"), LangFor("page_opened"),
                              ComposeCharsetAlert(cdptr->CharSetPtr));
            }
            else
            if (!BccPtr)
            {
               cdptr->ShowBccAddr = TRUE;
               rdptr->FieldFocusPtr = "document.getElementById(\'compose_bcc\').focus();";
               StatusMessage (FI_LI, 0, "%s %s%s",
                              LangFor("compose"), LangFor("page_opened"),
                              ComposeCharsetAlert(cdptr->CharSetPtr));
            }
            else
               StatusMessage (FI_LI, 1, LangFor("compose_add_none"));
         }
         else
         if (LangSame ("compose_reply_to", cptr))
         {
            if (cptr = cdptr->ContactListPtr)
            {
               cptr = ComposeCheckForSelf (rdptr, cptr);
               if (AddressIsRfc (cptr))
               {
                  sptr =  CgiLibVeeMemCalloc (strlen(cptr)+1);
                  if (!sptr) ErrorExit (vaxc$errno, FI_LI);
                  strcpy (cdptr->ReplyToPtr = sptr, cptr);
                  StatusMessage (FI_LI, 0, "%s %s: %s",
                                 LangFor("compose_reply_to"),
                                 LangFor("compose_add"),
                                 HTML_ESCAPE(cptr));
               }
               else
                  StatusMessage (FI_LI, 1, "%s: %s",
                                 LangFor("compose_reply_to_vms_address"),
                                 HTML_ESCAPE(cptr));
            }
            else
            if (ReplyToPtr && !ReplyToPtr[0])
            {
               cdptr->ShowBccAddr = FALSE;
               StatusMessage (FI_LI, 0, "%s %s%s",
                              LangFor("compose"), LangFor("page_opened"),
                              ComposeCharsetAlert(cdptr->CharSetPtr));
            }
            else
            if (!ReplyToPtr)
            {
               cdptr->ShowReplyToAddr = TRUE;
               rdptr->FieldFocusPtr = "document.getElementById(\'compose_reply_to\').focus();";
               StatusMessage (FI_LI, 0, "%s %s%s",
                              LangFor("compose"), LangFor("page_opened"),
                              ComposeCharsetAlert(cdptr->CharSetPtr));
            }
            else
               StatusMessage (FI_LI, 1, LangFor("compose_add_none"));
         }
         else
         if (LangSame ("compose_send", cptr))
            ComposeSendMessage (rdptr);
         else
         if (LangSame ("compose_send_close", cptr))
         {
            ComposeSendMessage (rdptr);
            if (cdptr->AlreadySent)
            {
               ComposeSendClose (rdptr);
               return (TRUE);
            }
         }
         else
         if (LangSame ("compose_sign", cptr))
         {
            CGIVARNULL (cptr, "FORM_COMPOSE_SIGFILE");
            ComposeSigFileLoad (rdptr, cptr);
         }
         else
         if (LangSame ("compose_save", cptr))
            DraftSave (rdptr);
         else
         if (LangSame ("compose_restore", cptr))
         {
            /* list the "Draft Items" folder */
            strcpy (rdptr->MailFileName, "MAIL");
            rdptr->MailFileNameLength = strlen(rdptr->MailFileName);
            strcpy (muptr->VmsMailFileName, rdptr->MailFileName);
            muptr->VmsMailFileNameLength = rdptr->MailFileNameLength;

            strcpy (rdptr->FolderName, uoptr->FolderDraftItems);
            rdptr->FolderNameLength = strlen(rdptr->FolderName);
            strcpy (muptr->VmsMailFolderName, rdptr->FolderName);
            muptr->VmsMailFolderNameLength = rdptr->FolderNameLength;

            MessageFolderPage (rdptr);
            return (TRUE);
         }
         else
         if (LangSame ("compose_clear", cptr))
         {
            cdptr->TextPtr = "";
            StatusMessage (FI_LI, 0, LangFor("compose_clear_done"));
         }
         else
         if (LangSame ("compose_new", cptr))
         {
            cdptr->FromPtr = cdptr->ToPtr = cdptr->CcPtr =
               cdptr->BccPtr = cdptr->ReplyToPtr = cdptr->SubjPtr =
               cdptr->TextPtr = cdptr->AttribPtr = NULL;
            cdptr->AlreadySent = cdptr->ContentTypeIs =
               cdptr->MsgForward = cdptr->MsgReply = 0;
            rdptr->FieldFocusPtr = "document.getElementById(\'compose_to\').focus();";
            StatusMessage (FI_LI, 0, LangFor("compose_new_done"));
         }
         else
         if (LangSame ("compose_edit_html", cptr))
         {
            cdptr->EditorType = COMPOSE_EDITOR_HTML;
            StatusMessage (FI_LI, 0, "%s.", LangFor("compose_edit_html"));
         }
         else
         if (LangSame ("compose_edit_plain", cptr))
         {
            cdptr->EditorType = COMPOSE_EDITOR_PLAIN;
            StatusMessage (FI_LI, 0, "%s.", LangFor("compose_edit_plain"));
         }
         else
         if (LangSame ("open", cptr))
         {
            /* open the contact list */
            StatusMessage (FI_LI, 0, "%s %s%s",
                           LangFor("compose"), LangFor("page_opened"),
                           ComposeCharsetAlert(cdptr->CharSetPtr));
         }
         else
         if (LangSame ("compose_preview", cptr))
            StatusMessage (FI_LI, 0, "%s%s",
                           LangFor("compose_preview"),
                           ComposeCharsetAlert(cdptr->CharSetPtr));
         else
            ErrorExit (SS$_BUGCHECK, FI_LI);
      }
      else
      {
         if (AttachComposeRequest (rdptr)) return (TRUE);
         if (muptr->VmsMailCopySend) ComposeCopySelf (rdptr);
         rdptr->FieldFocusPtr = "document.getElementById(\'compose_to\').focus();";
         StatusMessage (FI_LI, 0, "%s %s%s",
                        LangFor("compose"), LangFor("page_opened"),
                        ComposeCharsetAlert(cdptr->CharSetPtr));
      }
   }
   else
   if (rdptr->PrevPageIdent == PAGE_MESSAGE)
   {
      /**************************/
      /* from message read page */
      /**************************/

      vmptr->MessageId = sdptr->MessageId;
      vmptr->ConfirmHash = sdptr->MessageHash;

      cdptr->EditRows = ComposeEditRows (-1, uoptr);
      cdptr->EditCols = ComposeEditCols (-1, uoptr);
      cdptr->WrapAt = ComposeWrapAt (-1, uoptr);

      MsgReply = MsgReplyAll = MsgForward = MsgEditAsNew = FALSE;
      CGIVARNULL (cptr, "FORM_MSG_REPLY");
      if (!cptr) CGIVARNULL (cptr, "FORM_MSG_REPLY_POPUP");
      if (cptr) cdptr->MsgReply = MsgReply = TRUE;
      if (!cptr)
      {
         CGIVARNULL (cptr, "FORM_MSG_REPLY_ALL");
         if (!cptr) CGIVARNULL (cptr, "FORM_MSG_REPLY_ALL_POPUP");
         if (cptr) cdptr->MsgReply = MsgReplyAll = TRUE;
      }
      if (!cptr)
      {
         CGIVARNULL (cptr, "FORM_MSG_FORWARD");
         if (!cptr) CGIVARNULL (cptr, "FORM_MSG_FORWARD_POPUP");
         if (cptr) cdptr->MsgForward = MsgForward = TRUE;
         rdptr->FieldFocusPtr = "document.getElementById(\'compose_to\').focus();";
      }
      if (!cptr)
      {
         CGIVARNULL (cptr, "FORM_MSG_EDIT_NEW");
         if (!cptr) CGIVARNULL (cptr, "FORM_MSG_EDIT_NEW_POPUP");
         if (cptr) MsgEditAsNew = TRUE;
         rdptr->FieldFocusPtr = "document.getElementById(\'compose_to\').focus();";
      }

      if (MsgReply || MsgReplyAll || MsgForward || MsgEditAsNew)
      {
         CallMailMessageGet (muptr, NULL);
         if (VMSnok (muptr->MailVmsStatus))
         {
            StatusMessage (FI_LI, 1, "MAIL: %s.",
                           SysGetMsg(muptr->MailVmsStatus));
            ComposePage (rdptr);
            return (TRUE);
         }
         MessageBodyProcess (vmptr);

         /* note that this is using the state (i.e. previous request) data */
         if (SoyMailConfig.HtmlEditorLoad &&
             SoyMailConfig.HtmlEditorLoad[0] &&
             sdptr->ComposeData.ContentTypeIs == MIME_CONTENT_TEXT_HTML)
            mdptr = MessageBodyIdentify (vmptr, OPTIONS_READ_PLAIN0_HTML1);
         else
            mdptr = MessageBodyIdentify (vmptr, OPTIONS_READ_PLAIN1_HTML0);
         if (mdptr)
         {
            /* note what we have actually retrieved */
            cdptr->ContentTypeIs = mdptr->ContentTypeIs;
            cdptr->CharSetPtr = mdptr->CharSetPtr;
            if (MimeDecProcessPart (mdptr))
            {
               if (mdptr->ContentTypeIs == MIME_CONTENT_TEXT_PLAIN)
               {
                  if (sdptr->MassageMessage)
                     MessageMassage (mdptr->PartContentPtr, uoptr->MassageRead);
                  cdptr->TextPtr = mdptr->PartContentPtr;
                  cdptr->EditorType = COMPOSE_EDITOR_PLAIN;
               }
               else
               if (mdptr->ContentTypeIs == MIME_CONTENT_TEXT_HTML)
               {
                  HtmlSanitise (mdptr->PartContentPtr, cdptr->CharSetPtr);
                  cdptr->TextPtr = mdptr->PartContentPtr;
                  cdptr->EditorType = COMPOSE_EDITOR_HTML;
               }
               else
                  cdptr->TextPtr = "";
            }
            else
               cdptr->TextPtr = "";
            rdptr->CharSetPtr = mdptr->CharSetPtr;
         }
         else
         {
            cdptr->TextPtr = "";
            cdptr->CharSetPtr = ComposeSetCharSet (rdptr);
            if (!vmptr->VmsForeign)
               StatusMessage (FI_LI, 1, "%s", LangFor("mime_type_unknown"));
         }

         if (MsgReply)
         {
            if (vmptr->RfcReplyToPtr)
               cdptr->ToPtr = AddressListAppend (cdptr->ToPtr,
                                                 vmptr->RfcReplyToPtr);
            else
               cdptr->ToPtr = AddressListAppend (cdptr->ToPtr,
                                                 vmptr->MsgFromPtr);
            if (muptr->VmsMailCopyReply) ComposeCopySelf (rdptr);
            cdptr->ToPtr = AddressListMassage (cdptr->ToPtr, FALSE);
            cdptr->AttribPtr = LangFor("compose_a_reply");

            if (SoyMailConfig.MessageFlags & MAIL$M_REPLIED)
               CallMailMessageFlags (muptr, vmptr, MAIL$M_REPLIED);
         }
         else
         if (MsgReplyAll)
         {
            if (vmptr->RfcReplyToPtr)
               cdptr->ToPtr = AddressListAppend (cdptr->ToPtr,
                                                 vmptr->RfcReplyToPtr);
            else
               cdptr->ToPtr = AddressListAppend (cdptr->ToPtr,
                                                 vmptr->MsgFromPtr);
            if (vmptr->RfcToPtr)
               cdptr->ToPtr = AddressListAppend (cdptr->ToPtr,
                                                 vmptr->MsgToPtr);
            if (vmptr->RfcCcPtr)
               cdptr->CcPtr = AddressListAppend (cdptr->CcPtr,
                                                 vmptr->MsgCcPtr);
            if (muptr->VmsMailCopyReply) ComposeCopySelf (rdptr);
            cdptr->ToPtr = AddressListMassage (cdptr->ToPtr, FALSE);
            cdptr->CcPtr = AddressListMassage (cdptr->CcPtr, FALSE);
            cdptr->AttribPtr = LangFor("compose_a_reply");

            if (SoyMailConfig.MessageFlags & MAIL$M_REPLIED)
               CallMailMessageFlags (muptr, vmptr, MAIL$M_REPLIED);
         }
         else
         if (MsgForward)
         {
            cdptr->AttribPtr = LangFor("compose_a_forward");
            if (muptr->VmsMailCopyForward) ComposeCopySelf (rdptr);
         }
         else
         if (MsgEditAsNew)
         {
            if (muptr->VmsMailCopySend) ComposeCopySelf (rdptr);
         }

         if (MsgReply || MsgReplyAll)
         {
            if (vmptr->RfcMessageIdPtr)
            {
               /* in-reply-to */
               cdptr->InReplyToPtr =  CgiLibVeeMemCalloc (vmptr->RfcMessageIdLength+1);
               if (!cdptr->InReplyToPtr) ErrorExit (vaxc$errno, FI_LI);
               strcpy (cdptr->InReplyToPtr, vmptr->RfcMessageIdPtr);
            }

            len = vmptr->RfcMessageIdLength + vmptr->RfcReferencesLength;
            if (len)
            {
               /* references */
               sptr = cdptr->ReferencesPtr =  CgiLibVeeMemCalloc (len+1+1);
               if (!sptr) ErrorExit (vaxc$errno, FI_LI);
               if (cptr = vmptr->RfcMessageIdPtr)
                  while (*cptr) *sptr++ = *cptr++;
               if (cptr = vmptr->RfcReferencesPtr)
               {
                  if (vmptr->RfcMessageIdPtr) *sptr++ = ' ';
                  while (*cptr) *sptr++ = *cptr++;
               }
               *sptr = '\0';
               /* re-parse into space-separated tokens */
               cptr = sptr = cdptr->ReferencesPtr;
               while (*cptr)
               {
                  if (isspace(*cptr))
                  {
                     while (*cptr && isspace(*cptr)) cptr++;
                     if (!*cptr) break;
                     *sptr++ = ' ';
                  }
                  if (*cptr == '<')
                  {
                     while (*cptr && *cptr != '>') *sptr++ = *cptr++;
                     if (*cptr) *sptr++ = *cptr++;
                  }
                  else
                     while (*cptr && !isspace(*cptr)) *sptr++ = *cptr++;
               }
               *sptr = '\0';
               if (!cdptr->ReferencesPtr[0]) cdptr->ReferencesPtr = NULL;
            }
         }

         cdptr->SubjPtr = vmptr->MsgSubjPtr;
      }
      else
      if (muptr->VmsMailCopySend)
         ComposeCopySelf (rdptr);

      StatusMessage (FI_LI, 0, "%s %s%s",
                     LangFor("compose"), LangFor("page_opened"),
                     ComposeCharsetAlert(rdptr->CharSetPtr));
   }
   else
   if (rdptr->MessageId &&
       !strcmp (rdptr->FolderName, uoptr->FolderDraftItems))
   {            
      /**************/
      /* draft item */
      /**************/

      vmptr->MessageId = rdptr->MessageId;
      vmptr->ConfirmHash = rdptr->MessageHash;

      cdptr->EditRows = ComposeEditRows (-1, uoptr);
      cdptr->EditCols = ComposeEditCols (-1, uoptr);
      cdptr->WrapAt = ComposeWrapAt (-1, uoptr);

      CallMailMessageGet (muptr, NULL);

      if (VMSok (muptr->MailVmsStatus))
         DraftRestore (rdptr);
      else
      {
         if (muptr->MailVmsStatus == MAIL$_OPENIN)
         {
            /* error opening external message file */
            StatusMessage (FI_LI, 1, "MAIL: %s %s",
                           SysGetMsg(MAIL$_OPENIN), vmptr->ExtId);
         }
         else
         {
            if (muptr->MailVmsStatus == SS$_BADCHECKSUM ||
                muptr->MailVmsStatus == MAIL$_NOMOREMSG)
               StatusMessage (FI_LI, 1, "%s", LangFor("message_checksum"));
            else
               StatusMessage (FI_LI, 1, "MAIL: %s.",
                              SysGetMsg(muptr->MailVmsStatus));
         }
      }
   }
   else
   {
      /*****************/
      /* anywhere else */
      /*****************/

      cdptr->EditRows = ComposeEditRows (-1, uoptr);
      cdptr->EditCols = ComposeEditCols (-1, uoptr);
      cdptr->WrapAt = ComposeWrapAt (-1, uoptr);

      if (muptr->VmsMailCopySend) ComposeCopySelf (rdptr);

      rdptr->FieldFocusPtr = "document.getElementById(\'compose_to\').focus();";

      StatusMessage (FI_LI, 0, "%s %s%s",
                     LangFor("compose"), LangFor("page_opened"),
                     ComposeCharsetAlert(cdptr->CharSetPtr));
   }

   ComposePage (rdptr);

   return (TRUE);
}

/*****************************************************************************/
/*
Checks whether the 'to' address is VMS format and if it is compares the
supplied address (that from the selected contact list item) against the 'self'
address (generated in Internet format).  If it is it returns a pointer to the
VMS username.  If not just returns the original (contact list) address.  Used
for non-JavaScript contact list additions to ensure a copy-self is in VMS
format if the 'to' address is (i.e. a VMS mail message).
*/

char* ComposeCheckForSelf
(
REQUEST_DATA *rdptr,
char *AddrPtr
)
{
   COMPOSE_DATA  *cdptr;

   /*********/
   /* begin */
   /*********/

   if (WatchEnabled) WatchThis (FI_LI, "ComposeCheckForSelf()"); 

   cdptr = &rdptr->ComposeData;

   if (AddressIsVms (cdptr->ToPtr) && !strcmp (AddrPtr, cdptr->SelfPtr))
      return (rdptr->UserName);
   else
      return (AddrPtr);
}

/*****************************************************************************/
/*
Add the 'self' address to the CC list unless the To address is VMS format in
which case use the request username as the VMS Mail address.  Used for
non-JavaScript copy-self if the 'to' address is in VMS format.
*/

void ComposeCopySelf (REQUEST_DATA *rdptr)

{
   COMPOSE_DATA  *cdptr;

   /*********/
   /* begin */
   /*********/

   if (WatchEnabled) WatchThis (FI_LI, "ComposeCopySelf()"); 

   cdptr = &rdptr->ComposeData;

   if (cdptr->CopiedSelf) return;
   cdptr->CopiedSelf = TRUE;

   if (AddressIsVms (cdptr->ToPtr))
      cdptr->CcPtr = AddressListAppend (cdptr->CcPtr, rdptr->UserName);
   else
      cdptr->CcPtr = AddressListAppend (cdptr->CcPtr, cdptr->SelfPtr);
}


/*****************************************************************************/
/*
Display the message composition (edit) page.
*/

void ComposePage (REQUEST_DATA *rdptr)

{
   int  value,
        AddressTextAreaRows,
        AttachFileCount,
        AttribFwdLength,
        AttribReplyLength,
        ButtonWidth,
        ContactsRows,
        RowSpan,
        SignatureLength;
   char  *cptr, *sptr, *zptr,
         *AddrPtr,
         *AttribPtr,
         *AttribFwdPtr,
         *AttribReplyPtr,
         *CharSetDirPtr,
         *ContactList,
         *NamePtr,
         *SignaturePtr;
   COMPOSE_DATA  *cdptr;
   USER_OPTIONS  *uoptr;
   REQUEST_DATA  *sdptr;
   VMS_MAIL_MSG  *vmptr;
   VMS_MAIL_USER  *muptr;

   /*********/
   /* begin */
   /*********/

   if (WatchEnabled) WatchThis (FI_LI, "ComposePage()"); 

   muptr = &VmsMailUser;

   uoptr = &rdptr->UserOptions;
   sdptr = (REQUEST_DATA*)rdptr->StateDataPtr;
   vmptr = &muptr->VmsMailMsg;
   cdptr = &rdptr->ComposeData;

   if (muptr->SignaturePtr)
   {
      /* loaded due to [signature] button */
      SignaturePtr = muptr->SignaturePtr;
      SignatureLength = muptr->SignatureLength;
   }
   else
   {
      SignaturePtr = NULL;
      SignatureLength = 0;
      CGIVARNULL (muptr->SignaturePtr, "FORM_COMPOSE_SIGNATURE");
      if (muptr->SignaturePtr)
         muptr->SignatureLength = strlen(muptr->SignaturePtr);
      else
         ComposeSigFileLoad (rdptr, NULL);
   }

   /* if not otherwise set then focus on the message body */
   if (!rdptr->FieldFocusPtr)
      rdptr->FieldFocusPtr = "document.getElementById(\'compose_text\').focus();";

   /* get any contacts list */
   CGIVAR (cptr, "FORM_CONTACT_LIST");
   /* required for the contact load */
   rdptr->ThisPageIdent = PAGE_COMPOSE;
   ContactsLoad (rdptr, cptr, NULL);
   ContactList = TrnLnm (SOY_CONTACT_LIST, NULL, 0);

   cdptr->ToPtr = AddressListMassage (cdptr->ToPtr, FALSE);
   cdptr->CcPtr = AddressListMassage (cdptr->CcPtr, FALSE);
   cdptr->BccPtr = AddressListMassage (cdptr->BccPtr, FALSE);

   if (!cdptr->FromPtr) cdptr->FromPtr = cdptr->SelfPtr;
   if (!cdptr->ToPtr) cdptr->ToPtr = "";
   if (!cdptr->CcPtr) cdptr->CcPtr = "";
   if (!cdptr->BccPtr) cdptr->BccPtr = "";
   if (!cdptr->ReplyToPtr) cdptr->ReplyToPtr = "";
   if (!cdptr->SubjPtr) cdptr->SubjPtr = "";
   if (!cdptr->TextPtr) cdptr->TextPtr = "";

   /* get a hash of the current message header (in case it's changed) */
   cdptr->DraftHash = DraftHash (cdptr);

   if (AttribPtr = cdptr->AttribPtr)
   {
      /* do not infinitely increase reply- or forward- tos */
      AttribReplyPtr = LangFor("compose_a_reply");
      AttribReplyLength = strlen(AttribReplyPtr);
      if (strsame (cdptr->SubjPtr, AttribReplyPtr, AttribReplyLength) &&
          strsame (AttribPtr, AttribReplyPtr, AttribReplyLength))
         AttribPtr = "";
      else
      {
         AttribFwdPtr = LangFor("compose_a_forward");
         AttribFwdLength = strlen(AttribFwdPtr);
         if (strsame (cdptr->SubjPtr, AttribFwdPtr, AttribFwdLength) &&
             strsame (AttribPtr, AttribFwdPtr, AttribFwdLength))
            AttribPtr = "";
      }
   }
   else
      AttribPtr = "";

   if (cdptr->CcPtr[0]) cdptr->ShowCcAddr = TRUE;
   if (cdptr->BccPtr[0]) cdptr->ShowBccAddr = TRUE;
   if (cdptr->ReplyToPtr[0]) cdptr->ShowReplyToAddr = TRUE;

   if (AddressIsRfc(cdptr->ToPtr) ||
       AddressIsRfc(cdptr->CcPtr) ||
       AddressIsRfc(cdptr->BccPtr) ||
       AddressIsRfc(cdptr->ReplyToPtr))
      cdptr->LooksLikeRfc = TRUE;
   else
      cdptr->LooksLikeRfc = FALSE;

   RowSpan = 2;
   if (cdptr->ShowCcAddr) RowSpan++;
   if (cdptr->ShowBccAddr) RowSpan++;
   if (cdptr->ShowReplyToAddr) RowSpan++;

   ContactsRows = 3;
   if (cdptr->ShowCcAddr) ContactsRows += 2;
   if (cdptr->ShowBccAddr) ContactsRows += 2;
   if (cdptr->ShowReplyToAddr) ContactsRows += 2;

   if (!cdptr->EditorType)
   {
      /* not explicitly requesting one way or the other */
      if (SoyMailConfig.HtmlEditorLoad &&
          SoyMailConfig.HtmlEditorLoad[0])
      {
         if (cdptr->ContentTypeIs == MIME_CONTENT_TEXT_HTML)
            cdptr->EditorType = COMPOSE_EDITOR_HTML;
         else
         if (uoptr->ComposeHTML)
            cdptr->EditorType = COMPOSE_EDITOR_HTML;
         else
            cdptr->EditorType = COMPOSE_EDITOR_PLAIN;
      }
      else
         cdptr->EditorType = COMPOSE_EDITOR_PLAIN;
   }
   /* obviously x 2 */
   if (!SoyMailConfig.HtmlEditorLoad ||
       !SoyMailConfig.HtmlEditorLoad[0])
      cdptr->EditorType = COMPOSE_EDITOR_PLAIN;

   cptr = LangFor("compose_button_width");
   ButtonWidth = atoi(cptr);
   if (ButtonWidth <= 0 || ButtonWidth > 14) ButtonWidth = 7;
   /* monospace fonts need more width */
   if (uoptr->FontIsMono) ButtonWidth += 2;
   /* allow for the width of "HTML" in the change editor button */
   if (SoyMailConfig.HtmlEditorLoad &&
       SoyMailConfig.HtmlEditorLoad[0]) ButtonWidth++;

   /* Opera seems to require an extra row - scroll bar one whole row! */
   AddressTextAreaRows = 2;
   if (rdptr->UserAgent == SOY_USER_AGENT_OPERA) AddressTextAreaRows++;

   /**************/
   /* begin page */
   /**************/

   cdptr->CharSetPtr = rdptr->CharSetPtr = ComposeSetCharSet (rdptr);;
   if (cdptr->CharSetPtr[0] == '<') cdptr->CharSetPtr++;
   if (rdptr->CharSetPtr[0] == '<') rdptr->CharSetPtr++;

   MainMenuPageBegin (rdptr, PAGE_COMPOSE);
   MainMenuBar (rdptr);
   StatusInfoPanel (rdptr);

   /******************/
   /* message header */
   /******************/

   fprintf (stdout,
"<!-- message header begin -->\n\
<script language=\"JavaScript\">\n\
var lang_ComposeAlreadySent = \"%s\";\n\
var lang_ComposeNoRecipient = \"%s\";\n\
var lang_ComposeNoSubject = \"%s\";\n\
var lang_ComposeNoText = \"%s\";\n\
var lang_ComposeAddNone = \"%s\";\n\
var lang_ComposeAdd = \"%s\";\n\
var lang_ComposeBccVmsAddress = \"%s\";\n\
var lang_ComposeReplyToVmsAddress = \"%s\";\n\
var lang_ComposeResetConfirm = \"%s\";\n\
var lang_ComposeClearConfirm = \"%s\";\n\
var lang_ComposeClearDone = \"%s\";\n\
var lang_ComposeNewConfirm = \"%s\";\n\
var lang_ComposeNewDone = \"%s\";\n\
var lang_ComposeSend = \"%s\";\n\
var lang_ComposeSendConfirm = \"%s\";\n\
var lang_ComposeSignDone = \"%s\";\n\
var lang_ComposeWrapOn = \"%s\";\n\
var lang_ComposeWrapOff = \"%s\";\n\
var soy_ComposeAlreadySent = %s;\n\
var soy_ComposeContactsAfter = %d\n\
var soy_ComposeContactsSize = %d\n\
var soy_ComposeContactsWidth = %d\n\
var soy_SetWrapMozilla = %s;\n\
var soy_SetWrapMsie = %s;\n",
            LANG_FOR_CONFIRM("compose_already_sent"),
            LANG_FOR_CONFIRM("compose_no_recipient"),
            LANG_FOR_CONFIRM("compose_no_subject"),
            LANG_FOR_CONFIRM("compose_no_text"),
            LangFor("compose_add_none"),
            LangFor("compose_add"),
            LangFor("compose_bcc_vms_address"),
            LangFor("compose_reply_to_vms_address"),
            LANG_FOR_CONFIRM("compose_reset_confirm"),
            LANG_FOR_CONFIRM("compose_clear_confirm"),
            LangFor("compose_clear_done"),
            LANG_FOR_CONFIRM("compose_new_confirm"),
            LangFor("compose_new_done"),
            LangFor("compose_send"),
            LangFor("compose_send_confirm"),
            LangFor("compose_sign_done"),
            LangFor("compose_wrap_on"),
            LangFor("compose_wrap_off"),
            cdptr->AlreadySent ? "true" : "false",
            SoyMailConfig.ComposeContactsAfter,
            SoyMailConfig.ComposeContactsSize,
            CONTACTS_LIST_WIDTH,
            rdptr->UserAgent == SOY_USER_AGENT_MOZILLA ? "true" : "false",
            rdptr->UserAgent == SOY_USER_AGENT_MSIE ? "true" : "false");

   value = ComposeWrapAt (-1, uoptr);
   fprintf (stdout, "var soy_SetWrapValues = [%d", value);
   while ((value = ComposeWrapAt (value, uoptr)) >= 0)
      fprintf (stdout, ",%d", value);
   fprintf (stdout, "];\n");

   value = ComposeEditRows (-1, uoptr);
   fprintf (stdout, "var soy_SetRowsValues = [%d", value);
   while ((value = ComposeEditRows (value, uoptr)) > 0)
      fprintf (stdout, ",%d", value);
   fprintf (stdout, "];\n");

   value = ComposeEditCols (-1, uoptr);
   fprintf (stdout, "var soy_SetColsValues = [%d", value);
   while ((value = ComposeEditCols (value, uoptr)) > 0)
      fprintf (stdout, ",%d", value);
   fprintf (stdout, "];\n");

   fprintf (stdout, "</script>\n");

   fprintf (stdout,
"<p class=\"css_p06\">\
<table width=\"100%%\" cellpadding=\"0\" cellspacing=\"5\" \
class=\"css_panel_1\" align=\"center\">\n\
\
<tr><td style=\"width:1%%;vertical-align:top;text-align:right;\">\n\
\
<table cellpadding=\"2\" cellspacing=\"0\" id=\"contact_id_1\">\n");

   /* value can be -1, 0, 1 */
   if (SoyMailConfig.ComposeUserFrom)
      fprintf (stdout,
"<tr><td class=\"css_tabhdr\">%s:</td><td>\
<input class=\"css_text_edit\" style=\"width:50ch!important;\" \
id=\"compose_from\" name=\"compose_from\" size=\"40\" value=\"%s\"%s>\
</td></tr>\n",
               LangFor("from"), HTML_TEXT(cdptr->FromPtr),
               SoyMailConfig.ComposeUserFrom < 0 ? "DISABLED" : "");

   fprintf (stdout,
"<tr><td class=\"css_tabhdr\">%s:</td><td>\
<textarea class=\"css_text_edit\" \
style=\"vertical-align:top;width:50ch!important;white-space:pre;\
resize:vertical;\" \
id=\"compose_to\" name=\"compose_to\" \
rows=\"%d\" cols=\"40\" wrap=\"off\" tabindex=\"1\">%s</textarea>\
</td></tr>\n",
            LangFor("to"),
            AddressTextAreaRows,
            HTML_ADDR(cdptr->ToPtr));

   if (vmptr->XVmsToPtr)
      fprintf (stdout,
"<tr><td class=\"css_tabhdr\">X-VMS-To:</td>\
<td class=\"css_rfcval\" width=\"100%%\">%s</td></tr>\n",
               vmptr->XVmsToPtr);

   fprintf (stdout,
"<tr id=\"compose_cc_tr\"%s>\
<td class=\"css_tabhdr\">%s:</td><td>\
<textarea class=\"css_text_edit\" \
style=\"vertical-align:top;width:50ch!important;white-space:pre;\
resize:vertical;\" \
id=\"compose_cc\" name=\"compose_cc\" rows=\"%d\" cols=\"40\" \
wrap=\"off\" tabindex=\"2\">%s</textarea>\
</td></tr>\n",
         cdptr->ShowCcAddr ? "" : " style=\"display:none;\"",      
         LangFor("cc"),
         AddressTextAreaRows,
         HTML_ADDR(cdptr->CcPtr));

   fprintf (stdout,
"<tr id=\"compose_bcc_tr\"%s>\
<td class=\"css_tabhdr\">%s:</td><td>\
<textarea class=\"css_text_edit\" \
style=\"vertical-align:top;width:50ch!important;white-space:pre;\
resize:vertical;\" \
id=\"compose_bcc\" name=\"compose_bcc\" rows=\"%d\" cols=\"40\" \
wrap=\"off\" tabindex=\"3\">%s</textarea>\
</td></tr>\n",
            cdptr->ShowBccAddr ? "" : " style=\"display:none;\"",      
            LangFor("bcc"),
            AddressTextAreaRows,
            HTML_ADDR(cdptr->BccPtr));

   fprintf (stdout,
"<tr id=\"compose_reply_to_tr\"%s>\
<td class=\"css_tabhdr\">%s:</td><td>\
<input class=\"css_text_edit\" style=\"width:50ch!important;\" \
id=\"compose_reply_to\" name=\"compose_reply_to\" \
size=\"40\" value=\"%s\" tabindex=\"4\">\
</td></tr>\n",
            cdptr->ShowReplyToAddr ? "" : " style=\"display:none;\"",      
            LangFor("compose_reply_to"),
            HTML_ADDR(cdptr->ReplyToPtr));

   fprintf (stdout,
"<tr><td class=\"css_tabhdr\">%s:</td><td>\
<input class=\"css_text_edit\" style=\"width:50ch!important;\" \
id=\"compose_subj\" name=\"compose_subj\" \
size=\"40\" value=\"%s%s%s\" tabindex=\"5\">\
</td></tr>\n",
            LangFor("subject"),
            AttribPtr, *AttribPtr ? " " : "",
            HTML_TEXT(cdptr->SubjPtr));

   fprintf (stdout,
"</table>\n\
\
</td><td style=\"width:1%%;padding-left:10px;\
vertical-align:top;text-align:right;\">\n\
\
<table cellpadding=\"1\" cellspacing=\"0\" id=\"contact_id_2\">\n\
<tr><td style=\"padding-top:3px\">\
<input class=\"css_button_panel_1\" style=\"width:%dem;\" type=\"submit\" \
name=\"compose_btn\" value=\"%s\" \
onclick=\"return contactAdd(\'compose_to\',\'%s\')\">\
</td></tr>\n\
<tr><td>\
<input class=\"css_button_panel_1\" style=\"width:%dem;\" type=\"submit\" \
name=\"compose_btn\" value=\"%s\" \
onclick=\"return contactAdd(\'compose_cc\',\'%s\')\">\
</td></tr>\n\
<tr><td>\
<input class=\"css_button_panel_1\" style=\"width:%dem;\" type=\"submit\" \
name=\"compose_btn\" value=\"%s\" \
onclick=\"return contactAdd(\'compose_bcc\',\'%s\')\">\
</td></tr>\n\
<tr><td>\
<input class=\"css_button_panel_1\" style=\"width:%dem;\" type=\"submit\" \
name=\"compose_btn\" value=\"%s\" \
onclick=\"return contactAdd(\'compose_reply_to\',\'%s\')\">\
</td></tr>\n\
</table>\n\
\
</td><td style=\"width:98%%;vertical-align:top;text-align:left;\">\n",
            ButtonWidth,
            LangFor("compose_to"), LangFor("to"),
            ButtonWidth,
            LangFor("compose_cc"), LangFor("cc"),
            ButtonWidth,
            LangFor("compose_bcc"), LangFor("bcc"),
            ButtonWidth,
            LangFor("compose_reply_to"), LangFor("reply_to"));

   ContactsCompose (rdptr, ContactsRows);

   fprintf (stdout, "</td></tr>\n</table>\n");

   if (cdptr->InReplyToPtr)
      fprintf (stdout,
"<input type=\"hidden\" name=\"compose_in_reply_to\" value=\"%s\">\n",
               HTML_ESCAPE(cdptr->InReplyToPtr));

   if (cdptr->ReferencesPtr)
      fprintf (stdout,
"<input type=\"hidden\" name=\"compose_references\" value=\"%s\">\n",
               HTML_ESCAPE(cdptr->ReferencesPtr));

   fprintf (stdout, "<!-- message header end -->\n\n");

   /****************/
   /* message body */
   /****************/

   if (cdptr->EditorType == COMPOSE_EDITOR_HTML &&
       SoyMailConfig.HtmlEditorLoad &&
       strstr(SoyMailConfig.HtmlEditorLoad,"<script"))
      fprintf (stdout,
"<!-- HTML editor load -->\n\
%s\n\
<!-- HTML editor loaded! -->\n\
\n",
               SoyMailConfig.HtmlEditorLoad);

   fprintf (stdout,
"<!-- message body begin -->\n\
<p class=\"css_p06\">\
<table cellpadding=\"1\" cellspacing=\"5\" width=\"100%%\" \
class=\"css_panel_1\" align=\"center\">\n\
\
<tr id=\"compose_edit_tr\" onclick=\"contactListMinimise()\">\
<td width=\"5%%\" style=\"vertical-align:top;\">\n\
<input class=\"css_button_panel_1\" style=\"width:%dem;\" type=\"submit\" \
name=\"compose_btn\" value=\"%s\" \
onclick=\"if (!confirmSend()) return false; else { \
window.removeEventListener(\'beforeunload\',doBeforeUnload);\
return startWorking(); }\" \
tabindex=\"7\">\n\
\
<p class=\"css_p02\">\
<input type=\"hidden\" name=\"compose_send_close_child\" \
id=\"compose_send_close_child\" value=\"\">\n\
<input class=\"css_button_panel_1\" style=\"width:%dem;\" type=\"submit\" \
name=\"compose_btn\" id=\"compose_btn_send_close\" value=\"%s\" \
onclick=\"if (!confirmSendClose()) return false; else { \
window.removeEventListener(\'beforeunload\',doBeforeUnload);\
return startWorking(); }\">\n\
\
<p class=\"css_p02\" \
style=\"font-size:90%%;max-width:%dem;padding-left:0.3em;\">\
%s <input type=\"checkbox\" style=\"border:none;\" \
id=\"compose_send_confirm\" name=\"compose_send_confirm\" value=\"1\"%s>\n\
\
<p class=\"css_p06\">\
<input class=\"css_button_panel_1\" style=\"width:%dem;\" type=\"submit\" \
name=\"compose_btn\" value=\"%s\" onclick=\"\
window.removeEventListener(\'beforeunload\',doBeforeUnload);return true;\
\">\n\
\
<p class=\"css_p06\">\
<input class=\"css_button_panel_1\" style=\"width:%dem;\" type=\"submit\" \
name=\"compose_btn\" value=\"%s\" onclick=\"\
window.removeEventListener(\'beforeunload\',doBeforeUnload);return true;\
\">\n\
\
<p class=\"css_p02\">\
<input class=\"css_button_panel_1\" style=\"width:%dem;\" type=\"submit\" \
name=\"compose_btn\" value=\"%s\" onclick=\"\
window.removeEventListener(\'beforeunload\',doBeforeUnload);return true;\
\">\n",
            ButtonWidth,
            LangFor("compose_send"),
            ButtonWidth,
            LangFor("compose_send_close"),
            ButtonWidth,
            LangFor("compose_send_confirm"),
            cdptr->ConfirmBeforeSend ? " checked" : "",
            ButtonWidth,
            LangFor("compose_preview"),
            ButtonWidth,
            LangFor("compose_save"),
            ButtonWidth,
            LangFor("compose_restore"));

   ComposeSignature (rdptr, ButtonWidth);

   fprintf (stdout,
"<p class=\"css_p06\">\
<input class=\"css_button_panel_1\" style=\"width:%dem;%s\" type=\"reset\" \
value=\"%s\" onclick=\"%s\">\n",
            ButtonWidth,
            cdptr->EditorType == COMPOSE_EDITOR_HTML ?
               "font-style:italic;" : "",
            LangFor("compose_reset"),
            cdptr->EditorType == COMPOSE_EDITOR_HTML ?
               "return false" : "return composeReset()");

   fprintf (stdout,
"<p class=\"css_p02\">\
<input class=\"css_button_panel_1\" style=\"width:%dem;\" type=\"submit\" \
name=\"compose_btn\" value=\"%s\" ",
            ButtonWidth,
            LangFor("compose_clear"));

   if (cdptr->EditorType == COMPOSE_EDITOR_HTML)
      fprintf (stdout, "onclick=\"return confirm(\'%s %s\')\">\n",
               LANG_FOR_CONFIRM("compose_clear_confirm"),
               LANG_FOR_CONFIRM("sure?"));
   else
      fprintf (stdout, "onclick=\"return composeClear()\">\n");

   fprintf (stdout,
"<p class=\"css_p02\">\
<input class=\"css_button_panel_1\" style=\"width:%dem;\" type=\"submit\" \
name=\"compose_btn\" value=\"%s\" ",
            ButtonWidth,
            LangFor("compose_new"));

   if (cdptr->EditorType == COMPOSE_EDITOR_HTML)
      fprintf (stdout, "onclick=\"return confirm(\'%s %s\')\">\n",
               LANG_FOR_CONFIRM("compose_new_confirm"),
               LANG_FOR_CONFIRM("sure?"));
   else
      fprintf (stdout, "onclick=\"return composeNew()\">\n");

   if (SoyMailConfig.HtmlEditorLoad &&
       SoyMailConfig.HtmlEditorLoad[0])
      fprintf (stdout,
"<p class=\"css_p06\">\
<input class=\"css_button_panel_1\" style=\"width:%dem;\" type=\"submit\" \
name=\"compose_btn\" value=\"%s\">\n\
<p class=\"css_p02\">",
               ButtonWidth,
               cdptr->EditorType == COMPOSE_EDITOR_PLAIN ?
                  LangFor("compose_edit_html") :
                  LangFor("compose_edit_plain"));
   else
      fprintf (stdout, "<p class=\"css_p06\">");

   if (cdptr->EditorType == COMPOSE_EDITOR_PLAIN)
   {
      if (cdptr->WrapAt)
         fprintf (stdout,
"<input class=\"css_button_panel_1\" style=\"width:%dem;\" type=\"submit\" \
id=\"compose_btn_wrap\" name=\"compose_btn_wrap\" value=\"%s %d\" \
onclick=\"return setWrapAt()\">\n",
                  ButtonWidth,
                  LangFor("compose_wrap_on"),
                  cdptr->WrapAt);
      else
         fprintf (stdout,
"<input class=\"css_button_panel_1\" style=\"width:%dem;\" type=\"submit\" \
id=\"compose_btn_wrap\" name=\"compose_btn_wrap\" value=\"%s\" \
onclick=\"return setWrapAt()\">\n",
                  ButtonWidth,
                  LangFor("compose_wrap_off"));
   }
   else
      fprintf (stdout,
"<input class=\"css_button_panel_1\" style=\"width:%dem;font-style:italic;\" \
type=\"submit\" id=\"compose_btn_wrap\" name=\"compose_btn_wrap\" \
value=\"%s\" onclick=\"return false\">\n",
               ButtonWidth,
               LangFor("compose_wrap_off"));

   fprintf (stdout,
"<input type=\"hidden\" id=\"compose_edit_rows\" \
name=\"compose_edit_rows\" value=\"%d\">\n\
<input type=\"hidden\" id=\"compose_edit_cols\" \
name=\"compose_edit_cols\" value=\"%d\">\n\
<input type=\"hidden\" id=\"compose_wrap_at\" \
name=\"compose_wrap_at\" value=\"%d\">\n\
<input type=\"hidden\" id=\"compose_charset\" \
name=\"compose_charset\" value=\"%s\">\n\
<p class=\"css_p02\">\
\
<input class=\"css_button_panel_1\" \
style=\"width:%1.2fem;border-right-width:0px;\" \
type=\"submit\" id=\"compose_btn_rows\" name=\"compose_btn_rows\" \
value=\"   %d\" onclick=\"%s\">\
\
<input class=\"css_button_panel_1\" \
style=\"width:%1.2fem;border-left-width:0px;border-right-width:0px;\" \
type=\"submit\" name=\"compose_btn_rows_by_cols\" \
value=\" x \" onclick=\"%s\">\
\
<input class=\"css_button_panel_1\" \
style=\"width:%1.2fem;border-left-width:0px;\" \
type=\"submit\" id=\"compose_btn_cols\" name=\"compose_btn_cols\" \
value=\"%d  \" onclick=\"%s\">\n",
            cdptr->EditRows,
            cdptr->EditCols,
            cdptr->WrapAt,
            cdptr->CharSetPtr,
            (float)ButtonWidth / 2.5,
            cdptr->EditRows,
            cdptr->EditorType == COMPOSE_EDITOR_PLAIN ?
               "return setEditRows()" : "return true",
            (float)ButtonWidth / 5.0,
            cdptr->EditorType == COMPOSE_EDITOR_PLAIN ?
               "return resetRowsCols()" : "return true",
            (float)ButtonWidth / 2.5,
            cdptr->EditCols,
            cdptr->EditorType == COMPOSE_EDITOR_PLAIN ?
               "return setEditCols()" : "return true");

   /* if required provide "|>>" or "<<|" explicit script direction buttons */
   if (ComposeCharSet (rdptr, ButtonWidth) ||
       !MessageCharSetLtr (rdptr->CharSetPtr))
   {
      fprintf (stdout,
"<p class=\"css_p02\">\
<input class=\"css_button_panel_1\" style=\"width:%1.2fem;\" \
type=\"submit\" name=\"compose_ltr\" value=\"|&raquo;\" onclick=\
\"composeTextDirection(\'ltr\');return false\">\
<input type=\"submit\" style=\"width:%1.2fem;visibility:hidden;\">\
<input class=\"css_button_panel_1\" style=\"width:%1.2fem;\" \
type=\"submit\" name=\"compose_rtl\" value=\"&laquo;|\" onclick=\
\"composeTextDirection(\'rtl\');return false\">",
      (float)ButtonWidth / 2.5,
      (float)ButtonWidth / 5.0,
      (float)ButtonWidth / 2.5);
   }

   CharSetDirPtr = ComposeCharSetDir(rdptr->CharSetPtr);

   /* if just or previously used "|>>" or "<<|" explicit direction */
   CGIVARNULL (cptr, "FORM_COMPOSE_LTR");
   if (!cptr) CGIVARNULL (cptr, "FORM_COMPOSE_RTL");
   if (!cptr) CGIVARNULL (cptr, "FORM_COMPOSE_DIR");
   fprintf (stdout,
"<input type=\"hidden\" id=\"compose_dir\" \
name=\"compose_dir\" value=\"%s\">\n",
            cptr ? CharSetDirPtr : "");

   fprintf (stdout,
"</td>\
<td width=\"5%%\" \
style=\"height:%dem;padding-left:1em;vertical-align:top;\">\n\
\
<textarea class=\"css_text_edit\" \
id=\"compose_text\" name=\"compose_text\" \
rows=\"%d\" cols=\"%d\" dir=\"%s\" ",
            /* plus five to maintain the height when using FCKeditor */
            cdptr->EditorType == COMPOSE_EDITOR_PLAIN ?
               cdptr->EditRows : cdptr->EditRows + 5,
            cdptr->EditRows, cdptr->EditCols,
            CharSetDirPtr);

   if (rdptr->UserAgent == SOY_USER_AGENT_MSIE)
      fprintf (stdout, "wrap=\"%s\"", cdptr->WrapAt > 0 ? "soft" : "off");
   else
      fprintf (stdout, "wrap=\"%s\"", cdptr->WrapAt > 0 ? "virtual" : "off");

   fprintf (stdout, " style=\"width:%d.3ch;\"", cdptr->EditCols);

   fprintf (stdout, " tabindex=\"6\">");

   if (cdptr->AttribPtr && cdptr->MsgReply)
   {
      cptr = LangFor ("compose_quote");
      fprintf (stdout, cptr,
               HTML_ESCAPE(vmptr->MsgDatePtr),
               HTML_ESCAPE(vmptr->MsgFromPtr));
      cptr = HTML_ESCAPE(cdptr->TextPtr);
      if (cdptr->ContentTypeIs == MIME_CONTENT_TEXT_HTML)
      {
         fputs ("<br>\n", stdout);
         fputs (cptr, stdout);
      }
      else
      {
         fputs ("\n", stdout);
         while (*cptr)
         {
            sptr = cptr;
            while (*cptr && *cptr != '\n') cptr++;
            if (*cptr) cptr++;
            fprintf (stdout, "%s%*.*s",
                     uoptr->QuoteReply, cptr-sptr, cptr-sptr, sptr);
         }
      }
   }
   else
   if (cdptr->AttribPtr && cdptr->MsgForward)
   {
      if (cdptr->ContentTypeIs == MIME_CONTENT_TEXT_HTML)
      {
         fprintf (stdout,
"<br><br>--------------- %s ---------------\n\
<br>%s: %s\n\
<br>%s: %s\n\
<br>%s: %s\n\
<br>%s: %s\n\
<br><br>\n",
                  LangFor("compose_original"),
                  LangFor("subject"), HTML_TEXT(vmptr->MsgSubjPtr),
                  LangFor("to"), HTML_ADDR(vmptr->MsgToPtr),
                  LangFor("from"), HTML_ADDR(vmptr->MsgFromPtr),
                  LangFor("date"), HTML_ADDR(vmptr->MsgDatePtr));
      }
      else
      {
         fprintf (stdout,
"\n\n%s--------------- %s ---------------\n\
%s%s: %s\n\
%s%s: %s\n\
%s%s: %s\n\
%s%s: %s\n\
%s\n",
                  uoptr->QuoteForward,
                  LangFor("compose_original"),
                  uoptr->QuoteForward,
                  LangFor("subject"), HTML_TEXT(vmptr->MsgSubjPtr),
                  uoptr->QuoteForward,
                  LangFor("to"), HTML_ADDR(vmptr->MsgToPtr),
                  uoptr->QuoteForward,
                  LangFor("from"), HTML_ADDR(vmptr->MsgFromPtr),
                  uoptr->QuoteForward,
                  LangFor("date"), HTML_TEXT(vmptr->MsgDatePtr),
                  uoptr->QuoteForward);
      }

      if (cdptr->ContentTypeIs == MIME_CONTENT_TEXT_HTML)
      {
         cptr = cdptr->TextPtr;
         fputs (cptr, stdout);
      }
      else
      if (uoptr->QuoteForward[0])
      {
         cptr = HTML_ESCAPE(cdptr->TextPtr);
         while (*cptr)
         {
            sptr = cptr;
            while (*cptr && *cptr != '\n') cptr++;
            if (*cptr) cptr++;
            fprintf (stdout, "%s%*.*s",
                     uoptr->QuoteForward, cptr-sptr, cptr-sptr, sptr);
         }
      }
      else
      {
         cptr = HTML_ESCAPE(cdptr->TextPtr);
         fputs (cptr, stdout);
      }
   }
   else
   if (cdptr->ContentTypeIs == MIME_CONTENT_TEXT_HTML)
      fputs (cdptr->TextPtr, stdout);
   else
      fputs (HTML_ESCAPE(cdptr->TextPtr), stdout);

   if (cdptr->MsgReply) ComposeReplyAttachList (rdptr);

   if (SignaturePtr && SignatureLength)
   {
      if (cdptr->EditorType == COMPOSE_EDITOR_HTML)
      {
         cptr = SignaturePtr;
         while (*cptr)
         {
            sptr = cptr;
            while (*cptr && *cptr != '\n') cptr++;
            if (*cptr) cptr++;
            fprintf (stdout, "<br>%*.*s", cptr-sptr, cptr-sptr, sptr);
         }
      }
      else
         fprintf (stdout, "\n%s", HTML_TEXT(SignaturePtr));
   }

   fprintf (stdout, "</textarea>\n</td><td width=\"90%%\"></td></tr>\n");

   if (cdptr->MsgForward) ComposeForwardAttachList (rdptr);

   fprintf (stdout, "</table>\n");

   if (muptr->SignaturePtr && muptr->SignatureLength)
   {
      if (cdptr->CharSetPtr && strsame(cdptr->CharSetPtr,"UTF-8",-1))
         cptr = HTML_ESCAPE(HtmlUtf8Encode(muptr->SignaturePtr));
      else
         cptr = HTML_ESCAPE(muptr->SignaturePtr);
      fprintf (stdout,
"<input type=\"hidden\" name=\"compose_signature\" \
id=\"compose_signature\" value=\"%s\">\n",
               cptr);
   }

   fprintf (stdout, "<!-- message body end -->\n\n");

   /***************/
   /* attachments */
   /***************/

   AttachComposeList (rdptr);

   /***********/
   /* preview */
   /***********/

   CGIVARNULL (cptr, "FORM_COMPOSE_BTN");
   if (LangSame ("compose_preview", cptr))
      if (sptr = ComposePreviewMessage (rdptr))
         fprintf (stdout,
"<script>composePreview(\'%s\',\'%s\',\'%s %s\')</script>\n\n",
                  cdptr->CharSetPtr ? cdptr->CharSetPtr : "",
                  sptr,
                  LangFor("compose_preview"),
                  cdptr->SubjPtr[0] ? EscapeForJavaScript(cdptr->SubjPtr)
                                    : LangFor("(none)"));

   /************/
   /* end page */
   /************/

   MainMenuPageEnd (rdptr);
}

/*****************************************************************************/
/*
Provide [signature] button and if user signature files options a list of those
that may be selected from.
*/

void ComposeSignature
(
REQUEST_DATA *rdptr,
int ButtonWidth
)
{
   char  *cptr, *qptr;
   COMPOSE_DATA  *cdptr;
   USER_OPTIONS  *uoptr;
   VMS_MAIL_MSG  *vmptr;
   VMS_MAIL_USER  *muptr;

   /*********/
   /* begin */
   /*********/

   if (Debug) fprintf (stdout, "ComposeSignature()\n");

   muptr = &VmsMailUser;
   vmptr = &muptr->VmsMailMsg;
   uoptr = &rdptr->UserOptions;
   cdptr = &rdptr->ComposeData;

   fprintf (stdout,
"<p class=\"css_p06\">\
<input class=\"css_button_panel_1\" style=\"width:%dem;%s\" \
type=\"submit\" name=\"compose_btn\" value=\"%s\"",
            ButtonWidth,
            (!(muptr->SignaturePtr && muptr->SignatureLength)) ?
                "font-style:italic;" : "",
            LangFor("compose_sign"));

   if (!(muptr->SignaturePtr && muptr->SignatureLength))
      fprintf (stdout, " onclick=\"return false\">\n");
   else
   if (cdptr->EditorType == COMPOSE_EDITOR_HTML)
      fprintf (stdout, ">\n");
   else
      fprintf (stdout, " onclick=\"return signatureAppend();\">\n");

   if (!(muptr->SignaturePtr && muptr->SignatureLength)) return;

   cptr = FileSpecSearch (1, muptr->VmsMailFullDirectory,
                             SOYMAIL_SIGNATURE_NAME "*.TXT");

   if (!cptr) return;

   /***********************/
   /* multiple signatures */
   /***********************/

   CGIVARNULL (cptr, "FORM_COMPOSE_SIGFILE");
   if (!cptr) cptr = uoptr->SigFileName;

   fprintf (stdout,
"<p class=\"css_p02\">\
<input type=\"hidden\" name=\"compose_sigfile_selected\" \
id=\"compose_sigfile_selected\" value=\"%s\">\n",
            cptr); 

   fprintf (stdout,
"<select class=\"css_select_1\" style=\"width:%dem;\" \
name=\"compose_sigfile\" id=\"compose_sigfile\">\n",
            ButtonWidth);

   if (muptr->VmsMailSigFile[0])
      fprintf (stdout,
"<option value=\"(1)\"%s>%s (VMS)\n",
               !strcmp(cptr,"(1)") ? " selected" : "",
               LangFor("option_soy_sigfile_default"));

   fprintf (stdout,
"<option value=\"(2)\"%s>%s\n",
            !strcmp(cptr,"(2)") ? " selected" : "",
            LangFor("option_soy_sigfile_soymail"));

   OptionsSigSelectFile (rdptr, cptr);

   fprintf (stdout, "</select>\n");
}

/*****************************************************************************/
/*
Return a pointer to the composition character set.
*/

char* ComposeSetCharSet (REQUEST_DATA *rdptr)

{
   char  *csptr;
   USER_OPTIONS  *uoptr;

   /*********/
   /* begin */
   /*********/

   if (Debug) fprintf (stdout, "ComposeSetCharSet()\n");

   CGIVARNULL (csptr, "FORM_MESSAGE_CHARSET");
   if (csptr && *csptr) return (csptr);

   CGIVARNULL (csptr, "FORM_COMPOSE_SELECT_CHARSET");
   if (csptr && *csptr) return (csptr);

   uoptr = &rdptr->UserOptions;
   csptr = uoptr->ComposeCharSets;
   if (csptr && *csptr) return (csptr);

   csptr = SoyMailConfig.ComposeCharSets;
   if (csptr && *csptr) return (csptr);

   csptr = uoptr->CharSet;
   if (csptr && *csptr) return (csptr);

   return (SOY_DEFAULT_CHARSET);
}

/*****************************************************************************/
/*
Display a list of user and globally configured character sets that can be
associated with the composed message.
*/

BOOL ComposeCharSet
(
REQUEST_DATA *rdptr,
int ButtonWidth
)
{
   BOOL  selected;
   int  cnt;
   char  *cptr, *sptr, *zptr,
         *SelectCharSetPtr;
   char  CharSetName [128],
         CharSetDisplay [128];
   COMPOSE_DATA  *cdptr;
   USER_OPTIONS  *uoptr;

   /*********/
   /* begin */
   /*********/

   if (Debug) fprintf (stdout, "ComposeCharSet()\n");

   uoptr = &rdptr->UserOptions;
   cdptr = &rdptr->ComposeData;

   /* if not user or globally configured */
   if (!(uoptr->ComposeCharSets[0] || SoyMailConfig.ComposeCharSets))
      return (FALSE);

   /* disable the character set selection if there is edited content */
   for (cptr = cdptr->TextPtr; *cptr && isspace(*cptr); cptr++);
   if (*cptr) return (FALSE);

   SelectCharSetPtr = ComposeSetCharSet (rdptr);

   fprintf (stdout,
"<p class=\"css_p06\">\
<select class=\"css_select_1\" style=\"width:%dem;\" \
name=\"compose_select_charset\" id=\"compose_select_charset\" \
onchange=\"document.getElementById(\'soymail_form\').submit();\
return false;\">\n",
            ButtonWidth);

   for (cnt = 0; cnt <= 2; cnt++)
   {
      if (cnt == 0 && !*(cptr = uoptr->ComposeCharSets)) continue;
      if (cnt == 1 && !(cptr = SoyMailConfig.ComposeCharSets)) continue;
      if (cnt == 2 && !*(cptr = uoptr->CharSet)) continue;

      while (*cptr)
      {
         while (*cptr == ',' || isspace(*cptr)) cptr++;
         if (!*cptr) break;
         zptr = (sptr = CharSetDisplay) + sizeof(CharSetDisplay)-1;
         while (*cptr && *cptr != '=' && *cptr != ',' &&
                !isspace(*cptr) && sptr < zptr) *sptr++ = *cptr++;
         *sptr = '\0';
         if (*cptr == '=')
         {
            cptr++;
            zptr = (sptr = CharSetName) + sizeof(CharSetName)-1;
            while (*cptr && *cptr != ',' && !isspace(*cptr) && sptr < zptr)
                *sptr++ = *cptr++;
            *sptr = '\0';
         }
         else
            strcpy (CharSetName, CharSetDisplay);

         if (strsame (SelectCharSetPtr, CharSetName, -1) ||
             strsame (SelectCharSetPtr, CharSetName+1, -1))
            selected = TRUE;
         else
            selected = FALSE;

         fprintf (stdout,
"<option value=\"%s\"%s>%s\n",
                  HTML_ESCAPE(CharSetName),
                  selected ? " selected" : "",
                  HTML_ESCAPE(CharSetDisplay));

         if (selected) cdptr->CharSetPtr = CharSetName;
      }
   }

   fputs ("</select>\n", stdout);

   return (TRUE);
}

/*****************************************************************************/
/*
Determine script directionality in a compose context.
Functionality somewhat duplicated in MessageCharSetDir().
*/

char* ComposeCharSetDir (char *CharSetPtr)

{
   char  *cptr, *sptr, *zptr;
   char  CharSetName [128];

   /*********/
   /* begin */
   /*********/

   if (Debug) fprintf (stdout, "ComposeCharSetDir() |%s|\n", CharSetPtr);

   /* buttons from compose page */
   CGIVARNULL (cptr, "FORM_COMPOSE_LTR");
   if (cptr) return ("ltr");
   CGIVARNULL (cptr, "FORM_COMPOSE_RTL");
   if (cptr) return ("rtl");
   /* preset direction from compose page */
   CGIVARNULL (cptr, "FORM_COMPOSE_DIR");
   if (cptr && *cptr) return (cptr);

   if (!CharSetPtr || !CharSetPtr[0]) return ("ltr");

   if (!(cptr = SoyMailConfig.CharSetsRightToLeft)) return ("ltr");

   while (*cptr)
   {
      while (*cptr == ',' || isspace(*cptr)) cptr++;
      if (!*cptr) break;
      zptr = (sptr = CharSetName) + sizeof(CharSetName)-1;
      while (*cptr && *cptr != ',' && !isspace(*cptr) && sptr < zptr)
         *sptr++ = *cptr++;
      *sptr = '\0';
      if (strsame (CharSetName, CharSetPtr, -1)) return ("rtl");
   }

   return ("ltr");
}

/*****************************************************************************/
/*
Provide each attachment (part with a name or file-name) as "<<name>>" for
inclusion in the body of a replied-to message.
*/

void ComposeReplyAttachList (REQUEST_DATA *rdptr)

{
   char  *cptr, *qptr;
   COMPOSE_DATA  *cdptr;
   MIME_DATA  *mdptr;
   USER_OPTIONS  *uoptr;
   VMS_MAIL_MSG  *vmptr;
   VMS_MAIL_USER  *muptr;

   /*********/
   /* begin */
   /*********/

   if (Debug) fprintf (stdout, "ComposeReplyAttachList()\n");

   muptr = &VmsMailUser;
   vmptr = &muptr->VmsMailMsg;
   cdptr = &rdptr->ComposeData;
   uoptr = &rdptr->UserOptions;

   if (!vmptr->MimeDataPtr) return;

   if (cdptr->ContentTypeIs == MIME_CONTENT_TEXT_HTML)
      fprintf (stdout, "\n<blockquote>\n");
   else
   {
      qptr = uoptr->QuoteReply;
      fprintf (stdout, "\n%s\n", qptr);
   }

   for (mdptr = vmptr->MimeDataPtr; mdptr; mdptr = mdptr->NextMimePtr)
   {
      if (!(mdptr->NamePtr || mdptr->FileNamePtr)) continue;
      if (mdptr->NamePtr)
         cptr = mdptr->NamePtr;
      else
         cptr = mdptr->FileNamePtr;
      if (cdptr->ContentTypeIs == MIME_CONTENT_TEXT_HTML)
         fprintf (stdout, "[[%s]]\n", HTML_ESCAPE(cptr));
      else
         fprintf (stdout, "%s  [[%s]]\n", qptr, cptr);
   }

   if (cdptr->ContentTypeIs == MIME_CONTENT_TEXT_HTML)
      fprintf (stdout, "</blockquote>\n");
}                              

/*****************************************************************************/
/*
Provide each attachment (part with a name or file-name) as a checkbox and
associated name and content-type below the message body text area.  This is
used to selectively [forward] any attachments with a message.
*/

void ComposeForwardAttachList (REQUEST_DATA *rdptr)

{
   BOOL  OriginalForward;
   int  cnt, FwdCount;
   char  *cptr, *nptr, *tptr;
   char  buf [64];
   MIME_DATA  *mdptr;
   VMS_MAIL_MSG  *vmptr;
   VMS_MAIL_USER  *muptr;

   /*********/
   /* begin */
   /*********/

   if (Debug) fprintf (stdout, "ComposeForwardAttachList()\n");

   muptr = &VmsMailUser;
   vmptr = &muptr->VmsMailMsg;

   CGIVARNULL (cptr, "FORM_COMPOSE_FWD_COUNT");
   if (cptr)
   {
      /* this is the compose page at a subsequent request */
      FwdCount = atoi(cptr);
      if (FwdCount < 0 || FwdCount > 100) ErrorExit (SS$_BUGCHECK, FI_LI);
      /* if no attachments to forward */
      if (!FwdCount) return;
      OriginalForward = FALSE;
   }
   else
   {
      /* this is the compose page at the original [forward] request */
      FwdCount = 0;
      for (mdptr = vmptr->MimeDataPtr; mdptr; mdptr = mdptr->NextMimePtr)
         if (mdptr->NamePtr || mdptr->FileNamePtr) FwdCount++;
      /* if no attachments to forward */
      if (!FwdCount) return;
      mdptr = vmptr->MimeDataPtr;
      OriginalForward = TRUE;
   }

   fprintf (stdout,
"\n<!-- forwarded attachments begin -->\n\
<tr><td></td><td style=\"padding-left:1.5em;padding-bottom:0.8em;\">\n\
<input type=\"hidden\" name=\"compose_fwd_count\" \
id=\"compose_fwd_count\" value=\"%d\">\n",
            FwdCount);

   for (cnt = 1; cnt <= FwdCount; cnt++)
   {
      if (OriginalForward)
      {
         /* this is the compose page at the original [forward] request */
         while (mdptr && !(mdptr->NamePtr || mdptr->FileNamePtr))
         {
            mdptr = mdptr->NextMimePtr;
            continue;
         }
         if (!mdptr) break;

         cptr = " checked";
         if (mdptr->NamePtr)
            nptr = HTML_ESCAPE(mdptr->NamePtr);
         else
            nptr = HTML_ESCAPE(mdptr->FileNamePtr);
         tptr = HTML_ESCAPE(mdptr->ContentTypePtr);

         mdptr = mdptr->NextMimePtr;
      }
      else
      {
         /* this is the compose page at a subsequent request */
         sprintf (buf, "FORM_COMPOSE_FWD_%d", cnt);
         CGIVARNULL (cptr, buf);
         if (cptr) cptr = " checked"; else cptr = "";

         sprintf (buf, "FORM_COMPOSE_FWD_NAME_%d", cnt);
         CGIVARNULL (nptr, buf);
         if (!nptr) ErrorExit (SS$_BUGCHECK, FI_LI);

         sprintf (buf, "FORM_COMPOSE_FWD_TYPE_%d", cnt);
         CGIVARNULL (tptr, buf);
         if (!tptr) ErrorExit (SS$_BUGCHECK, FI_LI);

         nptr = HTML_ESCAPE(nptr);
         tptr = HTML_ESCAPE(tptr);
      }

      fprintf (stdout,
"%s<input type=\"checkbox\" style=\"border:none;\" \
name=\"compose_fwd_%d\" value=\"1\"%s>\
&nbsp;%s&nbsp;&nbsp;<I>(%s)</I>\n\
<input type=\"hidden\" name=\"compose_fwd_name_%d\" value=\"%s\">\n\
<input type=\"hidden\" name=\"compose_fwd_type_%d\" value=\"%s\">\n",
               cnt == 1 ? "" : "<br>",
               cnt, cptr,
               nptr, tptr,
               cnt, nptr,
               cnt, tptr);
   }

   fprintf (stdout, "</td></tr>\n\
<!-- forwarded attachments end -->\n\n");
}                              

/*****************************************************************************/
/*
Ensure the text of a message is reasonably formatted.
This includes wrapping lines at 78..132 characters if not quoted.
The 'escape sequence' of a backslash ('\') plus the (user-option) quote
character suppresses wrapping of that line (the 'escape sequence' is removed).
*/

char* ComposeBodyTextMassage
(
char *TextPtr,
int MaxLineLen,
char *QuoteString
)
{
   int  cnt, lcnt;
   char  *cptr, *c2ptr, *sptr, *s2ptr, *wsptr, *zptr,
         *RevisedTextPtr;

   /*********/
   /* begin */
   /*********/

   if (Debug) fprintf (stdout, "ComposeBodyTextMassage() |%s|\n", QuoteString);

   if (MaxLineLen <= 0) MaxLineLen = atoi(DEFAULT_WRAP_AT);

   /* first, a quick scan to see if any changes are necessary */
   lcnt = 0;
   cptr = TextPtr;
   while (*cptr)
   {
      zptr = cptr + MaxLineLen;
      if (*QuoteString == *cptr)
         while (*cptr && *cptr != '\r' && *cptr != '\n') cptr++;
      else
      {
         for (; *cptr && *cptr != '\r' && *cptr != '\n'; cptr++)
         {
            if (cptr < zptr) continue;
            zptr = cptr + MaxLineLen;
            lcnt++;
         }
      }
      while (*cptr == '\r' || *cptr == '\n') cptr++;
   }

   /* original length plus two CRLF for each additional line */
   cnt = cptr - TextPtr + lcnt + lcnt + 1;
   sptr = RevisedTextPtr = CgiLibVeeMemCalloc (cnt * 10);
   if (!sptr) ErrorExit (vaxc$errno, FI_LI); 

   cptr = TextPtr;
   while (*cptr)
   {
      zptr = sptr + MaxLineLen;
      if (*cptr == '\\' && *(cptr+1) == *QuoteString)
      {
         /* remove the 'escape sequence' and do not wrap the line */
         cptr += 2;
         /* replace consecutive quote characters by spaces */
         while (*cptr && *cptr == *QuoteString)
         {
            *sptr++ = ' ';
            cptr++;
         }
         while (*cptr && *cptr != '\r' && *cptr != '\n') *sptr++ = *cptr++;
      }
      else
      if (*cptr == *QuoteString)
      {
         /* 'quoted' - do not wrap the line */
         while (*cptr && *cptr != '\r' && *cptr != '\n') *sptr++ = *cptr++;
      }
      else
      {
         /* to the end of the current line */
         while (*cptr && *cptr != '\r' && *cptr != '\n')
         {
            /* note the current buffered char */
            s2ptr = sptr;
            if (*cptr == ' ' || *cptr == '\t')
            {
               /* white space - look ahead */
               c2ptr = cptr;
               while ((*c2ptr == ' ' || *c2ptr == '\t') && s2ptr < zptr)
               {
                  c2ptr++;
                  s2ptr++;
               }
               /* then further ahead at not white space */
               while (*c2ptr && *c2ptr != ' ' && *c2ptr != '\t' &&
                      *c2ptr != '\r' && *c2ptr != '\n' && s2ptr < zptr)
               {
                  c2ptr++;
                  s2ptr++;
               }
               if (s2ptr >= zptr)
               {
                  /* over line length, add a CRLF */
                  *sptr++ = '\r';
                  *sptr++ = '\n';
                  zptr = sptr + MaxLineLen;
                  /* and absorb remaining white-space */
                  while (*cptr == ' ' || *cptr == '\t') cptr++;
                  continue;
               }
            }
            *sptr++ = *cptr++;
         }
      }
      while (*cptr == '\r' || *cptr == '\n') *sptr++ = *cptr++;
   }
   *sptr = '\0';

   return (RevisedTextPtr);
}                              

/*****************************************************************************/
/*
Create a 'self' address for use as the [SELF] contact and as the SMTP/RFC
"From:" header field.
*/

char* ComposeSelfAddress (REQUEST_DATA *rdptr)

{
   int  len;
   char  *cptr, *fhptr, *pnptr, *sptr, *zptr;
   char  LocalPart [128];
   COMPOSE_DATA  *cdptr;
   USER_OPTIONS  *uoptr;
   VMS_MAIL_USER  *muptr;

   /*********/
   /* begin */
   /*********/

   if (Debug) fprintf (stdout, "ComposeSelfAddress()\n");

   muptr = &VmsMailUser;
   cdptr = &rdptr->ComposeData;
   uoptr = &rdptr->UserOptions;

   len = 16;  /* elbow room */
   pnptr = NULL;
   if (uoptr->PersonalName[0])
      len += strlen(pnptr = uoptr->PersonalName);
   else
   if (muptr->VmsMailPersonalName[0])
      len += strlen(pnptr = muptr->VmsMailPersonalName);

   fhptr = NULL;

   /* user option can just be a host/domain name for user@host/domain */
   if (uoptr->SmtpFrom) fhptr = uoptr->SmtpFrom;

   if (!fhptr || !*fhptr) fhptr = SoyMailConfig.SmtpFromHost;

   /* if not then try for MX logical names */
   if (!fhptr) fhptr = TrnLnm ("MX_FROM_HOST", NULL, 0);
   if (!fhptr) fhptr = TrnLnm ("MX_ENVELOPE_FROM_HOST", NULL, 0);
   if (!fhptr) fhptr = TrnLnm ("MX_NODE_NAME", NULL, 0);

   /* fall back to the IP host name */
   if (!fhptr) fhptr = LocalHostName;
   if (fhptr) len += strlen(fhptr);

   if (AddressIsRfc(fhptr))
   {
      /* full 'whomever@wherever' specified (usually in the user option) */
      sptr =  CgiLibVeeMemCalloc (len);
      if (!sptr) ErrorExit (vaxc$errno, FI_LI);
      if (pnptr)
         sprintf (sptr, "%s <%s>", pnptr, fhptr);
      else
         strcpy (sptr, fhptr);
   }
   else
   {
      zptr = (sptr = LocalPart) + sizeof(LocalPart)-1;
      if (SoyMailConfig.LoginAliasSmtpFrom && rdptr->LoginAliasLength)
      {
         /* use the login alias as the local part */
         for (cptr = rdptr->LoginAlias;
              *cptr && sptr < zptr;
              *sptr++ = *cptr++);
      }
      else
      {
         /* just the host/domain name */
         for (cptr = rdptr->UserName;
              *cptr && sptr < zptr;
              *sptr++ = tolower(*cptr++));
      }
      *sptr = '\0';
      len += sptr - LocalPart;
      sptr =  CgiLibVeeMemCalloc (len);
      if (!sptr) ErrorExit (vaxc$errno, FI_LI);
      if (pnptr)
         sprintf (sptr, "%s <%s@%s>",
                  pnptr, LocalPart, fhptr);
      else
         sprintf (sptr, "%s@%s", LocalPart, fhptr);
   }

   cdptr->SelfPtr = sptr;

   if (WatchEnabled) WatchThis (FI_LI, "SELF !AZ", cdptr->SelfPtr); 

   return (sptr);
}                              

/*****************************************************************************/
/*
This monolithic and over-long function creates a MIME compliant RFC822 message.

If there are forwarded attachments and/or soyMAIL attachment files that have
been checked for inclusion a "multipart/mixed" MIME message is generated. 
Each attachment with a "text/.." content-type is quoted-printable encoded in a
separate part.  Attachments with non-text content are provided as base-64
encoded parts.  If there is a message body this is appropriately encoded and
supplied as the first part with a content-type of "text/plain".

If there are no forwarded attachments or files present or checked the message
body is appropriately encoded and made into an in-line MIME message.
*/

BOOL ComposeMime (REQUEST_DATA *rdptr)

{
#define STRINIT(ptr,size)  zptr = (sptr = (char*)ptr) + size;
#define STRCAT(str)        for (cptr=str; *cptr && sptr<zptr; *sptr++=*cptr++);
#define STRNULL            if (sptr<zptr) *sptr = '\0';
#define STROVF             sptr >= zptr

static char  ThisMessageIsInMimeFormat [] =
"This message is in MIME format.  If your mail reader does not understand\r\n\
this format, some or all of the message may not be legible.\r\n";

   int  cnt, len, status,
        AttachFileCount,
        FwdCount,
        DataLength,
        FileSizeBytes;
   char  *cptr, *fnptr, *sptr, *zptr,
         *CgiHttpXForwardedForPtr,
         *CgiRemoteAddrPtr,
         *CgiRemoteHostPtr,
         *CgiRemotePortPtr,
         *CgiServerNamePtr,
         *CharSetPtr,
         *ContentTypePtr,
         *DataPtr,
         *HtmlTextPtr,
         *MessageTextPtr,
         *MimeEncodedPtr,
         *NamePtr,
         *PlainTextPtr,
         *QuotedPrintableTextPtr,
         *TransferEncodingPtr;
   char  AttId [64],
         HeaderBuffer [16384],
         MimeBoundary [64],
         ScratchBuffer [512];
   unsigned long  BinTime [2];
   stat_t  FstatBuffer;
   FILE  *fp;
   COMPOSE_DATA  *cdptr;
   MIME_DATA  *mdptr;
   REQUEST_DATA  *sdptr;
   USER_OPTIONS  *uoptr;
   TEXT_DATA  *tl1ptr, *tl2ptr, *tl3ptr;
   VMS_MAIL_MSG  *vmptr;
   VMS_MAIL_USER  *muptr;

   /*********/
   /* begin */
   /*********/

   if (Debug) fprintf (stdout, "ComposeMime()\n");

   muptr = &VmsMailUser;
   vmptr = &muptr->VmsMailMsg;
   cdptr = &rdptr->ComposeData;
   uoptr = &rdptr->UserOptions;
   sdptr = (REQUEST_DATA*)rdptr->StateDataPtr;

   if (!muptr->VmsMailFullDirectoryLength) ErrorExit (SS$_BUGCHECK, FI_LI);

   if (rdptr->MessageId)
   {
      vmptr->MessageId = rdptr->MessageId;
      vmptr->ConfirmHash = rdptr->MessageHash;
   }
   else
   {
      vmptr->MessageId = rdptr->MessageId = sdptr->MessageId;
      vmptr->ConfirmHash = rdptr->MessageHash = sdptr->MessageHash;
   }

   sys$gettim (&BinTime);
   MimeBoundary[0] = '\0';
   CharSetPtr = ComposeSetCharSet (rdptr);
   /* the right-to-left flag of a compose-page character set */
   if (CharSetPtr[0] == '<') CharSetPtr++;
   if (Debug) fprintf (stdout, "charset: %s\n", CharSetPtr);

   /****************/
   /* message text */
   /****************/

   if (cdptr->EditorType == COMPOSE_EDITOR_HTML)
      MessageTextPtr = cdptr->TextPtr;
   else
   if (cdptr->WrapAt)
      MessageTextPtr = ComposeBodyTextMassage (cdptr->TextPtr,
                                               cdptr->WrapAt,
                                               uoptr->QuoteReply);
   else
      MessageTextPtr = cdptr->TextPtr;

   HtmlTextPtr = MimeEncodedPtr =
      PlainTextPtr = QuotedPrintableTextPtr = NULL;
   if (cdptr->EditorType == COMPOSE_EDITOR_HTML)
   {
      /* the HTML text body requires 'quoted-printable'ing */
      MimeEncodedPtr = MimeEncQuotedPrintable (MessageTextPtr, -1);
      QuotedPrintableTextPtr = MimeEncodedPtr;
      HtmlTextPtr = MessageTextPtr;
   }
   else
   if (MimeEncNeedsQuotedPrintable (MessageTextPtr))
   {
      /* the plain text body requires 'quoted-printable'ing */
      MimeEncodedPtr = MimeEncQuotedPrintable (MessageTextPtr, -1);
      QuotedPrintableTextPtr = MimeEncodedPtr;
   }
   else
      PlainTextPtr = MimeEncodedPtr = MessageTextPtr;

   tl1ptr = tl2ptr = NULL;
   AttachFileCount = 0;

   /*************************/
   /* check for attachments */
   /*************************/

   /* for forwarded attachments */
   CGIVARNULL (cptr, "FORM_COMPOSE_FWD_COUNT");
   if (cptr)
      FwdCount = atoi(cptr);
   else
      FwdCount = 0;
   if (FwdCount < 0 || FwdCount > 100)
   {
      StatusMessage (FI_LI, 1, "%s", SysGetMsg(SS$_BUGCHECK));
      return (FALSE);
   }

   if (FwdCount)
   {
      /* check if any of the attachments are checked */
      for (cnt = 1; cnt <= FwdCount; cnt++)
      {
         sprintf (ScratchBuffer, "FORM_COMPOSE_FWD_%d", cnt);
         CGIVARNULL (cptr, ScratchBuffer);
         if (cptr) break;
      }
      if (!cptr) FwdCount = 0;
   }

   /* for file-system attachments */
   fnptr = FileSpecSearch (FALSE, muptr->VmsMailFullDirectory, "SOYMAIL_*.ATT");

   /* if either */
   if (FwdCount || fnptr)
   {
      /*******************************/
      /* prepare for multipart/mixed */
      /*******************************/

      /*
         Quite possibly, BUT NOT CERTAINLY, be multipart/mixed!!
         Won't know until we actually process the potential attachment files.
         In the meantime, prepare this first part (the message text) portion
         of a multi-part message.  It will just get overloaded later if no
         attachment files end up getting included (tracked by incrementing
         the counter 'AttachmentCount').
      */
      sprintf (MimeBoundary, "----_=_%08.08X%08.08X",
               BinTime[1], BinTime[0]);

      if (HtmlTextPtr && HtmlTextPtr[0])
      {
         ContentTypePtr = "text/html";
         MimeEncodedPtr = QuotedPrintableTextPtr;
         TransferEncodingPtr = "quoted-printable";
      }
      else
      if (PlainTextPtr && PlainTextPtr[0])
      {
         ContentTypePtr = "text/plain";
         MimeEncodedPtr = PlainTextPtr;
         TransferEncodingPtr = "7bit";
      }
      else
      if (QuotedPrintableTextPtr && QuotedPrintableTextPtr[0])
      {
         ContentTypePtr = "text/plain";
         MimeEncodedPtr = QuotedPrintableTextPtr;
         TransferEncodingPtr = "quoted-printable";
      }
      else
         ContentTypePtr = MimeEncodedPtr = TransferEncodingPtr = NULL;

      /************************************/
      /* "this message is in MIME format" */
      /************************************/

      /* use the 'tl2ptr' expressly so that it can be used to link later */
      len = strlen(ThisMessageIsInMimeFormat);
      TEXT_DATA_CALLOC (tl2ptr, len);
      if (!tl2ptr) ErrorExit (vaxc$errno, FI_LI);

      TEXT_DATA_TEXT(tl2ptr) = TEXT_DATA_SELF(tl2ptr);
      strcpy (TEXT_DATA_TEXT(tl2ptr), ThisMessageIsInMimeFormat);
      TEXT_DATA_LENGTH(tl2ptr) = len;

      /* this IS the first part of the MIME body */
      TEXT_DATA_NEXT (&cdptr->MimeBody) = tl2ptr;

      if (MimeEncodedPtr)
      {
         /****************/
         /* message text */
         /****************/

         if (WatchEnabled)
         {
            WatchThis (FI_LI, "ATTACH (message text)", fnptr); 
            WatchThis (FI_LI, "MIME TYPE !AZ", ContentTypePtr); 
            WatchThis (FI_LI, "MIME TRANSFER !AZ", TransferEncodingPtr); 
         }

         /* first will point to the MIME part header */
         TEXT_DATA_CALLOC (tl1ptr, 2048);
         if (!tl1ptr) ErrorExit (vaxc$errno, FI_LI);

         /* link the "this message..." to this new part */
         if (tl2ptr) tl2ptr->NextPtr = tl1ptr;

         /* second will point to the MIME part encoded content */
         TEXT_DATA_CALLOC (tl2ptr, 0);
         if (!tl2ptr) ErrorExit (vaxc$errno, FI_LI);

         TEXT_DATA_TEXT(tl1ptr) = TEXT_DATA_SELF(tl1ptr);
         STRINIT (TEXT_DATA_TEXT(tl1ptr), TEXT_DATA_SIZE(tl1ptr)) 

         STRCAT ("\r\n--")
         STRCAT (MimeBoundary)
         STRCAT ("\r\nContent-Type: ")
         STRCAT (ContentTypePtr)
         STRCAT ("; charset=\"")
         STRCAT (CharSetPtr)
         STRCAT ("\"\r\nContent-Transfer-Encoding: ")
         STRCAT (TransferEncodingPtr)
         STRCAT ("\r\n\r\n")
         STRNULL
         TEXT_DATA_LENGTH (tl1ptr) = sptr - TEXT_DATA_TEXT (tl1ptr);
         if (STROVF) ErrorExit (SS$_BUGCHECK, FI_LI);

         TEXT_DATA_TEXT (tl2ptr) = MimeEncodedPtr;
         TEXT_DATA_LENGTH (tl2ptr) = strlen(TEXT_DATA_TEXT (tl2ptr));

         TEXT_DATA_NEXT (tl1ptr) = tl2ptr;

         if (Debug)
            fprintf (stdout, "|%s|\n|%s|\n",
                     TEXT_DATA_TEXT(tl1ptr), TEXT_DATA_TEXT(tl2ptr));
      }
   }

   /*************************/
   /* forwarded attachments */
   /*************************/

   if (FwdCount)
   {
      CallMailMessageGet (muptr, NULL);
      if (VMSnok (muptr->MailVmsStatus))
      {
         StatusMessage (FI_LI, 1, "MAIL: %s.", SysGetMsg(muptr->MailVmsStatus));
         return (FALSE);
      }
      MessageBodyProcess (vmptr);

      for (cnt = 1; cnt <= FwdCount; cnt++)
      {
         sprintf (ScratchBuffer, "FORM_COMPOSE_FWD_%d", cnt);
         CGIVARNULL (cptr, ScratchBuffer);
         if (!cptr) continue;

         /********************/
         /* checkbox checked */
         /********************/

         AttachFileCount++;

         sprintf (ScratchBuffer, "FORM_COMPOSE_FWD_NAME_%d", cnt);
         CGIVARNULL (cptr, ScratchBuffer);
         if (!cptr) ErrorExit (SS$_BUGCHECK, FI_LI);

         for (mdptr = vmptr->MimeDataPtr; mdptr; mdptr = mdptr->NextMimePtr)
         {
            if (!(mdptr->NamePtr || mdptr->FileNamePtr)) continue;
            if (mdptr->NamePtr)
               NamePtr = mdptr->NamePtr;
            else
               NamePtr = mdptr->FileNamePtr;
            if (!strcmp (NamePtr, cptr)) break;
         }
         if (!mdptr)
         {
            StatusMessage (FI_LI, 1, "%s", SysGetMsg(SS$_BUGCHECK));
            return (FALSE);
         }

         ContentTypePtr = mdptr->ContentTypePtr;
         TransferEncodingPtr = mdptr->TransferEncodingPtr;

         if (!NamePtr || !ContentTypePtr || !TransferEncodingPtr)
         {
            StatusMessage (FI_LI, 1, "%s", SysGetMsg(SS$_BUGCHECK));
            return (FALSE);
         }

         if (WatchEnabled)
         {
            WatchThis (FI_LI, "MIME NAME !AZ", NamePtr); 
            WatchThis (FI_LI, "MIME TYPE !AZ", ContentTypePtr); 
            WatchThis (FI_LI, "MIME TRANSFER !AZ", TransferEncodingPtr); 
         }

         /* first will point to the MIME part header */
         TEXT_DATA_CALLOC (tl1ptr, 512);
         if (!tl1ptr) ErrorExit (vaxc$errno, FI_LI);

         /* if there was a previous part then link it to this new part */
         if (tl2ptr) tl2ptr->NextPtr = tl1ptr;

         /* second will point to the MIME part encoded content */
         TEXT_DATA_CALLOC (tl2ptr, 0);
         if (!tl2ptr) ErrorExit (vaxc$errno, FI_LI);

         TEXT_DATA_TEXT(tl1ptr) = TEXT_DATA_SELF(tl1ptr);
         STRINIT (TEXT_DATA_TEXT(tl1ptr), TEXT_DATA_SIZE(tl1ptr)) 
         STRCAT ("\r\n--")
         STRCAT (MimeBoundary)
         STRCAT ("\r\nContent-Type: ")
         STRCAT (ContentTypePtr)
         STRCAT (";\r\n  name=\"")
         STRCAT (NamePtr)
         STRCAT ("\"\r\nContent-Transfer-Encoding: ")
         STRCAT (TransferEncodingPtr)
         STRCAT ("\r\nContent-Disposition: attachment;\r\n  filename=\"")
         STRCAT (NamePtr)
         STRCAT ("\"\r\n\r\n")
         STRNULL
         TEXT_DATA_LENGTH (tl1ptr) = sptr - TEXT_DATA_TEXT (tl1ptr);
         if (STROVF) ErrorExit (SS$_BUGCHECK, FI_LI);

         /* terminate the MIME content just for these purposes */
         mdptr->PartContentPtr[mdptr->PartContentLength] = '\0';

         TEXT_DATA_TEXT (tl2ptr) = mdptr->PartContentPtr;
         TEXT_DATA_LENGTH (tl2ptr) = mdptr->PartContentLength;

         /* if this is the first part of the MIME body */
         if (!TEXT_DATA_NEXT (&cdptr->MimeBody))
            TEXT_DATA_NEXT (&cdptr->MimeBody) = tl1ptr;

         TEXT_DATA_NEXT (tl1ptr) = tl2ptr;

         if (Debug)
            fprintf (stdout, "%d\n|%s|\n|%s|\n",
                     AttachFileCount, TEXT_DATA_TEXT(tl1ptr),
                     TEXT_DATA_TEXT(tl2ptr));
      }
   }

   /********************/
   /* attachment files */
   /********************/

   while (fnptr)
   {
      /* get the digits from the file name */
      for (cptr = fnptr; *cptr; cptr++);
      while (cptr > fnptr && *cptr != ']') cptr--;
      if (*cptr == ']') cptr++;
      while (*cptr && !isdigit(*cptr)) cptr++;
      zptr = (sptr = AttId) + sizeof(AttId);
      while (isdigit(*cptr) && sptr < zptr) *sptr++ = *cptr++;
      if (sptr > zptr) ErrorExit (SS$_BUGCHECK, FI_LI);
      *sptr = '\0';

      if (WatchEnabled) WatchThis (FI_LI, "ATTACH !AZ", fnptr); 

      /* check if the attachment checkbox is currently checked */
      sprintf (ScratchBuffer, "FORM_PART_CBX_%s", AttId);
      CGIVARNULL (sptr, ScratchBuffer);
      /* if not checked just continue */
      if (!sptr)
      {
         fnptr = FileSpecSearch (FALSE, NULL, NULL);
         continue;
      }

      /********************/
      /* checkbox checked */
      /********************/

      AttachFileCount++;

      fp = fopen (fnptr, "r", "ctx=bin");
      if (!fp)
      {
         status = vaxc$errno;
         StatusMessage (FI_LI, 1, "%s: %s.", AttId, SysGetMsg(status));
         return (FALSE);
      }

      if (fstat (fileno(fp), &FstatBuffer))
      {
         status = vaxc$errno;
         StatusMessage (FI_LI, 1, "%s: %s.", AttId, SysGetMsg(status));
         return (FALSE);
      }
      FileSizeBytes = FstatBuffer.st_size;

      DataPtr =  CgiLibVeeMemCalloc (FileSizeBytes);
      if (!DataPtr)
      {
         status = vaxc$errno;
         StatusMessage (FI_LI, 1, "%s: %s.", AttId, SysGetMsg(status));
         return (FALSE);
      }

      DataLength = read (fileno(fp), DataPtr, FileSizeBytes);
      if (DataLength <= 0)
      {
         status = vaxc$errno;
         StatusMessage (FI_LI, 1, "%s: %s.", AttId, SysGetMsg(status));
         return (FALSE);
      }
      fclose (fp);

      NamePtr = ContentTypePtr = NULL;

      if (!strncmp (DataPtr, "[soyMAIL-version]", 17))
      {
         sptr = DataPtr + 17;
         while (*sptr) sptr++;
         sptr++;
         if (!strncmp (sptr, "[name]", 6))
         {
            NamePtr = sptr += 6;
            while (*sptr) sptr++;
            sptr++;
            if (!strncmp (sptr, "[type]", 6))
            {
               ContentTypePtr = sptr += 6;
               while (*sptr) sptr++;
               sptr++;
               if (!strncmp (sptr, "[content]", 9)) sptr += 9;
               FileSizeBytes -= sptr - DataPtr;
            }
         }
      }

      if (!NamePtr || !ContentTypePtr)
      {
         StatusMessage (FI_LI, 1, "%s: %s.", AttId, SysGetMsg(SS$_BUGCHECK));
         return (FALSE);
      }

      if (!strncmp (cptr, "text/", 5))
      {
         if (WatchEnabled)
            WatchThis (FI_LI, "MIME QUOTED-PRINTABLE !UL bytes", FileSizeBytes); 
         MimeEncodedPtr = MimeEncQuotedPrintable (sptr, FileSizeBytes);
         if (!MimeEncodedPtr)
         {
            StatusMessage (FI_LI, 1, "%s: %s.", AttId, SysGetMsg(SS$_BUGCHECK));
            return (FALSE);
         }
         TransferEncodingPtr = "quoted-printable";
      }
      else
      {
         if (WatchEnabled) WatchThis (FI_LI, "MIME BASE64 !UL bytes", FileSizeBytes); 
         MimeEncodedPtr = MimeEncBase64 (sptr, FileSizeBytes, "\r\n");
         if (!MimeEncodedPtr)
         {
            StatusMessage (FI_LI, 1, "%s: %s.", AttId, SysGetMsg(SS$_BUGCHECK));
            return (FALSE);
         }
         TransferEncodingPtr = "base64";
      }

      if (WatchEnabled)
      {
         WatchThis (FI_LI, "MIME NAME !AZ", NamePtr); 
         WatchThis (FI_LI, "MIME TYPE !AZ", ContentTypePtr); 
         WatchThis (FI_LI, "MIME TRANSFER !AZ", TransferEncodingPtr); 
      }

      /* first will point to the MIME part header */
      TEXT_DATA_CALLOC (tl1ptr, 512);
      if (!tl1ptr) ErrorExit (vaxc$errno, FI_LI);

      /* if there was a previous part then link it to this new part */
      if (tl2ptr) tl2ptr->NextPtr = tl1ptr;

      /* second will point to the MIME part encoded content */
      TEXT_DATA_CALLOC (tl2ptr, 0);
      if (!tl2ptr) ErrorExit (vaxc$errno, FI_LI);

      TEXT_DATA_TEXT(tl1ptr) = TEXT_DATA_SELF(tl1ptr);
      STRINIT (TEXT_DATA_TEXT(tl1ptr), TEXT_DATA_SIZE(tl1ptr)) 
      STRCAT ("\r\n--")
      STRCAT (MimeBoundary)
      STRCAT ("\r\nContent-Type: ")
      STRCAT (ContentTypePtr)
      STRCAT (";\r\n  name=\"")
      STRCAT (NamePtr)
      STRCAT ("\"\r\nContent-Transfer-Encoding: ")
      STRCAT (TransferEncodingPtr)
      STRCAT ("\r\nContent-Disposition: attachment;\r\n  filename=\"")
      STRCAT (NamePtr)
      STRCAT ("\"\r\n\r\n")
      STRNULL
      TEXT_DATA_LENGTH (tl1ptr) = sptr - TEXT_DATA_TEXT (tl1ptr);
      if (STROVF) ErrorExit (SS$_BUGCHECK, FI_LI);

      TEXT_DATA_TEXT (tl2ptr) = MimeEncodedPtr;
      TEXT_DATA_LENGTH (tl2ptr) = strlen(TEXT_DATA_TEXT (tl2ptr));

      /* if this is the first part of the MIME body */
      if (!TEXT_DATA_NEXT (&cdptr->MimeBody))
         TEXT_DATA_NEXT (&cdptr->MimeBody) = tl1ptr;

      TEXT_DATA_NEXT (tl1ptr) = tl2ptr;

      if (Debug)
         fprintf (stdout, "%d\n|%s|\n|%s|\n",
                  AttachFileCount, TEXT_DATA_TEXT(tl1ptr),
                  TEXT_DATA_TEXT(tl2ptr));

      /* free the attachment file content */
      CgiLibVeeMemFree (DataPtr);

      fnptr = FileSpecSearch (FALSE, NULL, NULL);
   }

   /******************************/
   /* end of (any) attachment(s) */
   /******************************/

   if (AttachFileCount)
   {
      /* INCLUDED ATTACHMENT(S), add the final trailing MIME boundary */
      TEXT_DATA_CALLOC (tl3ptr, 128);
      if (!tl3ptr) ErrorExit (vaxc$errno, FI_LI);
      TEXT_DATA_TEXT(tl3ptr) = TEXT_DATA_SELF(tl3ptr);
      STRINIT (TEXT_DATA_SELF(tl3ptr), TEXT_DATA_SIZE(tl3ptr)) 
      STRCAT ("\r\n--")
      STRCAT (MimeBoundary)
      STRCAT ("--\r\n")
      STRNULL
      TEXT_DATA_LENGTH (tl3ptr) = sptr - TEXT_DATA_TEXT (tl3ptr);
      if (STROVF) ErrorExit (SS$_BUGCHECK, FI_LI);
      TEXT_DATA_NEXT (tl2ptr) = tl3ptr;
   }
   else
   {
      /* NO ATTACHMENTS, make the text body in-line */
      TEXT_DATA_CALLOC (tl1ptr, 0);
      if (!tl1ptr) ErrorExit (vaxc$errno, FI_LI);
      TEXT_DATA_TEXT (tl1ptr) = MimeEncodedPtr;
      TEXT_DATA_LENGTH (tl1ptr) = strlen(TEXT_DATA_TEXT (tl1ptr));
      /* if this is the only part of the MIME body */
      TEXT_DATA_NEXT (&cdptr->MimeBody) = tl1ptr;
   }

   /***********************************/
   /* create an RFC822 message header */
   /***********************************/

   zptr = (sptr = HeaderBuffer) + sizeof(HeaderBuffer);

   CGIVARNULL (CgiServerNamePtr, "SERVER_NAME");
   if (!CgiServerNamePtr) CgiServerNamePtr = "?";

   CGIVARNULL (CgiRemoteHostPtr, "REMOTE_HOST");
   if (!CgiRemoteHostPtr) CgiRemoteHostPtr = "?";

   CGIVARNULL (CgiRemoteAddrPtr, "REMOTE_ADDR");
   if (!CgiRemoteAddrPtr) CgiRemoteAddrPtr = "?.?.?.?";

   CGIVARNULL (CgiRemotePortPtr, "REMOTE_PORT");
   if (!CgiRemotePortPtr) CgiRemotePortPtr = "?";

   CGIVARNULL (CgiHttpXForwardedForPtr, "HTTP_X_FORWARDED_FOR");

   zptr = (sptr = ScratchBuffer) + sizeof(ScratchBuffer);

   STRCAT ("from SOYMAIL (")
   if (strcmp (CgiRemoteHostPtr, CgiRemoteAddrPtr))
   {
      /* resolved host name in REMOTE_HOST */
      STRCAT (CgiRemoteHostPtr)
      STRCAT (" [")
      STRCAT (CgiRemoteAddrPtr)
      STRCAT ("]")
   }
   else
   {
      /* try to resolve host name from address */
      cptr = MtaHostNameByAddr (CgiRemoteAddrPtr);
      if (cptr)
      {
         STRCAT (cptr)
         STRCAT (" ")
      }

      STRCAT ("[")
      STRCAT (CgiRemoteAddrPtr)
      STRCAT ("]")
   }

   if (CgiHttpXForwardedForPtr)
   {
      STRCAT (" forwarded for ")
      STRCAT (CgiHttpXForwardedForPtr)
   }

   STRCAT (") by ")
   STRCAT (LocalHostName)
   STRCAT (" (")
   STRCAT (SoftwareId)
   STRCAT (") with HTTP; ")
   STRCAT (MtaRfcBinDate(NULL,NULL))
   STRNULL

   if (STROVF)
   {
      StatusMessage (FI_LI, 1, "RFC822: %s.", SysGetMsg(SS$_RESULTOVF));
      return (FALSE);
   }

   zptr = (sptr = HeaderBuffer) + sizeof(HeaderBuffer);

   len = InetMailFoldRfc822Header (sptr, zptr-sptr,
                                   "Received:", ScratchBuffer); 
   if (len < 0)
   {
      StatusMessage (FI_LI, 1, "RFC822: %s.", SysGetMsg(SS$_RESULTOVF));
      return (FALSE);
   }
   sptr += len;

   sptr += sprintf (sptr,
"Message-ID: <%s.%s.%08.08x%08.08x.soymail@%s>\r\nDate: %s\r\n",
                    CgiRemoteAddrPtr, CgiRemotePortPtr,
                    BinTime[1], BinTime[0], LocalHostName,
                    ComposeRfcDate());

   if (cdptr->InReplyToPtr)
   {
      len = InetMailFoldRfc822Header (sptr, zptr-sptr,
                                      "In-Reply-To:", cdptr->InReplyToPtr); 
      if (len < 0)
      {
         StatusMessage (FI_LI, 1, "RFC822: %s.", SysGetMsg(SS$_RESULTOVF));
         return (FALSE);
      }
      sptr += len;
   }

   if (cdptr->ReferencesPtr)
   {
      len = InetMailFoldRfc822Header (sptr, zptr-sptr,
                                      "References:", cdptr->ReferencesPtr); 
      if (len < 0)
      {
         StatusMessage (FI_LI, 1, "RFC822: %s.", SysGetMsg(SS$_RESULTOVF));
         return (FALSE);
      }
      sptr += len;
   }

   len = InetMailFoldRfc822Header (sptr, zptr-sptr, "Subject:",
                                   MimeEncIsoString(cdptr->SubjPtr,
                                                    CharSetPtr)); 
   if (len < 0)
   {
      StatusMessage (FI_LI, 1, "RFC822: %s.", SysGetMsg(SS$_RESULTOVF));
      return (FALSE);
   }
   if (len)
      sptr += len;
   else
      STRCAT ("Subject: (none)\r\n")

   len = InetMailFoldRfc822Header (sptr, zptr-sptr, "From:",
                           MimeEncIsoString(cdptr->FromPtr,CharSetPtr)); 
   if (len < 0)
   {
      StatusMessage (FI_LI, 1, "RFC822: %s.", SysGetMsg(SS$_RESULTOVF));
      return (FALSE);
   }
   sptr += len;

   if (cdptr->ReplyToPtr && cdptr->ReplyToPtr[0])
   {
      len = InetMailFoldRfc822Header (sptr, zptr-sptr, "Reply-to:",
                              MimeEncIsoString(cdptr->ReplyToPtr,CharSetPtr));
      if (len < 0)
      {
         StatusMessage (FI_LI, 1, "RFC822: %s.", SysGetMsg(SS$_RESULTOVF));
         return (FALSE);
      }
      sptr += len;
   }

   len = InetMailFoldRfc822Header (sptr, zptr-sptr, "To:",
                                   MimeEncIsoString(cdptr->ToPtr,
                                                    CharSetPtr));
   if (len < 0)
   {
      StatusMessage (FI_LI, 1, "RFC822: %s.", SysGetMsg(SS$_RESULTOVF));
      return (FALSE);
   }
   sptr += len;

   len = InetMailFoldRfc822Header (sptr, zptr-sptr, "CC:",
                                   MimeEncIsoString(cdptr->CcPtr,
                                                    CharSetPtr)); 
   if (len < 0)
   {
      StatusMessage (FI_LI, 1, "RFC822: %s.", SysGetMsg(SS$_RESULTOVF));
      return (FALSE);
   }
   sptr += len;

   if (cdptr->BccPtr && cdptr->BccPtr[0]) STRCAT ("BCC:\r\n")

   STRCAT ("MIME-Version: 1.0\r\n")

   if (AttachFileCount)
   {
      /* INCLUDED ATTACHMENT(S) so ended up being multipart/mixed */
      STRCAT ("Content-Type: multipart/mixed;\r\n  boundary=\"")
      STRCAT (MimeBoundary)
      STRCAT ("\"\r\n")
   }
   else
   if (HtmlTextPtr)
   {
      /* NO ATTACHMENT(S) so just inline content */
      STRCAT ("Content-Type: text/html; charset=")
      STRCAT (CharSetPtr)
      STRCAT ("\r\nContent-Transfer-Encoding: quoted-printable\r\n")
   }
   else
   if (QuotedPrintableTextPtr)
   {
      /* ditto */
      STRCAT ("Content-Type: text/plain; charset=")
      STRCAT (CharSetPtr)
      STRCAT ("\r\nContent-Transfer-Encoding: quoted-printable\r\n")
   }
   else
   {
      /* ditto again */
      STRCAT ("Content-Type: text/plain; charset=")
      STRCAT (CharSetPtr)
      STRCAT ("\r\nContent-Transfer-Encoding: 7bit\r\n")
   }

   STRCAT ("X-Mailer: ")
   STRCAT (SoftwareId)
   STRCAT ("\r\n\r\n")
   STRNULL

   if (STROVF)
   {
      StatusMessage (FI_LI, 1, "RFC822: %s.", SysGetMsg(SS$_RESULTOVF));
      return (FALSE);
   }

   len = sptr - HeaderBuffer;
   sptr =  CgiLibVeeMemCalloc (len);
   if (!sptr) ErrorExit (vaxc$errno, FI_LI);
   memcpy (sptr, HeaderBuffer, len+1);

   if (WatchEnabled) WatchThis (FI_LI, "RFC822 HEADER\n!AZ", HeaderBuffer);

   TEXT_DATA_TEXT (&cdptr->MimeHeader) = sptr;
   TEXT_DATA_SIZE (&cdptr->MimeHeader) = len+1;
   TEXT_DATA_LENGTH (&cdptr->MimeHeader) = len;

   return (TRUE);
}                              

/*****************************************************************************/
/*
Send the mail message contained in the 'compose data'.
*/

void ComposeSendMessage (REQUEST_DATA *rdptr)

{
   int  len, status;
   char  *cptr, *sptr, *zptr,
         *AddrListPtr,
         *SentItemsFileNamePtr,
         *SentItemsFromPtr,
         *SmtpServerPtr;
   char  UpCaseRemoteUser [256];
   COMPOSE_DATA  *cdptr;
   USER_OPTIONS  *uoptr;

   /*********/
   /* begin */
   /*********/

   if (Debug) fprintf (stdout, "ComposeSendMessage()\n");

   cdptr = &rdptr->ComposeData;
   uoptr = &rdptr->UserOptions;

   for (cptr = cdptr->ToPtr; *cptr && isspace(*cptr); cptr++);
   if (!*cptr)
   {
      StatusMessage (FI_LI, 1, "%s", LangFor("compose_no_recipient"));
      return;
   }

   /* ensure each of the addresses are comma-delimitted */
   cdptr->ToPtr = AddressListMassage (cdptr->ToPtr, TRUE);
   cdptr->CcPtr = AddressListMassage (cdptr->CcPtr, TRUE);
   cdptr->BccPtr = AddressListMassage (cdptr->BccPtr, TRUE);

   if (!cdptr->FromPtr) cdptr->FromPtr = cdptr->SelfPtr;

   /* if it looks like one or more RFC822 addresses */ 
   if (AddressIsRfc(cdptr->ToPtr) ||
       AddressIsRfc(cdptr->CcPtr) ||
       AddressIsRfc(cdptr->BccPtr) ||
       AddressIsRfc(cdptr->ReplyToPtr))
   {
      /************************/
      /* send via an SMTP MTA */
      /************************/

      if (AddressIsVms(cdptr->ToPtr) ||
          AddressIsVms(cdptr->CcPtr) ||
          AddressIsVms(cdptr->BccPtr) ||
          AddressIsVms(cdptr->ReplyToPtr))
      {
         StatusMessage (FI_LI, 1, "%s", LangFor("compose_either_or"));
         return;
      }

      ComposeMime (rdptr);

      /* create a single list of all the addresses for the MTA SMTP server */
      len = strlen(cdptr->ToPtr) +
            strlen(cdptr->CcPtr) +
            strlen(cdptr->BccPtr) + 8;
      AddrListPtr =  CgiLibVeeMemCalloc (len);
      if (!AddrListPtr) ErrorExit (vaxc$errno, FI_LI);
      sprintf (AddrListPtr, "%s%s%s%s%s",
               cdptr->ToPtr,
               cdptr->CcPtr[0] ? "\n" : "", cdptr->CcPtr,
               cdptr->BccPtr[0] ? "\n" : "", cdptr->BccPtr);

      SmtpServerPtr = SoyMailConfig.SmtpServerHost;
      if (!SmtpServerPtr) SmtpServerPtr = "localhost";

      cptr = MtaMsgSmtpToServer (SmtpServerPtr,
                                 SentItemsFromPtr = cdptr->FromPtr,
                                 AddrListPtr,
                                 NULL,
                                 &cdptr->MimeHeader,
                                 NULL,
                                 &cdptr->MimeBody);
      if (cptr)
         StatusMessage (FI_LI, 1, "SMTP: %s", HTML_ESCAPE(cptr));
      else
      if (uoptr->FolderSentItems[0])
      {
         /* message sent OK, place it into the "sent Items" folder */
         SentItemsFileNamePtr = SoyMailTempFileName();
         status = MtaMsgToFile (SentItemsFileNamePtr,
                                SentItemsFromPtr, cdptr->ToPtr,
                                cdptr->CcPtr, cdptr->SubjPtr,
                                &cdptr->MimeHeader,
                                &cdptr->MimeBody);
         if (VMSok (status))
            status = CallMailCopyFile (&VmsMailUser,
                                       "MAIL", uoptr->FolderSentItems,
                                       SentItemsFileNamePtr);
         if (VMSnok (status))
            StatusMessage (FI_LI, 1, "%s: %s.",
                           uoptr->FolderSentItems, SysGetMsg(status));

         while (!remove (SentItemsFileNamePtr));
      }
   }
   else
   {
      /****************************************/
      /* otherwise send via VMS callable mail */
      /****************************************/

      if (cdptr->BccPtr && cdptr->BccPtr[0])
      {
         StatusMessage (FI_LI, 1, "%s: %s", 
                        LangFor("compose_bcc_vms_address"),
                        cdptr->BccPtr);
         return;
      }
      else
      if (cdptr->ReplyToPtr && cdptr->ReplyToPtr[0])
      {
         StatusMessage (FI_LI, 1, "%s: %s",
                        LangFor("compose_reply_to_vms_address"),
                        cdptr->ReplyToPtr);
         return;
      }

      /* ensure that the body of the message is correctly wrapped */
      cdptr->TextPtr = ComposeBodyTextMassage (cdptr->TextPtr,
                                               cdptr->WrapAt,
                                               uoptr->QuoteReply);

      /* autogenous authentication login may mean this is in lower-case */
      zptr = (sptr = UpCaseRemoteUser) + sizeof(UpCaseRemoteUser)-1;
      for (cptr = rdptr->RemoteUser;
           *cptr && sptr < zptr;
           *sptr++ = toupper(*cptr++));
      *sptr = '\0';

      cptr = SendMailMessage (SentItemsFromPtr = UpCaseRemoteUser,
                              cdptr->ToPtr,
                              cdptr->CcPtr,
                              cdptr->SubjPtr,
                              cdptr->TextPtr);
      if (cptr)
         StatusMessage (FI_LI, 1, "MAIL: %s", cptr);
      else
      if (uoptr->FolderSentItems[0])
      {
         /* message sent OK, place it into the "sent Items" folder */
         SentItemsFileNamePtr = SoyMailTempFileName();
         status = SendMailToFile (SentItemsFileNamePtr,
                                  SentItemsFromPtr, cdptr->ToPtr,
                                  cdptr->CcPtr, cdptr->SubjPtr,
                                  cdptr->TextPtr);
         if (VMSok (status))
            status = CallMailCopyFile (&VmsMailUser,
                                       "MAIL", uoptr->FolderSentItems,
                                       SentItemsFileNamePtr);
         if (VMSnok (status))
            StatusMessage (FI_LI, 1, "%s: %s.",
                           uoptr->FolderSentItems, SysGetMsg(status));

         while (!remove (SentItemsFileNamePtr));
      }
   }

   if (!cptr)
   {
      /* if the 'delete all attachments after sending' checkbox is */
      CGIVARNULL (cptr, "FORM_ATTACH_CBX_DELETE");
      if (cptr) AttachDelete (rdptr, NULL);

      StatusMessage (FI_LI, -1, "%s", LangFor("compose_sent_ok"));
      cdptr->AlreadySent = TRUE;
   }
}                              

/*****************************************************************************/
/*
*/

void ComposeSendClose (REQUEST_DATA *rdptr)

{
   char  *cptr;

   /*********/
   /* begin */
   /*********/

   if (Debug) fprintf (stdout, "ComposeSendClose()\n");

   CGIVAR (cptr, "FORM_COMPOSE_SEND_CLOSE_CHILD");
   if (cptr && *cptr)
   {
      /* close the child window */
      CgiLibResponseHeader (200, "text/html",
                            "Script-Control: X-content-encoding-gzip=0\n%s",
                            rdptr->LoginSetCookiePtr);
      fprintf (stdout,
"<script language=\"JavaScript\">window.close()</script>\n");
   }
   else
   {
      /* go back to the message list */
      MessageFolderPage (rdptr);
   }
}

/*****************************************************************************/
/*
Load a signature file.  If enabled by soyMAIL configuration check for either
one or both

  [SMTP-from] <address>
  [SMTP-reply-to] <address>

To set the corresponding message header fields.  This allows for a form of
mail 'persona' where the one user can have multiple mailing identities. These
must be the first lines in the file. 
*/

void ComposeSigFileLoad
(
REQUEST_DATA *rdptr,
char *SigFileName
)
{
   int  cnt;
   char  *cptr, *sptr, *zptr;
   char  buf [256];
   COMPOSE_DATA  *cdptr;
   VMS_MAIL_USER  *muptr;

   /*********/
   /* begin */
   /*********/

   if (Debug) fprintf (stdout, "ComposeSigFileLoad()\n");

   muptr = &VmsMailUser;
   cdptr = &rdptr->ComposeData;

   OptionsSigFileLoad (rdptr, SigFileName);

   if (!muptr->SignaturePtr || !muptr->SignatureLength) return;

   if (SigFileName) StatusMessage (FI_LI, 0, LangFor("compose_sign_done"));

   /* is it enabled? value can be -1, 0, 1 */
   if (SoyMailConfig.ComposeUserFrom <= 0) return;

   cnt = 0;
   cptr = muptr->SignaturePtr;
   while (*cptr == '[')
   {
      if (strsame (cptr, "[SMTP-from]", 11))
      {
         for (cptr += 11; *cptr && *cptr != '\n' && isspace(*cptr); cptr++);
         if (*cptr && *cptr != '\n')
         {
            zptr = (sptr = buf) + sizeof(buf)-1;
            while (*cptr && *cptr != '\n' && sptr < zptr) *sptr++ = *cptr++;
            *sptr = '\0';
            if (buf[0])
            {
               cdptr->FromPtr =  CgiLibVeeMemCalloc (sptr-buf+1);
               if (!cdptr->FromPtr) ErrorExit (vaxc$errno, FI_LI);
               strcpy (cdptr->FromPtr, buf);
               cnt++;
            }
         }
      }
      else
      if (strsame (cptr, "[SMTP-reply-to]", 15))
      {
         for (cptr += 15; *cptr && *cptr != '\n' && isspace(*cptr); cptr++);
         if (*cptr && *cptr != '\n')
         {
            zptr = (sptr = buf) + sizeof(buf)-1;
            while (*cptr && *cptr != '\n' && sptr < zptr) *sptr++ = *cptr++;
            *sptr = '\0';
            if (buf[0])
            {
               cdptr->ReplyToPtr =  CgiLibVeeMemCalloc (sptr-buf+1);
               if (!cdptr->ReplyToPtr) ErrorExit (vaxc$errno, FI_LI);
               strcpy (cdptr->ReplyToPtr, buf);
               cnt++;
            }
         }
      }
      while (*cptr && *cptr != '\n') cptr++;
      if (*cptr) cptr++;
   }
   if (cnt)
   {
      muptr->SignatureLength -= cptr - muptr->SignaturePtr;
      muptr->SignaturePtr = cptr;
   }
}

/*****************************************************************************/
/*
Check for the JavaScript created and set form field CLIENT_TIME and use this to
set the message origination date/time if available (and makes sense). 
Otherwise just fall back to the time/zone VMS system soyMAIL is running on.
*/

char* ComposeRfcDate ()

{
   int  cnt, status;
   int  IntTime [6];
   unsigned long  BinTime [2];
   unsigned short  NumTime [7];
   char  *cptr, *sptr;

   /*********/
   /* begin */
   /*********/

   if (Debug) fprintf (stdout, "ComposeRfcDate()\n");

   CGIVARNULL (cptr, "FORM_CLIENT_TIME");
   if (!cptr) return (MtaRfcBinDate(NULL,NULL));

   cnt = sscanf (cptr, "%d:%d:%d:%d:%d:%d:",
                 &IntTime[0], &IntTime[1], &IntTime[2],
                 &IntTime[3], &IntTime[4], &IntTime[5]);
   if (Debug) fprintf (stdout, "sscanf() %d\n", cnt);
   if (cnt != 6) return (MtaRfcBinDate(NULL,NULL));

   NumTime[0] = IntTime[0];
   NumTime[1] = IntTime[1];
   NumTime[2] = IntTime[2];
   NumTime[3] = IntTime[3];
   NumTime[4] = IntTime[4];
   NumTime[5] = IntTime[5];
   NumTime[6] = 0;

   status = lib$cvt_vectim (&NumTime, &BinTime);
   if (Debug) fprintf (stdout, "lib$cvt_vectim() %%X%08.08X\n", status);
   if (VMSnok (status)) return (MtaRfcBinDate(NULL,NULL)); 

   for (sptr = cptr; *sptr; sptr++);
   while (sptr > cptr && *sptr != ':') sptr--;
   if (*sptr != ':') return (MtaRfcBinDate(NULL,NULL)); 
      
   return (MtaRfcBinDate(BinTime,sptr+1)); 
}

/*****************************************************************************/
/*
Return the next value from that supplied in the parameter.
Initially call with -1 to begin sequence.
*/

int ComposeWrapAt
(
int WrapAt,
USER_OPTIONS *uoptr
)
{
   static int  ValueCount;
   static int  WrapAtValue [16];

   int  idx, idx2, value;
   char  *cptr;

   /*********/
   /* begin */
   /*********/

   if (Debug) fprintf (stdout, "ComposeWrapAt() %d\n", WrapAt);

   if (!ValueCount)
   {
      if (!(cptr = uoptr->ComposeWrapAt)[0])
         if (!(cptr = SoyMailConfig.ComposeWrapAt))
            cptr = DEFAULT_WRAP_AT;
      for (idx = 0; idx < 15;)
      {
         while (*cptr && !isdigit(*cptr)) cptr++;
         if (!*cptr) break;
         WrapAtValue[idx] = atoi(cptr);
         while (*cptr && isdigit(*cptr)) cptr++;
         for (idx2 = 0; idx2 < idx; idx2++)
            if (WrapAtValue[idx2] == WrapAtValue[idx]) break;
         if (idx2 >= idx) idx++;
      }
      ValueCount = idx;
   }

   if (WrapAt < 0) return (WrapAtValue[0]);
   for (idx = 0; idx < ValueCount-1; idx++)
      if (WrapAtValue[idx] == WrapAt)
         return (WrapAtValue[idx+1]);
   return (-1);
}

/*****************************************************************************/
/*
Return the next value from that supplied in the parameter.
Initially call with -1 to begin sequence.
*/

int ComposeEditRows
(
int EditRows,
USER_OPTIONS *uoptr
)
{
   static int  PrevUsageCount = -1,
               ValueCount;
   static int  EditRowsValue [16];

   int  idx, idx2, value;
   char  *cptr;

   /*********/
   /* begin */
   /*********/

   if (Debug) fprintf (stdout, "ComposeEditRows() %d\n", EditRows);

   if (CgiPlusUsageCount != PrevUsageCount)
   {
      PrevUsageCount = CgiPlusUsageCount;
      if (!(cptr = uoptr->ComposeEditRows)[0])
         if (!(cptr = SoyMailConfig.ComposeEditRows))
            cptr = DEFAULT_EDIT_ROWS;
      for (idx = 0; idx < sizeof(EditRowsValue)/sizeof(int); idx++)
      {
         while (*cptr && !isdigit(*cptr)) cptr++;
         if (!*cptr) break;
         EditRowsValue[idx] = atoi(cptr);
         while (*cptr && isdigit(*cptr)) cptr++;
      }
      ValueCount = idx;
   }

   if (EditRows <= 0) return (EditRowsValue[0]);
   for (idx = 0; idx < ValueCount-1; idx++)
      if (EditRowsValue[idx])
         if (EditRowsValue[idx] == EditRows)
            return (EditRowsValue[idx+1]);
   return (-1);
}

/*****************************************************************************/
/*
Return the next value from that supplied in the parameter.
Initially call with -1 to begin sequence.
*/

int ComposeEditCols
(            
int EditCols,
USER_OPTIONS *uoptr
)
{
   static int  PrevUsageCount = -1,
               ValueCount;
   static int  EditColsValue [16];

   int  idx, idx2, value;
   char  *cptr;

   /*********/
   /* begin */
   /*********/

   if (Debug) fprintf (stdout, "ComposeEditCols() %d\n", EditCols);

   if (CgiPlusUsageCount != PrevUsageCount)
   {
      PrevUsageCount = CgiPlusUsageCount;
      if (!(cptr = uoptr->ComposeEditCols)[0])
         if (!(cptr = SoyMailConfig.ComposeEditCols))
            cptr = DEFAULT_EDIT_COLS;
      for (idx = 0; idx < sizeof(EditColsValue)/sizeof(int); idx++)
      {
         while (*cptr && !isdigit(*cptr)) cptr++;
         if (!*cptr) break;
         EditColsValue[idx] = atoi(cptr);
         while (*cptr && isdigit(*cptr)) cptr++;
      }
      ValueCount = idx;
   }

   if (EditCols <= 0) return (EditColsValue[0]);
   for (idx = 0; idx < ValueCount-1; idx++)
      if (EditColsValue[idx])
         if (EditColsValue[idx] == EditCols)
            return (EditColsValue[idx+1]);
   return (-1);
}

/*****************************************************************************/
/*
Open a new page with an approximation of the received message.
*/

char* ComposePreviewMessage (REQUEST_DATA *rdptr)

{
   int  cnt, status,
        ReadPreference,
        WrapAt;
   char  *aptr, *cptr, *sptr, *zptr,
         *CharSetPtr,
         *TextPtr;
   PRINT_MEM  *pmptr;
   COMPOSE_DATA  *cdptr;
   MIME_DATA  *mdptr;
   REQUEST_DATA  *sdptr;
   USER_OPTIONS  *uoptr;
   VMS_MAIL_MSG  *vmptr;
   VMS_MAIL_USER  *muptr;

   /*********/
   /* begin */
   /*********/

   if (WatchEnabled) WatchThis (FI_LI, "ComposePreviewMessage()");

   muptr = &VmsMailUser;
 
   uoptr = &rdptr->UserOptions;
   sdptr = (REQUEST_DATA*)rdptr->StateDataPtr;
   vmptr = &muptr->VmsMailMsg;
   cdptr = &rdptr->ComposeData;

   pmptr = (PRINT_MEM*)cdptr->PreviewMem;
   memset (pmptr, 0, sizeof(PRINT_MEM));

   if (SoyMailConfig.ComposeUserFrom > 0)
   {
      /* only when enabled (1) not when display (-1) */
      CGIVARNULL (cdptr->FromPtr, "FORM_COMPOSE_FROM");
      if (!cdptr->FromPtr) cdptr->FromPtr = cdptr->SelfPtr;
      /* ensure it's not empty (or effectively empty) */
      for (cptr = cdptr->FromPtr; *cptr && isspace(*cptr); cptr++);
      if (!*cptr) cdptr->FromPtr = cdptr->SelfPtr;
   }

   CGIVARNULL (cptr, "FORM_COMPOSE_WRAP_AT");
   if (cptr) WrapAt = atoi(cptr);

   CGIVAR (CharSetPtr, "FORM_COMPOSE_CHARSET");
   aptr = "ltr";
   if (CharSetPtr && CharSetPtr[0] == '<')
   {
      aptr = "rtl";
      CharSetPtr++;
   }

   PrintMem (pmptr,
"%s\n\
<html>\n\
<head>\n\
%s\
<meta name=\"generator\" content=\"%s\">\n\
<meta name=\"environment\" content=\"%s\">\n\
<meta name=\"copyright\" content=\"%s - Apache-2.0 licensed\">\n",
            DocType,
            CgiLibXUACompatible(NULL),
            SoftwareEnv,
            CgiEnvironmentPtr,
            SoftwareCopy);

   if (!(CharSetPtr && CharSetPtr[0])) CharSetPtr = rdptr->CharSetPtr;
   if (CharSetPtr && CharSetPtr[0])
      PrintMem (pmptr, 
"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=%s\">\n\
<meta charset=\"%s\">\n",
                CharSetPtr, CharSetPtr);

   /* basic soyMAIL styles */
   PrintMem (pmptr,
"<link rel=\"stylesheet\" type=\"text/css\" href=\"%s%s%s\">\n",
            SOY_PATH, "soymail.css", SoyMailQueryVersion);

   PrintMem (pmptr,
"<style type=\"text/css\">\n\
* { font-family:%s; font-size:%dpt; }\n\
</style>\n",
             uoptr->FontFamily,
             uoptr->FontSize);

   PrintMem (pmptr,
"<title>%s %s</title>\n\
</head>\n\n\
\n\
<table width=\"100%%\" cellpadding=\"5\">\n\n",
            LangFor("compose_preview"),
            cdptr->SubjPtr[0] ? cdptr->SubjPtr : LangFor("(none)"));

   /******************/
   /* message header */
   /******************/

   PrintMem (pmptr,
"<!-- message begin -->\n\
<tr><td>\n\
<table width=\"100%%\" cellpadding=\"0\" cellspacing=\"2\" border=\"0\">\n\
<tr><td class=\"css_rfchdr\">%s:</td>\
<td class=\"css_rfcval\" width=\"100%%\">%s</td>\
<td width=\"100%%\">&nbsp;</td></tr>\n\
<tr><td class=\"css_rfchdr\">%s:</td>\
<td class=\"css_rfcval\">%s</td></tr>\n\
<tr><td class=\"css_rfchdr\">%s:</td>\
<td class=\"css_rfcval\" style=\"white-space:nowrap;\">%s</td>\
<td width=\"100%%\"></td></tr>\n\
<tr><td class=\"css_rfchdr\">%s:</td>\
<td class=\"css_rfcval\">%s</td></tr>\n",
            LangFor("subject"),
            cdptr->SubjPtr[0] ? cdptr->SubjPtr : LangFor("(none)"),
            LangFor("from"),
            cdptr->FromPtr[0] ? HTML_ADDR(cdptr->FromPtr) : LangFor("(none)"),
            LangFor("date"), InetMailRfcVmsDate(CurrentVmsTimeString),
            LangFor("to"),
            cdptr->ToPtr[0] ? HTML_ADDR(cdptr->ToPtr) : LangFor("(none)"));

   if (cdptr->CcPtr && cdptr->CcPtr[0])
      PrintMem (pmptr,
"<tr><td class=\"css_rfchdr\">%s:</td>\
<td class=\"css_rfcval\">%s</td></tr>\n",
               LangFor("cc"), HTML_ADDR(cdptr->CcPtr));

   if (cdptr->ReplyToPtr && cdptr->ReplyToPtr[0])
      PrintMem (pmptr,
"<tr><td class=\"css_rfchdr\">%s:</td>\
<td class=\"css_rfcval\">%s</td></tr>\n",
               LangFor("compose_reply_to"), HTML_ADDR(cdptr->ReplyToPtr));

   PrintMem (pmptr,
"<tr><td colspan=\"2\" width=\"100%%\"><hr align=\"left\" \
width=\"90%%\" size=\"1\" noshade class=\"css_print_hr\"></td></tr>\n");

   if (cdptr->EditorType == COMPOSE_EDITOR_PLAIN)
   {
      TextPtr = ComposeBodyTextMassage (cdptr->TextPtr,
                                        WrapAt,
                                        uoptr->QuoteReply);
      TextPtr = EscapeForHtml (TextPtr);
      PrintMem (pmptr,
"<tr><td colspan=\"2\" id=\"message_text\" dir=\"%s\">\
<pre class=\"css_text_message\" \
style=\"overflow:visible;font-family:monospace;font-size:%dpt;\">\
%s\
</pre></td></tr>\n",
                aptr,
                uoptr->FontSize-1,
                TextPtr);
   }
   else
   {
      TextPtr = cdptr->TextPtr;
      PrintMem (pmptr,
"<tr><td colspan=\"2\" id=\"message_text\" dir=\"%s\">\
<div class=\"css_text_message\" style=\"overflow:visible;\">%s\
</div></td></tr>\n",
                aptr, TextPtr);
   }

   PrintMem (pmptr,
"<tr><td colspan=\"2\" width=\"100%%\"><hr align=\"left\" \
width=\"90%%\" size=\"1\" noshade class=\"css_print_hr\"></td></tr>\n\
<!-- message end -->\n\n");

   /************/
   /* page end */
   /************/

   PrintMem (pmptr,
"</table>\n\
</body>\n\
</html>\n");

   sptr = EscapeForJavaScript (pmptr->buffer);

   return (sptr);
}

/*****************************************************************************/
/*
*/

char* ComposeCharsetAlert (char *CharsetPtr)

{
   char  *sptr;

   /*********/
   /* begin */
   /*********/

   if (Debug) fprintf (stdout, "ComposeCharsetAlert()\n");

   if (!CharsetPtr || !*CharsetPtr) return ("");
   sptr =  CgiLibVeeMemCalloc (strlen(CharsetPtr)+48);
   sprintf (sptr, " &nbsp;<span style=\"font-size:110%%\">%s</span>",
            CharsetPtr);
   return (sptr);
}

/*****************************************************************************/

