/*****************************************************************************/ /* message.c Displays a list of messages in a folder. Displays the contents of a message (header, body, links to attachments). Displays a 'print-view' (more suitable for printing) of a message. Displays the raw message (header+body, or header alone). Deletes specified messages. CHARACTER SETS AND DIRECTIONALITY --------------------------------- Most scripts are written left-to-right. This is certainly the case for the lion's share of soyMAIL environments. However soyMAIL does use a mail message's or MIME part's content-type specified character set if available. It applies this to the entire message page. On browsers and platforms supporting multiple such sets this will leave the soyMAIL labeling in it's original format (some Latin character set) while displaying the message content in the transmitted character set. This can be a non-Latin set. Of course, some scripts (a minority in number though a significant population of users) are written right-to-left. Two common examples are Arabic (and languages using that character set) and Hebrew. HTML supports rendering using this direction. All that needs to be done is identify the encoding. With the deprecated ISO-8859-6 (Arabic) and ISO-8859-8 (Hebrew) this was straight-forward. However with the more modern UTF-8 this is less obvious. Hence with character sets not explicitly known to be left-to-right renderings only two buttons are prevised in the attachment panel, "|>>" and "<<|" to allow the directionality to be explicitly requested by the user. Known left-to-rights are specified by macro CHARSETS_COMMONLY_LTR and known right-to-lefts by CHARSETS_COMMONLY_RTL and configuration directive [charsets-rtl] (which must (re-)specify all such characters sets desired). THE FILE DOES NOT BEGIN WITH '%PDF-' ------------------------------------ And other similar (MS platform) issues. Often this sort of issue occurs because the attachment button does not result in a URL with a trailing file type (extension). The associated [^] button results in a request with such a URL. To allow the main [attachment] button to provide a similar function (and thus avoid the issue of users getting their knickers in a knot) the following configuration directive can be employed. [if-CGI-http_user_agent] Windows NT [attachment-MIME-types] text/ image/ [if-END] Modify as required. COPYRIGHT --------- Copyright (C) 2005-2024 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 --------------- 25-MAR-2023 MGD MessageCollapseLineBreaks() reduce '\r\n\r\n' to '\r\n' 13-JUL-2016 MGD bugfix; MessagePrintView() missing SoyMailJavaScriptStuff() 09-JUN-2013 MGD MessagePreview() munge end-of-line into newline so that new CSS can preview as pre-formatted text 25-FEB-2010 MGD HtmlToPlain() supply charset for UTF-8 deentify 16-FEB-2010 MGD MessagePreView() and associated JavaScript 22-AUG-2008 MGD MessageAttachments() improve attachment name parsing 29-JUL-2008 MGD MessageHeader() [^] immediate edit when adding contact MessagePageMsgRequest() change contact addition processing 05-JUL-2008 MGD MessageBodyText() and MessagePrintView() hack of CGILIB to make "mailto:"s access soyMAIL as mail agent 07-NOV-2007 MGD MessageFolderList() use JavaScript and ONMOUSE.. to highlight currently focused message item 12-AUG-2007 MGD MessageFolderList() add JavaScript to popup a message by using any of the 'Received', 'From' or 'Subject' fields 11-JUL-2007 MGD bugfix; MessageBodyProcess() return if RFC822 header error 31-MAY-2007 MGD relax 'cannot copy to' sent items (allows NEWMAIL, etc.) 13-MAY-2007 MGD bugfix; MessagePageFolderRequest() apply 'MessageCountDelta' to 'RangeIdStart' after MOVE, WASTE, DELETE or EMPTY 20-APR-2007 MGD MessageBodyProcess() refine RFC822 header error reporting 16-MAR-2007 MGD RequestVmsMailContext() establishes file/folder context 10-FEB-2007 MGD Purveyor support removed 27-JAN-2007 MGD specially process PMDF IMAP folder subdirectories MessagePage() position MessageAttachments() above or below message body according to [message-attachment-panel] 03-JAN-2007 MGD bugfix; MessageAttachments() perform URL_ENCODE() rather than HTML_ESCAPE() on buttons for message attachment URLs 28-DEC-2006 MGD MessageDestListChange() for destination list refresh 15-OCT-2006 MGD MessageBodyButtons() rearrange [delete][waste] buttons to be that same order as MessagePageFolderRequest() 19-JUL-2006 MGD MessageFolderList() user optional message ordering 24-JUN-2006 MGD MessageBodyProcess() to perform message part processing 20-JUN-2006 MGD CallMailUserDiskQuota() and MessageDiskQuotaUsed() notifies the client with each folder open once disk quota reaches the percentage specified by default or configuration 18-JUN-2006 MGD MessageAttachments() for any RFC822 message identify and make available any HTML content in a non-MIME message or in MIME text/plain in-line content 24-APR-2006 MGD MessageHeader() show reply-to: field when different to from: 03-APR-2005 MGD significant rework to rationalise public and private MessageMassage() massaged to be massaged in situ! 30-MAR-2006 MGD MessagePage() precede each message part/attachment with an anchor/link to allow for right-click browser save-as 25-MAR-2006 MGD MessagePgpSigned() and MessagePgpCut() remove PGP signature MessagePart() small accomodation for Purveyor 14-MAR-2006 MGD bugfix; MessageBodyIdentify() when generating plain from HTML-only content call MimeDecProcessPart() to decode bugfix; MessageBodyIdentify() default a non-MIME message message to 8-bit, plain-text 10-MAR-2006 MGD check return from MimeDecParseBody() and process accordingly remove 'noisy' message count from [message] range button 04-MAR-2006 MGD MessagePage() add MessageBodyButtons() after longer messages 25-FEB-2006 MGD refine MessageBodyIdentify() to manufacture plain-text content when there is only HTML content 07-FEB-2006 MGD optional folder buttons on left-side of message listing 05-FEB-2006 MGD support saving message as "message/rfc822" 'attachment' 01-FEB-2005 MGD initial */ /*****************************************************************************/ #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 #pragma nomember_alignment /* standard C header files */ #include #include #include #include #include /* VMS related header files */ #include #include #include #include #include /* application header file */ #include "soymail.h" #include "attach.h" #include "callmail.h" #include "cgilib.h" #include "compose.h" #include "config.h" #include "html.h" #include "mainmenu.h" #include "other.h" #include "options.h" #include "message.h" #include "request.h" /* can't get the include dependencies settled (this'll do in the interim) */ int ComposeWrapAt (int, USER_OPTIONS*); #define FI_LI __FILE__, __LINE__ /* number of message lines before second set of message disposition buttons */ #define MESSAGE_BUTTONS_AFTER 32 #define MAIL$M_NEWMSG 0x01 #define MAIL$M_REPLIED 0x02 #define MAIL$M_MARKED 0x80 #define FLAG_NEWMSG "×" #define FLAG_MARKED "‡" #define FLAG_REPLIED "•" /********************/ /* external storage */ /********************/ extern BOOL Debug, IsCgiPlus, WatchEnabled; extern int VmsVersion; extern char *CgiEnvironmentPtr, *CgiLib__soyMAILhack; extern char CurrentVmsTimeString[], DocType[], SoftwareCopy[], SoftwareEnv[], SoftwareId[], SoftwareVn[]; extern CONFIG_DATA SoyMailConfig; extern USER_OPTIONS UserOptions; extern REQUEST_DATA RequestData; extern VMS_MAIL_USER VmsMailUser; /*****************************************************************************/ /* This is a fundamental function used before (nearly) all message content access. soyMAIL processes all message content as one or more real and pseudo MIME parts linked together from the root at 'vmptr->MimeDataPtr'. This function processes message header and body so as to create these. If the message is not MIME, or even not RFC822, it creates a plain-text pseudo-MIME part. */ void MessageBodyProcess (VMS_MAIL_MSG *vmptr) { MIME_DATA *mdptr, *HtmlMimePtr, *PlainMimePtr; /*********/ /* begin */ /*********/ if (WatchEnabled) WatchThis ("MessageBodyProcess()"); /* only need to do it the once! */ if (vmptr->BodyProcessed) return; vmptr->BodyProcessed = TRUE; if (!InetMailParseHeader (vmptr)) if (vmptr->LooksLikeRfc) { StatusMessage (FI_LI, 1, LangFor("rfc822_header_error"), vmptr->RfcErrorLineNumber); return; } if (vmptr->VmsForeign) { mdptr = MimeDecManufacVmsForeign (vmptr->BodyPtr, vmptr->BodyLength); vmptr->MimeDataPtr = mdptr; return; } if (vmptr->MimeDataPtr) { /* the message contained a MIME header */ if (!MimeDecParseBody (vmptr->MimeDataPtr)) { /* the message is broken in some way */ vmptr->MimeDataPtr = NULL; return; } } if (!vmptr->MimeDataPtr) { /* not a MIME message, manufacture a MIME-like part from the body */ if (vmptr->RfcBodyPtr) mdptr = MimeDecManufacTextPlain (vmptr->RfcBodyPtr, vmptr->RfcBodyLength, 1); else mdptr = MimeDecManufacTextPlain (vmptr->BodyPtr, vmptr->BodyLength, 1); vmptr->MimeDataPtr = mdptr; } HtmlMimePtr = PlainMimePtr = NULL; mdptr = vmptr->MimeDataPtr; if (mdptr->ContentTypeIsMultipart) { /* look for the first plain-text and first HTML non-named parts */ for (mdptr = vmptr->MimeDataPtr; mdptr; mdptr = mdptr->NextMimePtr) if (mdptr->ContentTypeIs == MIME_CONTENT_TEXT_PLAIN) if (!mdptr->NamePtr && !mdptr->FileNamePtr) break; PlainMimePtr = mdptr; for (mdptr = vmptr->MimeDataPtr; mdptr; mdptr = mdptr->NextMimePtr) if (mdptr->ContentTypeIs == MIME_CONTENT_TEXT_HTML) if (!mdptr->NamePtr && !mdptr->FileNamePtr) break; HtmlMimePtr = mdptr; } else { /* must be in-line content */ if (mdptr->ContentTypeIs == MIME_CONTENT_TEXT_PLAIN) PlainMimePtr = mdptr; else if (mdptr->ContentTypeIs == MIME_CONTENT_TEXT_HTML) HtmlMimePtr = mdptr; } if (WatchEnabled) WatchThis ("PLAIN:!AZ HTML:!AZ", PlainMimePtr ? "yes" : "no", HtmlMimePtr ? "yes" : "no"); if (mdptr && !HtmlMimePtr && PlainMimePtr) { if (HtmlInDisguise (mdptr->PartContentPtr)) { for (mdptr = vmptr->MimeDataPtr; mdptr && mdptr->NextMimePtr; mdptr = mdptr->NextMimePtr); mdptr->PartCount++; mdptr = MimeDecManufacTextHtml (mdptr->PartContentPtr, mdptr->PartContentLength, mdptr->PartCount); HtmlMimePtr = mdptr; for (mdptr = vmptr->MimeDataPtr; mdptr && mdptr->NextMimePtr; mdptr = mdptr->NextMimePtr); mdptr->NextMimePtr = HtmlMimePtr; } } if (vmptr->MimeDataPtr) { if (vmptr->MimeDataPtr->HtmlToPlainMimePtr) { /* a plain-text from HTML part has already been manufactured */ PlainMimePtr = vmptr->MimeDataPtr->HtmlToPlainMimePtr; } else if (HtmlMimePtr && !PlainMimePtr) { /* there is an HTML but no plain-text part, manufacture one! */ if (MimeDecProcessPart (HtmlMimePtr)) { for (mdptr = vmptr->MimeDataPtr; mdptr && mdptr->NextMimePtr; mdptr = mdptr->NextMimePtr); mdptr->PartCount++; mdptr = MimeDecManufacTextPlain (HtmlMimePtr->PartContentPtr, HtmlMimePtr->PartContentLength, mdptr->PartCount); /* point the primary MIME structure to this manufactured part */ PlainMimePtr = vmptr->MimeDataPtr->HtmlToPlainMimePtr = mdptr; PlainMimePtr->CharSetPtr = HtmlMimePtr->CharSetPtr; for (mdptr = vmptr->MimeDataPtr; mdptr && mdptr->NextMimePtr; mdptr = mdptr->NextMimePtr); mdptr->NextMimePtr = PlainMimePtr; PlainMimePtr->PartContentLength = HtmlToPlain (PlainMimePtr->PartContentPtr, HtmlMimePtr->CharSetPtr); } } } } /****************************************************************************/ /* Parse the buttons associated with the folder message list. */ void MessagePageFolderRequest (REQUEST_DATA *rdptr) { int cnt, hash, msgid; char *cptr, *sptr, *zptr, *MsgRangeListPtr; VMS_MAIL_USER *muptr; USER_OPTIONS *uoptr; REQUEST_DATA *sdptr; /*********/ /* begin */ /*********/ if (WatchEnabled) WatchThis ("MessagePageFolderRequest()"); /* a bit of sanity checking */ if (!rdptr->PrivateAccess) ErrorExit (SS$_BUGCHECK, FI_LI); muptr = &VmsMailUser; sdptr = (REQUEST_DATA*)rdptr->StateDataPtr; uoptr = &rdptr->UserOptions; rdptr->MassageMessage = uoptr->MassageRead; /* the string containing comma-separated message id dot hashes */ CGIVAR (MsgRangeListPtr, "FORM_MSG_RANGE_LIST"); /*****************************/ /* individual message button */ /*****************************/ CGIVARNULL (cptr, "FORM_MSG_BTN"); if (cptr) { rdptr->MessageId = atol(cptr); rdptr->MessageHash = MessageRangeString (rdptr->MessageId, MsgRangeListPtr); if (!strcmp (rdptr->FolderName, uoptr->FolderDraftItems)) ComposePageRequest (rdptr); else { rdptr->ResetNewMsgFlag = TRUE; MessagePage (rdptr); } return; } /************************/ /* popup message button */ /************************/ CGIVARNULL (cptr, "FORM_MSG_BTN_POPUP"); if (cptr && (cnt = atol(cptr))) { rdptr->MessageId = cnt; rdptr->MessageHash = MessageRangeString (rdptr->MessageId, MsgRangeListPtr); if (!strcmp (rdptr->FolderName, uoptr->FolderDraftItems)) ComposePageRequest (rdptr); else { rdptr->ResetNewMsgFlag = TRUE; MessagePage (rdptr); } return; } /*****************/ /* message range */ /*****************/ /* the range button or range 'onchange=' */ CGIVARNULL (cptr, "FORM_RANGE_MSGS"); if (!cptr) CGIVARNULL (cptr, "FORM_RANGE_LIST_CHANGE"); if (cptr && *cptr) CGIVARNULL (cptr, "FORM_RANGE_LIST"); /* also check that the range list is not empty */ if (cptr && *cptr) { rdptr->RangeIdStart = atoi(cptr); while (*cptr && *cptr != '-') cptr++; if (*cptr) { cptr++; rdptr->RangeIdCount = atoi(cptr); rdptr->RangeIdCount -= rdptr->RangeIdStart - 1; } else rdptr->RangeIdCount = uoptr->MessagesPerPage; rdptr->RangeIdVector = +1; MessageFolderPage (rdptr); return; } /******************************/ /* previous range of messages */ /******************************/ CGIVARNULL (cptr, "FORM_RANGE_PREV"); if (cptr) { /* get the lowest id in the list and start one less than that */ rdptr->RangeIdStart = MessageRangeString (-1, MsgRangeListPtr); if (rdptr->RangeIdStart <= 1) { rdptr->RangeIdStart = sdptr->RangeIdStart; StatusMessage (FI_LI, 1, LangFor("no_more_messages")); } else rdptr->RangeIdStart--; rdptr->RangeIdCount = uoptr->MessagesPerPage; rdptr->RangeIdVector = -1; MessageFolderPage (rdptr); return; } /**************************/ /* next range of messages */ /**************************/ CGIVARNULL (cptr, "FORM_RANGE_NEXT"); if (cptr) { /* the highest id is always the first one in the string */ rdptr->RangeIdStart = atoi(MsgRangeListPtr) + 1; if (rdptr->RangeIdStart >= sdptr->FolderMessageCount) { /* just go back to the start of whatever we had before */ rdptr->RangeIdStart = sdptr->RangeIdStart; StatusMessage (FI_LI, 1, LangFor("no_more_messages")); } rdptr->RangeIdCount = uoptr->MessagesPerPage; rdptr->RangeIdVector = +1; MessageFolderPage (rdptr); return; } /*****************************/ /* copy, move, waste, delete */ /*****************************/ CGIVARNULL (cptr, "FORM_MSG_COPY"); if (!cptr) CGIVARNULL (cptr, "FORM_MSG_MOVE"); if (!cptr) CGIVARNULL (cptr, "FORM_MSG_WASTE"); if (!cptr) CGIVARNULL (cptr, "FORM_MSG_DELETE"); if (!cptr) CGIVARNULL (cptr, "FORM_MSG_EMPTY"); if (cptr) { MessageIdList (rdptr); CGIVARNULL (cptr, "FORM_MSG_COPY"); if (cptr) MessageCopy (rdptr, FALSE); else { CGIVARNULL (cptr, "FORM_MSG_MOVE"); if (cptr) MessageCopy (rdptr, TRUE); else { CGIVARNULL (cptr, "FORM_MSG_WASTE"); if (cptr) { strcpy (rdptr->DestMailFileName, "MAIL"); rdptr->DestMailFileNameLength = strlen(rdptr->DestMailFileName); strcpy (rdptr->DestFolderName, uoptr->FolderWasteItems); rdptr->DestFolderNameLength = strlen(rdptr->DestFolderName); RequestVmsMailContext (rdptr); MessageCopy (rdptr, TRUE); } else { CGIVARNULL (cptr, "FORM_MSG_EMPTY"); if (cptr) { /* all messages in soyWASTE folder! */ if (!strcmp (rdptr->FolderName, uoptr->FolderWasteItems)) { /* CAREFUL! only the waste items folder!! */ rdptr->RangeIdStart = rdptr->RangeIdCount = 0; rdptr->MsgIdListPtr = "*"; rdptr->MsgIdListCount = 1; MessageDelete (rdptr); } } else { CGIVARNULL (cptr, "FORM_MSG_DELETE"); if (cptr) MessageDelete (rdptr); else ErrorExit (SS$_BUGCHECK, FI_LI); } } } } rdptr->RangeIdStart = sdptr->RangeIdStart - rdptr->MessageCountDelta; if (rdptr->RangeIdStart <= 0) rdptr->RangeIdStart = 1; rdptr->RangeIdCount = uoptr->MessagesPerPage; rdptr->RangeIdVector = -1; MessageFolderPage (rdptr); /* make sure (any) deleted messages are purged from the WASTEBASKET */ CallMailFileClose (muptr); return; } /*****************/ /* folder button */ /*****************/ /* this button is also checked for in RequestPrivateDefaults() */ CGIVARNULL (cptr, "FORM_FOLDER_BTN"); if (cptr) { /* the list was peremtorially generated by RequestPrivateDefaults() */ if (rdptr->MsgIdListCount) { /* message checkboxes result in message moves to buttoned folder */ RequestParseDestFileFolder (rdptr, cptr); RequestVmsMailContext (rdptr); MessageCopy (rdptr, TRUE); } MessageFolderPage (rdptr); return; } /*********************/ /* none of the above */ /*********************/ rdptr->RangeIdStart = sdptr->RangeIdStart; rdptr->RangeIdCount = uoptr->MessagesPerPage; MessageFolderPage (rdptr); } /****************************************************************************/ /* When 'MessageId' is positive find that message number in the string and return it's associated hash. Return zero if the message is not found (error). When 'MessageId' is zero step through each of the message numbers in the string, returning zero at end of string. When 'MessageId' is negative find the first message id (the last one in the string) and return it's message number. Return zero if the string is empty (error). */ int MessageRangeString ( int MessageId, char *RangeStringPtr ) { static char *LastPtr = NULL; int id, hash; char *cptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "MessageRangeString() %d |%s|\n", MessageId, RangeStringPtr); if (MessageId > 0) { /* return the hash of the specified message id from the string */ cptr = RangeStringPtr; while (*cptr) { id = atol(cptr); if (!id) return (0); if (id == MessageId) { while (*cptr && *cptr != '.') cptr++; if (*cptr != '.') return (0); cptr++; hash = atol(cptr); return (hash); } while (*cptr && *cptr != ',') cptr++; if (*cptr) cptr++; } return (0); } if (MessageId == 0) { /* return each of the message IDs in the string in turn */ if (!LastPtr) LastPtr = RangeStringPtr; cptr = LastPtr; id = atol(cptr); if (!id) { LastPtr = NULL; return (0); } while (*cptr && *cptr != ',') cptr++; if (*cptr) cptr++; LastPtr = cptr; return (id); } if (MessageId < 0) { /* return the number of the very last message id in the string */ cptr = RangeStringPtr; id = 0; while (*cptr) { id = atol(cptr); if (!id) return (0); while (*cptr && *cptr != ',') cptr++; if (*cptr) cptr++; } return (id); } } /****************************************************************************/ /* Parse the buttons from the message display page. */ void MessagePageMsgRequest (REQUEST_DATA *rdptr) { static char SingleMessageList [32]; char *cptr, *sptr, *zptr; USER_OPTIONS *uoptr; REQUEST_DATA *sdptr; VMS_MAIL_USER *muptr; /*********/ /* begin */ /*********/ if (WatchEnabled) WatchThis ("MessagePageMsgRequest()"); /* a bit of sanity checking */ if (!rdptr->PrivateAccess) ErrorExit (SS$_BUGCHECK, FI_LI); muptr = &VmsMailUser; uoptr = &rdptr->UserOptions; sdptr = (REQUEST_DATA*)rdptr->StateDataPtr; rdptr->MassageMessage = uoptr->MassageRead; /****************************/ /* previous/at/next message */ /****************************/ CGIVARNULL (cptr, "FORM_MSG_PREV"); if (cptr) { rdptr->MessageId = sdptr->MessageId; if (rdptr->MessageId > 1) rdptr->MessageId--; else StatusMessage (FI_LI, 1, LangFor("no_more_messages")); rdptr->MessageHash = 0; rdptr->ResetNewMsgFlag = TRUE; MessagePage (rdptr); return; } CGIVARNULL (cptr, "FORM_MSG_FOLDER_AT"); if (cptr) { rdptr->RangeIdCount = uoptr->MessagesPerPage; for (rdptr->RangeIdStart = sdptr->FolderMessageCount; rdptr->RangeIdStart >= sdptr->MessageId; rdptr->RangeIdStart -= rdptr->RangeIdCount); rdptr->RangeIdStart++; if (rdptr->RangeIdStart < 1) rdptr->RangeIdStart = 1; MessageFolderPage (rdptr); return; } CGIVARNULL (cptr, "FORM_MSG_NEXT"); if (cptr) { rdptr->MessageId = sdptr->MessageId; if (rdptr->MessageId < sdptr->FolderMessageCount) rdptr->MessageId++; else StatusMessage (FI_LI, 1, LangFor("no_more_messages")); rdptr->MessageHash = 0; rdptr->ResetNewMsgFlag = TRUE; MessagePage (rdptr); return; } /********************/ /* specific message */ /********************/ CGIVAR (cptr, "FORM_MSG_ID_HASH"); rdptr->MessageId = atol(cptr); while (*cptr && *cptr != '.') cptr++; if (*cptr) cptr++; if (!*cptr) ErrorExit (SS$_BUGCHECK, FI_LI);; rdptr->MessageHash = atol(cptr); /**********************************/ /* copy/move/waste/delete message */ /**********************************/ CGIVARNULL (cptr, "FORM_MSG_COPY"); if (!cptr) CGIVARNULL (cptr, "FORM_MSG_MOVE"); if (!cptr) CGIVARNULL (cptr, "FORM_MSG_WASTE"); if (!cptr) CGIVARNULL (cptr, "FORM_MSG_DELETE"); if (cptr) { sprintf (SingleMessageList, "%d.%d", rdptr->MessageId, rdptr->MessageHash); rdptr->MsgIdListPtr = SingleMessageList; rdptr->MsgIdListCount = 1; CGIVARNULL (cptr, "FORM_MSG_COPY"); if (cptr) MessageCopy (rdptr, FALSE); else { CGIVARNULL (cptr, "FORM_MSG_MOVE"); if (cptr) MessageCopy (rdptr, TRUE); else { CGIVARNULL (cptr, "FORM_MSG_WASTE"); if (cptr) { strcpy (rdptr->DestMailFileName, "MAIL"); rdptr->DestMailFileNameLength = strlen(rdptr->DestMailFileName); strcpy (rdptr->DestFolderName, uoptr->FolderWasteItems); rdptr->DestFolderNameLength = strlen(rdptr->DestFolderName); RequestVmsMailContext (rdptr); MessageCopy (rdptr, TRUE); } else { CGIVARNULL (cptr, "FORM_MSG_DELETE"); if (cptr) MessageDelete (rdptr); else ErrorExit (SS$_BUGCHECK, FI_LI); } } if (sdptr->MessageId == sdptr->FolderMessageCount) rdptr->MessageId = sdptr->MessageId - 1; } if (VMSnok (muptr->MailVmsStatus)) { /* same behaviour and formula as folder-at */ rdptr->RangeIdCount = uoptr->MessagesPerPage; for (rdptr->RangeIdStart = sdptr->FolderMessageCount; rdptr->RangeIdStart >= sdptr->MessageId; rdptr->RangeIdStart -= rdptr->RangeIdCount); rdptr->RangeIdStart++; if (rdptr->RangeIdStart < 1) rdptr->RangeIdStart = 1; MessageFolderPage (rdptr); } else { /* adjust this to reflect the successful move/copy/delete */ if (sdptr->FolderMessageCount) sdptr->FolderMessageCount--; if (rdptr->MessageId) { /* still have message(s) in the folder */ rdptr->MessageHash = 0; MessagePage (rdptr); } else { /* folder empty */ rdptr->RangeIdStart = RANGE_MOST_RECENT; rdptr->RangeIdCount = uoptr->MessagesPerPage; rdptr->FolderMessageCount = 0; MessageFolderPage (rdptr); } } /* make sure (any) deleted messages are purged from the WASTEBASKET */ CallMailFileClose (muptr); return; } /*****************/ /* message parts */ /*****************/ CGIVARNULL (cptr, "FORM_PART_BTN"); if (cptr) { zptr = (sptr = rdptr->AttachmentName) + sizeof(rdptr->AttachmentName)-1; while (*cptr && sptr < zptr) *sptr++ = *cptr++; *sptr = '\0'; CGIVARNULL (cptr, "FORM_MESSAGE_PART"); if (cptr && isdigit(*cptr)) sprintf (rdptr->AttachPartName, "%s_%d", LangFor("mime_part"), atoi(cptr)); /* if the attachment is successfully accessed then just finish up */ if (MessagePart (rdptr)) return; MessagePage (rdptr); return; } CGIVARNULL (cptr, "FORM_PART_SAVE"); if (cptr) { AttachSave (rdptr, FALSE); MessagePage (rdptr); return; } if (!SoyMailConfig.VmsOccluded) { CGIVARNULL (cptr, "FORM_PART_EXTRACT"); if (cptr) { AttachSave (rdptr, TRUE); MessagePage (rdptr); return; } } /*************************/ /* message reply buttons */ /*************************/ CGIVARNULL (cptr, "FORM_MSG_REPLY"); if (!cptr) CGIVARNULL (cptr, "FORM_MSG_REPLY_POPUP"); if (!cptr) CGIVARNULL (cptr, "FORM_MSG_REPLY_ALL"); if (!cptr) CGIVARNULL (cptr, "FORM_MSG_REPLY_ALL_POPUP"); if (!cptr) CGIVARNULL (cptr, "FORM_MSG_FORWARD"); if (!cptr) CGIVARNULL (cptr, "FORM_MSG_FORWARD_POPUP"); if (!cptr) CGIVARNULL (cptr, "FORM_MSG_EDIT_NEW"); if (!cptr) CGIVARNULL (cptr, "FORM_MSG_EDIT_NEW_POPUP"); if (cptr) { ComposePageRequest (rdptr); return; } /************************/ /* message read buttons */ /************************/ CGIVARNULL (cptr, "FORM_MSG_PRINT"); if (!cptr) CGIVARNULL (cptr, "FORM_MSG_PRINT_POPUP"); if (cptr) { if (MessagePrintView (rdptr)) return; MessagePage (rdptr); return; } CGIVARNULL (cptr, "FORM_MSG_RAW"); if (!cptr) CGIVARNULL (cptr, "FORM_MSG_RAW_POPUP"); if (cptr) { if (MessageRawView (rdptr)) return; MessagePage (rdptr); return; } CGIVARNULL (cptr, "FORM_MSG_HDR"); if (!cptr) CGIVARNULL (cptr, "FORM_MSG_HDR_POPUP"); if (cptr) { if (MessageHdrView (rdptr)) return; MessagePage (rdptr); return; } /**************/ /* HTML/plain */ /**************/ CGIVARNULL (cptr, "FORM_MSG_READ_PREF"); if (cptr) { /* one is on, minus one is off, zero is not set! */ if (LangSame ("message_read_html", cptr)) rdptr->MessageReadPreference = OPTIONS_READ_PLAIN2_HTML1; if (LangSame ("message_read_plain", cptr)) rdptr->MessageReadPreference = OPTIONS_READ_PLAIN1_HTML2; MessagePage (rdptr); return; } /****************/ /* message wrap */ /****************/ CGIVARNULL (cptr, "FORM_MSG_WRAP"); if (cptr) { while (*cptr && !isdigit(*cptr)) cptr++; rdptr->MassageMessage = atoi(cptr); /* use the read preference from the previous request */ rdptr->MessageReadPreference = sdptr->MessageReadPreference; MessagePage (rdptr); return; } /******************/ /* add as contact */ /******************/ CGIVARNULL (cptr, "FORM_MSG_ADD_CONTACT"); if (cptr) { if (*cptr == '^') { /****************/ /* edit contact */ /****************/ ContactsLoad (rdptr, NULL, NULL); ContactsPage (rdptr, FALSE); return; } ContactsAppend (rdptr); MessagePage (rdptr); return; } /****************/ /* flag message */ /****************/ if (SoyMailConfig.MessageFlags & 0xff) { CGIVARNULL (cptr, "FORM_MSG_FLAG"); if (cptr) { /* trim the character used to represent the flag */ for (sptr = cptr; *sptr && *sptr != ' '; sptr++); *sptr = '\0'; if (strsame ("mark", cptr, -1)) MessageFlags (rdptr, MAIL$M_MARKED); else if (strsame ("unmark", cptr, -1)) MessageFlags (rdptr, ~MAIL$M_MARKED); else if (strsame ("unread", cptr, -1)) MessageFlags (rdptr, MAIL$M_NEWMSG); else if (strsame ("read", cptr, -1)) MessageFlags (rdptr, ~MAIL$M_NEWMSG); else if (strsame ("replied-to", cptr, -1)) MessageFlags (rdptr, MAIL$M_REPLIED); else if (strsame ("unreplied-to", cptr, -1)) MessageFlags (rdptr, ~MAIL$M_REPLIED); MessagePage (rdptr); return; } } /* whatever we were doing before (disabled search can do this) */ MessagePage (rdptr); } /****************************************************************************/ /* Generate a string containing a comma-separated list of message ID numbers (in textual form of course) representing the checkboxes against each message on the message list page. Return the number of checkboxes checked. */ int MessageIdList (REQUEST_DATA *rdptr) { int cnt, hash, id; char *cptr, *sptr, *MsgRangeListPtr; char MsgCbxString [32]; REQUEST_DATA *sdptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "MessageIdList()\n"); sdptr = (REQUEST_DATA*)rdptr->StateDataPtr; rdptr->MsgIdListPtr = NULL; rdptr->MsgIdListCount = 0; CGIVARNULL (cptr, "FORM_MSG_CBX_ALL"); if (cptr && *cptr) { /* all messages checkbox! */ rdptr->RangeIdStart = rdptr->RangeIdCount = 0; rdptr->MsgIdListPtr = "*"; rdptr->MsgIdListCount = 1; } else { /* the string containing comma-separated message id dot hashes */ CGIVAR (MsgRangeListPtr, "FORM_MSG_RANGE_LIST"); /* count the number of messages in the range */ for (cnt = 0; MessageRangeString (0, MsgRangeListPtr); cnt++); if (cnt) { /* message checkboxes */ rdptr->MsgIdListPtr = sptr = CgiLibVeeMemCalloc (cnt * 32); if (!rdptr->MsgIdListPtr) ErrorExit (vaxc$errno, FI_LI); while (id = MessageRangeString (0, MsgRangeListPtr)) { sprintf (MsgCbxString, "FORM_MSG_CBX_%d", id); CGIVARNULL (cptr, MsgCbxString); if (cptr && *cptr) { hash = MessageRangeString (id, MsgRangeListPtr); sptr += sprintf (sptr, "%s%d.%d", rdptr->MsgIdListCount ? "," : "", id, hash); rdptr->MsgIdListCount++; } } *sptr = '\0'; if (WatchEnabled) WatchThis ("CBX !UL!AZ!AZ", rdptr->MsgIdListCount, rdptr->MsgIdListCount ? " " : "", rdptr->MsgIdListPtr); } } if (Debug) fprintf (stdout, "%d %d %d %d|%s|\n", rdptr->MessageId, rdptr->RangeIdStart, rdptr->RangeIdCount, rdptr->MsgIdListCount, rdptr->MsgIdListPtr); return (rdptr->MsgIdListCount); } /*****************************************************************************/ /* Display the basic private folder and message list page. */ void MessageFolderPage (REQUEST_DATA *rdptr) { USER_OPTIONS *uoptr; REQUEST_DATA *sdptr; VMS_MAIL_MSG *vmptr; VMS_MAIL_USER *muptr; /*********/ /* begin */ /*********/ if (WatchEnabled) WatchThis ("MessageFolderPage() !AZ !AZ !SL !SL !SL", rdptr->MailFileName, rdptr->FolderName, rdptr->RangeIdStart, rdptr->RangeIdCount, rdptr->RangeIdVector); muptr = &VmsMailUser; uoptr = &rdptr->UserOptions; vmptr = &muptr->VmsMailMsg; sdptr = (REQUEST_DATA*)rdptr->StateDataPtr; if (rdptr->SearchData.InProgress) SearchCriteria (rdptr); /* determine if we're changing folders */ if (strcmp (rdptr->MailFileName, sdptr->MailFileName) || strcmp (rdptr->FolderName, sdptr->FolderName)) rdptr->RangeIdCount = 0; else rdptr->FolderMessageCount = sdptr->FolderMessageCount; if (!rdptr->RangeIdCount) { rdptr->RangeIdStart = RANGE_MOST_RECENT; rdptr->RangeIdCount = uoptr->MessagesPerPage; rdptr->RangeIdVector = -1; } if (uoptr->CharSetDefault[0]) CgiLibResponseSetCharset (uoptr->CharSetDefault); else if (SoyMailConfig.CharSetDefault && SoyMailConfig.CharSetDefault[0]) CgiLibResponseSetCharset (SoyMailConfig.CharSetDefault); else CgiLibResponseSetCharset (CHARSET_DEFAULT); MessageListInit (rdptr); MainMenuPageBegin (rdptr, PAGE_FOLDER); MainMenuBar (rdptr); StatusInfoPanel (rdptr); MessageFolderList (rdptr); MainMenuPageEnd (rdptr); } /*****************************************************************************/ /* Display the message header and body page. */ void MessagePage (REQUEST_DATA *rdptr) { int idx, status, LineCount; MIME_DATA *mdptr; USER_OPTIONS *uoptr; REQUEST_DATA *sdptr; VMS_MAIL_MSG *vmptr; VMS_MAIL_USER *muptr; VMS_MAIL_USER UserData; /*********/ /* begin */ /*********/ if (WatchEnabled) WatchThis ("MessagePage()"); muptr = &VmsMailUser; uoptr = &rdptr->UserOptions; vmptr = &muptr->VmsMailMsg; sdptr = (REQUEST_DATA*)rdptr->StateDataPtr; if (rdptr->SearchData.InProgress) SearchCriteria (rdptr); rdptr->FolderMessageCount = sdptr->FolderMessageCount; vmptr->MessageId = rdptr->MessageId; vmptr->ConfirmHash = rdptr->MessageHash; vmptr->ResetNewMsgFlag = rdptr->ResetNewMsgFlag; CallMailMessageGet (muptr, NULL); /* in case a message hash was not supplied use the one just generated */ rdptr->MessageHash = vmptr->MessageHash; if (VMSnok (muptr->MailVmsStatus)) { 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)); } MessageFolderPage (rdptr); return; } /********************/ /* display the page */ /********************/ MessageContentIdentify (rdptr); MainMenuPageBegin (rdptr, PAGE_MESSAGE); MainMenuBar (rdptr); StatusInfoPanel (rdptr); MessageHeaderButtons (rdptr); MessageHeader (rdptr); MessageBodyButtons (rdptr); if (uoptr->MessageAttachmentPanel == 1) MessageAttachments (rdptr); LineCount = MessageBodyText (rdptr); if (LineCount > MESSAGE_BUTTONS_AFTER) MessageBodyButtons (rdptr); if (uoptr->MessageAttachmentPanel == 0) MessageAttachments (rdptr); MainMenuPageEnd (rdptr); } /*****************************************************************************/ /* Check the message for textual content based on user preferences. Once decided generate a description of that and the overall message for the status information panel. */ void MessageContentIdentify (REQUEST_DATA *rdptr) { int cnt, status, ReadPreference; char *sptr; char Description [256], Scratch [128]; MIME_DATA *mdptr; USER_OPTIONS *uoptr; VMS_MAIL_MSG *vmptr; VMS_MAIL_USER *muptr; /*********/ /* begin */ /*********/ if (WatchEnabled) WatchThis ("MessageContentIdentify()"); muptr = &VmsMailUser; vmptr = &muptr->VmsMailMsg; uoptr = &rdptr->UserOptions; MessageBodyProcess (vmptr); /**********************/ /* determine the text */ /**********************/ /* 'probe' the content to see if there is an alternative */ mdptr = MessageBodyIdentify (vmptr, OPTIONS_READ_PLAIN0_HTML1); if (mdptr) rdptr->HtmlTextIsPresent = TRUE; mdptr = MessageBodyIdentify (vmptr, OPTIONS_READ_PLAIN1_HTML0); if (mdptr) rdptr->PlainTextIsPresent = TRUE; if (rdptr->MessageReadPreference) ReadPreference = rdptr->MessageReadPreference; else ReadPreference = uoptr->ReadPreference; mdptr = MessageBodyIdentify (vmptr, ReadPreference); if (mdptr) { MimeDecProcessPart (mdptr); rdptr->CharSetPtr = mdptr->CharSetPtr; if (mdptr->ContentTypeIs == MIME_CONTENT_TEXT_PLAIN) rdptr->ReadPlainText = TRUE; else if (mdptr->ContentTypeIs == MIME_CONTENT_TEXT_HTML) rdptr->ReadHtmlText = TRUE; else { StatusMessage (FI_LI, 1, "%s", LangFor("mime_type_unknown")); return; } } else if (!vmptr->VmsForeign) { StatusMessage (FI_LI, 1, "%s", LangFor("mime_type_unknown")); return; } /************************/ /* describe the content */ /************************/ if (rdptr->PrivateAccess || rdptr->PublicWildcard) sprintf (Scratch, "%s / %04d", HTML_ESCAPE(rdptr->FolderName), rdptr->MessageId); else sprintf (Scratch, "%s / %04d", HTML_ESCAPE(rdptr->PublicAccessString), rdptr->MessageId); sptr += sprintf (sptr = Description, LangFor("message_opened"), Scratch); if (vmptr->MimeDataPtr->VersionNumber) sptr += sprintf (sptr, "  MIME."); else if (vmptr->LooksLikeRfc) sptr += sprintf (sptr, "  RFC."); else if (vmptr->VmsForeign) sptr += sprintf (sptr, "  VMS/FOREIGN."); else sptr += sprintf (sptr, "  VMS."); if (vmptr->RfcBccPtr) sptr += sprintf (sptr, "  BCC."); if (mdptr) if (MessagePgpSigned (mdptr->PartContentPtr)) sptr += sprintf (sptr, "  PGP."); for (mdptr = vmptr->MimeDataPtr; mdptr; mdptr = mdptr->NextMimePtr) if (mdptr->ContentTypeIs == MIME_CONTENT_TEXT_HTML) if (mdptr->ManufacturedMime) break; if (mdptr) sptr += sprintf (sptr, "  HTML?"); if (rdptr->CharSetPtr && !strsame (rdptr->CharSetPtr, uoptr->CharSet, -1)) sptr += sprintf (sptr, "  %s.", rdptr->CharSetPtr); cnt = 0; for (mdptr = vmptr->MimeDataPtr; mdptr; mdptr = mdptr->NextMimePtr) if (mdptr->NamePtr) cnt++; if (cnt) sptr += sprintf (sptr, "  %d %s.", cnt, LangFor("message_attachments")); cnt = 0; for (mdptr = vmptr->MimeDataPtr; mdptr; mdptr = mdptr->NextMimePtr) if (!mdptr->ContentTypeIsMultipart && mdptr->PartName[0]) cnt++; if (cnt > 1) sptr += sprintf (sptr, "  %d %s.", cnt, LangFor("message_parts")); if (rdptr->ReadPlainText && vmptr->MimeDataPtr && vmptr->MimeDataPtr->HtmlToPlainMimePtr) sptr += sprintf (sptr, "  %s.", LangFor("message_html_to_plain")); if (vmptr->BodyLength < 2048) sptr += sprintf (sptr, "  %d bytes.", vmptr->BodyLength); else sptr += sprintf (sptr, "  %d kbytes.", vmptr->BodyLength >> 10); if (vmptr->ExtIdLength) sptr += sprintf (sptr, "\n", vmptr->ExtId); StatusMessage (FI_LI, 0, "%s", Description); } /*****************************************************************************/ /* Display the message header ("Subject:", "From:", "Date:", etc). */ void MessageHeader (REQUEST_DATA *rdptr) { USER_OPTIONS *uoptr; VMS_MAIL_MSG *vmptr; VMS_MAIL_USER *muptr; /*********/ /* begin */ /*********/ if (WatchEnabled) WatchThis ("MessageHeader()"); muptr = &VmsMailUser; vmptr = &muptr->VmsMailMsg; uoptr = &rdptr->UserOptions; fprintf (stdout, "\n\

