/*****************************************************************************/
#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]
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=
#include
#include
#include
#include
#include
#include
/* VMS related header files */
#include
#include
#include
#include
#include
/* 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 __FILE__, __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 ("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');
/* disable the character set selection if there is edited content */
for (cptr = cdptr->TextPtr; *cptr && isspace(*cptr); cptr++);
if (!*cptr)
CGIVARNULL (cdptr->CharSetPtr, "FORM_COMPOSE_SELECT_CHARSET");
if (!cdptr->CharSetPtr)
CGIVAR (cdptr->CharSetPtr, "FORM_COMPOSE_CHARSET");
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",
LangFor("compose"), LangFor("page_opened"));
}
else
if (!CcPtr)
{
cdptr->ShowCcAddr = TRUE;
rdptr->FieldFocusPtr = "document.getElementById(\'compose_cc\').focus();";
StatusMessage (FI_LI, 0, "%s %s",
LangFor("compose"), LangFor("page_opened"));
}
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",
LangFor("compose"), LangFor("page_opened"));
}
else
if (!BccPtr)
{
cdptr->ShowBccAddr = TRUE;
rdptr->FieldFocusPtr = "document.getElementById(\'compose_bcc\').focus();";
StatusMessage (FI_LI, 0, "%s %s",
LangFor("compose"), LangFor("page_opened"));
}
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",
LangFor("compose"), LangFor("page_opened"));
}
else
if (!ReplyToPtr)
{
cdptr->ShowReplyToAddr = TRUE;
rdptr->FieldFocusPtr = "document.getElementById(\'compose_reply_to\').focus();";
StatusMessage (FI_LI, 0, "%s %s",
LangFor("compose"), LangFor("page_opened"));
}
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",
LangFor("compose"), LangFor("page_opened"));
}
else
if (LangSame ("compose_preview", cptr))
StatusMessage (FI_LI, 0, LangFor("compose_preview"));
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",
LangFor("compose"), LangFor("page_opened"));
}
}
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->TextPtr = mdptr->PartContentPtr;
cdptr->EditorType = COMPOSE_EDITOR_HTML;
}
else
cdptr->TextPtr = "";
}
else
cdptr->TextPtr = "";
rdptr->CharSetPtr = mdptr->CharSetPtr;
}
else
{
cdptr->TextPtr = "";
cdptr->CharSetPtr = uoptr->CharSet;
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",
LangFor("compose"), LangFor("page_opened"));
}
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",
LangFor("compose"), LangFor("page_opened"));
}
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 ("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 ("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 ("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 */
/**************/
if (cdptr->CharSetPtr) rdptr->CharSetPtr = cdptr->CharSetPtr;
if (!rdptr->CharSetPtr || !rdptr->CharSetPtr[0])
rdptr->CharSetPtr = SOY_DEFAULT_CHARSET;
cdptr->CharSetPtr = rdptr->CharSetPtr;
if (rdptr->CharSetPtr[0] == '<') rdptr->CharSetPtr++;
MainMenuPageBegin (rdptr, PAGE_COMPOSE);
MainMenuBar (rdptr);
StatusInfoPanel (rdptr);
/******************/
/* message header */
/******************/
fprintf (stdout,
"\n\
\n");
fprintf (stdout,
"
\
\n\
\
\n\
\
\n");
/* value can be -1, 0, 1 */
if (SoyMailConfig.ComposeUserFrom)
fprintf (stdout,
"
\
\n",
cptr);
fprintf (stdout,
"\n");
}
/*****************************************************************************/
/*
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
)
{
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);
CGIVARNULL (SelectCharSetPtr, "FORM_COMPOSE_SELECT_CHARSET");
if (!SelectCharSetPtr) SelectCharSetPtr = cdptr->CharSetPtr;
fprintf (stdout,
"
\
\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 "<>" 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
\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\n\
\n\
\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\
%s (%s)\n\
\n\
\n",
cnt == 1 ? "" : " ",
cnt, cptr,
nptr, tptr,
cnt, nptr,
cnt, tptr);
}
fprintf (stdout, "
\n\
\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, llen;
char *aptr, *cptr, *sptr, *wsptr,
*NewTextPtr;
/*********/
/* 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)
{
if (*QuoteString == *cptr)
while (*cptr && *cptr != '\n' && *cptr != '\r') cptr++;
else
{
llen = 0;
while (*cptr && *cptr != '\n' && *cptr != '\r')
{
llen++;
cptr++;
if (llen <= MaxLineLen) continue;
lcnt++;
llen = 0;
}
}
while (*cptr == '\n' || *cptr == '\r') cptr++;
if (*cptr == '\\' && *(cptr+1) == *QuoteString) lcnt++;
}
/* if none required then just return the original message */
if (!lcnt) return (TextPtr);
cnt = cptr - TextPtr + (lcnt * strlen(QuoteString)) + 1;
sptr = NewTextPtr = CgiLibVeeMemCalloc (cnt * 10);
if (!sptr) ErrorExit (vaxc$errno, FI_LI);
cptr = TextPtr;
while (*cptr)
{
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 &&
*cptr != '\n' && *cptr != '\r')
{
*sptr++ = ' ';
cptr++;
}
while (*cptr && *cptr != '\n' && *cptr != '\r') *sptr++ = *cptr++;
}
else
if (*cptr == *QuoteString)
{
/* 'quoted' - do not wrap the line */
while (*cptr && *cptr != '\n' && *cptr != '\r') *sptr++ = *cptr++;
}
else
{
llen = 0;
while (*cptr && *cptr != '\n' && *cptr != '\r')
{
wsptr = NULL;
while (*cptr && *cptr != '\n' && *cptr != '\r')
{
if (*cptr == ' ' || *cptr == '\t')
{
wsptr = sptr;
while (*cptr == ' ' || *cptr == '\t')
{
*sptr++ = *cptr++;
llen++;
}
if (llen >= MaxLineLen)
{
*(sptr = wsptr) = '\n';
sptr++;
llen = 0;
wsptr = NULL;
}
}
else
{
*sptr++ = *cptr++;
llen++;
if (llen >= MaxLineLen && wsptr)
{
while (*(wsptr+1) == ' ' || *(wsptr+1) == '\t') wsptr++;
*wsptr++ = '\n';
for (llen = 0; *wsptr; wsptr++) llen++;
wsptr = NULL;
}
}
}
if (*cptr == '\n')
{
*sptr++ = *cptr++;
llen = 0;
}
else
if (*cptr)
cptr++;
}
}
while (*cptr == '\n' || *cptr == '\r') *sptr++ = *cptr++;
}
*sptr = '\0';
return (NewTextPtr);
}
/*****************************************************************************/
/*
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 ("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
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 = cdptr->CharSetPtr;
if (!CharSetPtr || !CharSetPtr[0]) CharSetPtr = SOY_DEFAULT_CHARSET;
/* 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 ("ATTACH (message text)", fnptr);
WatchThis ("MIME TYPE !AZ", ContentTypePtr);
WatchThis ("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 ("MIME NAME !AZ", NamePtr);
WatchThis ("MIME TYPE !AZ", ContentTypePtr);
WatchThis ("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 ("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 ("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 ("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 ("MIME NAME !AZ", NamePtr);
WatchThis ("MIME TYPE !AZ", ContentTypePtr);
WatchThis ("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 ("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,
"\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]
[SMTP-reply-to]
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 ("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";
PrintMem (pmptr,
"%s\n\
\n\
\n\
%s\
\n\
\n\
\n",
DocType,
CgiLibXUACompatible(NULL),
SoftwareEnv,
CgiEnvironmentPtr,
SoftwareCopy);
/* basic soyMAIL styles */
PrintMem (pmptr,
"\n",
SOY_PATH, "soymail.css", SoyMailQueryVersion);
/* setTimeout() gives Firefox 1.5 a chance to render the page */
PrintMem (pmptr,
"%s %s\n\
\n\n\
\n\
\n\