\n\ \n\ \n\
\n\ \n\ \n\ \ \ \n\ \ ", vmptr->MessageId, vmptr->MessageHash, LangFor("subject"), vmptr->MsgSubjPtr[0] ? HTML_TEXT(vmptr->MsgSubjPtr) : LangFor("(none)"), vmptr->MessageId, rdptr->FolderMessageCount, LangFor("from"), vmptr->MsgFromPtr[0] ? HTML_ADDR(vmptr->MsgFromPtr) : LangFor("(none)")); if (rdptr->PrivateAccess) { fprintf (stdout, "\n", LangFor("message_add_contact"), HTML_ESCAPE(ContactsNameAs(vmptr->MsgFromPtr)), HTML_ESCAPE(InetMailStripTransport(vmptr->MsgFromPtr))); } if (vmptr->RfcReplyToPtr && !strsame (vmptr->RfcFromPtr, vmptr->RfcReplyToPtr, -1)) fprintf (stdout, "\ \n", LangFor("compose_reply_to"), HTML_ADDR(vmptr->RfcReplyToPtr)); fprintf (stdout, "\ \n\ \ ", LangFor("date"), HTML_TEXT(vmptr->MsgDatePtr), vmptr->LooksLikeRfc ? "  [" : "", vmptr->LooksLikeRfc ? InetMailRfcVmsDate(vmptr->Date) : "", vmptr->LooksLikeRfc ? "]" : "", LangFor("to"), vmptr->MsgToPtr[0] ? HTML_ADDR(vmptr->MsgToPtr) : LangFor("(none)")); if (rdptr->PrivateAccess) if (vmptr->XVmsToPtr) fprintf (stdout, "\ \n", vmptr->XVmsToPtr); if (SoyMailConfig.MessageFlags & 0xff) { if (rdptr->PrivateAccess) { fprintf (stdout, "", stdout); } else fputs ("", stdout); } else fputs ("", stdout); fputs ("\n", stdout); if (vmptr->MsgCcPtr[0]) fprintf (stdout, "\ \n", LangFor("cc"), HTML_ADDR(vmptr->MsgCcPtr)); fprintf (stdout, "
%s:%s%04d / %04d
%s:%s\ \ \n\ \ \
%s:%s
%s:%s%s%s%s
%s:%s
X-VMS-To:%s
", vmptr->MessageFlags); if (SoyMailConfig.MessageFlags & MAIL$M_MARKED) fprintf (stdout, "", vmptr->MessageFlags & MAIL$M_MARKED ? "Unmark" : "Mark", FLAG_MARKED); if (SoyMailConfig.MessageFlags & MAIL$M_NEWMSG) fprintf (stdout, "", vmptr->MessageFlags & MAIL$M_NEWMSG ? "Read" : "Unread", FLAG_NEWMSG); if (SoyMailConfig.MessageFlags & MAIL$M_REPLIED) fprintf (stdout, "", vmptr->MessageFlags & MAIL$M_REPLIED ? "Unreplied-to" : "Replied-to", FLAG_REPLIED); fputs ("
%s:%s
\n\
\n\ \n\n"); } /*****************************************************************************/ /* Provide the [print], [raw], header] buttons, etc. */ void MessageHeaderButtons (REQUEST_DATA *rdptr) { char *cptr; USER_OPTIONS *uoptr; VMS_MAIL_MSG *vmptr; VMS_MAIL_USER *muptr; /*********/ /* begin */ /*********/ if (WatchEnabled) WatchThis ("MessageHeaderButtons()"); muptr = &VmsMailUser; vmptr = &muptr->VmsMailMsg; uoptr = &rdptr->UserOptions; fprintf (stdout, "\n\

\n\ \n\ \ \n\
\n\ \ \n\ \ \n\ \ \n\ \n\ \n\ \n\ \n\ \n\ \n", LangFor("print"), LangFor("raw"), vmptr->LooksLikeRfc ? "" : " onclick=\"return notApplicable();\"", vmptr->LooksLikeRfc ? "onclick=\"targetBlank();\"" : "onclick=\"return notApplicable();\"", LangFor("header"), vmptr->LooksLikeRfc ? "" : " onclick=\"return notApplicable();\"", vmptr->LooksLikeRfc ? "onclick=\"targetBlank();\"" : "onclick=\"return notApplicable();\"", LangFor("prev"), CLICK_NOMORE_STARTWORKING(vmptr->MessageId == 1), LangFor("folder_at"), CLICK_STARTWORKING, LangFor("next"), CLICK_NOMORE_STARTWORKING(vmptr->MessageId == rdptr->FolderMessageCount), LangFor("copy"), CLICK_STARTWORKING, LangFor("move"), CLICK_STARTWORKING); MessageDestSelector (rdptr); fprintf (stdout, "
\n\ \n\n"); } /*****************************************************************************/ /* Display the message body. */ int MessageBodyText (REQUEST_DATA *rdptr) { BOOL ReadPreference; int status, LineCount = 0; char *cptr, *sptr, *zptr; char PartUri [512], MailTo [128]; MIME_DATA *mdptr; USER_OPTIONS *uoptr; VMS_MAIL_MSG *vmptr; VMS_MAIL_USER *muptr; /*********/ /* begin */ /*********/ if (WatchEnabled) WatchThis ("MessageBodyText()"); muptr = &VmsMailUser; vmptr = &muptr->VmsMailMsg; uoptr = &rdptr->UserOptions; /* hack of CGILIB to make "mailto:"s access soyMAIL as mail agent */ zptr = (sptr = MailTo) + sizeof(MailTo)-1; for (cptr = rdptr->FormAction; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = "?mailto:"; *cptr && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; fprintf (stdout, "\n"); fprintf (stdout, "

\n\ \n"); if (rdptr->MessageReadPreference) ReadPreference = rdptr->MessageReadPreference; else ReadPreference = rdptr->UserOptions.ReadPreference; mdptr = MessageBodyIdentify (vmptr, ReadPreference); if (mdptr) { if ((cptr = mdptr->CharSetPtr) || (cptr = rdptr->CharSetPtr)) { fprintf (stdout, "\n", HTML_ESCAPE(cptr)); } /* this in case a 'reply', etc., button is used */ rdptr->ComposeData.ContentTypeIs = mdptr->ContentTypeIs; if (mdptr->ContentTypeIs == MIME_CONTENT_TEXT_PLAIN) { if (MimeDecProcessPart (mdptr)) { LineCount = MessageLineCount (mdptr->PartContentPtr); fprintf (stdout, "\n", stdout); } } else if (mdptr->ContentTypeIs == MIME_CONTENT_TEXT_HTML) { if (MimeDecProcessPart (mdptr)) { /* bit hit-and-miss for HTML but what the hey! */ LineCount = MessageLineCount (mdptr->PartContentPtr); if (rdptr->PrivateAccess) sprintf (PartUri, "%s/%s/%s/%d.%d/--%s/--%s", rdptr->FormAction, rdptr->MailFileName, HTML_ESCAPE(rdptr->FolderName), rdptr->MessageId, rdptr->MessageHash, HTML_ESCAPE(mdptr->PartName), HTML_ESCAPE(mdptr->PartName)); else if (rdptr->PublicWildcard) sprintf (PartUri, "%s%s/%d.%d/--%s/--%s", rdptr->FormAction, HTML_ESCAPE(rdptr->FolderName), rdptr->MessageId, rdptr->MessageHash, HTML_ESCAPE(mdptr->PartName), HTML_ESCAPE(mdptr->PartName)); else sprintf (PartUri, "%s%d.%d/--%s/--%s", rdptr->FormAction, rdptr->MessageId, rdptr->MessageHash, HTML_ESCAPE(mdptr->PartName), HTML_ESCAPE(mdptr->PartName)); fprintf (stdout, "\n", MessageCharSetDir(mdptr->CharSetPtr), PartUri); } } else ErrorExit (SS$_BUGCHECK, FI_LI); } fprintf (stdout, "
\
",
                     MessageCharSetDir(mdptr->CharSetPtr));

            if (rdptr->MassageMessage)
               MessageMassage (mdptr->PartContentPtr, rdptr->MassageMessage);

            CgiLib__soyMAILhack = MailTo;
            CgiLibVeeMemContinue (1);
            if (sptr = HTML_ANCHOR(mdptr->PartContentPtr))
               fputs (sptr, stdout);
            else
               fputs ("%LIB-F-INSVIRMEM, insufficient virtual memory\n", stdout);
            CgiLibVeeMemContinue (0);
            CgiLib__soyMAILhack = NULL;

            fputs ("
\n\n\n"); return (LineCount); } /*****************************************************************************/ /* Display (any) message attachment buttons. */ void MessageAttachments (REQUEST_DATA *rdptr) { int len, status, ImageCount, PartCount; char *cptr, *sptr, *zptr, *ButtonNamePtr; char scratch [256], PartUri [512]; MIME_DATA Rfc822mime; MIME_DATA *mdptr; USER_OPTIONS *uoptr; VMS_MAIL_MSG *vmptr; VMS_MAIL_USER *muptr; /*********/ /* begin */ /*********/ if (WatchEnabled) WatchThis ("MessageAttachments()"); muptr = &VmsMailUser; vmptr = &muptr->VmsMailMsg; uoptr = &rdptr->UserOptions; /* if a MIME message is 'broken' there will be no MIME data at all!! */ if (!(mdptr = vmptr->MimeDataPtr)) return; fprintf (stdout, "\n\

\n\ \n"); if (vmptr->LooksLikeRfc) { /***************/ /* RFC822 part */ /***************/ memset (&Rfc822mime, 0, sizeof(Rfc822mime)); zptr = (sptr = scratch) + sizeof(scratch)-1; if (vmptr->RfcSubjPtr) for (cptr = vmptr->RfcSubjPtr; *cptr && sptr < zptr; *sptr++ = *cptr++); else if (vmptr->Subject[0]) for (cptr = vmptr->Subject; *cptr && sptr < zptr; *sptr++ = *cptr++); else for (cptr = vmptr->Date; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = ".eml"; *cptr && sptr < zptr; *sptr++ = *cptr++); *sptr++ = '\0'; Rfc822mime.NamePtr = CgiLibVeeMemCalloc (sptr - scratch); strcpy (Rfc822mime.NamePtr, scratch); strcpy (Rfc822mime.PartName, "message_part_999"); Rfc822mime.ContentTypePtr = "message/rfc822"; Rfc822mime.PartCount = 999; Rfc822mime.NextMimePtr = mdptr; mdptr = &Rfc822mime; } /****************/ /* (MIME) parts */ /****************/ if (uoptr->ImageInline <= 0) uoptr->ImageInline = 0; else if (uoptr->ImageInline <= 25) uoptr->ImageInline = 25; else if (uoptr->ImageInline <= 50) uoptr->ImageInline = 50; else if (uoptr->ImageInline <= 75) uoptr->ImageInline = 75; else if (uoptr->ImageInline > 100) uoptr->ImageInline = 100; ImageCount = PartCount = 0; for (; mdptr; mdptr = mdptr->NextMimePtr) { /************/ /* continue */ /************/ if (mdptr->PartCount == 0) ButtonNamePtr = LangFor("attach_message"); else if (!(ButtonNamePtr = mdptr->NamePtr)) { if (ButtonNamePtr = mdptr->FileNamePtr) { while (*ButtonNamePtr) ButtonNamePtr++; while (ButtonNamePtr > mdptr->FileNamePtr) { if (*ButtonNamePtr == '\\' || *ButtonNamePtr == '/' || *ButtonNamePtr == ']') break; ButtonNamePtr--; } if (ButtonNamePtr > mdptr->FileNamePtr) ButtonNamePtr++; } else ButtonNamePtr = mdptr->PartName; } if (!mdptr->ContentTypeIsMultipart) { if (rdptr->PrivateAccess) sprintf (PartUri, "%s/%s/%s/%d.%d/%s/%s", rdptr->FormAction, URL_ENCODE(rdptr->MailFileName), URL_ENCODE(rdptr->FolderName), rdptr->MessageId, rdptr->MessageHash, URL_ENCODE(mdptr->PartName), mdptr->NamePtr ? URL_ENCODE(mdptr->NamePtr) : URL_ENCODE(mdptr->PartName)); else if (rdptr->PublicWildcard) sprintf (PartUri, "%s%s/%d.%d/%s/%s", rdptr->FormAction, URL_ENCODE(rdptr->FolderName), rdptr->MessageId, rdptr->MessageHash, URL_ENCODE(mdptr->PartName), mdptr->NamePtr ? URL_ENCODE(mdptr->NamePtr) : URL_ENCODE(mdptr->PartName)); else sprintf (PartUri, "%s%d.%d/%s/%s", rdptr->FormAction, rdptr->MessageId, rdptr->MessageHash, URL_ENCODE(mdptr->PartName), mdptr->NamePtr ? URL_ENCODE(mdptr->NamePtr) : URL_ENCODE(mdptr->PartName)); fprintf (stdout, "\n\ \n"); PartCount = mdptr->PartCount; } fprintf (stdout, "
\ %s ", PartUri, SAVE_AS_LINK); if (cptr = SoyMailConfig.AttachmentMimeTypes) { /********************/ /* separate window? */ /********************/ while (*cptr) { sptr = mdptr->ContentTypePtr; if (WatchEnabled) WatchThis ("MIME \"!AZ\" !AZ", sptr, cptr); while (*cptr && !isspace(*cptr) && *sptr && tolower(*cptr) == tolower(*sptr)) { cptr++; sptr++; } if (!*cptr || isspace(*cptr)) { cptr = NULL; break; } while (*cptr && !isspace(*cptr)) cptr++; while (*cptr && isspace(*cptr)) cptr++; } } /* cptr is non-NULL to indicate hit */ if (!cptr) { /******/ /* no */ /******/ /* default or internally handled type */ fprintf (stdout, "", HTML_ESCAPE(ButtonNamePtr), mdptr->PartCount); } else { /*******/ /* yes */ /*******/ /* not an internally handled type (with JavaScript) */ fprintf (stdout, "", mdptr->PartCount, mdptr->PartCount, mdptr->PartCount, EscapeForQuotes(PartUri), HTML_ESCAPE(EscapeForQuotes(ButtonNamePtr)), mdptr->PartCount, mdptr->PartCount); } fprintf (stdout, " \ \  \ ", mdptr->PartCount, mdptr->PartCount, mdptr->PartCount, EscapeForQuotes(PartUri), mdptr->PartCount, mdptr->PartCount, mdptr->PartCount, mdptr->PartCount, mdptr->ContentTypePtr ? HTML_ESCAPE(mdptr->ContentTypePtr) : "application/octet-stream", mdptr->PartCount, mdptr->PartCount, HTML_ESCAPE(EscapeForQuotes(ButtonNamePtr))); if (uoptr->ImageInline) { /****************/ /* inline image */ /****************/ if (strsame (mdptr->ContentTypePtr, "image/", 6)) { if (ImageCount++) scratch[0]= '\0'; else sprintf (scratch, "
\ %s\n", LangFor("message_image_inline")); /* bit convoluted; avoid saturating server CGI(plus) */ /* delay image load by the set period for each */ fprintf (stdout, "%s\
\n\ \n", scratch, mdptr->PartCount, mdptr->PartCount, uoptr->ImageInline, EscapeForQuotes(PartUri), HTML_ESCAPE(EscapeForQuotes(ButtonNamePtr)), SoyMailConfig.ImageInline ? SoyMailConfig.ImageInline * ImageCount : 500 * ImageCount); } } } if (mdptr->PartCount == 999) { /******************************/ /* save/extract/reset buttons */ /******************************/ fputs ("  ", stdout); if (rdptr->PrivateAccess) { fprintf (stdout, " ", LangFor ("attach_save_part")); if (!SoyMailConfig.VmsOccluded) fprintf (stdout, " ", LangFor ("attach_extract_part")); } fprintf (stdout, "", LangFor ("reset")); } /***************/ /* finish here */ /***************/ fprintf (stdout, "
\n\ \n\ \n\ \n\n", PartCount); } /*****************************************************************************/ /* Display the [reply], [reply to all], [forward], etc., buttons. */ void MessageBodyButtons (REQUEST_DATA *rdptr) { int status, value; char MassageBuffer [256]; USER_OPTIONS *uoptr; VMS_MAIL_MSG *vmptr; VMS_MAIL_USER *muptr; /*********/ /* begin */ /*********/ if (WatchEnabled) WatchThis ("MessageBodyButtons()"); muptr = &VmsMailUser; vmptr = &muptr->VmsMailMsg; uoptr = &rdptr->UserOptions; fprintf (stdout, "\n\

\n\ \n\ \n
\n\ \ \n\ \ \n\ \ \n\ \ \n", LangFor("reply"), LangFor("reply_all"), LangFor("forward"), LangFor("edit_as_new")); if (rdptr->ReadPlainText && rdptr->HtmlTextIsPresent) { fprintf (stdout, "\n", LangFor("message_read_html")); } else if (rdptr->ReadHtmlText && rdptr->PlainTextIsPresent) { fprintf (stdout, "\n", LangFor("message_read_plain")); } if (rdptr->ReadPlainText) { value = ComposeWrapAt (rdptr->MassageMessage, uoptr); if (value < 0) value = ComposeWrapAt (value, uoptr); if (value) fprintf (stdout, "\n", LangFor("message_massage_on"), value); else fprintf (stdout, "\n", LangFor("message_massage_off")); } fprintf (stdout, "\n\ \n\ \n\ \n\ \n\ \n", LangFor("prev"), CLICK_NOMORE_STARTWORKING(vmptr->MessageId == 1), LangFor("folder_at"), CLICK_STARTWORKING, LangFor("next"), CLICK_NOMORE_STARTWORKING(vmptr->MessageId == rdptr->FolderMessageCount), LangFor("waste"), CLICK_STARTWORKING, LangFor("delete"), CLICK_STARTWORKING); fprintf (stdout, "
\n\ \n\n"); } /*****************************************************************************/ /* Provide a message in a format more suitable for printing. */ BOOL MessagePrintView (REQUEST_DATA *rdptr) { int cnt, status, ReadPreference; char *cptr, *sptr, *zptr; char MailTo [128], MessageDescription [512]; MIME_DATA *mdptr; USER_OPTIONS *uoptr; REQUEST_DATA *sdptr; VMS_MAIL_MSG *vmptr; VMS_MAIL_USER *muptr; VMS_MAIL_USER UserData; /*********/ /* begin */ /*********/ if (WatchEnabled) WatchThis ("MessagePrintView()"); uoptr = &rdptr->UserOptions; sdptr = (REQUEST_DATA*)rdptr->StateDataPtr; muptr = &VmsMailUser; vmptr = &muptr->VmsMailMsg; /* hack of CGILIB to make "mailto:"s access soyMAIL as mail agent */ zptr = (sptr = MailTo) + sizeof(MailTo)-1; for (cptr = rdptr->FormAction; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = "?mailto:"; *cptr && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; /* use the read preference from the previous request */ if (sdptr->MessageReadPreference) ReadPreference = sdptr->MessageReadPreference; else ReadPreference = uoptr->ReadPreference; vmptr->MessageId = rdptr->MessageId; vmptr->ConfirmHash = rdptr->MessageHash; CallMailMessageGet (muptr, NULL); if (VMSnok (muptr->MailVmsStatus)) { 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)); return (FALSE); } MessageBodyProcess (vmptr); if (rdptr->PrivateAccess) sprintf (MessageDescription, "%s / %s / %s / %04d", rdptr->UserName, rdptr->MailFileName, rdptr->FolderName, rdptr->MessageId); else if (rdptr->PublicAccess) sprintf (MessageDescription, "%s / %04d", rdptr->PublicAccessString, rdptr->MessageId); else MessageDescription[0] = '\0'; mdptr = MessageBodyIdentify (vmptr, ReadPreference); if (mdptr) rdptr->CharSetPtr = mdptr->CharSetPtr; else if (!vmptr->VmsForeign) StatusMessage (FI_LI, 1, "%s", LangFor("mime_type_unknown")); if (rdptr->CharSetPtr) CgiLibResponseSetCharset (rdptr->CharSetPtr); CgiLibResponseHeader (200, "text/html", "%s", rdptr->LoginSetCookiePtr); fprintf (stdout, "%s\n\ \n\ \n\ %s\ \n\ \n\ \n", DocType, CgiLibXUACompatible(NULL), SoftwareEnv, CgiEnvironmentPtr, SoftwareCopy); SoyMailJavaScriptStuff (rdptr); SoyMailStyleStuff (rdptr, "print"); /* setTimeout() gives Firefox 1.5 a chance to render the page */ fprintf (stdout, "%s\n\ \n\n\ \n\ \n\ \n\n", SoyMailPageTitle(rdptr)); if (SoyMailConfig.PrintHeader) { /***************/ /* page header */ /***************/ /* if it appears to contain it's own markup then use that */ if (strchr (SoyMailConfig.PrintHeader, '<')) cptr = ""; else cptr = " style=\"font-size:120%;font-weight:bold;\ text-decoration:underline;text-align:center;\""; fprintf (stdout, "\n\ \n\ \n\n", cptr, SoyMailConfig.PrintHeader); } /******************/ /* message header */ /******************/ fprintf (stdout, "\n\ \n\n"); return; } if (rdptr->SearchData.InProgress) { /* get highest and lowest id from this last search */ LastId = rdptr->SearchData.LastIdSearched; FirstId = rdptr->SearchData.FirstIdSearched; } else if (rdptr->MsgIdListPtr) { /* get highest and lowest id in the current message list */ LastId = atol(rdptr->MsgIdListPtr); FirstId = MessageRangeString (-1, rdptr->MsgIdListPtr); } else FirstId = LastId = 0; if (FirstId > LastId) { /* can happen when using the [prev][next] buttons */ ScratchId = LastId; LastId = FirstId; FirstId = ScratchId; } if (WatchEnabled) WatchThis ("first:!UL last:!UL", FirstId, LastId); fprintf (stdout, "\n\ \n\ \n\n", LangFor("next"), CLICK_NOMORE_STARTWORKING(LastId == rdptr->FolderMessageCount)); } /*****************************************************************************/ /* Generate the list of messages. */ void MessageListInit (REQUEST_DATA *rdptr) { int cnt, status; unsigned long EndSeconds, StartSeconds; char *cptr, *fldptr, *sptr, *zptr; char MessageBuffer [512]; USER_OPTIONS *uoptr; VMS_MAIL_MSG *vmptr, *vmlptr; VMS_MAIL_USER *muptr; /*********/ /* begin */ /*********/ if (WatchEnabled) WatchThis ("MessageListInit() !SL !SL !SL", rdptr->RangeIdStart, rdptr->RangeIdCount, rdptr->RangeIdVector); muptr = &VmsMailUser; vmptr = &muptr->VmsMailMsg; uoptr = &rdptr->UserOptions; muptr->ListMessageIdStart = rdptr->RangeIdStart; muptr->ListMessageIdCount = rdptr->RangeIdCount; muptr->ListMessageIdVector = rdptr->RangeIdVector; if (rdptr->SearchData.InProgress) { muptr->SearchFunction = &SearchMessage; muptr->SearchDataPtr = (void*)&rdptr->SearchData; muptr->ListMessageIdMax = rdptr->SearchData.ResultsEvery; if (SoyMailConfig.SelectNotSearch) muptr->SelectNotSearch = SoyMailConfig.SelectNotSearch; else muptr->SelectNotSearch = rdptr->SearchData.SelectNotSearch; rdptr->SearchData.SearchCount = rdptr->SearchData.HitCount = rdptr->SearchData.FirstIdSearched = rdptr->SearchData.LastIdSearched = 0; time (&StartSeconds); } else rdptr->SearchData.SearchSeconds = 0; CallMailMessageList (muptr); if (VMSnok (muptr->MailVmsStatus)) { /* shouldn't get any of this nonsense with vanilla callable Mail */ if (!uoptr->PmdfImap) return; /* FISH is used to indicate a non-existant PMDF IMAP MAIL.MAI Folders can non-exist because they are listed in PMDF IMAP files. */ if (muptr->MailVmsStatus != SS$_FISH && muptr->MailVmsStatus != MAIL$_NOTEXIST) return; muptr->MailVmsStatus = SS$_NORMAL; } if (rdptr->SearchData.InProgress) { time (&EndSeconds); rdptr->SearchData.SearchSeconds = EndSeconds - StartSeconds; muptr->SearchFunction = NULL; muptr->SearchDataPtr = NULL; muptr->ListMessageIdMax = 0; /* restore if the process priority was changed during the search */ SearchRestorePriority (); } if (Debug) fprintf (stdout, "%%X%08.08X MessageListCount:%d\n", muptr->MailVmsStatus, muptr->MessageListCount); if (rdptr->PrivateAccess) fldptr = FolderDescribe (rdptr->MailFileName, rdptr->FolderName); else if (rdptr->PublicWildcard) fldptr = HTML_ESCAPE(rdptr->FolderName); else fldptr = rdptr->PublicAccessString; if (VMSnok (muptr->MailVmsStatus)) { if (muptr->MailVmsStatus == MAIL$_OPENIN && muptr->MailFileDeletedCount) StatusMessage (FI_LI, -1, LangFor("mail_file_deleted"), rdptr->MailFileName); else if (muptr->MailVmsStatus == MAIL$_OPENIN && vmptr->ExtIdLength) StatusMessage (FI_LI, 1, "MAIL: %s %s", SysGetMsg(muptr->MailVmsStatus), vmptr->ExtId); else if (muptr->MailVmsStatus == MAIL$_NOTEXIST) StatusMessage (FI_LI, 0, LangFor("folder_notexist"), HTML_ESCAPE(fldptr)); else StatusMessage (FI_LI, 1, "MAIL: %s.", SysGetMsg(muptr->MailVmsStatus)); /* not a razoo */ rdptr->RangeIdStart = rdptr->RangeIdCount = rdptr->FolderMessageCount = 0; } else { if (rdptr->SearchData.InProgress) { sptr = MessageBuffer; sptr += sprintf (sptr, LangFor("search_searched"), HTML_ESCAPE(fldptr), vmptr->MessageSelectedCount, rdptr->SearchData.SearchCount, HTML_ESCAPE(rdptr->SearchData.Criteria)); if (rdptr->SearchData.SearchSeconds >= 10) sprintf (sptr, "  %s: %02.02d:%02.02d.", LangFor("duration"), rdptr->SearchData.SearchSeconds / 60, rdptr->SearchData.SearchSeconds % 60); StatusMessage (FI_LI, 0, "%s", MessageBuffer); } else { CGIVARNULL (cptr, "FORM_LIST_DEST_CHANGE"); if (cptr && *cptr) CGIVARNULL (cptr, "FORM_FOLDER_LIST_DEST"); if (cptr && *cptr) { fldptr = FolderDescribe (NULL, cptr); StatusMessage (FI_LI, -1, LangFor("folder_dest"), fldptr); } else { /* a folder has been opened */ status = CallMailUserDiskQuota (muptr); if (VMSnok (status)) StatusMessage (FI_LI, 1, "DISK-QUOTA: %s.", SysGetMsg(status)); else { sprintf (MessageBuffer, LangFor("folder_opened"), HTML_ESCAPE(fldptr), vmptr->MessageSelectedCount); StatusMessage (FI_LI, 0, "%s%s", MessageBuffer, MessageDiskQuotaUsed(rdptr)); } } } /* how many messages are in the folder */ rdptr->FolderMessageCount = vmptr->MessageSelectedCount; } if (rdptr->MsgIdListPtr) { /* it is also used to store checkbox numbers */ CgiLibVeeMemFree (rdptr->MsgIdListPtr); rdptr->MsgIdListPtr = NULL; } rdptr->MsgIdListCount = muptr->MessageListCount; if (rdptr->MsgIdListCount) { rdptr->MsgIdListPtr = sptr = CgiLibVeeMemCalloc (rdptr->MsgIdListCount * 32); if (!rdptr->MsgIdListPtr) ErrorExit (vaxc$errno, FI_LI); cptr = ""; for (vmlptr = muptr->MessageList; vmlptr; vmlptr = vmlptr->NextPtr) { sptr += sprintf (sptr, "%s%d.%d", cptr, vmlptr->CurrentId, vmlptr->MessageHash); cptr = ","; } } else rdptr->MsgIdListPtr = ""; } /*****************************************************************************/ /* Display the basic message folder and message list page. */ void MessageFolderList (REQUEST_DATA *rdptr) { int cnt, idx, status; BOOL BoxChecked, DraftItemsFolder, ListDestChange, SentItemsFolder; char *cptr, *sptr, *zptr; char AddrBuffer [256], FolderDest [256], MsgCbxString [32], PreviewUri [512], SubjBuffer [256]; USER_OPTIONS *uoptr; VMS_MAIL_MSG *vmptr; VMS_MAIL_USER *muptr; /*********/ /* begin */ /*********/ if (WatchEnabled) WatchThis ("MessageFolderList()"); muptr = &VmsMailUser; uoptr = &rdptr->UserOptions; vmptr = &muptr->VmsMailMsg; fprintf (stdout, "

\n\

\n\ \n\ %s\n\

\n\

\n\ \n\ \ \ \n\ \ \n\ \ \ \n\ \ \n", LangFor("subject"), vmptr->MsgSubjPtr[0] ? HTML_TEXT(vmptr->MsgSubjPtr) : LangFor("(none)"), InetMailRfcVmsDate(CurrentVmsTimeString), LangFor("from"), vmptr->MsgFromPtr[0] ? HTML_ADDR(vmptr->MsgFromPtr) : LangFor("(none)"), LangFor("date"), HTML_TEXT(vmptr->MsgDatePtr), vmptr->LooksLikeRfc ? "  [" : "", vmptr->LooksLikeRfc ? InetMailRfcVmsDate(vmptr->Date) : "", vmptr->LooksLikeRfc ? "]" : "", LangFor("to"), vmptr->MsgToPtr[0] ? HTML_ADDR(vmptr->MsgToPtr) : LangFor("(none)")); if (vmptr->MsgCcPtr[0]) fprintf (stdout, "\ \n", LangFor("cc"), HTML_ADDR(vmptr->MsgCcPtr)); fprintf (stdout, "\ \n\ \n\n", LangFor("message"), HTML_ESCAPE(MessageDescription)); mdptr = MessageBodyIdentify (vmptr, ReadPreference); if (mdptr) { if (mdptr->ContentTypeIs == MIME_CONTENT_TEXT_PLAIN) { if (MimeDecProcessPart (mdptr)) { fprintf (stdout, "\n", stdout); } } else if (mdptr->ContentTypeIs == MIME_CONTENT_TEXT_HTML) { fprintf (stdout, "\n"); } else ErrorExit (SS$_BUGCHECK, FI_LI); } fprintf (stdout, "\n\ \n\n"); /*******************/ /* any attachments */ /*******************/ for (mdptr = vmptr->MimeDataPtr; mdptr; mdptr = mdptr->NextMimePtr) if (mdptr->NamePtr || mdptr->FileNamePtr) break; if (mdptr) { fprintf (stdout, "\n\ \n\ \n\n"); } if (SoyMailConfig.PrintFooter) { /***************/ /* page footer */ /***************/ /* if it appears to contain it's own markup then use that */ if (strchr (SoyMailConfig.PrintFooter, '<')) cptr = ""; else cptr = " style=\"font-weight:bold;text-align:center;\""; fprintf (stdout, "\n\ \n\ \n\n", cptr, SoyMailConfig.PrintFooter); } /************/ /* page end */ /************/ fprintf (stdout, "
%s:%s%s
%s:%s
%s:%s%s%s%s
%s:%s
%s:%s
%s:%s

\
",
            MessageCharSetDir(mdptr->CharSetPtr));

            if (sdptr->MassageMessage)
               MessageMassage (mdptr->PartContentPtr, sdptr->MassageMessage);

            CgiLib__soyMAILhack = MailTo;
            CgiLibVeeMemContinue (1);
            if (sptr = HTML_ANCHOR(mdptr->PartContentPtr))
               fputs (sptr, stdout);
            else
               fputs ("%LIB-F-INSVIRMEM, insufficient virtual memory\n", stdout);
            CgiLibVeeMemContinue (0);
            CgiLib__soyMAILhack = NULL;

            fputs ("
\

\n\ \n"); while (mdptr) { fprintf (stdout, "\ \n", mdptr->NamePtr ? mdptr->NamePtr : mdptr->FileNamePtr, mdptr->ContentTypePtr ? mdptr->ContentTypePtr : "?"); mdptr = mdptr->NextMimePtr; } fprintf (stdout, "
[%s]%s
\n\

\n\ \n\ %s\n\
\n\

\n\ \n\ \n"); return (TRUE); } /*****************************************************************************/ /* Display the entire mail message (header and body) as a plain text document. */ BOOL MessageRawView (REQUEST_DATA *rdptr) { int status; VMS_MAIL_MSG *vmptr; VMS_MAIL_USER *muptr; /*********/ /* begin */ /*********/ if (WatchEnabled) WatchThis ("MessageRawView()"); muptr = &VmsMailUser; vmptr = &muptr->VmsMailMsg; vmptr->MessageId = rdptr->MessageId; vmptr->ConfirmHash = rdptr->MessageHash; CallMailMessageGet (muptr, NULL); if (VMSnok (muptr->MailVmsStatus)) { 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)); return (FALSE); } InetMailParseHeader (vmptr); if (!vmptr->LooksLikeRfc) { StatusMessage (FI_LI, 1, "%s", LangFor("not_applicable")); return (FALSE); } CgiLibResponseHeader (200, "text/plain", "%s", rdptr->LoginSetCookiePtr); fputs (vmptr->BodyPtr, stdout); return (TRUE); } /*****************************************************************************/ /* Display the RFC822/2822 header as a plain text document. */ BOOL MessageHdrView (REQUEST_DATA *rdptr) { int status; char ch; VMS_MAIL_MSG *vmptr; VMS_MAIL_USER *muptr; /*********/ /* begin */ /*********/ if (WatchEnabled) WatchThis ("MessageHdrView()"); muptr = &VmsMailUser; vmptr = &muptr->VmsMailMsg; vmptr->MessageId = rdptr->MessageId; vmptr->ConfirmHash = rdptr->MessageHash; CallMailMessageGet (muptr, NULL); if (VMSnok (muptr->MailVmsStatus)) { 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)); return (FALSE); } InetMailParseHeader (vmptr); if (!vmptr->LooksLikeRfc) { StatusMessage (FI_LI, 1, "%s", LangFor("not_applicable")); return (FALSE); } CgiLibResponseHeader (200, "text/plain", "%s", rdptr->LoginSetCookiePtr); if (vmptr->RfcHeaderPtr) { ch = vmptr->RfcHeaderPtr[vmptr->RfcHeaderLength]; vmptr->RfcHeaderPtr[vmptr->RfcHeaderLength] = '\0'; fputs (vmptr->RfcHeaderPtr, stdout); vmptr->RfcHeaderPtr[vmptr->RfcHeaderLength] = ch; } else fputs (vmptr->BodyPtr, stdout); return (TRUE); } /*****************************************************************************/ /* Provide a message preview. */ void MessagePreView (REQUEST_DATA *rdptr) { static char AttachReport [] = "%d %s", StatusReport [] = "%s"; int attcnt, chcnt, status, ReadPreference; char *aptr, *cptr, *sptr, *zptr, *rtlptr = "", *PreviewText = ""; char StatusText [512] = ""; MIME_DATA *mdptr; USER_OPTIONS *uoptr; REQUEST_DATA *sdptr; VMS_MAIL_MSG *vmptr; VMS_MAIL_USER *muptr; /*********/ /* begin */ /*********/ if (WatchEnabled) WatchThis ("MessagePreView()"); uoptr = &rdptr->UserOptions; sdptr = (REQUEST_DATA*)rdptr->StateDataPtr; muptr = &VmsMailUser; vmptr = &muptr->VmsMailMsg; RequestVmsMailContext (rdptr); vmptr->MessageId = rdptr->MessageId; vmptr->ConfirmHash = rdptr->MessageHash; CallMailMessageGet (muptr, NULL); if (VMSnok (muptr->MailVmsStatus)) { if (muptr->MailVmsStatus == SS$_BADCHECKSUM || muptr->MailVmsStatus == MAIL$_NOMOREMSG) sprintf (StatusText, StatusReport, LangFor("message_checksum")); else sprintf (StatusText, StatusReport, SysGetMsg(muptr->MailVmsStatus)); } if (!StatusText[0]) { MessageBodyProcess (vmptr); if (rdptr->MessageReadPreference) ReadPreference = rdptr->MessageReadPreference; else ReadPreference = uoptr->ReadPreference; mdptr = MessageBodyIdentify (vmptr, ReadPreference); if (mdptr) { rdptr->CharSetPtr = mdptr->CharSetPtr; if (!strcmp (MessageCharSetDir(mdptr->CharSetPtr), "rtl")) rtlptr = " dir=\"rtl\""; } else if (!vmptr->VmsForeign) sprintf (StatusText, StatusReport, LangFor("mime_type_unknown")); } if (!StatusText[0]) { attcnt = 0; for (mdptr = vmptr->MimeDataPtr; mdptr; mdptr = mdptr->NextMimePtr) if (mdptr->NamePtr) attcnt++; if (!(mdptr = MessageBodyIdentify (vmptr, ReadPreference))) sprintf (StatusText, StatusReport, "?"); } if (rdptr->CharSetPtr) CgiLibResponseSetCharset (rdptr->CharSetPtr); CgiLibResponseHeader (200, "text/html", "%s", rdptr->LoginSetCookiePtr); PreviewText = CgiLibVeeMemCalloc (uoptr->PreviewSize+256); if (!PreviewText) ErrorExit (vaxc$errno, FI_LI); zptr = (sptr = PreviewText) + uoptr->PreviewSize; if (StatusText[0]) for (cptr = StatusText; *cptr; *sptr++ = *cptr++); else { MimeDecProcessPart (mdptr); mdptr->PartContentLength = MessageRemovePgpSig (mdptr->PartContentPtr); if (mdptr->ContentTypeIs == MIME_CONTENT_TEXT_HTML) HtmlSanitise (mdptr->PartContentPtr); for (cptr = "
PartContentPtr;
           *cptr == '\r' || *cptr == '\n';
           cptr++);
      chcnt = 0;
      while (*cptr && sptr < zptr)
      {
         switch (*cptr)
         {
            case '<' :
               strcpy (sptr, "<");
               sptr += 4;
               chcnt++;
               break;
            case '>' :
               strcpy (sptr, ">");
               sptr += 4;
               chcnt++;
               break;
            case '&' :
               strcpy (sptr, "&");
               sptr += 5;
               chcnt++;
               break;
            case '\'' :
               strcpy (sptr, "'");
               sptr += 5;
               chcnt++;
               break;
            case ' ' :
            case '\t' :
               *sptr++ = ' ';
               chcnt++;
               break;
            case '\r' :
            case '\n' :
               while (*(cptr+1) == '\r' || *(cptr+1) == '\n') cptr++;
               if (!*(cptr+1) || sptr >= zptr) break;
               *sptr++ = '\\';
               *sptr++ = 'n';
               chcnt = 0;
               break;
            default :
               *sptr++ = *cptr;
               chcnt++;
         }
         cptr++;
         if (chcnt > 96)
         {
            strcpy (sptr, "\\n↵");
            sptr += 9;
            chcnt = 0;
         }
      }
      if (sptr >= zptr)
      {
         while (sptr > PreviewText && *(sptr-1) != ' ') sptr--;
         if (sptr > PreviewText) sptr--;
         while (sptr > PreviewText && *(sptr-1) == ' ') sptr--;
      }
      *sptr = '\0';
      if (sptr == aptr)
         sptr = PreviewText;
      else
      {
         if (*cptr)
         {
            while (sptr > PreviewText && *(USHORTPTR)(sptr-2) == '\\n')
               sptr -= 2;
            for (cptr = " ✄";
                 *cptr;
                 *sptr++ = *cptr++); 
         }
         for (cptr = "
"; *cptr; *sptr++ = *cptr++); } *sptr = '\0'; if (!PreviewText[0]) sprintf (StatusText, StatusReport, LangFor("message_empty")); if (attcnt) sptr += sprintf (sptr, AttachReport, attcnt, LangFor("message_attachments")); if (StatusText[0]) { if (PreviewText[0]) for (cptr = " "; *cptr; *sptr++ = *cptr++); for (cptr = StatusText; *cptr; *sptr++ = *cptr++); } *sptr = '\0'; } fprintf (stdout, "%s\n\ \n\ \n\ \n\ \n\ \n\ \n\ \n", DocType, rdptr->MessageId, PreviewText); } /*****************************************************************************/ /* Looks through each of (any) MIME parts for a likely message text. Likely parts are the first "text/plain" of a multipart without a file name, or a non-multipart "text/plain" body. If the message is not MIME then allocate and populate a MIME structure using the textual body. The 'ReadPreference' parameter provides the OPTIONS_READ_.. constants that specify whether plain or HTML text is the preference. Return a pointer to the MIME data representing the message body, NULL if there is a problem. */ MIME_DATA* MessageBodyIdentify ( VMS_MAIL_MSG *vmptr, int ReadPreference ) { char *cptr; MIME_DATA *mdptr, *HtmlMimePtr, *PlainMimePtr; /*********/ /* begin */ /*********/ if (WatchEnabled) WatchThis ("MessageBodyIdentify() !UL", ReadPreference); if (vmptr->VmsForeign) return (NULL); HtmlMimePtr = PlainMimePtr = NULL; mdptr = vmptr->MimeDataPtr; if (mdptr) { if (mdptr->ContentTypeIsMultipart) { /* a MIME message with a multipart content-type */ for (mdptr = vmptr->MimeDataPtr; mdptr; mdptr = mdptr->NextMimePtr) { /* get the first instance of each of these */ if (mdptr->ContentTypeIs == MIME_CONTENT_TEXT_PLAIN) { /* looking for plain text part without a file name */ if (!PlainMimePtr) if (!mdptr->NamePtr && !mdptr->FileNamePtr) PlainMimePtr = mdptr; } else if (mdptr->ContentTypeIs == MIME_CONTENT_TEXT_HTML) { /* looking for HTML text part without a file name */ if (!HtmlMimePtr) if (!mdptr->NamePtr && !mdptr->FileNamePtr) HtmlMimePtr = mdptr; } } } else { /* look for the first plain-text and first HTML non-named parts */ for (mdptr = vmptr->MimeDataPtr; mdptr; mdptr = mdptr->NextMimePtr) if (mdptr->ContentTypeIs == MIME_CONTENT_TEXT_PLAIN) if (!mdptr->NamePtr && !mdptr->FileNamePtr) break; PlainMimePtr = mdptr; for (mdptr = vmptr->MimeDataPtr; mdptr; mdptr = mdptr->NextMimePtr) if (mdptr->ContentTypeIs == MIME_CONTENT_TEXT_HTML) if (!mdptr->NamePtr && !mdptr->FileNamePtr) break; HtmlMimePtr = mdptr; } } mdptr = NULL; if (ReadPreference == OPTIONS_READ_PLAIN1_HTML2) { if (PlainMimePtr) mdptr = PlainMimePtr; else mdptr = HtmlMimePtr; } else if (ReadPreference == OPTIONS_READ_PLAIN2_HTML1) { if (HtmlMimePtr) mdptr = HtmlMimePtr; else mdptr = PlainMimePtr; } else if (ReadPreference == OPTIONS_READ_PLAIN0_HTML1) { /* only used to 'probe' whether an HTML alternative is present! */ if (HtmlMimePtr) mdptr = HtmlMimePtr; } else { /* anything else, including zero (not set), gets any plain text */ mdptr = PlainMimePtr; } if (WatchEnabled) { if (mdptr) WatchThis ("BODY !AZ", mdptr->ContentTypePtr); else WatchThis ("BODY not found"); } return (mdptr); } /*****************************************************************************/ /* Make a message (ostensibly) more readable by ... 1) Remove any PGP signature 2) Collapse extra line breaks 3) Wrap lines longer than specified by the macro SOY_WRAP_AT All these are performed on the text in situ! */ int MessageMassage ( char *TextPtr, int WrapAt ) { int colnum; char *cptr, *sptr, *tptr; /*********/ /* begin */ /*********/ if (WatchEnabled) WatchThis ("MessageMassage() %d\n", WrapAt); if (WrapAt < 0) WrapAt = atoi(DEFAULT_WRAP_AT); MessageRemovePgpSig (TextPtr); MessageCollapseLineBreaks (TextPtr, 2); colnum = 0; cptr = sptr = TextPtr; while (*cptr) { if (*cptr == '\n') { colnum = 0; cptr++; continue; } if (colnum++ < WrapAt) { cptr++; continue; } for (tptr = cptr; tptr >= sptr; tptr--) if (isspace(*tptr)) break; if (tptr >= sptr) { /* found an intervening space */ if (*(tptr+1) == '\n') tptr++; else *tptr++ = '\n'; cptr = tptr; colnum = 0; } else { /* no intervening space, find next */ while (*cptr && !isspace(*cptr)) { colnum++; cptr++; } } sptr = cptr; } return (cptr-TextPtr); } /*****************************************************************************/ /* Eliminate all empty lines and then collapse the number of consecutive newlines down to whatevers specified in the parameter. */ int MessageCollapseLineBreaks ( char *StringPtr, int MaxConsecutive ) { int nlcnt; char *cptr, *sptr, *solptr; /*********/ /* begin */ /*********/ if (WatchEnabled) WatchThis ("MessageCollapseLineBreaks() !UL !AZ", MaxConsecutive, StringPtr); for (cptr = sptr = StringPtr; *cptr; *sptr++ = *cptr++) { if (*cptr != '\r') continue; if (*(ULONGPTR)cptr != '\r\n\r\n') continue; cptr += 2; } *sptr = '\0'; if (MaxConsecutive < 1) MaxConsecutive = 1; cptr = sptr = StringPtr; while (*cptr) { solptr = sptr; while (*cptr && isspace(*cptr) && *cptr != '\n') *sptr++ = *cptr++; if (*cptr == '\n') sptr = solptr; else while (*cptr && *cptr != '\n') *sptr++ = *cptr++; if (*cptr) *sptr++ = *cptr++; } *sptr = '\0'; cptr = sptr = StringPtr; while (*cptr == '\n') cptr++; while (*cptr) { while (*cptr && *cptr != '\n') *sptr++ = *cptr++; nlcnt = MaxConsecutive; while (*cptr == '\n' && nlcnt--) *sptr++ = *cptr++; while (*cptr == '\n') cptr++; } while (sptr > StringPtr && *(sptr-1) == '\n') sptr--; *sptr = '\0'; return (sptr-StringPtr); } /*****************************************************************************/ /* Return true if text appears PGP clear-text signed (RFC2440). */ BOOL MessagePgpSigned (char *TextPtr) { /*********/ /* begin */ /*********/ if (WatchEnabled) WatchThis ("MessagePgpSigned()"); if (!TextPtr) return (FALSE); if (memcmp (TextPtr, "-----BEGIN PGP ", 15)) return (FALSE); return (TRUE); } /*****************************************************************************/ /* Remove any PGP clear-text signature (RFC2440) from a message part. */ int MessageRemovePgpSig (char *TextPtr) { char *cptr, *sptr, *zptr; /*********/ /* begin */ /*********/ if (WatchEnabled) WatchThis ("MessageRemovePgpSig()"); /* return if it doesn't appear to be PGP signed text */ if (memcmp (TextPtr, "-----BEGIN PGP ", 15)) return (strlen(TextPtr)); /* find the end-of-armor header empty line */ for (cptr = sptr = TextPtr; *cptr; cptr++) if (*(USHORTPTR)cptr == '\n\n' || *(ULONGPTR)cptr == '\r\n\r\n') break; if (!*cptr) return (strlen(TextPtr)); if (*(USHORTPTR)cptr == '\n\n') cptr += 2; else cptr += 4; while (*cptr) { while (*cptr && *cptr != '\n') *sptr++ = *cptr++; if (!*cptr) break; while (*cptr == '\n') *sptr++ = *cptr++; if (!*cptr) break; /* start of line */ if (*cptr == '-') { if (*(USHORTPTR)cptr == '- ') cptr += 2; else if (!memcmp (cptr, "-----BEGIN PGP ", 15)) break; } } *sptr = '\0'; return (sptr - TextPtr); } /*****************************************************************************/ /* Return number of newline characters (lines) in text. */ int MessageLineCount (char *TextPtr) { int LineCount = 0; char *cptr; /*********/ /* begin */ /*********/ if (WatchEnabled) WatchThis ("MessageLineCount()"); if (!TextPtr) return (0); for (cptr = TextPtr; *cptr; cptr++) if (*cptr == '\n') LineCount++; if (MessagePgpSigned (TextPtr)) LineCount -= 10; if (LineCount < 0) LineCount = 0; return (LineCount); } /*****************************************************************************/ /* Retrieve the content of a specific MIME part (often an attachment) and provide it to the client using the appropriate content-type. If an HTML part then sanitise the HTML unless it's in a popup (a full window of it's own) and the user has set the appropriate configuration option. */ BOOL MessagePart (REQUEST_DATA *rdptr) { BOOL SanitiseHtml = FALSE; int status, DataLength, PartCount, RightToLeft = 0; char *cptr, *DataPtr; char PartTypeField [64], ResponseBuffer [256]; MIME_DATA *mdptr; USER_OPTIONS *uoptr; VMS_MAIL_MSG *vmptr; VMS_MAIL_USER *muptr; /*********/ /* begin */ /*********/ if (WatchEnabled) WatchThis ("MessagePart()"); muptr = &VmsMailUser; vmptr = &muptr->VmsMailMsg; uoptr = &rdptr->UserOptions; vmptr->MessageId = rdptr->MessageId; vmptr->ConfirmHash = rdptr->MessageHash; CallMailMessageGet (muptr, NULL); if (VMSnok (muptr->MailVmsStatus)) { 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)); return (FALSE); } MessageBodyProcess (vmptr); cptr = rdptr->AttachPartName; if (!*cptr) cptr = rdptr->AttachmentName; if (WatchEnabled) WatchThis ("!AZ", cptr); if (*(USHORTPTR)cptr == '--') /* hyphens */ { SanitiseHtml = TRUE; cptr += 2; } if (strsame (cptr, "message_part_999", -1)) { /******************/ /* message/rfc822 */ /******************/ DataPtr = vmptr->BodyPtr; DataLength = vmptr->BodyLength; CGIVARNULL (cptr, "FORM_PART_TYPE_999"); if (!cptr) if (!*(cptr = rdptr->CgiQueryStringPtr)) cptr = "message/rfc822"; if (!*cptr || !isalpha(*cptr)) cptr = "application/octet-stream"; sprintf (ResponseBuffer, "Content-Length: %d\n", DataLength); CgiLibResponseHeader (200, cptr, ResponseBuffer, rdptr->LoginSetCookiePtr); fwrite (DataPtr, DataLength, 1, stdout); return (TRUE); } if (*cptr) { /*****************/ /* look for part */ /*****************/ for (mdptr = vmptr->MimeDataPtr; mdptr; mdptr = mdptr->NextMimePtr) { if (strsame (mdptr->PartName, cptr, -1)) break; if (!mdptr->ContentTypeIsMultipart) { if (WatchEnabled) WatchThis ("!UL |!AZ|!AZ", mdptr->PartCount, mdptr->PartName, mdptr->NamePtr ? mdptr->NamePtr : "(null)"); if (mdptr->PartName[0] && strsame (mdptr->PartName, cptr, -1)) break; if (mdptr->NamePtr && strsame (mdptr->NamePtr, cptr, -1)) break; } } } else { /* empty part name just allows retrieval of first part */ mdptr = vmptr->MimeDataPtr; } if (mdptr) { /**************/ /* part found */ /**************/ if (MimeDecProcessPart (mdptr)) { DataPtr = mdptr->PartContentPtr; DataLength = mdptr->PartContentLength; if (!mdptr->ContentTypePtr) ErrorExit (SS$_BUGCHECK, FI_LI); /* use the content-type from the form */ sprintf (PartTypeField, "FORM_PART_TYPE_%d", mdptr->PartCount); CGIVARNULL (cptr, PartTypeField); if (!cptr || !*cptr) if (!*(cptr = rdptr->CgiQueryStringPtr)) cptr = mdptr->ContentTypePtr; if (!*cptr || !isalpha(*cptr)) cptr = "application/octet-stream"; } else { /* can't do any more */ StatusMessage (FI_LI, 1, LangFor("part_not_found")); return (FALSE); } } else if (!memcmp (cptr, HTML_IN_DISGUISE, strlen(HTML_IN_DISGUISE))) { /* looks like HTML as text/plain */ DataPtr = vmptr->RfcBodyPtr; DataLength = vmptr->RfcBodyLength; cptr = "text/html"; if (uoptr->MassageRead) DataLength = MessageRemovePgpSig (DataPtr); if (SanitiseHtml) DataLength = HtmlSanitise (DataPtr); } else { /******************/ /* part not found */ /******************/ StatusMessage (FI_LI, 1, LangFor("part_not_found")); return (FALSE); } /***************/ /* return part */ /***************/ if (mdptr && mdptr->ContentTypeIs == MIME_CONTENT_TEXT_HTML) { if (!cptr || !strncmp (cptr, "text/", 5)) { if (uoptr->MassageRead) DataLength = MessageRemovePgpSig (DataPtr); if (SanitiseHtml) DataLength = HtmlSanitise (DataPtr); } /* try to ensure right-to-left directionality is rendered */ if (!strcmp (MessageCharSetDir(mdptr->CharSetPtr), "rtl")) RightToLeft = 33 + 8; } if (mdptr && !strncmp (cptr, "text/", 5) && mdptr->CharSetPtr) sprintf (ResponseBuffer, "; charset=%s\nContent-Length: %d\n%%s", mdptr->CharSetPtr, DataLength+RightToLeft); else sprintf (ResponseBuffer, "Content-Length: %d\n%%s", DataLength+RightToLeft); CgiLibResponseHeader (200, cptr, ResponseBuffer, rdptr->LoginSetCookiePtr); if (RightToLeft) { fwrite (DocType, strlen(DocType), 1, stdout); fwrite ("\n\n", 33, 1, stdout); } fwrite (DataPtr, DataLength, 1, stdout); if (RightToLeft) fwrite ("\n", 8, 1, stdout); return (TRUE); } /*****************************************************************************/ /* Provide an HTML form selection list of the available message ranges. */ void MessageRangeSelect (REQUEST_DATA *rdptr) { int first, last, FirstId, LastId, ScratchId; char *MsgRangeListPtr; VMS_MAIL_USER *muptr; /*********/ /* begin */ /*********/ if (WatchEnabled) WatchThis ("MessageRangeSelect()"); muptr = &VmsMailUser; if (rdptr->FolderMessageCount <= rdptr->UserOptions.MessagesPerPage) { fprintf (stdout, "
\n\  \n", LangFor("prev"), CLICK_NOMORE_STARTWORKING(FirstId == 1)); if (rdptr->SearchData.InProgress) { /* more of a reminder that we're searching than anything else */ fprintf (stdout, " \n", LangFor("search_new")); } else { fprintf (stdout, "\n\ \n\  \n", RANGE_ALL_MSGS, rdptr->RangeIdStart == RANGE_ALL_MSGS ? " selected" : "", LangFor("all")); } fprintf (stdout, "\n\
\n\ \n"); /******************/ /* message ranges */ /******************/ MessageRangeSelect (rdptr); /**************************/ /* message action buttons */ /**************************/ fprintf (stdout, "\n"); fprintf (stdout, "\n\ \n\ \n\n", LangFor("delete"), LANG_FOR_CONFIRM("delete")); /******************/ /* folder buttons */ /******************/ if (uoptr->FolderButtons) { fprintf (stdout, "\n\ \n\n\
\n\ \n\ \n\ \n", LANG_FOR_CONFIRM("all"), LANG_FOR_CONFIRM("all_messages"), LangFor("copy"), LANG_FOR_CONFIRM("copy"), LangFor("move"), LANG_FOR_CONFIRM("move")); MessageDestSelector (rdptr); if (!strcmp (rdptr->FolderName, uoptr->FolderWasteItems) && rdptr->FolderMessageCount) fprintf (stdout, "\n", LangFor("empty"), LANG_FOR_CONFIRM("empty")); else fprintf (stdout, "\n", LangFor("waste"), LANG_FOR_CONFIRM("waste")); fprintf (stdout, "\n\ \n\
\n\ \n\ \n\ \n\ \n\ \n\ \n\ \n
\n"); FolderButtonListEach (rdptr); fprintf (stdout, "\n"); } else fprintf (stdout, "\n\
\n"); if (!muptr->MessageListCount) { /****************/ /* empty folder */ /****************/ rdptr->RangeIdStart = rdptr->RangeIdCount = 0; if (uoptr->FolderButtons) fprintf (stdout, "
\n"); fprintf (stdout, "
\n\n"); MessageListFooter (); return; } /********************/ /* list of messages */ /********************/ DraftItemsFolder = SentItemsFolder = FALSE; if (!strcmp (rdptr->MailFileName, "MAIL")) { if (!strcmp (muptr->VmsMailFolderName, uoptr->FolderDraftItems)) DraftItemsFolder = TRUE; if (!strcmp (muptr->VmsMailFolderName, uoptr->FolderSentItems)) if (strcmp (muptr->VmsMailFolderName, "MAIL") && strcmp (muptr->VmsMailFolderName, "NEWMAIL")) SentItemsFolder = TRUE; } CGIVARNULL (cptr, "FORM_LIST_DEST_CHANGE"); if (cptr && *cptr) CGIVARNULL (cptr, "FORM_FOLDER_LIST_DEST"); if (cptr && *cptr) ListDestChange = TRUE; else ListDestChange = FALSE; if (!DraftItemsFolder && uoptr->PreviewAfter) { if (rdptr->PrivateAccess) sprintf (PreviewUri, "%s/%s/%s/", rdptr->FormAction, rdptr->MailFileName, HTML_ESCAPE(rdptr->FolderName)); else if (rdptr->PublicWildcard) sprintf (PreviewUri, "%s%s/", rdptr->FormAction, HTML_ESCAPE(rdptr->FolderName)); else sprintf (PreviewUri, "%s/", rdptr->FormAction); fprintf (stdout, "\n\ \n", uoptr->PreviewAfter, PreviewUri, (rdptr->UserAgent == SOY_USER_AGENT_MSIE) ? /* MSIE being its usual contrarian self! */ "inline" : "table-row"); } fprintf (stdout, "\n\ \n\ \n\ \ \ \ \ \n", cptr, sptr, LangFor("subject")); if (uoptr->MessageOrder < 0) { /* find the last entry in the list */ for (vmptr = muptr->MessageList; vmptr && vmptr->NextPtr; vmptr = vmptr->NextPtr); } else vmptr = muptr->MessageList; while (vmptr) { if (Debug) fprintf (stdout, "%%X%08.08X id:%d size:%d hash:%d\n|%s|\n|%s|\n|%s|\n|%s|\n|%s|\n\n", muptr->MailVmsStatus, vmptr->CurrentId, vmptr->SizeInRecords, vmptr->MessageHash, vmptr->Date, DraftItemsFolder || SentItemsFolder ? vmptr->To : vmptr->From, vmptr->Subject, vmptr->To, vmptr->Cc); zptr = (sptr = AddrBuffer) + sizeof(AddrBuffer)-1; if (DraftItemsFolder || SentItemsFolder) for (cptr = vmptr->To; *cptr && sptr < zptr; *sptr++ = *cptr++); else for (cptr = vmptr->From; *cptr && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; cnt = MimeDecIsoString (AddrBuffer); if (cnt > 40) *((unsigned long*)(AddrBuffer+40)) = '...\0'; zptr = (sptr = SubjBuffer) + sizeof(SubjBuffer)-1; if (vmptr->Subject[0]) cptr = vmptr->Subject; else cptr = LangFor("(none)"); while (*cptr && sptr < zptr) *sptr++ = *cptr++; *sptr = '\0'; cnt = MimeDecIsoString (SubjBuffer); if (cnt > MSG_LIST_SUBJ_ELLIPSIS) { for (sptr = SubjBuffer + MSG_LIST_SUBJ_ELLIPSIS; sptr > SubjBuffer && *sptr != ' '; sptr--); if (sptr == SubjBuffer) sptr = SubjBuffer + MSG_LIST_SUBJ_ELLIPSIS; *((unsigned long*)sptr) = '...\0'; } BoxChecked = FALSE; if (ListDestChange) { /* reset checkbox if the destination list was refreshed */ sprintf (MsgCbxString, "FORM_MSG_CBX_%d", vmptr->CurrentId); CGIVARNULL (cptr, MsgCbxString); if (cptr) BoxChecked = TRUE; } /* this mix of styles to get both Mozilla and MSIE to look right! */ fprintf (stdout, "\ \ ", vmptr->MessageFlags); if (SoyMailConfig.MessageFlags && vmptr->MessageFlags) fprintf (stdout, "", ((SoyMailConfig.MessageFlags & MAIL$M_REPLIED) && (vmptr->MessageFlags & MAIL$M_NEWMSG)) ? FLAG_NEWMSG : "", ((SoyMailConfig.MessageFlags & MAIL$M_MARKED) && (vmptr->MessageFlags & MAIL$M_MARKED)) ? FLAG_MARKED : "", ((SoyMailConfig.MessageFlags & MAIL$M_MARKED) && (vmptr->MessageFlags & MAIL$M_REPLIED)) ? FLAG_REPLIED : ""); else fprintf (stdout, ""); fprintf (stdout, "\ \ \ \n\ \ \ \ \n", vmptr->CurrentId, vmptr->CurrentId, InetMailRfcVmsDate(vmptr->Date), vmptr->CurrentId, vmptr->CurrentId, HTML_ADDR(AddrBuffer), vmptr->CurrentId, vmptr->CurrentId, HTML_TEXT(SubjBuffer), vmptr->CurrentId, vmptr->CurrentId); if (uoptr->Accessability1) fprintf (stdout, "\ \ \ \n"); if (uoptr->MessageOrder < 0) vmptr = vmptr->PrevPtr; else vmptr = vmptr->NextPtr; } if (!rdptr->SearchData.InProgress) fprintf (stdout, "\ \ \n", LangFor("all"), rdptr->FolderMessageCount, LangFor("all_messages")); fprintf (stdout, "
", rdptr->MsgIdListPtr); /* careful how this is done or Opera produces a checkbox artifact */ fprintf (stdout, ""); if (DraftItemsFolder) { cptr = LangFor("compose_save_last") ; sptr = LangFor("to") ; } else if (SentItemsFolder) { cptr = LangFor("sent") ; sptr = LangFor("to") ; } else { cptr = LangFor("received") ; sptr = LangFor("from") ; } fprintf (stdout, "%s%s%s
\ \ ", vmptr->MessageId, vmptr->MessageFlags & MAIL$M_NEWMSG ? "b" : "", vmptr->MessageId, vmptr->MessageId, vmptr->CurrentId, BoxChecked ? " checked" : "", vmptr->CurrentId, vmptr->MessageHash, vmptr->MessageId, vmptr->MessageId, vmptr->MessageId, vmptr->CurrentId); fprintf (stdout, "%s%s%s %17.17s%s%s

%s %d %s
\n"); if (uoptr->FolderButtons) fprintf (stdout, "\n\n"); fprintf (stdout, "\n\ \n\n\ \n\n"); MessageListFooter (); } /*****************************************************************************/ /* Provide the message [copy][move] destination selector and input field(s). */ void MessageDestSelector (REQUEST_DATA *rdptr) { char *cptr; USER_OPTIONS *uoptr; VMS_MAIL_USER *muptr; /*********/ /* begin */ /*********/ if (WatchEnabled) WatchThis ("MessageDestSelector()"); muptr = &VmsMailUser; uoptr = &rdptr->UserOptions; fprintf (stdout, "\n\ \n"); if (uoptr->AllMailFiles) { CGIVAR (cptr, "FORM_FILE_TEXT_DEST"); fprintf (stdout, "\ /", HTML_ESCAPE(cptr)); } CGIVAR (cptr, "FORM_FOLDER_TEXT_DEST"); fprintf (stdout, "\n", HTML_ESCAPE(cptr)); } /*****************************************************************************/ /* */ void MessageListFooter () { /*********/ /* begin */ /*********/ if (!SoyMailConfig.MessageListFooter) return; fprintf (stdout, "\n\

\n\ \n\ \n\
\ %s\n\
\n\ \n\n", SoyMailConfig.MessageListFooter); } /*****************************************************************************/ /* Copy, with optional delete (i.e. a move), from one folder to another. */ void MessageCopy ( REQUEST_DATA *rdptr, BOOL DeleteAfterCopy ) { int cnt, idx; int *midptr; char *cptr; USER_OPTIONS *uoptr; VMS_MAIL_USER *muptr; /*********/ /* begin */ /*********/ uoptr = &rdptr->UserOptions; muptr = &VmsMailUser; if (WatchEnabled) WatchThis ("MessageCopy() !AZ !AZ/!AZ !AZ/!AZ !AZ", DeleteAfterCopy ? "MOVE" : "COPY", rdptr->MailFileName, rdptr->FolderName, rdptr->DestMailFileName, rdptr->DestFolderName, rdptr->MsgIdListPtr ? rdptr->MsgIdListPtr : "(null)"); if (!strcmp (rdptr->MailFileName, rdptr->DestMailFileName) && !strcmp (rdptr->FolderName, rdptr->DestFolderName)) { StatusMessage (FI_LI, 1, "%s", LangFor("folder_same")); return; } if (!strcmp (rdptr->DestMailFileName, "MAIL")) { if (!strcmp (rdptr->DestFolderName, uoptr->FolderDraftItems)) { StatusMessage (FI_LI, 1, "%s", LangFor("folder_cannot")); return; } } muptr->MsgIdListPtr = rdptr->MsgIdListPtr; muptr->MsgIdListCount = rdptr->MsgIdListCount; if (!muptr->MsgIdListCount) { StatusMessage (FI_LI, 1, "%s", LangFor("none_selected")); return; } if (!muptr->VmsMailDestFolderNameLength) { StatusMessage (FI_LI, 1, "%s", LangFor("dest_not_specified")); return; } if (DeleteAfterCopy) CallMailMessageMove (muptr); else CallMailMessageCopy (muptr); if (Debug) fprintf (stdout, "%%X%08.08X\n", muptr->MailVmsStatus); if (DeleteAfterCopy) rdptr->MessageCountDelta = muptr->MessageCopiedCount; if (VMSnok (muptr->MailVmsStatus)) { 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 { /* just in case we've tinkered with NEWMAIL */ rdptr->NewMessages = muptr->VmsMailNewMessages; cptr = FolderDescribe (rdptr->DestMailFileName, rdptr->DestFolderName); StatusMessage (FI_LI, 0, "%d %s "%s".", muptr->MessageCopiedCount, DeleteAfterCopy ? LangFor("msgs_moved") : LangFor("msgs_copied"), HTML_ESCAPE(cptr)); } } /*****************************************************************************/ /* Delete the specified messages from the folder. */ void MessageDelete (REQUEST_DATA *rdptr) { int cnt, idx; int *midptr; char *cptr; VMS_MAIL_USER *muptr; /*********/ /* begin */ /*********/ muptr = &VmsMailUser; if (WatchEnabled) WatchThis ("MessageDelete() !AZ", rdptr->MsgIdListPtr ? rdptr->MsgIdListPtr : "(null)"); strcpy (muptr->VmsMailFileName, rdptr->MailFileName); muptr->VmsMailFileNameLength = rdptr->MailFileNameLength; muptr->MsgIdListPtr = rdptr->MsgIdListPtr; muptr->MsgIdListCount = rdptr->MsgIdListCount; if (!muptr->MsgIdListCount) { StatusMessage (FI_LI, 1, "%s", LangFor("none_selected")); return; } CallMailMessageDelete (muptr); if (Debug) fprintf (stdout, "%%X%08.08X\n", muptr->MailVmsStatus); rdptr->MessageCountDelta = muptr->MessageDeletedCount; if (VMSnok (muptr->MailVmsStatus)) { 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 { /* just in case we've tinkered with NEWMAIL */ rdptr->NewMessages = muptr->VmsMailNewMessages; cptr = FolderDescribe (rdptr->MailFileName, rdptr->FolderName); StatusMessage (FI_LI, 0, "%d %s "%s".", muptr->MessageDeletedCount, LangFor("msgs_deleted"), HTML_ESCAPE(cptr)); } } /*****************************************************************************/ /* Determine script directionlity in a message read context. Functionality somewhat duplicated in ComposeCharSetDir(). */ char* MessageCharSetDir (char *CharSetPtr) { char *cptr, *sptr, *zptr; char CharSetName [128]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "MessageCharSetDir() |%s|\n", CharSetPtr); /* buttons from message pages */ CGIVARNULL (cptr, "FORM_MSG_LTR"); if (cptr) return ("ltr"); CGIVARNULL (cptr, "FORM_MSG_RTL"); if (cptr) return ("rtl"); 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"); } /*****************************************************************************/ /* Return true is the character set is known to be left-to-right only. */ BOOL MessageCharSetLtr (char *CharSetPtr) { char *cptr, *sptr, *zptr; char CharSetName [128]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "MessageCharSetLtr() |%s|\n", CharSetPtr); /* no charset then assume left-to-right */ if (!CharSetPtr || !CharSetPtr[0]) return (TRUE); cptr = CHARSETS_COMMONLY_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 (TRUE); } return (FALSE); } /*****************************************************************************/ /* Just wrap a call to CallMailMessageFlags() to set/reset header flags. */ void MessageFlags ( REQUEST_DATA *rdptr, unsigned short MailFlags ) { VMS_MAIL_MSG *vmptr; VMS_MAIL_USER *muptr; /*********/ /* begin */ /*********/ muptr = &VmsMailUser; if (WatchEnabled) WatchThis ("MessageFlags() %X!4XL", MailFlags); vmptr = &muptr->VmsMailMsg; vmptr->MessageId = rdptr->MessageId; vmptr->ConfirmHash = rdptr->MessageHash; CallMailMessageFlags (muptr, vmptr, MailFlags); if (VMSok (muptr->MailVmsStatus)) StatusMessage (FI_LI, 0, "%s", "Message flagged."); else StatusMessage (FI_LI, 1, "MAIL: %s", SysGetMsg(muptr->MailVmsStatus)); } /*****************************************************************************/ /* */ char* MessageDiskQuotaUsed (REQUEST_DATA *rdptr) { static char DiskQuotaUsed [96] = " "; int PercentUsed; VMS_MAIL_USER *muptr; /*********/ /* begin */ /*********/ muptr = &VmsMailUser; if (muptr->DiskQuotaPerm == (unsigned long)-1) { if (WatchEnabled) WatchThis ("DISK-QUOTA disabled"); return (""); } if (muptr->DiskQuotaPerm) PercentUsed = muptr->DiskQuotaUsed * 100 / muptr->DiskQuotaPerm; else PercentUsed = 0; if (WatchEnabled) WatchThis ("DISK-QUOTA !UL/!UL blocks !UL/!UL MB !UL%", muptr->DiskQuotaUsed, muptr->DiskQuotaPerm, muptr->DiskQuotaUsed >> 11, muptr->DiskQuotaPerm >> 11, PercentUsed); if (PercentUsed < SoyMailConfig.DiskQuotaPercent) return (""); sprintf (DiskQuotaUsed+6, LangFor("disk_quota_used"), muptr->DiskQuotaUsed >> 11, muptr->DiskQuotaPerm >> 11, PercentUsed); return (DiskQuotaUsed); } /*****************************************************************************/