/*****************************************************************************/ /* CallMail.c All of the functions interacting with the VMS callable Mail API. PMDP IMAP -------- Information supplied 03-JUL-2008 by Jeremy Begg on info-WASD mailing list: From an email discussion on this topic back at the beginning of last year... ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Mark> soyMAIL displays these PMDF IMAP subfolder mail files in the selectable Mark> list along with other file and folders. I have chosen to indicate Mark> it's a PMDF IMAP folder using a double trailing slash. User's need to Mark> be aware of what they are dealing with because if they move messages Mark> into or out of these subfolder areas, or create additional subfolders Mark> in an exisiting area, PMDF IMAP will not necessarily reflect the Mark> changes (presumably because it keeps it's own state in all it's Mark> ancilliary files). Jeremy> I don't think there's anything to worry about there. PMDF IMAP uses a Jeremy> few sources to find the folders assocated with a given VMS MAIL .MAI Jeremy> ISAM file: the folder list as provided by Callable MAIL, the Jeremy> MAIL.MAI-UIDDIR file, and its' own super-index file Jeremy> (PMDF_IMAP.MAILBOX) in the user's primary mail directory. If the user Jeremy> creates a folder using some other means (e.g. VMS MAIL or soyMAIL) the Jeremy> IMAP server will notice it. (The user may have to re-subscribe their Jeremy> IMAP client to find the new folder, depending on the IMAP client.) This means that soyMAIL should look for PMDF_IMAP.MAILBOX in the user's primary mail directory, this file will contain all the folders which the IMAP server knows about. The folder names will end in "!" or "=" but I'm not sure of the significance of that character, suffice to say it's a flag of some sort and not part of the folder's name. Any folder name with a "/" in it indicates that the folder is a two-level folder. For example the folder "H3/LEVEL7" indicates there is a folder called "H3" and a subfolder called "LEVEL7". The messages for "H3/LEVEL7" will be held in a subdirectory. That subdirectory is defined by the file PMDF_IMAP.MBXDIR, an RMS indexed file. The first 128 characters of each record in PMDF_IMAP.MBXDIR are the "parent" folder name (e.g. "H3") and the rest of the record identifies the directory. Both fields are NUL-filled to pad them out to the required length. Here's a sample: $ dump/rec pmdf_imap.mbxdir Dump of file FLASH$USER:[JEREMY.MAIL]PMDF_IMAP.MBXDIR;1 on 3-JUL-2008 12:58 File ID (30392,10,0) End of file block 8 / Allocated 9 Record number 1 (00000001), 256 (0100) bytes, RFA(0003,0000,0001) 00000000 00000000 00000000 00003348 H3.............. 000000 00000000 00000000 00000000 00000000 ................ 000010 00000000 00000000 00000000 00000000 ................ 000020 00000000 00000000 00000000 00000000 ................ 000030 00000000 00000000 00000000 00000000 ................ 000040 00000000 00000000 00000000 00000000 ................ 000050 00000000 00000000 00000000 00000000 ................ 000060 00000000 00000000 00000000 00000000 ................ 000070 4B31302E 4C49414D 2E594D45 52454A5B [JEREMY.MAIL.01K 000080 5D5A4757 445A3845 36534F35 5A533946 F9SZ5OS6E8ZDWGZ] 000090 00000000 00000000 00000000 00000000 ................ 0000A0 00000000 00000000 00000000 00000000 ................ 0000B0 00000000 00000000 00000000 00000000 ................ 0000C0 00000000 00000000 00000000 00000000 ................ 0000D0 00000000 00000000 00000000 00000000 ................ 0000E0 00000000 00000000 00000000 00000000 ................ 0000F0 etc. Hope this helps. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Notes on soyMAIL processing of PMDF IMAP: 1) The user option [PMDF-IMAP] flag is always checked and all of the following PMDF IMAP-specific behaviours predicated on that. 2) When listing folders the VMS folders are first listed and then the PMDF_IMAP.MAILBOX file read and the folders listed. This consolidated list is then sorted and duplicates eliminated. When the [PMDF-IMAP] flag is set soyMAIL will never attempt to list folders in other than the primary MAIL.MAI. 3) Before opening a mail file the name is checked. If it is "MAIL" then the usual soyMAIL callable Mail code paths followed. If not "MAIL" then the PMDF_IMAP.MBXDIR is read and the file name searched for to obtain the PMDF IMAP-specific subdirectory specification to which MAIL.MAI is appended. If the search is unsuccessful an empty file specification is set (which will ultimately result in callable mail reporting mail file not found). 4) When a subfolder is created by PMDF IMAP there is no MAIL.MAI file in associated directory until a message is placed there. soyMAIL needs to allow for this situation. 5) PMDF IMAP does recreate the PMDF_IMAP.MAILBOX and PMDF_IMAP.MBXDIR if necessary but in my observation only when messages are moved around. 6) An exclamation mark at the end of a PMDF_IMAP.MAILBOX folder name indicates a trashed (but presumably not yet deleted) folder. I'm guessing the equate symbol is used as something different to that :-) The trashed folder name seems to be appended to the list and is changed to an exclamation mark when finally deleted from the trash folder. COPYRIGHT --------- Copyright (C) 2005-2022 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 --------------- 19-FEB-2021 MGD bugfix; get it RIGHT! CallMailMessageContextEnd() 03-JAN-2021 MGD bugfix; DEFAULT_ZONE memory leak from multiple missing CallMailMessageContextEnd() after CallMailMessageContext() (applicable to CGIplus usage only) 10-DEC-2013 MGD bugfix; CallMailCopyFile() finalise with mail$mailfile_end() 15-NOV-2008 MGD when VMS-newmsg set comply with VMS Mail handling of the new mail count, do not reset new message flag 08-JUL-2008 MGD major revision in conjunction with PMDF IMAP processing 06-JAN-2008 MGD Happy Birthday Naomi! bugfix; CallMailFileIn() do not prefix if name contains a device or logical name (i.e. if ':' present) bugfix; CallMailFileIn() if SYS$LOGIN: present in a file name get the username home directory and use that! bugfix; CallMailFileIn() do not munge if ODS-5 volume 30-NOV-2007 MGD CallMailFileClose() accomodate bogus OPENIN return value (see description of issue in function prologue) CallMailFileClose() restore full close only if auto-purge 02-NOV-2007 MGD CallMailDeleteIfEmpty() modularise empty mail file deletion bugfix; delete empty mail files after msg move (and deletion) 12-AUG-2007 MGD CallMailMessageGet() resets the NEWMSG flag as appropriate CallMailMessageFlags() can be used to set flags 21-APR-2007 MGD bugfix; CallMailFileList() set name length from PMDF folder 12-APR-2007 MGD CallMailCopyFile() update newmail count if necessary 05-FEB-2007 MGD CallMailCopyFile() allows 'sequential Mail files' to be copied into mail file/folders CallMailFileClose() explicitly handle MAIL$_OPENIN return if mail file locked during close and reclaim 27-JAN-2007 MGD specially process PMDF IMAP folder subdirectories see CallMailPmdfImapFolder() for explanation 30-DEC-2006 MGD CallMailFolderList() ensure an non-existant/empty mail file produces an empty folder list bugfix; CallMailMessageCopyMove() new message count update 28-DEC-2006 MGD CallMailVolumeStruct() for volume structure identification CallMailFileOpen() and CallMailMessageCopyMove() provide rudimentary support for EFS (ODS-5) mail file names 21-OCT-2006 MGD CallMailFileClose() enforce full-close purge of WASTEBASKET CallMailMessageDelete() now checks non-MAIL.MAI mail files and deletes them if empty (contain no folders) 04-JUL-2006 MGD bugfix; CallMailMessageList() restore from temporary 'MessageContext' when searching 20-JUN-2006 MGD CallMailUserDiskQuota() to obtain permanent and used 26-APR-2006 MGD optimise CallMailMessageHeader() by completely replacing mail$message_get() with mail$message_info() and adding mail$message_get() to CallMailMessageGet() for non-external mail messages only (suggested by Dave Jones after his investigations demonstrated a reduction to 35% of original search durations by eliminating an RMS file open implicit in mail$message_get() with external message files). 20-APR-2006 MGD bugfix; CallMailFileList() parsing of directory entry name length and entry flags (per Dave Jones) and use memmove() for shuffling inside the one buffer 25-FEB-2006 MGD CallMailGoToNewMail() if the new message count differs from the messages selected count then adjust it bugfix; CallMailSetUserProfile() VMS option copy-send bugfix; CallMailMessageDelete() close mail file to empty WASTEBASKET before continuing request bugfix; CallMailMessageCopyMove() adjust new message count regardless of whether it's a copy or a move 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 #include #include #include /* VMS related header files */ #include #include #include #include #include #include #include #include #include #include #include #include /* application header file */ #include "soymail.h" #include "cgilib.h" #include "callmail.h" #include "other.h" #include "request.h" #define FI_LI __FILE__, __LINE__ /* global storage */ char CallMailNoSuchUser [] = "An entry for this user could not be found in VMSMAIL_PROFILE (new account?)"; VMS_ITEM_LIST3 CallMailNullItem = {0,0,0,0}; VMS_ITEM_LIST3 CallMailNoSignalItem [] = { { 0, MAIL$_NOSIGNAL, 0, 0 }, { 0,0,0,0 } }; /* external storage */ extern BOOL Debug; extern int VmsVersion; /* required prototypes (some older VAXen have no MAIL$ROUTINES.H) */ int mail$message_begin (__unknown_params); int mail$message_copy (__unknown_params); int mail$message_delete (__unknown_params); int mail$message_end (__unknown_params); int mail$message_get (__unknown_params); int mail$message_info (__unknown_params); int mail$message_modify (__unknown_params); int mail$message_select (__unknown_params); int mail$mailfile_begin (__unknown_params); int mail$mailfile_close (__unknown_params); int mail$mailfile_end (__unknown_params); int mail$mailfile_info_file (__unknown_params); int mail$mailfile_open (__unknown_params); int mail$mailfile_purge_waste (__unknown_params); int mail$user_begin (__unknown_params); int mail$user_get_info (__unknown_params); int mail$user_set_info (__unknown_params); int mail$user_end (__unknown_params); int sys$setprv (__unknown_params); /* some older VAXen do not have these either */ #ifndef MAIL$_USER_SIGFILE # define MAIL$_USER_SIGFILE 3141 #endif #ifndef MAIL$_USER_SET_SIGFILE # define MAIL$_USER_SET_SIGFILE 3110 #endif /*****************************************************************************/ /* Mail status returns may be found in 'MailVmsStatus'. A list of 'muptr->ListMessageIdStart' messages from 'muptr->ListMessageIdStart' is placed into a linked list of VMS_MAIL_MSG structures pointed to by 'muptr->MessageList' beginning with the most recent (larger ID numbers). Automatically closes the message context on an error (including no-more-messages) so an explicit close call is not required. It could be closed peremtorially by setting 'MailVmsStatus' to an error value can calling. */ int CallMailMessageList (VMS_MAIL_USER *muptr) { int hit, status; long MessageIdVector; unsigned long MessageCount, MessageId; VMS_MAIL_MSG *vmptr, *vmlptr, *PrevMessagePtr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "CallMailMessageList() %d %d %d\n", muptr->ListMessageIdStart, muptr->ListMessageIdCount, muptr->ListMessageIdVector); vmptr = &muptr->VmsMailMsg; if (!muptr->VmsMailFullDirectoryLength) ErrorExit (SS$_BUGCHECK, FI_LI); muptr->MessageListCount = 0; if (!vmptr->MessageContext) { status = CallMailMessageContext (muptr, vmptr); if (VMSnok (status)) return (muptr->MailVmsStatus = status); } if (muptr->ListMessageIdStart == RANGE_ALL_MSGS) { /* absolutely all messages from last to first */ MessageId = MessageCount = vmptr->MessageSelectedCount; MessageIdVector = -1; } else if (muptr->ListMessageIdStart == RANGE_MOST_RECENT || muptr->ListMessageIdStart > vmptr->MessageSelectedCount) { /* from the highest numbered message down for the count */ MessageId = vmptr->MessageSelectedCount; MessageCount = muptr->ListMessageIdCount; MessageIdVector = -1; } else if (muptr->ListMessageIdVector < 0) { /* stepping from higher to lower numbers (more to less recent) */ MessageId = muptr->ListMessageIdStart; MessageCount = muptr->ListMessageIdCount; MessageIdVector = -1; } else { /* stepping from lower to higher numbers (less to more recent) */ MessageId = muptr->ListMessageIdStart; MessageCount = muptr->ListMessageIdCount; MessageIdVector = +1; } /* when searching this puts a limit on the number of hits before results */ if (muptr->ListMessageIdMax) MessageCount = muptr->ListMessageIdMax; /********************/ /* process messages */ /********************/ vmlptr = NULL; while (VMSok (muptr->MailVmsStatus)) { if (!MessageCount || !MessageId) break; if (!vmlptr) vmlptr = CgiLibVeeMemCalloc (sizeof(VMS_MAIL_MSG)); if (!vmlptr) { muptr->MailVmsStatus = vaxc$errno; break; } vmlptr->MessageId = MessageId; /* either up or down */ MessageId += MessageIdVector; /* borrow the message context from the main structure */ vmlptr->MessageContext = vmptr->MessageContext; CallMailMessageHeader (muptr, vmlptr); if (VMSnok (muptr->MailVmsStatus)) { /* and return it! */ vmptr->MessageContext = vmlptr->MessageContext; break; } if (muptr->SearchFunction) { CallMailMessageGet (muptr, vmlptr); /* and return it! */ vmptr->MessageContext = vmlptr->MessageContext; if (VMSnok (muptr->MailVmsStatus)) { if (muptr->MailVmsStatus == MAIL$_OPENIN) { /* special-case, propogate it up to where we can report it */ vmptr->ExtIdLength = vmlptr->ExtIdLength; strcpy (vmptr->ExtId, vmlptr->ExtId); } break; } hit = muptr->SearchFunction (vmlptr, muptr->SearchDataPtr); CallMailMessageFree (vmlptr); if (Debug) fprintf (stdout, "HIT: %d\n", hit); if (!hit) { /* save potentially freeing and reallocating the memory */ memset (vmlptr, 0, sizeof(VMS_MAIL_MSG)); continue; } } /* ensure the most recent is always at the front of the list */ if (!muptr->MessageList) { /* first entry in list */ muptr->MessageList = vmlptr; } else if (MessageIdVector < 0) { /* message IDs will be decreasing, append this to the list */ vmlptr->PrevPtr = PrevMessagePtr; PrevMessagePtr->NextPtr = vmlptr; } else { /* message IDs will be increasing, prepend this to the list */ PrevMessagePtr->PrevPtr = vmlptr; vmlptr->NextPtr = PrevMessagePtr; muptr->MessageList = vmlptr; } PrevMessagePtr = vmlptr; /* make sure the context borrow is not propagated in any way */ vmlptr->MessageContext = 0; /* this memory has been used, more will be allocated */ vmlptr = NULL; /* and on to the next */ muptr->MessageListCount++; MessageCount--; } /* just to be tidy */ if (vmlptr) CgiLibVeeMemFree (vmlptr); /* if we run out before reaching the end-of-range */ if (muptr->MailVmsStatus == MAIL$_NOMOREMSG) muptr->MailVmsStatus = SS$_NORMAL; if (vmptr->MessageContext && VMSnok (muptr->MailVmsStatus)) { /*************************/ /* close message context */ /*************************/ status = mail$message_end (&vmptr->MessageContext, &CallMailNoSignalItem, &CallMailNullItem); if (Debug) fprintf (stdout, "mail$message_end() %%X%08.08X\n", status); vmptr->MessageContext = 0; } CallMailMessageContextEnd (muptr, vmptr); return (muptr->MailVmsStatus); } /*****************************************************************************/ /* Mail status returns may be found in 'MailVmsStatus'. Read the VMS Mail header fields from the specified message ID. Generate a 32 bit hash 'MessageHash' from selected fields of the header and if 'ConfirmHash' is non-zero compare the two, returning SS$_BADCHECKSUM if they are different. */ int CallMailMessageHeader ( VMS_MAIL_USER *muptr, VMS_MAIL_MSG *vmptr ) { int status; char *cptr; VMS_ITEM_LIST3 *itptr; /* reminder: these require one extra element for the null item */ VMS_ITEM_LIST3 MessageIdItem [3]; VMS_ITEM_LIST3 MessageInfoItem [12]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "CallMailMessageHeader()\n"); if (!muptr->VmsMailFullDirectoryLength) ErrorExit (SS$_BUGCHECK, FI_LI); /* default to the current message */ if (!vmptr) vmptr = &muptr->VmsMailMsg; itptr = &MessageIdItem[0]; memset (itptr, 0, sizeof(MessageIdItem)); itptr->buf_len = sizeof(vmptr->MessageId); itptr->item = MAIL$_MESSAGE_ID; itptr->buf_addr = &vmptr->MessageId; itptr++; itptr->item = MAIL$_NOSIGNAL; itptr = &MessageInfoItem[0]; memset (itptr, 0, sizeof(MessageInfoItem)); itptr->buf_len = sizeof(vmptr->CurrentId); itptr->item = MAIL$_MESSAGE_CURRENT_ID; itptr->buf_addr = (void*)&vmptr->CurrentId; itptr++; itptr->buf_len = sizeof(vmptr->ExtId)-1; itptr->item = MAIL$_MESSAGE_EXTID; itptr->buf_addr = vmptr->ExtId; itptr->ret_len = (void*)&vmptr->ExtIdLength; itptr++; itptr->buf_len = sizeof(vmptr->MessageFlags); itptr->item = MAIL$_MESSAGE_RETURN_FLAGS; itptr->buf_addr = (void*)&vmptr->MessageFlags; itptr++; itptr->buf_len = sizeof(vmptr->SizeInRecords); itptr->item = MAIL$_MESSAGE_SIZE; itptr->buf_addr = (void*)&vmptr->SizeInRecords; itptr++; itptr->buf_len = sizeof(vmptr->BinDate); itptr->item = MAIL$_MESSAGE_BINARY_DATE; itptr->buf_addr = &vmptr->BinDate; itptr++; itptr->buf_len = sizeof(vmptr->Date)-1; itptr->item = MAIL$_MESSAGE_DATE; itptr->buf_addr = vmptr->Date; itptr->ret_len = (void*)&vmptr->DateLength; itptr++; itptr->buf_len = sizeof(vmptr->From)-1; itptr->item = MAIL$_MESSAGE_FROM; itptr->buf_addr = vmptr->From; itptr->ret_len = (void*)&vmptr->FromLength; itptr++; itptr->buf_len = sizeof(vmptr->Subject)-1; itptr->item = MAIL$_MESSAGE_SUBJECT; itptr->buf_addr = vmptr->Subject; itptr->ret_len = (void*)&vmptr->SubjectLength; itptr++; itptr->buf_len = sizeof(vmptr->To)-1; itptr->item = MAIL$_MESSAGE_TO; itptr->buf_addr = vmptr->To; itptr->ret_len = (void*)&vmptr->ToLength; itptr++; itptr->buf_len = sizeof(vmptr->Cc)-1; itptr->item = MAIL$_MESSAGE_CC; itptr->buf_addr = vmptr->Cc; itptr->ret_len = (void*)&vmptr->CcLength; itptr++; itptr->item = MAIL$_NOSIGNAL; status = mail$message_info (&vmptr->MessageContext, &MessageIdItem, &MessageInfoItem); if (Debug) fprintf (stdout, "mail$message_info() %%X%08.08X\n", status); if (VMSok (status)) { vmptr->Cc[vmptr->CcLength] = '\0'; vmptr->Date[vmptr->DateLength] = '\0'; /* trim trailing spaces (that appear with VMS addresses) */ cptr = vmptr->From + vmptr->FromLength; *cptr = '\0'; if (cptr > vmptr->From) cptr--; while (cptr > vmptr->From && isspace(*cptr)) cptr--; if (!isspace(*cptr)) cptr++; vmptr->FromLength = cptr - vmptr->From; vmptr->From[vmptr->FromLength] = '\0'; vmptr->Subject[vmptr->SubjectLength] = '\0'; vmptr->To[vmptr->ToLength] = '\0'; vmptr->ExtId[vmptr->ExtIdLength] = '\0'; vmptr->MessageHash = CallMailHeaderHash (vmptr); if (Debug) fprintf (stdout, "id:%d size:%d hash:%d/%d flags:0x%04.04X\n\ |%s|\n|%s|\n|%s|\n|%s|\n|%s|\n|%s|\n", vmptr->CurrentId, vmptr->SizeInRecords, vmptr->MessageHash, vmptr->ConfirmHash, vmptr->MessageFlags, vmptr->Date, vmptr->ExtId, vmptr->From, vmptr->Subject, vmptr->To, vmptr->Cc); if (vmptr->ConfirmHash && vmptr->ConfirmHash != vmptr->MessageHash) status = SS$_BADCHECKSUM; } return (muptr->MailVmsStatus = status); } /*****************************************************************************/ /* Read the message body into a dynamically allocated buffer pointed at by 'vmptr->BodyPtr' and 'vmptr->BodyLength' in length. Mail status returns may be found in 'MailVmsStatus'. Automatically closes the message context on an error (including NOMOREREC) so an explicit close call is not required. It could be closed peremtorially by setting 'MailVmsStatus' to an error value can calling. */ int CallMailMessageGet ( VMS_MAIL_USER *muptr, VMS_MAIL_MSG *vmptr ) { /* at an unknown VMS version max record size went from 255 to 2048 */ static unsigned long MaxRecordSize = 2048; static VMS_ITEM_LIST3 MessageContinueItem [] = { { 0, MAIL$_MESSAGE_CONTINUE, 0, 0 }, { 0, MAIL$_NOSIGNAL, 0, 0 }, { 0,0,0,0 } }; int efd, retval, status, BytesAllocated; unsigned short MessageFlags; unsigned long rlen; char *cptr, *rptr, *sptr, *zptr; char ExtFileName [256]; stat_t StatBuffer; VMS_ITEM_LIST3 *itptr; /* reminder: these require one extra element for the null item */ VMS_ITEM_LIST3 MessageIdItem [3]; VMS_ITEM_LIST3 MessageModifyItem [4]; VMS_ITEM_LIST3 MessageRecordItem [3]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "CallMailMessageGet()\n"); if (!muptr->VmsMailFullDirectoryLength) ErrorExit (SS$_BUGCHECK, FI_LI); /* default to the current message */ if (!vmptr) vmptr = &muptr->VmsMailMsg; if (!vmptr->MessageContext) { /****************/ /* open message */ /****************/ status = CallMailMessageContext (muptr, vmptr); if (VMSnok (status)) return (muptr->MailVmsStatus = status); CallMailMessageHeader (muptr, vmptr); /* if it wasn't the message we were seeking */ if (muptr->MailVmsStatus == SS$_BADCHECKSUM) { CallMailMessageContextEnd (muptr, vmptr); return (muptr->MailVmsStatus); } } else muptr->MailVmsStatus = SS$_NORMAL; /*******************/ /* process message */ /*******************/ if (VMSok (muptr->MailVmsStatus)) { /********************/ /* buffer record(s) */ /********************/ if (vmptr->ExtIdLength) { zptr = (sptr = ExtFileName) + sizeof(ExtFileName)-1; for (cptr = muptr->VmsMailFullDirectory; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = vmptr->ExtId; *cptr && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; if (Debug) fprintf (stdout, "stat() |%s|\n", ExtFileName); if (stat (ExtFileName, &StatBuffer) < 0) { status = vaxc$errno; if (Debug) fprintf (stdout, "stat() %%X%08.08X\n", status); if (status == RMS$_FNF) muptr->MailVmsStatus = MAIL$_OPENIN; else muptr->MailVmsStatus = status; BytesAllocated = 0; } else BytesAllocated = StatBuffer.st_size; } else BytesAllocated = CALLMAIL_MSG_SIZE; if (Debug) fprintf (stdout, "%d bytes\n", BytesAllocated); vmptr->BodyPtr = rptr = CgiLibVeeMemCalloc (BytesAllocated+32); if (!vmptr->BodyPtr) muptr->MailVmsStatus = vaxc$errno; vmptr->BodyLength = 0; } if (VMSok (muptr->MailVmsStatus)) { if (vmptr->ExtIdLength) { /**********************/ /* external mail file */ /**********************/ /* avoid the overhead of mail$message_get */ efd = open (ExtFileName, O_RDONLY, 0, "ctx=stm", "shr=get"); if (!efd) { if (Debug) fprintf (stdout, "open() %%X%08.08X\n", vaxc$errno); if (vaxc$errno == RMS$_FNF) muptr->MailVmsStatus = MAIL$_OPENIN; else muptr->MailVmsStatus = vaxc$errno; } else { retval = read (efd, vmptr->BodyPtr, BytesAllocated); if (Debug) fprintf (stdout, "retval: %d\n", retval); if (retval == -1) { muptr->MailVmsStatus = vaxc$errno; if (Debug) fprintf (stdout, "read() %%X%08.08X\n", vaxc$errno); close (efd); } else if (vmptr->MessageFlags & MAIL$M_EXTNSTD) { /* must be a fixed 512 byte foreign message */ close (efd); vmptr->BodyLength = retval; } else { /* just assume it's variable-length record mode */ close (efd); zptr = (sptr = rptr = vmptr->BodyPtr) + BytesAllocated; for (;;) { rlen = (unsigned int)*(USHORTPTR)rptr; if (rlen == 0xffff) break; rptr += sizeof(unsigned short); /* as it's shuffling inside the one buffer use memove() */ memmove (sptr, rptr, rlen); sptr += rlen; *sptr++ = '\n'; rptr += rlen; /* records are always on even byte (word) boundaries */ if ((int)rptr & 1) rptr++; /* sanity check */ if (rptr >= zptr) break; } *sptr = '\0'; vmptr->BodyLength = sptr - vmptr->BodyPtr; } } } else { /************************/ /* use MAIL$MESSAGE_GET */ /************************/ itptr = &MessageIdItem[0]; memset (itptr, 0, sizeof(MessageIdItem)); itptr->buf_len = sizeof(vmptr->MessageId); itptr->item = MAIL$_MESSAGE_ID; itptr->buf_addr = &vmptr->MessageId; itptr++; itptr->item = MAIL$_NOSIGNAL; status = mail$message_get (&vmptr->MessageContext, &MessageIdItem, &CallMailNullItem); if (Debug) fprintf (stdout, "mail$message_get() %%X%08.08X\n", status); itptr = &MessageRecordItem[0]; memset (itptr, 0, sizeof(MessageRecordItem)); /* might be a longword specification but only a short is written! */ rlen = 0; while (VMSok (muptr->MailVmsStatus)) { itptr = &MessageRecordItem[0]; if (BytesAllocated >= MaxRecordSize) itptr->buf_len = MaxRecordSize; else itptr->buf_len = BytesAllocated; itptr->item = MAIL$_MESSAGE_RECORD; itptr->buf_addr = rptr; itptr->ret_len = &rlen; itptr++; itptr->item = MAIL$_NOSIGNAL; status = mail$message_get (&vmptr->MessageContext, &MessageContinueItem, &MessageRecordItem); if (Debug) fprintf (stdout, "mail$message_get() %%X%08.08X %d/%d bytes\n", status, rlen, MaxRecordSize); if (VMSok (status)) { /* not interested in the VMS Mail header */ if (vmptr->RecordCount++ < 5) { rptr[0] = '\0'; continue; } rptr[rlen++] = '\n'; rptr += rlen; rptr[0] = '\0'; vmptr->BodyLength += rlen; BytesAllocated -= rlen; continue; } if (status == MAIL$_INVITMLEN && MaxRecordSize > 255) { /* drop back from 2048 to 255 and try again */ MaxRecordSize = 255; continue; } muptr->MailVmsStatus = status; break; } } if (muptr->MailVmsStatus == MAIL$_NOMOREREC) muptr->MailVmsStatus = SS$_NORMAL; } if (Debug) fprintf (stdout, "%d |%s|\n", vmptr->BodyLength, vmptr->BodyPtr); if (vmptr->RecordCount >= 5) vmptr->RecordCount -= 5; else vmptr->RecordCount = 0; if (VMSok (muptr->MailVmsStatus) && vmptr->MessageFlags & MAIL$M_NEWMSG && vmptr->ResetNewMsgFlag && !muptr->VmsNewMsg) { /**********************/ /* not unread anymore */ /**********************/ /* leave the bit set in the in-memory flags (for later detection) */ MessageFlags = vmptr->MessageFlags & ~MAIL$M_NEWMSG; itptr = &MessageModifyItem[0]; memset (itptr, 0, sizeof(MessageModifyItem)); itptr->buf_len = sizeof(vmptr->MessageId); itptr->item = MAIL$_MESSAGE_ID; itptr->buf_addr = &vmptr->MessageId; itptr++; itptr->buf_len = sizeof(MessageFlags); itptr->item = MAIL$_MESSAGE_FLAGS; itptr->buf_addr = &MessageFlags; itptr++; itptr->item = MAIL$_NOSIGNAL; status = mail$message_modify (&vmptr->MessageContext, &MessageModifyItem, &CallMailNullItem); if (Debug) fprintf (stdout, "mail$message_modify() %%X%08.08X\n", status); if (VMSok(status)) vmptr->MessageFlags &= ~MAIL$M_NEWMSG; } if (vmptr->MessageContext && VMSnok (muptr->MailVmsStatus)) { /*****************/ /* close message */ /*****************/ status = mail$message_end (&vmptr->MessageContext, &CallMailNoSignalItem, &CallMailNullItem); if (Debug) fprintf (stdout, "mail$message_end() %%X%08.08X\n", status); CallMailMessageContextEnd (muptr, vmptr); } return (muptr->MailVmsStatus); } /*****************************************************************************/ /* Modify the specified message flags to the bit-vector supplied. To reset one or more bits complement the required pattern with the most significant bit set. To set one or more bits supply the bit pattern with the most significant bit reset. */ int CallMailMessageFlags ( VMS_MAIL_USER *muptr, VMS_MAIL_MSG *vmptr, unsigned short MessageFlags ) { int status; VMS_ITEM_LIST3 *itptr; /* reminder: these require one extra element for the null item */ VMS_ITEM_LIST3 MessageModifyItem [4]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "CallMailMessageFlags() %%x%04.04X\n", MessageFlags); if (!muptr->VmsMailFullDirectoryLength) ErrorExit (SS$_BUGCHECK, FI_LI); /* default to the current message */ if (!vmptr) vmptr = &muptr->VmsMailMsg; if (!vmptr->MessageContext) { /****************/ /* open message */ /****************/ status = CallMailMessageContext (muptr, vmptr); if (VMSnok (status)) return (muptr->MailVmsStatus = status); CallMailMessageHeader (muptr, vmptr); /* if it wasn't the message we were seeking */ if (muptr->MailVmsStatus == SS$_BADCHECKSUM) { CallMailMessageContextEnd (muptr, vmptr); return (muptr->MailVmsStatus); } } else muptr->MailVmsStatus = SS$_NORMAL; /*************/ /* set flags */ /*************/ if (MessageFlags & 0x8000) vmptr->MessageFlags &= MessageFlags; else vmptr->MessageFlags |= MessageFlags; itptr = &MessageModifyItem[0]; memset (itptr, 0, sizeof(MessageModifyItem)); itptr->buf_len = sizeof(vmptr->MessageId); itptr->item = MAIL$_MESSAGE_ID; itptr->buf_addr = &vmptr->MessageId; itptr++; itptr->buf_len = sizeof(vmptr->MessageFlags); itptr->item = MAIL$_MESSAGE_FLAGS; itptr->buf_addr = &vmptr->MessageFlags; itptr++; itptr->item = MAIL$_NOSIGNAL; status = mail$message_modify (&vmptr->MessageContext, &MessageModifyItem, &CallMailNullItem); if (Debug) fprintf (stdout, "mail$message_modify() %%X%08.08X\n", status); muptr->MailVmsStatus = status; if (VMSnok (muptr->MailVmsStatus)) { /*****************/ /* close message */ /*****************/ status = mail$message_end (&vmptr->MessageContext, &CallMailNoSignalItem, &CallMailNullItem); if (Debug) fprintf (stdout, "mail$message_end() %%X%08.08X\n", status); CallMailMessageContextEnd (muptr, vmptr); } return (muptr->MailVmsStatus); } /*****************************************************************************/ /* Release memory allocated by CallMailMessageGet(). */ void CallMailMessageFree (VMS_MAIL_MSG *vmptr) { /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "CallMailMessageFree()\n"); if (!vmptr) ErrorExit (SS$_BUGCHECK, FI_LI); CgiLibVeeMemFree (vmptr->BodyPtr); vmptr->BodyPtr = NULL; vmptr->BodyLength = 0; } /*****************************************************************************/ /* Establish a message context. Then select message(s) based on item number, subject contents, etc. This context can then be used to list messages, read a message, etc. */ int CallMailMessageContext ( VMS_MAIL_USER *muptr, VMS_MAIL_MSG *vmptr ) { int status; VMS_ITEM_LIST3 *itptr; /* reminder: these require one extra element for the null item */ VMS_ITEM_LIST3 MessageBeginItem [3]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "CallMailMessageContext()\n"); status = CallMailFileOpen (muptr, muptr->VmsMailFileName); if (VMSnok (status)) return (muptr->MailVmsStatus = status); itptr = &MessageBeginItem[0]; memset (itptr, 0, sizeof(MessageBeginItem)); itptr->buf_len = sizeof(muptr->VmsMailFileContext); itptr->item = MAIL$_MESSAGE_FILE_CTX; itptr->buf_addr = (void*)&muptr->VmsMailFileContext; itptr++; itptr->item = MAIL$_NOSIGNAL; status = mail$message_begin (&vmptr->MessageContext, &MessageBeginItem, &CallMailNullItem); if (Debug) fprintf (stdout, "mail$message_begin() %%X%08.08X\n", status); if (VMSok (status)) status = CallMailMessageSelect (muptr); return (muptr->MailVmsStatus = status); } /*****************************************************************************/ /* Close a message context. */ int CallMailMessageContextEnd ( VMS_MAIL_USER *muptr, VMS_MAIL_MSG *vmptr ) { int status; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "CallMailMessageContextEnd()\n"); if (!vmptr->MessageContext) return (SS$_NORMAL);; status = mail$message_end (&vmptr->MessageContext, &CallMailNoSignalItem, &CallMailNullItem); if (Debug) fprintf (stdout, "mail$message_end() %%X%08.08X\n", status); vmptr->MessageContext = 0; return (muptr->MailVmsStatus = status); } /*****************************************************************************/ /* Reset all of the input items that control message selection. */ void CallMailMessageSelectReset (VMS_MAIL_USER *muptr) { VMS_MAIL_MSG *vmptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "CallMailMessageSelectReset()\n"); vmptr = &muptr->VmsMailMsg; vmptr->CcLength = vmptr->FromLength = vmptr->SubjectLength = vmptr->ToLength = vmptr->TimeBeforeLength = vmptr->TimeSinceLength = 0; vmptr->Cc[0] = vmptr->From[0] = vmptr->Subject[0] = vmptr->To[0] = vmptr->TimeBefore[0] = vmptr->TimeSince[0] = '\0'; } /*****************************************************************************/ /* Select message(s) based on item number, subject contents, etc. */ int CallMailMessageSelect (VMS_MAIL_USER *muptr) { int status; VMS_MAIL_MSG *vmptr; VMS_ITEM_LIST3 *itptr; /* reminder: these require one extra element for the null item */ VMS_ITEM_LIST3 MessageSelectItem [7]; VMS_ITEM_LIST3 MessageSelectedItem [8]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "CallMailMessageSelect() %d |%s|\n", muptr->VmsMailFolderNameLength, muptr->VmsMailFolderName); /* the current msg structure is being kept in the user data area */ vmptr = &muptr->VmsMailMsg; if (!vmptr->MessageContext) { status = CallMailMessageContext (muptr, vmptr); if (VMSnok (status)) return (muptr->MailVmsStatus = status); } /**********/ /* select */ /**********/ if (!vmptr->MessageContext) ErrorExit (SS$_BUGCHECK, FI_LI); itptr = &MessageSelectItem[0]; memset (itptr, 0, sizeof(MessageSelectItem)); itptr->buf_len = muptr->VmsMailFolderNameLength; itptr->item = MAIL$_MESSAGE_FOLDER; itptr->buf_addr = muptr->VmsMailFolderName; itptr++; if (muptr->SelectNotSearch) { if (vmptr->CcLength) { itptr->buf_len = vmptr->CcLength; itptr->item = MAIL$_MESSAGE_CC_SUBSTRING; itptr->buf_addr = vmptr->Cc; itptr++; } if (vmptr->FromLength) { itptr->buf_len = vmptr->FromLength; itptr->item = MAIL$_MESSAGE_FROM_SUBSTRING; itptr->buf_addr = vmptr->From; itptr++; } if (vmptr->SubjectLength) { itptr->buf_len = vmptr->SubjectLength; itptr->item = MAIL$_MESSAGE_SUBJ_SUBSTRING; itptr->buf_addr = vmptr->Subject; itptr++; } if (vmptr->ToLength) { itptr->buf_len = vmptr->ToLength; itptr->item = MAIL$_MESSAGE_TO_SUBSTRING; itptr->buf_addr = vmptr->To; itptr++; } if (vmptr->TimeBeforeLength) { itptr->buf_len = vmptr->TimeBeforeLength; itptr->item = MAIL$_MESSAGE_BEFORE; itptr->buf_addr = vmptr->TimeBefore; itptr++; } if (vmptr->TimeSinceLength) { itptr->buf_len = vmptr->TimeSinceLength; itptr->item = MAIL$_MESSAGE_SINCE; itptr->buf_addr = vmptr->TimeSince; itptr++; } if (vmptr->SelectMessageFlags) { itptr->buf_len = sizeof(vmptr->SelectMessageFlags); itptr->item = MAIL$_MESSAGE_FLAGS; itptr->buf_addr = &vmptr->SelectMessageFlags; itptr++; } } itptr->item = MAIL$_NOSIGNAL; itptr = &MessageSelectedItem[0]; memset (itptr, 0, sizeof(MessageSelectedItem)); itptr->buf_len = sizeof(vmptr->MessageSelectedCount); itptr->item = MAIL$_MESSAGE_SELECTED; itptr->buf_addr = &vmptr->MessageSelectedCount; itptr++; itptr->item = MAIL$_NOSIGNAL; status = mail$message_select (&vmptr->MessageContext, &MessageSelectItem, &MessageSelectedItem); if (Debug) fprintf (stdout, "mail$message_select() %%X%08.08X selected: %d\n", status, vmptr->MessageSelectedCount); return (muptr->MailVmsStatus = status); } /*****************************************************************************/ /* Open the VMS Mail file specified by argument MailFileName. If a mail file is currently open then close it first. */ int CallMailFileOpen ( VMS_MAIL_USER *muptr, char *MailFileName ) { BOOL PmdfImap; int status, OpenFileNameLength; unsigned long MailFileNameLength; char *cptr, *sptr, *zptr; char OpenMailFileName [256]; VMS_ITEM_LIST3 *itptr; /* reminder: these require one extra element for the null item */ VMS_ITEM_LIST3 MailFileItem [3]; VMS_ITEM_LIST3 WasteBasketItem [3]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "CallMailFileOpen() |%s|\n", MailFileName); if (!muptr->VmsMailFullDirectoryLength) ErrorExit (SS$_BUGCHECK, FI_LI); /* look for the indicative trailing slash */ for (cptr = MailFileName; *cptr && *cptr != '/'; cptr++); if (*cptr) PmdfImap = TRUE; else PmdfImap = FALSE; if (PmdfImap) OpenFileNameLength = CallMailPmdfImapMbxDir (muptr, MailFileName, OpenMailFileName, sizeof(OpenMailFileName)); else OpenFileNameLength = CallMailBuildMailFileName (muptr, MailFileName, OpenMailFileName, sizeof(OpenMailFileName)); if (Debug) fprintf (stdout, "|%s|%s|\n", OpenMailFileName, muptr->OpenMailFileName); if (muptr->VmsMailFileContext) { if (OpenFileNameLength == muptr->OpenMailFileNameLength && !strcmp (OpenMailFileName, muptr->OpenMailFileName)) return (muptr->MailVmsStatus = SS$_NORMAL); CallMailFileClose (muptr); } zptr = (sptr = muptr->OpenMailFileName) + sizeof(muptr->OpenMailFileName)-1; for (cptr = OpenMailFileName; *cptr && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; muptr->OpenMailFileNameLength = sptr - muptr->OpenMailFileName; muptr->MailDotMai = FALSE; status = mail$mailfile_begin (&muptr->VmsMailFileContext, &CallMailNullItem, &CallMailNullItem); if (Debug) fprintf (stdout, "mail$mailfile_begin() %%X%08.08X\n", status); if (VMSnok (status)) return (muptr->MailVmsStatus = status); itptr = &MailFileItem[0]; memset (itptr, 0, sizeof(MailFileItem)); itptr->buf_len = muptr->OpenMailFileNameLength; itptr->item = MAIL$_MAILFILE_NAME; itptr->buf_addr = muptr->OpenMailFileName; itptr->ret_len = &MailFileNameLength; itptr++; itptr->item = MAIL$_NOSIGNAL; itptr = &WasteBasketItem[0]; memset (itptr, 0, sizeof(WasteBasketItem)); itptr->buf_len = sizeof(muptr->VmsMailWasteBasketName)-1; itptr->item = MAIL$_MAILFILE_WASTEBASKET; itptr->buf_addr = muptr->VmsMailWasteBasketName; itptr->ret_len = &muptr->VmsMailWasteBasketNameLength; itptr->item = MAIL$_NOSIGNAL; status = mail$mailfile_open (&muptr->VmsMailFileContext, &MailFileItem, &WasteBasketItem); if (Debug) fprintf (stdout, "mail$mailfile_open() %%X%08.08X\n", status); if (VMSok (status)) { /* set a boolean if it's the primary "MAIL.MAI" */ for (cptr = muptr->OpenMailFileName + muptr->OpenMailFileNameLength; cptr > muptr->OpenMailFileName && *cptr != ']'; cptr--); if (*cptr == ']') cptr++; if (!memcmp (cptr, "MAIL\0", 5) || !memcmp (cptr, "MAIL.", 5)) muptr->MailDotMai = TRUE; muptr->VmsMailWasteBasketName[muptr->VmsMailWasteBasketNameLength] = '\0'; if (Debug) fprintf (stdout, "muptr->VmsMailWasteBasketName |%s|\n", muptr->VmsMailWasteBasketName); } else { /* couldn't open the mail file, end the context */ mail$mailfile_end (&muptr->VmsMailFileContext, &CallMailNullItem, &CallMailNullItem); if (Debug) fprintf (stdout, "mail$mailfile_end() %%X%08.08X\n", status); muptr->OpenMailFileNameLength = muptr->VmsMailFileContext = 0; muptr->OpenMailFileName[0] = '\0'; /* non-existant PMDF IMAP MAIL.MAI is non-fatal, indicate using this */ if (PmdfImap && status == MAIL$_OPENIN) status = SS$_FISH; } return (muptr->MailVmsStatus = status); } /*****************************************************************************/ /* Close the currently open mail file. If the request involved a message delete the also purge the WASTEBASKET. BOGUS_OPENIN: mail$mailfile_close() (under at least V7.3 and V8.3) can report %X007E109A (8261786) which KLAATU$ exit %X007E109A %MAIL-E-OPENIN, error opening !AS as input but is not the value defined in the header file #define MAIL$_OPENIN 8290802 Obviously a disconnect between callable mail and the header (the value 8261786 does not appear to represent anything anywhere in SYS$STARLET_C.TLB)! Sooo ... define our own, bogus OPENIN status to correspond to this real-world return. */ CallMailFileClose (VMS_MAIL_USER *muptr) { #define BOGUS_OPENIN 0x007E109A static unsigned long TotalReclaim; static VMS_ITEM_LIST3 FullCloseItem [] = { { 0, MAIL$_MAILFILE_FULL_CLOSE, 0, 0 }, { 0, MAIL$_NOSIGNAL, 0, 0 }, { 0,0,0,0 } }; static VMS_ITEM_LIST3 TotalReclaimItem [] = { { sizeof(TotalReclaim), MAIL$_MAILFILE_TOTAL_RECLAIM, &TotalReclaim, 0 }, { 0, MAIL$_NOSIGNAL, 0, 0 }, { 0,0,0,0 } }; int status; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "CallMailFileClose() %d |%s|\n", muptr->VmsMailFileContext, muptr->VmsMailFileName); if (!muptr->VmsMailFileContext) return (SS$_NORMAL); muptr->TotalReclaimBytes = TotalReclaim = 0; if (muptr->VmsMailAutoPurge) status = mail$mailfile_close (&muptr->VmsMailFileContext, &FullCloseItem, &TotalReclaimItem); else status = mail$mailfile_close (&muptr->VmsMailFileContext, &CallMailNoSignalItem, &CallMailNullItem); if (Debug) fprintf (stdout, "mail$mailfile_close() %%X%08.08X\n", status); if (VMSnok (status)) { /* Callable Mail reports this if the Mail file is open (presumably for read) by some other application (apparently PMDF IMAP is a common culprit) when the close attempts to reclaim delete space. */ if (status != MAIL$_OPENIN && status != BOGUS_OPENIN) { muptr->MailVmsStatus = status; return (muptr->MailVmsStatus = status); } } muptr->TotalReclaimBytes = TotalReclaim; status = mail$mailfile_end (&muptr->VmsMailFileContext, &CallMailNoSignalItem, &CallMailNullItem); if (Debug) fprintf (stdout, "mail$mailfile_end() %%X%08.08X\n", status); muptr->OpenMailFileNameLength = muptr->VmsMailFileContext = 0; muptr->OpenMailFileName[0] = '\0'; return (muptr->MailVmsStatus = status); } /*****************************************************************************/ /* This function retrieves the folder names in the current mail file context. It places into the data area each null-terminated folder name, with the list itself terminated by an empty name (i.e. "FOLDER1\0FOLDER2\0\0"). */ int CallMailFolderList ( VMS_MAIL_USER *muptr, BOOL DestFolder, BOOL PmdfImap ) { int status, ListCount, ListLength, ListSize; char *cptr, *NamePtr; VMS_ITEM_LIST3 *itptr; /* reminder: these require one extra element for the null item */ VMS_ITEM_LIST3 CallbackItem [4]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "CallMailFolderList() %d\n", DestFolder); if (DestFolder) { /* destination mail file */ if (muptr->DestFolderList.ListPtr) { CgiLibVeeMemFree (muptr->DestFolderList.ListPtr); muptr->DestFolderList.ListPtr = NULL; muptr->DestFolderList.ListCount = muptr->DestFolderList.ListLength = muptr->DestFolderList.ListSize = 0; } /* folders are always generated from "MAIL" with PMDF IMAP enabled */ if (PmdfImap) NamePtr = "MAIL"; else NamePtr = muptr->VmsMailDestFileName; } else { /* primary mail file */ if (muptr->MailFolderList.ListPtr) { CgiLibVeeMemFree (muptr->MailFolderList.ListPtr); muptr->MailFolderList.ListPtr = NULL; muptr->MailFolderList.ListCount = muptr->MailFolderList.ListLength = muptr->MailFolderList.ListSize = 0; } /* folders are always generated from "MAIL" with PMDF IMAP enabled */ if (PmdfImap) NamePtr = "MAIL"; else NamePtr = muptr->VmsMailFileName; } status = CallMailFileOpen (muptr, NamePtr); muptr->TempFolderList.ListPtr = NULL; muptr->TempFolderList.ListCount = muptr->TempFolderList.ListLength = muptr->TempFolderList.ListSize = 0; if (VMSok (status)) { muptr->DataBufferLength = 0; muptr->DataBufferPtr = muptr->DataBuffer; itptr = &CallbackItem[0]; memset (itptr, 0, sizeof(CallbackItem)); itptr->buf_len = sizeof(void*); itptr->item = MAIL$_MAILFILE_FOLDER_ROUTINE; itptr->buf_addr = (void*)CallMailFolderListCallback; itptr++; itptr->buf_len = sizeof(VMS_MAIL_USER*); itptr->item = MAIL$_MAILFILE_USER_DATA; itptr->buf_addr = muptr; itptr++; itptr->item = MAIL$_NOSIGNAL; status = mail$mailfile_info_file (&muptr->VmsMailFileContext, &CallbackItem, &CallMailNullItem); if (Debug) fprintf (stdout, "mail$mailfile_info_file() %%X%08.08X\n", status); if (!muptr->TempFolderList.ListCount) { /* ensure an non-existant/empty mail file results in an empty list */ muptr->TempFolderList.ListPtr = CgiLibVeeMemCalloc (muptr->TempFolderList.ListSize = 64); muptr->TempFolderList.ListLength = muptr->TempFolderList.ListCount = 0; if (!muptr->TempFolderList.ListPtr) ErrorExit (vaxc$errno, FI_LI); } else if (VMSnok (status)) { /* ensure the list is empty in the case of an error */ if (muptr->TempFolderList.ListPtr) muptr->TempFolderList.ListPtr[0] = '\0'; muptr->TempFolderList.ListLength = muptr->TempFolderList.ListCount = 0; } } if (PmdfImap) { if (VMSok (status)) { /* append the PMDF_IMAP.MAILBOX listed folders */ status = CallMailPmdfImapMailBox (muptr); if (Debug) fprintf (stdout, "%%X%08.08X\n", status); /* sort eliminating duplicates in callable Mail and PMDF IMAP */ if (VMSok (status)) CallMailPmdfImapSort (muptr); } } if (DestFolder) { muptr->DestFolderList.ListPtr = muptr->TempFolderList.ListPtr; muptr->DestFolderList.ListCount = muptr->TempFolderList.ListCount; muptr->DestFolderList.ListLength = muptr->TempFolderList.ListLength; muptr->DestFolderList.ListSize = muptr->TempFolderList.ListSize; } else { muptr->MailFolderList.ListPtr = muptr->TempFolderList.ListPtr; muptr->MailFolderList.ListCount = muptr->TempFolderList.ListCount; muptr->MailFolderList.ListLength = muptr->TempFolderList.ListLength; muptr->MailFolderList.ListSize = muptr->TempFolderList.ListSize; } muptr->TempFolderList.ListPtr = NULL; muptr->TempFolderList.ListCount = muptr->TempFolderList.ListLength = muptr->TempFolderList.ListSize = 0; return (muptr->MailVmsStatus = status); } /*****************************************************************************/ /* Called once from CallMailFolderList() for each folder name in the current mail file. */ int CallMailFolderListCallback ( VMS_MAIL_USER *muptr, struct dsc$descriptor *FolderDscPtr ) { int status, NewListSize; char *cptr, *dzptr, *sptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "CallMailFolderListCallback() %d %*.*s\n", FolderDscPtr->dsc$w_length, FolderDscPtr->dsc$w_length, FolderDscPtr->dsc$w_length, FolderDscPtr->dsc$a_pointer); if (!FolderDscPtr->dsc$w_length) return (SS$_NORMAL); NewListSize = muptr->TempFolderList.ListLength + FolderDscPtr->dsc$w_length + 1; while (NewListSize > muptr->TempFolderList.ListSize) { muptr->TempFolderList.ListSize = NewListSize + 256; muptr->TempFolderList.ListPtr = CgiLibVeeMemRealloc (muptr->TempFolderList.ListPtr, muptr->TempFolderList.ListSize); if (!muptr->TempFolderList.ListPtr) ErrorExit (vaxc$errno, FI_LI); } sptr = muptr->TempFolderList.ListPtr + muptr->TempFolderList.ListLength; dzptr = FolderDscPtr->dsc$a_pointer + FolderDscPtr->dsc$w_length; for (cptr = FolderDscPtr->dsc$a_pointer; cptr < dzptr; *sptr++ = *cptr++); *sptr = '\0'; muptr->TempFolderList.ListLength += FolderDscPtr->dsc$w_length + 1; muptr->TempFolderList.ListCount++; return (SS$_NORMAL); } /*****************************************************************************/ /* Copy or copy-then-delete (move) messages between folders. */ int CallMailMessageCopy (VMS_MAIL_USER *muptr) { return (CallMailMessageCopyMove (muptr, 0)); } int CallMailMessageMove (VMS_MAIL_USER *muptr) { return (CallMailMessageCopyMove (muptr, 1)); } int CallMailMessageCopyMove ( VMS_MAIL_USER *muptr, BOOL DeleteAfterCopy ) { int status, DestFileNameLength, VolumeStruct; unsigned long MsgCnt; char *cptr; char DestFileName [256]; VMS_ITEM_LIST3 *itptr; VMS_MAIL_MSG *vmptr; VMS_MAIL_MSG VmsMailMsg; /* reminder: these require one extra element for the null item */ VMS_ITEM_LIST3 MessageIdItem [6]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "CallMailMessageCopyMove() |%s|\n", muptr->MsgIdListPtr); muptr->MessageCopiedCount = muptr->NewMsgFlagCopiedCount = 0; vmptr = &muptr->VmsMailMsg; if (!vmptr->MessageContext) { /************************/ /* open message context */ /************************/ status = CallMailMessageContext (muptr, vmptr); if (VMSnok (status)) return (muptr->MailVmsStatus = status); } if (Debug) fprintf (stdout, "|%s|%s|\n", muptr->VmsMailFileName, muptr->VmsMailDestFileName); if (!strcmp (muptr->VmsMailFileName, muptr->VmsMailDestFileName)) { /******************/ /* same mail file */ /******************/ DestFileName[DestFileNameLength = 0] = '\0'; } else { /***********************/ /* different mail file */ /***********************/ /* look for the indicative trailing slash */ for (cptr = muptr->VmsMailDestFileName; *cptr && *cptr != '/'; cptr++); if (*cptr) { /* copying to a PMDF subfolder */ DestFileNameLength = CallMailPmdfImapMbxDir (muptr, muptr->VmsMailDestFileName, DestFileName, sizeof(DestFileName)); if (!DestFileNameLength) { /* PMDF IMAP has not bee able to map the specification! */ DestFileNameLength = 1; strcpy (DestFileName, "*"); } } else { /* source and destination file names differ */ char *cptr, *sptr, *zptr; /* check if there are ODS-2 illegal characters present */ for (cptr = muptr->VmsMailDestFileName; *cptr; cptr++) if (!(isalnum(*cptr) || *cptr == '$' || *cptr == '-' || *cptr == '_')) break; if (*cptr) VolumeStruct = CallMailVolumeStruct (muptr->VmsMailFullDirectory); /* build the destination mail file name */ zptr = (sptr = DestFileName) + sizeof(DestFileName)-1; for (cptr = muptr->VmsMailFullDirectory; *cptr && sptr < zptr; *sptr++ = *cptr++); cptr = muptr->VmsMailDestFileName; while (*cptr && sptr < zptr) { if (isalnum(*cptr) || *cptr == '$' || *cptr == '-' || *cptr == '_') *sptr++ = *cptr++; else if (*cptr == '^') { /* assume it's an escaped EFS-compliant character */ *sptr++ = *cptr++; if (sptr < zptr) *sptr++ = *cptr++; } else if (VolumeStruct == 5) { /* assume EFS-compliant character requiring escaping */ if (sptr < zptr) *sptr++ = '^'; if (*cptr == ' ') { if (sptr < zptr) *sptr++ = '_'; cptr++; } else if (sptr < zptr) *sptr++ = *cptr++; } else { /* assume illegal character, substitute an underscore */ *sptr++ = '_'; cptr++; } } for (cptr = ".MAI;"; *cptr && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; DestFileNameLength = sptr - DestFileName; } } if (Debug) fprintf (stdout, "%d |%s|\n", DestFileNameLength, DestFileName); /**********************/ /* process message(s) */ /**********************/ if (!(cptr = muptr->MsgIdListPtr)) ErrorExit (SS$_BUGCHECK, FI_LI); memset (&VmsMailMsg, 0, sizeof(VmsMailMsg)); VmsMailMsg.MessageContext = vmptr->MessageContext; while (*cptr) { if (*cptr == '*') VmsMailMsg.MessageId++; else { VmsMailMsg.MessageId = atol(cptr); while (*cptr && *cptr != '.') cptr++; if (*cptr != '.') ErrorExit (SS$_BUGCHECK, FI_LI); cptr++; VmsMailMsg.ConfirmHash = atol(cptr); while (*cptr && *cptr != ',') cptr++; if (*cptr) cptr++; CallMailMessageHeader (muptr, &VmsMailMsg); /* if it wasn't the message we were seeking */ if (muptr->MailVmsStatus == SS$_BADCHECKSUM) break; } itptr = &MessageIdItem[0]; memset (itptr, 0, sizeof(MessageIdItem)); itptr->buf_len = sizeof(VmsMailMsg.MessageId); itptr->item = MAIL$_MESSAGE_ID; itptr->buf_addr = &VmsMailMsg.MessageId; itptr++; if (DestFileNameLength) { itptr->buf_len = DestFileNameLength; itptr->item = MAIL$_MESSAGE_FILENAME; itptr->buf_addr = DestFileName; itptr++; } itptr->buf_len = muptr->VmsMailDestFolderNameLength; if (itptr->buf_len > 39) itptr->buf_len = 39; itptr->item = MAIL$_MESSAGE_FOLDER; itptr->buf_addr = &muptr->VmsMailDestFolderName; itptr++; if (DeleteAfterCopy) { itptr->item = MAIL$_MESSAGE_DELETE; itptr++; } itptr->item = MAIL$_NOSIGNAL; status = mail$message_copy (&VmsMailMsg.MessageContext, &MessageIdItem, &CallMailNullItem); if (Debug) fprintf (stdout, "mail$message_copy() %%X%08.08X\n", status); if (VMSnok (muptr->MailVmsStatus = status)) break; muptr->MessageCopiedCount++; if (VmsMailMsg.MessageFlags & MAIL$M_NEWMSG) muptr->NewMsgFlagCopiedCount++; } if (*cptr == '*' && (status == MAIL$_NOMSGS || status == MAIL$_NOMOREMSG)) status = muptr->MailVmsStatus = SS$_NORMAL; /***************************************/ /* update NEWMAIL count (if necessary) */ /***************************************/ MsgCnt = 0; /* if moved from MAIL.MAI's NEWMAIL folder */ if (DeleteAfterCopy) if (muptr->MailDotMai && !strcmp (muptr->VmsMailFolderName, "NEWMAIL")) if (muptr->VmsNewMsg) MsgCnt -= muptr->NewMsgFlagCopiedCount; else MsgCnt -= muptr->MessageCopiedCount; /* if copied into MAIL.MAI's NEWMAIL folder */ for (cptr = muptr->VmsMailDestFileName + muptr->VmsMailDestFileNameLength; cptr > muptr->VmsMailDestFileName && *cptr != ']'; cptr--); if (*cptr == ']') cptr++; if (!memcmp (cptr, "MAIL\0", 5) || !memcmp (cptr, "MAIL.", 5)) if (!strcmp (muptr->VmsMailDestFolderName, "NEWMAIL")) if (muptr->VmsNewMsg) MsgCnt += muptr->NewMsgFlagCopiedCount; else MsgCnt += muptr->MessageCopiedCount; if (MsgCnt) { status = CallMailSetUserNewMessages (muptr, MsgCnt); if (VMSok (muptr->MailVmsStatus)) if (VMSnok (status)) muptr->MailVmsStatus = status; } /*************************/ /* close message context */ /*************************/ status = mail$message_end (&vmptr->MessageContext, &CallMailNoSignalItem, &CallMailNullItem); if (Debug) fprintf (stdout, "mail$message_end() %%X%08.08X\n", status); vmptr->MessageContext = 0; /* if a move then check for a resulting empty mail file */ if (DeleteAfterCopy) CallMailDeleteIfEmpty (muptr); return (muptr->MailVmsStatus); } /*****************************************************************************/ /* Copy the contents of a sequential message (mail) file into the destination indexed Mail file and folder. The sequential message file must have a VAR record format with CR record attributes. The format for the content of this file is: From:[] To:[] CC:[] Subj:[] Note the leading record that is some sort of sentinal for each (if multiple) message in the sequential file. Many thanks to Andy Harper for originally asking the question and Hein van den Heuvel for answering it way back in 1996, providing a significant insight into this undocumented (or at least unobvious) facility. (Search Google groups for "harper van den vms mail sequential".) Also Lance J. Wilkinson ("never underestimate the bandwidth of a station wagon full of mag tapes") for asking similar questions and elucidating the date requirement. Although (oddly enough) not generated by a $MAIL/EXTRACT/MAIL the first line ("From:") requires a trailing date/time to correctly insert the email into the folder. Without it it appears to go into the folder but not be accessable at all! This is perhaps because Mail file keys on the quadword date/time as well as the folder name and a zero date is not processed correctly or is a sentinal of some sort. This is intimated in a posting by Hunter Goatley where he quotes the V5.0 VMS sources and it notes "Quadword: DATIM Date/time message received (Date/times with high quadword 0 are reserved as info records)". This requirement for such a date tacked onto the "From:" field seems a Mail kludge and is bit disruptive to the header display, so I have decided to 'hide' it by separating the address and date/time with a null character which tends to conceal it from parsers such as soyMAIL (which scan as far as the null 'terminator') but still allows VMS (with it's 'records') to process it. */ int CallMailCopyFile ( VMS_MAIL_USER *muptr, char *DestFileName, char *DestFolderName, char *FileName ) { int status, VmsMailStatus; unsigned long MailFileContext, MailFileIndexed, MailFileNameLength, MessageContext, MessageId, MessageSelected; char *cptr, *sptr; char MailFileName [256]; VMS_ITEM_LIST3 *itptr; /* reminder: these require one extra element for the null item */ VMS_ITEM_LIST3 MailFileInItem [3]; VMS_ITEM_LIST3 MailFileOutItem [3]; VMS_ITEM_LIST3 MessageBeginInItem [3]; VMS_ITEM_LIST3 MessageBeginOutItem [3]; VMS_ITEM_LIST3 MessageIdItem [5]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "CallMailCopyFile() |%s|%s|%s|\n", DestFileName, DestFolderName, FileName); if (!muptr->VmsMailFullDirectoryLength) ErrorExit (SS$_BUGCHECK, FI_LI); if (!muptr->VmsMailFileNameLength) ErrorExit (SS$_BUGCHECK, FI_LI); MailFileNameLength = CallMailBuildMailFileName (muptr, DestFileName, MailFileName, sizeof(MailFileName)); /*********************/ /* mail file context */ /*********************/ MailFileContext = 0; status = mail$mailfile_begin (&MailFileContext, &CallMailNullItem, &CallMailNullItem); if (Debug) fprintf (stdout, "mail$mailfile_begin() %%X%08.08X\n", status); VmsMailStatus = status; if (VMSok (VmsMailStatus)) { itptr = &MailFileInItem[0]; memset (itptr, 0, sizeof(MailFileInItem)); itptr->buf_len = strlen(FileName); itptr->item = MAIL$_MAILFILE_NAME; itptr->buf_addr = FileName; itptr++; itptr->item = MAIL$_NOSIGNAL; itptr = &MailFileOutItem[0]; memset (itptr, 0, sizeof(MailFileOutItem)); itptr->buf_len = sizeof(MailFileIndexed); itptr->item = MAIL$_MAILFILE_INDEXED; itptr->buf_addr = &MailFileIndexed; itptr++; itptr->item = MAIL$_NOSIGNAL; status = mail$mailfile_open (&MailFileContext, &MailFileInItem, &MailFileOutItem); if (Debug) fprintf (stdout, "mail$mailfile_open() %%X%08.08X INDEXED:%d\n", status, MailFileIndexed); if (VMSnok (status)) VmsMailStatus = status; } /*******************/ /* message context */ /*******************/ if (VMSok (VmsMailStatus)) { itptr = &MessageBeginInItem[0]; memset (itptr, 0, sizeof(MessageBeginInItem)); itptr->buf_len = sizeof(MailFileContext); itptr->item = MAIL$_MESSAGE_FILE_CTX; itptr->buf_addr = (void*)&MailFileContext; itptr++; itptr->item = MAIL$_NOSIGNAL; itptr = &MessageBeginOutItem[0]; memset (itptr, 0, sizeof(MessageBeginOutItem)); itptr->buf_len = sizeof(MessageSelected); itptr->item = MAIL$_MESSAGE_SELECTED; itptr->buf_addr = (void*)&MessageSelected; itptr++; itptr->item = MAIL$_NOSIGNAL; MessageContext = 0; status = mail$message_begin (&MessageContext, &MessageBeginInItem, &MessageBeginOutItem); if (Debug) fprintf (stdout, "mail$message_begin() %%X%08.08X selected:%d\n", status, MessageSelected); if (VMSnok (status)) VmsMailStatus = status; } /****************/ /* copy message */ /****************/ if (VMSok (VmsMailStatus)) { MessageId = 1; itptr = &MessageIdItem[0]; memset (itptr, 0, sizeof(MessageIdItem)); itptr->buf_len = sizeof(MessageId); itptr->item = MAIL$_MESSAGE_ID; itptr->buf_addr = (void*)&MessageId; itptr++; itptr->buf_len = MailFileNameLength; itptr->item = MAIL$_MESSAGE_FILENAME; itptr->buf_addr = MailFileName; itptr++; itptr->buf_len = strlen(DestFolderName); itptr->item = MAIL$_MESSAGE_FOLDER; itptr->buf_addr = DestFolderName; itptr++; itptr->item = MAIL$_NOSIGNAL; status = mail$message_copy (&MessageContext, &MessageIdItem, &CallMailNullItem); if (Debug) fprintf (stdout, "mail$message_copy() %%X%08.08X\n", status); /* NOMOREMSG when there has been a problem with the message format */ if (status == MAIL$_NOMOREMSG) status = SS$_FORMAT; if (VMSnok (status)) VmsMailStatus = status; } /***************************************/ /* update NEWMAIL count (if necessary) */ /***************************************/ if (VMSok (VmsMailStatus) && muptr->MailDotMai && !strcmp (muptr->VmsMailFolderName, "NEWMAIL") && !muptr->VmsNewMsg) { /* NEWMAIL folder in primary MAIL.MAI file */ status = CallMailSetUserNewMessages (muptr, 1); if (Debug) fprintf (stdout, "CallMailSetUserNewMessages() %%X%08.08X\n", status); if (VMSok (muptr->MailVmsStatus)) if (VMSnok (status)) muptr->MailVmsStatus = status; } /******************/ /* close contexts */ /******************/ status = mail$message_end (&MessageContext, &CallMailNoSignalItem, &CallMailNullItem); if (Debug) fprintf (stdout, "mail$message_end() %%X%08.08X\n", status); if (VMSnok (status)) VmsMailStatus = status; status = mail$mailfile_close (&MailFileContext, &CallMailNoSignalItem, &CallMailNoSignalItem); if (Debug) fprintf (stdout, "mail$mailfile_close() %%X%08.08X\n", status); if (VMSnok (status)) VmsMailStatus = status; status = mail$mailfile_end (&MailFileContext, &CallMailNoSignalItem, &CallMailNullItem); if (Debug) fprintf (stdout, "mail$mailfile_end() %%X%08.08X\n", status); return (VmsMailStatus); } /*****************************************************************************/ /* Delete the specified message(s). */ int CallMailMessageDelete (VMS_MAIL_USER *muptr) { int status; char *cptr; VMS_ITEM_LIST3 *itptr; VMS_MAIL_MSG *vmptr; VMS_MAIL_MSG VmsMailMsg; /* reminder: these require one extra element for the null item */ VMS_ITEM_LIST3 MessageIdItem [3]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "CallMailMessageDelete() |%s|\n", muptr->MsgIdListPtr); muptr->MessageDeletedCount = muptr->NewMsgFlagDeletedCount = 0; vmptr = &muptr->VmsMailMsg; if (!vmptr->MessageContext) { /************************/ /* open message context */ /************************/ status = CallMailMessageContext (muptr, vmptr); if (VMSnok (status)) return (muptr->MailVmsStatus = status); } if (!(cptr = muptr->MsgIdListPtr)) ErrorExit (SS$_BUGCHECK, FI_LI); if (*cptr == '*' && muptr->MailDotMai && !strcmp (muptr->VmsMailFolderName, "WASTEBASKET")) { /*********************/ /* purge wastebasket */ /*********************/ itptr = &MessageIdItem[0]; memset (itptr, 0, sizeof(MessageIdItem)); itptr->item = MAIL$_NOSIGNAL; status = mail$mailfile_purge_waste (&muptr->VmsMailFileContext, &MessageIdItem, &CallMailNullItem); if (Debug) fprintf (stdout, "mail$mailfile_purge_waste() %%X%08.08X\n", status); if (VMSok (status)) muptr->MessageDeletedCount = 999999; muptr->MailVmsStatus = status; return (muptr->MailVmsStatus); } /**********************/ /* process message(s) */ /**********************/ memset (&VmsMailMsg, 0, sizeof(VmsMailMsg)); VmsMailMsg.MessageContext = vmptr->MessageContext; while (*cptr) { if (*cptr == '*') VmsMailMsg.MessageId++; else { VmsMailMsg.MessageId = atol(cptr); while (*cptr && *cptr != '.') cptr++; if (*cptr != '.') ErrorExit (SS$_BUGCHECK, FI_LI); cptr++; VmsMailMsg.ConfirmHash = atol(cptr); while (*cptr && *cptr != ',') cptr++; if (*cptr) cptr++; CallMailMessageHeader (muptr, &VmsMailMsg); /* if it wasn't the message we were seeking */ if (muptr->MailVmsStatus == SS$_BADCHECKSUM) break; } itptr = &MessageIdItem[0]; memset (itptr, 0, sizeof(MessageIdItem)); itptr->buf_len = sizeof(VmsMailMsg.MessageId); itptr->item = MAIL$_MESSAGE_ID; itptr->buf_addr = &VmsMailMsg.MessageId; itptr++; itptr->item = MAIL$_NOSIGNAL; status = mail$message_delete (&VmsMailMsg.MessageContext, &MessageIdItem, &CallMailNullItem); if (Debug) fprintf (stdout, "mail$message_delete() %%X%08.08X\n", status); muptr->MailVmsStatus = status; if (VMSnok (status)) break; muptr->MessageDeletedCount++; if (VmsMailMsg.MessageFlags & MAIL$M_NEWMSG) muptr->NewMsgFlagDeletedCount++; } if (*cptr == '*' && (status == MAIL$_NOMSGS || status == MAIL$_NOMOREMSG)) muptr->MailVmsStatus = status = SS$_NORMAL; /***************************************/ /* update NEWMAIL count (if necessary) */ /***************************************/ if (muptr->MailDotMai && !strcmp (muptr->VmsMailFolderName, "NEWMAIL")) { /* NEWMAIL folder in primary MAIL.MAI file */ if (muptr->VmsNewMsg) status = CallMailSetUserNewMessages (muptr, -muptr->NewMsgFlagDeletedCount); else status = CallMailSetUserNewMessages (muptr, -muptr->MessageDeletedCount); if (Debug) fprintf (stdout, "CallMailSetUserNewMessages() %%X%08.08X\n", status); if (VMSok (muptr->MailVmsStatus)) if (VMSnok (status)) muptr->MailVmsStatus = status; } /*************************/ /* close message context */ /*************************/ status = mail$message_end (&vmptr->MessageContext, &CallMailNoSignalItem, &CallMailNullItem); if (Debug) fprintf (stdout, "mail$message_end() %%X%08.08X\n", status); vmptr->MessageContext = 0; /* prevent the message complaining you can't delete from the WASTEBASKET */ if (status == MAIL$_DELWASTE) muptr->MailVmsStatus = SS$_NORMAL; /* 'cause you can! */ CallMailFileClose (muptr); CallMailDeleteIfEmpty (muptr); return (muptr->MailVmsStatus); } /*****************************************************************************/ /* If empty, non-primary Mail file then delete it. */ int CallMailDeleteIfEmpty (VMS_MAIL_USER *muptr) { int retval, status; char *cptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "CallMailDeleteIfEmpty()\n"); /* if primary mail file (MAIL.MAI) quit here */ if (muptr->MailDotMai) return (SS$_NORMAL); /* look for the indicative slash (do not delete PMDF IMAP MAIL.MAIs) */ for (cptr = muptr->OpenMailFileName; *cptr && *cptr != '/'; cptr++); if (*cptr) return (SS$_NORMAL); status = CallMailFolderList (muptr, FALSE, FALSE); if (VMSnok (status)) return (muptr->MailVmsStatus = status); if (Debug) fprintf (stdout, "%d\n", muptr->MailFolderList.ListCount); CallMailFileClose (muptr); /* if one or more folders left in the non-primary mail file return now */ if (muptr->MailFolderList.ListCount) return (muptr->MailVmsStatus); /* ensure system:rwed,owner:rwed */ retval = chmod (muptr->VmsMailFileName, 0x180); if (retval == -1) return (muptr->MailVmsStatus = vaxc$errno); for (muptr->MailFileDeletedCount = 0; !remove (muptr->VmsMailFileName); muptr->MailFileDeletedCount++); if (!muptr->MailFileDeletedCount) return (muptr->MailVmsStatus = vaxc$errno); return (SS$_NORMAL); } /*****************************************************************************/ /* Move the message context to standard VMS Mail "NEWMAIL" folder. */ int CallMailGoToNewMail (VMS_MAIL_USER *muptr) { int cnt, status; VMS_ITEM_LIST3 *itptr; VMS_MAIL_MSG *vmptr; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "CallMailGoToNewMail()\n"); vmptr = &muptr->VmsMailMsg; /* close any existing message context */ if (vmptr->MessageContext) { status = mail$message_end (&vmptr->MessageContext, &CallMailNoSignalItem, &CallMailNullItem); if (Debug) fprintf (stdout, "mail$message_end() %%X%08.08X\n", status); vmptr->MessageContext = 0; } /* open message context */ strcpy (muptr->VmsMailFileName, "MAIL"); muptr->VmsMailFileNameLength = strlen(muptr->VmsMailFileName); strcpy (muptr->VmsMailFolderName, "NEWMAIL"); muptr->VmsMailFolderNameLength = strlen(muptr->VmsMailFolderName); status = CallMailMessageContext (muptr, vmptr); if (VMSnok (status)) { if (status == MAIL$_NOTEXIST) { /* NEWMAIL folder does not exist */ muptr->MailVmsStatus = SS$_NORMAL; if (muptr->VmsMailNewMessages) { /* why then does it have a new message count? reset to zero! */ CallMailSetUserNewMessages (muptr, -999999); } } return (muptr->MailVmsStatus); } else { /* if the counts don't match then adjust by difference */ cnt = muptr->VmsMailNewMessages - vmptr->MessageSelectedCount; /* if VMS new msg option enabled then only if count is larger */ if (muptr->VmsNewMsg && cnt < 0) cnt = 0; if (cnt) CallMailSetUserNewMessages (muptr, -cnt); } return (muptr->MailVmsStatus); } /*****************************************************************************/ /* This function retrieves user profile information. */ int CallMailGetUserProfile (VMS_MAIL_USER *muptr) { int status; unsigned long UserNameLength; VMS_ITEM_LIST3 *itptr; /* reminder: these require one extra element for the null item */ VMS_ITEM_LIST3 ProfileItem [14]; VMS_ITEM_LIST3 SigFileItem [3]; VMS_ITEM_LIST3 UserNameItem [3]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "VmsMailGetUserProfile() |%s|\n", muptr->UserName); muptr->DiskQuotaPerm = muptr->DiskQuotaUsed = (unsigned long)-1; itptr = &UserNameItem[0]; memset (itptr, 0, sizeof(UserNameItem)); itptr->buf_len = muptr->UserNameLength; itptr->item = MAIL$_USER_USERNAME; itptr->buf_addr = muptr->UserName; itptr->ret_len = &UserNameLength; itptr++; itptr->item = MAIL$_NOSIGNAL; if (!muptr->VmsMailUserContext) { status = mail$user_begin (&muptr->VmsMailUserContext, &CallMailNoSignalItem, &CallMailNullItem); if (Debug) fprintf (stdout, "mail$user_begin() %%X%08.08X\n", status); if (VMSnok (status)) return (muptr->MailVmsStatus = status); } itptr = &ProfileItem[0]; memset (itptr, 0, sizeof(ProfileItem)); itptr->buf_len = sizeof(muptr->VmsMailNewMessages); itptr->item = MAIL$_USER_NEW_MESSAGES; itptr->buf_addr = &muptr->VmsMailNewMessages; itptr++; itptr->buf_len = sizeof(muptr->VmsMailAutoPurge); itptr->item = MAIL$_USER_AUTO_PURGE; itptr->buf_addr = &muptr->VmsMailAutoPurge; itptr++; itptr->buf_len = sizeof(muptr->VmsMailCcPrompt); itptr->item = MAIL$_USER_CC_PROMPT; itptr->buf_addr = &muptr->VmsMailCcPrompt; itptr++; itptr->buf_len = sizeof(muptr->VmsMailCopyForward); itptr->item = MAIL$_USER_COPY_FORWARD ; itptr->buf_addr = &muptr->VmsMailCopyForward; itptr++; itptr->buf_len = sizeof(muptr->VmsMailCopyReply); itptr->item = MAIL$_USER_COPY_REPLY; itptr->buf_addr = &muptr->VmsMailCopyReply; itptr++; itptr->buf_len = sizeof(muptr->VmsMailCopySend); itptr->item = MAIL$_USER_COPY_SEND; itptr->buf_addr = &muptr->VmsMailCopySend; itptr++; itptr->buf_len = sizeof(muptr->VmsMailForm)-1; itptr->item = MAIL$_USER_FORM; itptr->buf_addr = muptr->VmsMailForm; itptr->ret_len = &muptr->VmsMailFormLength; itptr++; itptr->buf_len = sizeof(muptr->VmsMailEditor)-1; itptr->item = MAIL$_USER_EDITOR; itptr->buf_addr = muptr->VmsMailEditor; itptr->ret_len = &muptr->VmsMailEditorLength; itptr++; itptr->buf_len = sizeof(muptr->VmsMailForwarding)-1; itptr->item = MAIL$_USER_FORWARDING; itptr->buf_addr = muptr->VmsMailForwarding; itptr->ret_len = &muptr->VmsMailForwardingLength; itptr++; itptr->buf_len = sizeof(muptr->VmsMailFullDirectory)-1; itptr->item = MAIL$_USER_FULL_DIRECTORY; itptr->buf_addr = muptr->VmsMailFullDirectory; itptr->ret_len = &muptr->VmsMailFullDirectoryLength; itptr++; itptr->buf_len = sizeof(muptr->VmsMailPersonalName)-1; itptr->item = MAIL$_USER_PERSONAL_NAME; itptr->buf_addr = muptr->VmsMailPersonalName; itptr->ret_len = &muptr->VmsMailPersonalNameLength; itptr++; itptr->buf_len = sizeof(muptr->VmsMailQueue)-1; itptr->item = MAIL$_USER_QUEUE; itptr->buf_addr = muptr->VmsMailQueue; itptr->ret_len = &muptr->VmsMailQueueLength; itptr++; itptr->item = MAIL$_NOSIGNAL; status = mail$user_get_info (&muptr->VmsMailUserContext, &UserNameItem, &ProfileItem); if (Debug) fprintf (stdout, "mail$user_get_info() %%X%08.08X\n", status); if (VMSok (status)) { muptr->VmsMailEditor[muptr->VmsMailEditorLength] = '\0'; muptr->VmsMailForwarding[muptr->VmsMailForwardingLength] = '\0'; muptr->VmsMailForm[muptr->VmsMailFormLength] = '\0'; muptr->VmsMailFullDirectory[muptr->VmsMailFullDirectoryLength] = '\0'; muptr->VmsMailPersonalName[muptr->VmsMailPersonalNameLength] = '\0'; muptr->VmsMailQueue[muptr->VmsMailQueueLength] = '\0'; muptr->VmsMailAutoPurge = muptr->VmsMailAutoPurge & 1; muptr->VmsMailCcPrompt = muptr->VmsMailCcPrompt & 1; muptr->VmsMailCopyForward = muptr->VmsMailCopyForward & 1; muptr->VmsMailCopyReply = muptr->VmsMailCopyReply & 1; muptr->VmsMailCopySend = muptr->VmsMailCopySend & 1; if (Debug) fprintf (stdout, "%d %d %d %d %d %d\n|%s|\n|%s|\n|%s|\n|%s|\n|%s|\n|%s|\n", muptr->VmsMailNewMessages, muptr->VmsMailAutoPurge, muptr->VmsMailCcPrompt, muptr->VmsMailCopyForward, muptr->VmsMailCopyReply, muptr->VmsMailCopySend, muptr->VmsMailEditor, muptr->VmsMailForm, muptr->VmsMailForwarding, muptr->VmsMailFullDirectory, muptr->VmsMailPersonalName, muptr->VmsMailQueue); } muptr->VmsMailSigFile[0] = '\0'; muptr->VmsMailSigFileLength = 0; if (VmsVersion >= 700 && VMSok (status)) { itptr = &SigFileItem[0]; memset (itptr, 0, sizeof(SigFileItem)); itptr->buf_len = sizeof(muptr->VmsMailSigFile)-1; itptr->item = MAIL$_USER_SIGFILE; itptr->buf_addr = muptr->VmsMailSigFile; itptr->ret_len = &muptr->VmsMailSigFileLength; itptr++; itptr->item = MAIL$_NOSIGNAL; status = mail$user_get_info (&muptr->VmsMailUserContext, &UserNameItem, &SigFileItem); if (Debug) fprintf (stdout, "mail$user_get_info() %%X%08.08X\n", status); if (VMSok (status)) { muptr->VmsMailSigFile[muptr->VmsMailSigFileLength] = '\0'; if (Debug) fprintf (stdout, "|%s|\n", muptr->VmsMailSigFile); } status = SS$_NORMAL; } else { /* just substitute a fallback signature file name */ strcpy (muptr->VmsMailSigFile, SOY_SIGFILE); muptr->VmsMailSigFileLength = strlen(muptr->VmsMailSigFile); } if (VMSnok (status)) CallMailUserClose (muptr); return (muptr->MailVmsStatus = status); } /*****************************************************************************/ /* This function sets user profile information. It's expects all relevant fields to have been populated. */ int CallMailSetUserProfile (VMS_MAIL_USER *muptr) { int status; unsigned long UserNameLength; VMS_ITEM_LIST3 *itptr; /* reminder: these require one extra element for the null item */ VMS_ITEM_LIST3 ProfileItem [13]; VMS_ITEM_LIST3 SigFileItem [4]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "VmsMailSetUserProfile()\n"); if (!muptr->VmsMailFullDirectoryLength) ErrorExit (SS$_BUGCHECK, FI_LI); if (!muptr->VmsMailUserContext) { status = mail$user_begin (&muptr->VmsMailUserContext, &CallMailNullItem, &CallMailNullItem); if (Debug) fprintf (stdout, "mail$user_begin() %%X%08.08X\n", status); if (VMSnok (status)) return (muptr->MailVmsStatus = status); } itptr = &ProfileItem[0]; memset (itptr, 0, sizeof(ProfileItem)); itptr->buf_len = muptr->UserNameLength; itptr->item = MAIL$_USER_USERNAME; itptr->buf_addr = muptr->UserName; itptr->ret_len = &UserNameLength; itptr++; if (muptr->VmsMailAutoPurge) itptr->item = MAIL$_USER_SET_AUTO_PURGE; else itptr->item = MAIL$_USER_SET_NO_AUTO_PURGE; itptr++; if (muptr->VmsMailCcPrompt) itptr->item = MAIL$_USER_SET_CC_PROMPT; else itptr->item = MAIL$_USER_SET_NO_CC_PROMPT; itptr++; if (muptr->VmsMailCopyForward) itptr->item = MAIL$_USER_SET_COPY_FORWARD ; else itptr->item = MAIL$_USER_SET_NO_COPY_FORWARD ; itptr++; if (muptr->VmsMailCopyReply) itptr->item = MAIL$_USER_SET_COPY_REPLY; else itptr->item = MAIL$_USER_SET_NO_COPY_REPLY; itptr++; if (muptr->VmsMailCopySend) itptr->item = MAIL$_USER_SET_COPY_SEND; else itptr->item = MAIL$_USER_SET_NO_COPY_SEND; itptr++; if (muptr->VmsMailFormLength) { itptr->buf_len = muptr->VmsMailFormLength; itptr->item = MAIL$_USER_SET_FORM; itptr->buf_addr = muptr->VmsMailForm; } else itptr->item = MAIL$_USER_SET_NO_FORM; itptr++; if (muptr->VmsMailEditorLength) { itptr->buf_len = muptr->VmsMailEditorLength; itptr->item = MAIL$_USER_SET_EDITOR; itptr->buf_addr = muptr->VmsMailEditor; } else itptr->item = MAIL$_USER_SET_NO_EDITOR; itptr++; if (muptr->VmsMailForwardingLength) { itptr->buf_len = muptr->VmsMailForwardingLength; itptr->item = MAIL$_USER_SET_FORWARDING; itptr->buf_addr = muptr->VmsMailForwarding; } else itptr->item = MAIL$_USER_SET_NO_FORWARDING; itptr++; if (muptr->VmsMailPersonalNameLength) { itptr->buf_len = muptr->VmsMailPersonalNameLength; itptr->item = MAIL$_USER_SET_PERSONAL_NAME; itptr->buf_addr = muptr->VmsMailPersonalName; } else itptr->item = MAIL$_USER_SET_NO_PERSONAL_NAME; itptr++; if (muptr->VmsMailQueueLength) { itptr->buf_len = muptr->VmsMailQueueLength; itptr->item = MAIL$_USER_SET_QUEUE; itptr->buf_addr = muptr->VmsMailQueue; } else itptr->item = MAIL$_USER_SET_NO_QUEUE; itptr++; itptr->item = MAIL$_NOSIGNAL; status = mail$user_set_info (&muptr->VmsMailUserContext, &ProfileItem, &CallMailNullItem); if (Debug) fprintf (stdout, "mail$user_set_info() %%X%08.08X\n", status); if (VmsVersion >= 700 && VMSok (status)) { itptr = &SigFileItem[0]; memset (itptr, 0, sizeof(SigFileItem)); itptr->buf_len = muptr->UserNameLength; itptr->item = MAIL$_USER_USERNAME; itptr->buf_addr = muptr->UserName; itptr->ret_len = &UserNameLength; itptr++; itptr->buf_len = muptr->VmsMailSigFileLength; itptr->item = MAIL$_USER_SET_SIGFILE; itptr->buf_addr = muptr->VmsMailSigFile; itptr++; itptr->item = MAIL$_NOSIGNAL; status = mail$user_set_info (&muptr->VmsMailUserContext, &SigFileItem, &CallMailNullItem); if (Debug) fprintf (stdout, "mail$user_set_info() %%X%08.08X\n", status); } if (VMSnok (status)) CallMailUserClose (muptr); return (muptr->MailVmsStatus = status); } /*****************************************************************************/ /* If messages are moved to or from, or deleted from the NEWMAIL folder update the count of new messages. */ CallMailSetUserNewMessages ( VMS_MAIL_USER *muptr, int DeltaValue ) { static short NewMessages; static VMS_ITEM_LIST3 UserGetItem [] = { { sizeof(NewMessages), MAIL$_USER_NEW_MESSAGES, &NewMessages, 0 }, { 0, MAIL$_NOSIGNAL, 0, 0 }, { 0,0,0,0 } }; static VMS_ITEM_LIST3 UserSetItem [] = { { sizeof(NewMessages), MAIL$_USER_SET_NEW_MESSAGES, &NewMessages, 0 }, { 0, MAIL$_USER_USERNAME, 0, 0 }, { 0, MAIL$_NOSIGNAL, 0, 0 }, { 0,0,0,0 } }; static VMS_ITEM_LIST3 UserNameItem [] = { { 0, MAIL$_USER_USERNAME, 0, 0 }, { 0, MAIL$_NOSIGNAL, 0, 0 }, { 0,0,0,0 } }; int status; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "CallMailSetUserNewMessages()\n"); if (!muptr->VmsMailUserContext) { status = mail$user_begin (&muptr->VmsMailUserContext, &CallMailNoSignalItem, &CallMailNullItem); if (Debug) fprintf (stdout, "mail$user_begin() %%X%08.08X\n", status); if (VMSnok (status)) return (muptr->MailVmsStatus = status); } UserNameItem[0].buf_len = muptr->UserNameLength; UserNameItem[0].buf_addr = muptr->UserName; status = mail$user_get_info (&muptr->VmsMailUserContext, &UserNameItem, &UserGetItem); if (Debug) fprintf (stdout, "mail$user_get_info() %%X%08.08X\n", status); if (VMSok (status)) { if (Debug) fprintf (stdout, "NewMessages: %d (%d) %d\n", NewMessages, DeltaValue, NewMessages+DeltaValue); NewMessages += DeltaValue; if (NewMessages < 0) NewMessages = 0; UserSetItem[1].buf_addr = muptr->UserName; UserSetItem[1].buf_len = muptr->UserNameLength; status = mail$user_set_info (&muptr->VmsMailUserContext, &UserSetItem, &CallMailNullItem); if (Debug) fprintf (stdout, "mail$user_set_info() %%X%08.08X\n", status); if (VMSok (status)) muptr->VmsMailNewMessages = NewMessages; } if (VMSnok (status)) CallMailUserClose (muptr); return (muptr->MailVmsStatus = status); } /*****************************************************************************/ /* Close the user context. */ CallMailUserClose (VMS_MAIL_USER *muptr) { /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "CallMailUserClose()\n"); if (!muptr->VmsMailUserContext) return (SS$_NORMAL); mail$user_end (&muptr->VmsMailUserContext, &CallMailNoSignalItem, &CallMailNullItem); muptr->VmsMailUserContext = 0; } /*****************************************************************************/ /* Using the device from the full directory specification of the VMS mail profile and the $GETUI UIC queue an ACP $QIo to obtain the permananet and used disk quotas. Minus one on the permanent quota datum indicates quotas were disabled or that an error status was returned. */ CallMailUserDiskQuota (VMS_MAIL_USER *muptr) { static unsigned long UaiContext = -1; static unsigned long UaiUic; static VMS_ITEM_LIST3 UaiItems [] = { { sizeof(UaiUic), UAI$_UIC, &UaiUic, 0 }, { 0,0,0,0 } }; int status; unsigned short DeviceChannel, OutLength; char *cptr; $DESCRIPTOR (DeviceDsc, ""); $DESCRIPTOR (UserNameDsc, ""); struct fibdef DeviceFib; static VMS_ITEM_LIST2 DeviceFibDsc, InQuotaDsc, OutQuotaDsc; struct { unsigned long flags; unsigned long uic; unsigned long used; unsigned long perm; unsigned long over; unsigned long unused[3]; } InQuota, OutQuota; struct { unsigned short iosb$w_status; unsigned short iosb$w_bcnt; unsigned long iosb$l_reserved; } IOsb; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "CallMailUserDiskQuota()\n"); muptr->DiskQuotaPerm = muptr->DiskQuotaUsed = (unsigned long)-1; UserNameDsc.dsc$a_pointer = muptr->UserName; UserNameDsc.dsc$w_length = muptr->UserNameLength; status = sys$getuai (0, &UaiContext, &UserNameDsc, &UaiItems, 0, 0, 0); if (Debug) fprintf (stdout, "sys$getuai() %%X%08.08X\n", status); if (VMSnok (status)) return (status); DeviceDsc.dsc$a_pointer = cptr = muptr->VmsMailFullDirectory; while (*cptr && *cptr != ':') cptr++; if (*cptr == ':') cptr++; DeviceDsc.dsc$w_length = cptr - DeviceDsc.dsc$a_pointer; if (Debug) fprintf (stdout, "|%*.*s|\n", DeviceDsc.dsc$w_length, DeviceDsc.dsc$w_length, DeviceDsc.dsc$a_pointer); status = sys$assign (&DeviceDsc, &DeviceChannel, 0, 0); if (Debug) fprintf (stdout, "sys$assign() %%X%08.08X\n", status); if (VMSnok (status)) return (status); memset (&DeviceFib, 0, sizeof(DeviceFib)); DeviceFib.fib$w_exctl = FIB$C_EXA_QUOTA; DeviceFibDsc.buf_len = sizeof(DeviceFib); DeviceFibDsc.buf_addr = &DeviceFib; memset (&InQuota, 0, sizeof(InQuota)); InQuotaDsc.buf_len = sizeof(InQuota); InQuotaDsc.buf_addr = &InQuota; InQuota.uic = UaiUic; memset (&OutQuota, 0, sizeof(OutQuota)); OutQuotaDsc.buf_len = sizeof(OutQuota); OutQuotaDsc.buf_addr = &OutQuota; status = sys$qiow (0, DeviceChannel, IO$_ACPCONTROL, &IOsb, 0, 0, &DeviceFibDsc, &InQuotaDsc, &OutLength, &OutQuotaDsc, 0, 0); if (Debug) fprintf (stdout, "sys$qiow() %%X%08.08X %%X%08.08X\n", status, IOsb.iosb$w_status); if (VMSok (status)) status = IOsb.iosb$w_status; sys$dassgn (DeviceChannel); if (status == SS$_NORMAL) { muptr->DiskQuotaPerm = OutQuota.perm; muptr->DiskQuotaUsed = OutQuota.used; } if (Debug) fprintf (stdout, "%d/%d\n", muptr->DiskQuotaPerm, muptr->DiskQuotaUsed); if (status == SS$_QFNOTACT) status = SS$_NORMAL; return (status); } /*****************************************************************************/ /* The 'FileName' can be a fully-specified mail file name (e.g. DEVICE:[DIRECTORY]MAIL.MAI) or a file name only (e.g. MAIL). Build a fully-specified mail file name from it in the supplied storage. Any non-ODS-2 compliant characters are caret-escaped assuming it's an ODS-5 specification. */ int CallMailBuildMailFileName ( VMS_MAIL_USER *muptr, char *FileName, char *MailFileName, int MailFileNameSize ) { char *cptr, *sptr, *zptr; char FileNameBuffer [256]; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "CallMailBuildMailFileName() |%s|\n", FileName); if (FileName == MailFileName) { /* it's destination is the same as the source storage */ zptr = (sptr = FileNameBuffer) + sizeof(FileNameBuffer)-1; for (cptr = FileName; *cptr && sptr < zptr; *sptr++ = *cptr++); if (sptr >= zptr) ErrorExit (SS$_BUGCHECK, FI_LI); *sptr = '\0'; FileName = FileNameBuffer; } zptr = (sptr = MailFileName) + MailFileNameSize - 1; for (cptr = FileName; *cptr && *cptr != ':'; cptr++); if (!*cptr) { /* build a file specification from a name-only */ for (cptr = muptr->VmsMailFullDirectory; *cptr && sptr < zptr; *sptr++ = *cptr++); cptr = FileName; while (*cptr && sptr < zptr) { if (isalnum(*cptr) || *cptr == '$' || *cptr == '-' || *cptr == '_') *sptr++ = *cptr++; else { /* assume it's a EFS-compliant character requiring escaping */ if (sptr < zptr) *sptr++ = '^'; if (*cptr == ' ') { if (sptr < zptr) *sptr++ = '_'; cptr++; } else if (sptr < zptr) *sptr++ = *cptr++; } } } else { /* fully specified file name */ for (cptr = FileName; *cptr && sptr < zptr; *sptr++ = *cptr++); } *sptr = '\0'; for (cptr = sptr; cptr > MailFileName && *cptr != ']' && *cptr != '.' && *(USHORTPTR)(cptr-1) != '^.'; cptr--); if (*cptr != '.') { /* add the default file type */ for (cptr = ".MAI;"; *cptr && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; } if (sptr >= zptr) ErrorExit (SS$_BUGCHECK, FI_LI); if (Debug) fprintf (stdout, "%d |%s|\n", sptr-MailFileName, MailFileName); /* return it's length */ return (sptr - MailFileName); } /****************************************************************************/ /* This function retrieves the the mail file names in the user's Mail directory. It places into the data area each null-terminated file name (i.e. "FILE1\0FILE22\0"), and a count in 'MailFileListCount'. CAUTION: RMS processing of these *.MAI to get the few mail files (as contrasted to message files) can be VERY EXPENSIVE in disk activity and can introduce noticable latency into folder listings. This is why it has been done at this low-level. Can temporarily chew up a bit of memory. */ CallMailFileList (VMS_MAIL_USER *muptr) { int bytes, dfd, retval, rlen, status, EntryFlags, FileCount, NameLength, NewListSize; char *bptr, *cptr, *dzptr, *rptr, *sptr, *zptr, *BufferPtr, *NamePtr; char DirFileName [256+128]; stat_t StatBuffer; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "CallMailFileList()\n"); if (!muptr->VmsMailFullDirectoryLength) ErrorExit (SS$_BUGCHECK, FI_LI); /* a clumsy way of getting to the mail directory file via itself */ zptr = (sptr = DirFileName) + sizeof(DirFileName)-1; for (cptr = muptr->VmsMailFullDirectory; *cptr && sptr < zptr-128; *sptr++ = *cptr++); *sptr = '\0'; while (sptr > muptr->VmsMailFullDirectory && *sptr != ']') sptr--; if (*sptr != ']') ErrorExit (SS$_BUGCHECK, FI_LI); cptr = sptr; while (cptr > muptr->VmsMailFullDirectory && *cptr != '[') { if (*cptr == '.' && *(cptr-1) != '^') break; cptr--; } if (*cptr != '[' && *cptr != '.') ErrorExit (SS$_BUGCHECK, FI_LI); cptr++; *sptr++ = '.'; *sptr++ = '-'; *sptr++ = ']'; while (*cptr != '.' && sptr < zptr) { if (*cptr == '^') cptr++; *sptr++ = *cptr++; } for (cptr = ".DIR"; *cptr && sptr < zptr; *sptr++ = *cptr++); if (sptr > zptr) ErrorExit (SS$_BUGCHECK, FI_LI); *sptr = '\0'; if (Debug) fprintf (stdout, "DirMailFile |%s|\n", DirFileName); if (muptr->MailFileList.ListPtr) { CgiLibVeeMemFree (muptr->MailFileList.ListPtr); muptr->MailFileList.ListPtr = NULL; muptr->MailFileList.ListCount = muptr->MailFileList.ListLength = muptr->MailFileList.ListSize = 0; } if (stat (DirFileName, &StatBuffer) < 0) { status = vaxc$errno; if (Debug) fprintf (stdout, "stat() %%X%08.08X\n", status); return (status); } bytes = StatBuffer.st_size; if (Debug) fprintf (stdout, "%d/%d bytes/blocks\n", bytes, bytes/512); dfd = open (DirFileName, O_RDONLY, 0, "ctx=stm", "shr=get"); if (!dfd) { status = vaxc$errno; if (Debug) fprintf (stdout, "open() %%X%08.08X\n", status); return (status); } BufferPtr = CgiLibVeeMemCalloc (bytes); if (!BufferPtr) { status = vaxc$errno; if (Debug) fprintf (stdout, "calloc() %%X%08.08X\n", status); close (dfd); return (status); } retval = read (dfd, BufferPtr, bytes); if (Debug) fprintf (stdout, "retval: %d\n", retval); if (retval == -1) { status = vaxc$errno; if (Debug) fprintf (stdout, "read() %%X%08.08X\n", status); close (dfd); return (status); } close (dfd); rlen = 0; rptr = bptr = BufferPtr; for (;;) { rptr += rlen; /* records are always on even byte (word) boundaries */ if ((int)rptr & 1) rptr++; for (;;) { /* directory files have no-span record attributes */ rlen = (unsigned int)*(USHORTPTR)rptr; if (rlen != 0xffff) break; rptr = (bptr += 512); if (bptr >= BufferPtr + bytes) break; } if (bptr >= BufferPtr + bytes) break; rptr += sizeof(unsigned short); EntryFlags = *(unsigned char*)(rptr+2); if (EntryFlags & 0x07) continue; /* not an fid-type entry */ if ((EntryFlags & 0x38) > 0x08) continue; /* not using ASCII chars */ if (EntryFlags & 0x80) continue; /* duplicate of previous entry */ NameLength = *(unsigned char*)(rptr+3); NamePtr = rptr + 4; if (Debug) fprintf (stdout, "%d %02d %02d |%*.*s|\n", (bptr-BufferPtr)/512+1, rlen, NameLength, NameLength, NameLength, NamePtr); /* not much point proceding with this one */ if (NameLength <= 4) continue; /* if it doesn't have a .MAI file type */ cptr = NamePtr + NameLength - 4; if (*(ULONGPTR)cptr != '.MAI') continue; /* if it's an externally stored mail message */ if (NameLength == 21+4 && !strncmp (NamePtr, "MAIL$", 5)) continue; /* terminate on the period (assigned above) */ *cptr = '\0'; NewListSize = muptr->MailFileList.ListLength + NameLength + 1; while (NewListSize > muptr->MailFileList.ListSize) { muptr->MailFileList.ListSize = NewListSize + 256; muptr->MailFileList.ListPtr = CgiLibVeeMemRealloc (muptr->MailFileList.ListPtr, muptr->MailFileList.ListSize); if (!muptr->MailFileList.ListPtr) ErrorExit (vaxc$errno, FI_LI); } sptr = muptr->MailFileList.ListPtr + muptr->MailFileList.ListLength; zptr = muptr->MailFileList.ListPtr + muptr->MailFileList.ListSize; for (cptr = NamePtr; *cptr && sptr < zptr; *sptr++ = *cptr++); if (sptr < zptr) *sptr++ = '\0'; if (sptr >= zptr) ErrorExit (SS$_BUGCHECK, FI_LI); muptr->MailFileList.ListCount++; muptr->MailFileList.ListLength = sptr - muptr->MailFileList.ListPtr; } CgiLibVeeMemFree (BufferPtr); return (SS$_NORMAL); } /*****************************************************************************/ /* This function retrieves the the mail file names in the user's Mail directory. It places into the data area each null-terminated file name (i.e. "FILE1\0FILE22\0"), and a count in 'MailFileListCount'. CAUTION: Listing *.MAI to get the few mail files (as contrasted to message files) can be VERY EXPENSIVE in disk activity and can introduce noticable latency into folder listings. This is why it should only be done on demand. */ #ifdef FAR_TOO_EXPENSIVE /* see my implementation immediately above */ CallMailFileList (VMS_MAIL_USER *muptr) { int status, FileCount, NewListSize; char *cptr, *dzptr, *sptr, *zptr; char Directory [255+1], ExpFileName [255+1], ResFileName [255+1]; struct FAB SearchFab; struct NAM SearchNam; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "CallMailFileList()\n"); if (!muptr->VmsMailFullDirectoryLength) ErrorExit (SS$_BUGCHECK, FI_LI); if (muptr->MailFileList.ListPtr) { CgiLibVeeMemFree (muptr->MailFileList.ListPtr); muptr->MailFileList.ListPtr = NULL; muptr->MailFileList.ListCount = muptr->MailFileList.ListLength = muptr->MailFileList.ListSize = 0; } SearchFab = cc$rms_fab; SearchFab.fab$l_dna = muptr->VmsMailFullDirectory; SearchFab.fab$b_dns = muptr->VmsMailFullDirectoryLength; SearchFab.fab$l_fna = "*.MAI;"; SearchFab.fab$b_fns = 6; SearchFab.fab$l_nam = &SearchNam; SearchNam = cc$rms_nam; SearchNam.nam$l_esa = ExpFileName; SearchNam.nam$b_ess = sizeof(ExpFileName)-1; SearchNam.nam$l_rsa = ResFileName; SearchNam.nam$b_rss = sizeof(ResFileName)-1; if (VMSnok (status = sys$parse (&SearchFab, 0, 0))) return (status); SearchNam.nam$l_ver[SearchNam.nam$b_ver] = '\0'; if (Debug) fprintf (stdout, "|%s|\n", ExpFileName); FileCount = 0; while (VMSok (status = sys$search (&SearchFab, 0, 0))) { SearchNam.nam$l_ver[SearchNam.nam$b_ver] = '\0'; if (Debug) fprintf (stdout, "%d |%s|\n", FileCount, ResFileName); /* if it's an externally stored mail message */ if (SearchNam.nam$b_name == 21 && !strncmp (SearchNam.nam$l_name, "MAIL$", 5)) continue; SearchNam.nam$l_name[SearchNam.nam$b_name] = '\0'; if (Debug) fprintf (stdout, "|%s|\n", SearchNam.nam$l_name); NewListSize = muptr->MailFileListLength + strlen(SearchNam.nam$l_name)+1; while (NewListSize > muptr->MailFileListSize) { muptr->MailFileList.ListSize += 1024; muptr->MailFileList.ListPtr = CgiLibVeeMemRealloc (muptr->MailFileList.ListPtr, muptr->MailFileList.ListSize); if (!muptr->MailFileList.ListPtr) ErrorExit (vaxc$errno, FI_LI); } sptr = muptr->MailFileList.ListPtr + muptr->MailFileList.ListLength; zptr = muptr->MailFileList.ListPtr + muptr->MailFileList.ListSize; for (cptr = SearchNam.nam$l_name; *cptr && sptr < zptr; *sptr++ = *cptr++); if (sptr < zptr) *sptr++ = '\0'; if (sptr >= zptr) ErrorExit (SS$_BUGCHECK, FI_LI); muptr->MailFileList.ListCount++; muptr->MailFileList.ListLength = sptr - muptr->MailFileList.ListPtr; SearchNam.nam$l_name[SearchNam.nam$b_name] = '.'; } if (status == RMS$_FNF || status == RMS$_NMF) status = SS$_NORMAL; if (VMSnok (status)) { /* ensure the list is empty in the case of an error */ if (muptr->MailFileList.ListPtr) muptr->MailFileList.ListPtr[0] = '\0'; muptr->MailFileList.ListLength = muptr->MailFileList.ListCount = 0; } return (status); } #endif /* FAR_TOO_EXPENSIVE */ /*****************************************************************************/ /* Supplied with a file name construct the full specification based on the user's Mail directory and return a pointer to that. This function massages the file name to be ODS-2 compliant. */ char* CallMailFileIn ( VMS_MAIL_USER *muptr, char *NamePrefix, char *FileName ) { /* some elbow room so we can do things like 'strcat(ptr,";-3")' */ static unsigned long UaiContext = -1; static char FullFileName [256+16], UaiDefDev [1+31+1], UaiDefDir [1+63+1]; static VMS_ITEM_LIST3 UaiItems [] = { { sizeof(UaiDefDev)-1, UAI$_DEFDEV, &UaiDefDev, 0 }, { sizeof(UaiDefDir)-1, UAI$_DEFDIR, &UaiDefDir, 0 }, { 0,0,0,0 } }; int status, VolumeStruct; char *cptr, *pptr, *sptr, *zptr; $DESCRIPTOR (UserNameDsc, ""); /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "CallMailFileIn() |%s|\n", FileName); zptr = (sptr = FullFileName) + 255; *sptr = '\0'; if (!strncmp (FileName, "SYS$LOGIN:", 10) || !strncmp (FileName, "sys$login:", 10)) { /* use SYSUAF-defined home directory */ FileName += 10; UserNameDsc.dsc$a_pointer = muptr->UserName; UserNameDsc.dsc$w_length = muptr->UserNameLength; status = sys$getuai (0, &UaiContext, &UserNameDsc, &UaiItems, 0, 0, 0); if (Debug) fprintf (stdout, "sys$getuai() %%X%08.08X\n", status); if (VMSnok (status)) ErrorExit (status, FI_LI); UaiDefDev[UaiDefDev[0]+1] = '\0'; UaiDefDir[UaiDefDir[0]+1] = '\0'; for (cptr = UaiDefDev+1; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = UaiDefDir+1; *cptr && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; VolumeStruct = CallMailVolumeStruct (FullFileName); } else if (!strchr (FileName, ':')) { /* does not contain device/logical delimitting colon */ for (cptr = muptr->VmsMailFullDirectory; *cptr && sptr < zptr; *sptr++ = *cptr++); *sptr = '\0'; VolumeStruct = CallMailVolumeStruct (FullFileName); } else VolumeStruct = CallMailVolumeStruct (FileName); if (NamePrefix) for (cptr = NamePrefix; *cptr && sptr < zptr; *sptr++ = *cptr++); if (FileName) { pptr = NULL; for (cptr = FileName; *cptr && sptr < zptr; cptr++) { if (*cptr == '.') pptr = sptr; if (isspace(*cptr)) *sptr++ = '_'; else if (VolumeStruct == 5) *sptr++ = *cptr; else if (isalnum(*cptr)) *sptr++ = *cptr; else *sptr++ = '_'; } if (VolumeStruct != 5 && pptr) *pptr = '.'; } if (sptr >= zptr) ErrorExit (SS$_BUGCHECK, FI_LI); *sptr = '\0'; if (Debug) fprintf (stdout, "FullFileName |%s|\n", FullFileName); return (FullFileName); } /*****************************************************************************/ /* Generates and returns a 32 bit hash of the major components of the VMS mail message. This is intended to help ensure that the message specified by the 'MessageId' number is indeed the message intended. */ unsigned int CallMailHeaderHash (VMS_MAIL_MSG *vmptr) { unsigned int hash32; /*********/ /* begin */ /*********/ CallMailElfHash (NULL, 0); /* initialize */ CallMailElfHash (vmptr->ExtId, vmptr->ExtIdLength); CallMailElfHash (vmptr->Date, vmptr->DateLength); CallMailElfHash (vmptr->From, vmptr->FromLength); CallMailElfHash (vmptr->To, vmptr->ToLength); hash32 = CallMailElfHash (vmptr->Subject, vmptr->SubjectLength); return (hash32); } /****************************************************************************/ /* Hashing function that allows the hash to be developed over multiple calls. It must be initialized before each hash generation with a CallMailElfHash(NULL,0). Based on code by Arash Partow. */ unsigned int CallMailElfHash ( char *str, int len ) { static unsigned int hash; unsigned int x, i; /*********/ /* begin */ /*********/ /* initialize */ if (!str) return (hash = 0); for (i = 0; i < len; str++, i++) { hash = (hash << 4) + (*str); if ((x = hash & 0xF0000000L) != 0) { hash ^= (x >> 24); hash &= ~x; } } return (hash & 0x7FFFFFFF); } /****************************************************************************/ /* Get the volume's underlying file system (ODS-2 or ODS-5), by sys$getdvi() the ACPTYPE for the device. Return 5 if ODS-5 (EFS), 2 if ODS-2, zero on error or anything else. */ /* if pre-7.2 Alpha then define this */ #ifndef DVI$C_ACP_F11V5 #define DVI$C_ACP_F11V5 11 #endif int CallMailVolumeStruct (char *DeviceName) { static int DevOds; static unsigned long DevAcpType, DevChar; static $DESCRIPTOR (DevNameDsc, ""); static VMS_ITEM_LIST3 AcpTypeItemList [] = { { sizeof(DevAcpType), DVI$_ACPTYPE, &DevAcpType, 0 }, { sizeof(DevChar), DVI$_DEVCHAR, &DevChar, 0 }, { 0, 0, 0, 0 } }; int status; char *cptr; struct { unsigned short Status; unsigned short Count; unsigned long Unused; } IOsb; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "CallMailVolumeStruct()\n"); for (cptr = DeviceName; *cptr && *cptr != ':'; cptr++); /* if we can't find a device in it */ if (!*cptr) return (0); DevNameDsc.dsc$a_pointer = DeviceName; DevNameDsc.dsc$w_length = cptr - DeviceName; DevChar = DevAcpType = 0; status = sys$getdviw (0, 0, &DevNameDsc, &AcpTypeItemList, &IOsb, 0, 0, 0); if (Debug) fprintf (stdout, "sys$getdviw() |%*s| %%X%08.08X %%X%08.08X %d\n", DevNameDsc.dsc$w_length, DevNameDsc.dsc$a_pointer, status, IOsb.Status, DevAcpType); if (VMSok (status)) status = IOsb.Status; if (VMSnok (status)) return (0); /* if volume not mounted */ if (!(DevChar & DEV$M_MNT)) return (0); if (DevAcpType == DVI$C_ACP_F11V5) return (5); if (DevAcpType == DVI$C_ACP_F11V2) return (2); return (0); } /*****************************************************************************/ /* This function retrieves the folder names from the PMDF IMAP mailbox. This is a stream-LF file with newline separated folder names. See description in module prologue. It places into the data area each null-terminated folder name, with the list itself terminated by an empty name (i.e. "FOLDER1\0FOLDER2\0\0"). Return status values with the MSB set (inhib msg) to indicate to error reporting it's from the IMAP routine. */ int CallMailPmdfImapMailBox (VMS_MAIL_USER *muptr) { int bytes, retval, status, FolderNameLength, ImapTextSize, NewListSize; char *cptr, *itptr, *sptr, *zptr, *ImapTextPtr, *FolderNamePtr; char FileName [256]; FILE *fp; stat_t StatBuffer; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "CallMailPmdfImapMailBox()\n"); zptr = (sptr = FileName) + sizeof(FileName)-1; for (cptr = muptr->VmsMailFullDirectory; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = "PMDF_IMAP.MAILBOX"; *cptr && sptr < zptr; *sptr++ = *cptr++); if (sptr > zptr) ErrorExit (SS$_BUGCHECK, FI_LI); *sptr = '\0'; if (Debug) fprintf (stdout, "|%s|\n", FileName); if (stat (FileName, &StatBuffer) < 0) { status = vaxc$errno; if (Debug) fprintf (stdout, "stat() %%X%08.08X\n", status); return (status | 0x80000000); } bytes = StatBuffer.st_size; if (Debug) fprintf (stdout, "%d/%d bytes/blocks\n", bytes, bytes/512); ImapTextSize = bytes; ImapTextPtr = CgiLibVeeMemCalloc (ImapTextSize+128); if (!ImapTextPtr) return (vaxc$errno | 0x80000000); fp = fopen (FileName, "r", "ctx=bin", "shr=get"); status = vaxc$errno; if (Debug) fprintf (stdout, "fopen() %%X%08.08X\n", status); if (!fp) return (vaxc$errno | 0x80000000); retval = fread (ImapTextPtr, 1, ImapTextSize, fp); status = vaxc$errno; if (Debug) fprintf (stdout, "fread() %d %%X%08.08X\n", retval, status); if (Debug) fprintf (stdout, "|%s|\n", ImapTextPtr); fclose (fp); if (!retval) return (status | 0x80000000); itptr = ImapTextPtr; while (*itptr) { FolderNamePtr = cptr = itptr; while (*cptr && *(USHORTPTR)cptr != '= ' && *(USHORTPTR)cptr != '! ') cptr++; if (!*cptr) break; if (*(USHORTPTR)cptr == '! ') { /* trashed but not deleted folder */ while (*cptr && *cptr != '\n') cptr++; if (*cptr) cptr++; itptr = cptr; continue; } if (!(FolderNameLength = cptr - FolderNamePtr)) break; *cptr++ = '\0'; if (Debug) fprintf (stdout, "|%s|\n", FolderNamePtr); while (*cptr && *cptr != '\n') cptr++; if (*cptr) cptr++; itptr = cptr; NewListSize = muptr->TempFolderList.ListLength + FolderNameLength; while (NewListSize > muptr->TempFolderList.ListSize) { muptr->TempFolderList.ListSize = NewListSize + 256; muptr->TempFolderList.ListPtr = CgiLibVeeMemRealloc (muptr->TempFolderList.ListPtr, muptr->TempFolderList.ListSize); if (!muptr->TempFolderList.ListPtr) ErrorExit (vaxc$errno, FI_LI); } sptr = muptr->TempFolderList.ListPtr + muptr->TempFolderList.ListLength; for (cptr = FolderNamePtr; *cptr; *sptr++ = *cptr++); *sptr = '\0'; muptr->TempFolderList.ListLength += FolderNameLength + 1; muptr->TempFolderList.ListCount++; } return (SS$_NORMAL); } /*****************************************************************************/ /* Generate a full VMS Mail file specification (source or destination) by using the PMDF_IMAP.MBXDIR indexed file to lookup the directory associated with the PMDF subfolder. See description in module prologue. */ int CallMailPmdfImapMbxDir ( VMS_MAIL_USER *muptr, char *FileName, char *MailFileName, int MailFileNameSize ) { int retval, status; char *cptr, *czptr, *sptr, *zptr; char ImapName [256], MbxDirFileName [256], RecordBuffer [256]; FILE *fp; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "CallMailPmdfImapMbxDir() |%s|\n", FileName); /* buffer to remove the indicative trailing PMDF IMAP trailing slash */ zptr = (sptr = ImapName) + sizeof(ImapName)-1; for (cptr = FileName; *cptr && sptr < zptr; *sptr++ = *cptr++); if (sptr > FileName && *(sptr-1) == '/') sptr--; *sptr = '\0'; zptr = (sptr = MbxDirFileName) + sizeof(MbxDirFileName)-1; for (cptr = muptr->VmsMailFullDirectory; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = "PMDF_IMAP.MBXDIR"; *cptr && sptr < zptr; *sptr++ = *cptr++); if (sptr > zptr) ErrorExit (SS$_BUGCHECK, FI_LI); *sptr = '\0'; if (Debug) fprintf (stdout, "|%s|\n", MbxDirFileName); fp = fopen (MbxDirFileName, "r", "ctx=rec", "shr=get"); status = vaxc$errno; if (Debug) fprintf (stdout, "fopen() %%X%08.08X\n", status); if (!fp) { MailFileName[0] = '\0'; return (0); } for (;;) { retval = fread (RecordBuffer, sizeof(RecordBuffer), 1, fp); if (Debug) fprintf (stdout, "fread() %d %%X%08.08X\n", retval, vaxc$errno); if (retval != 1) break; if (Debug) WatchDump (RecordBuffer, sizeof(RecordBuffer)); if (!strcmp (RecordBuffer, ImapName)) break; } fclose (fp); if (retval == 1) { /* hit! */ zptr = (sptr = MailFileName) + MailFileNameSize-1; for (cptr = muptr->VmsMailFullDirectory; *cptr && *cptr != ':' && sptr < zptr; *sptr++ = *cptr++); if (sptr < zptr) *sptr++ = ':'; for (cptr = RecordBuffer+128; *cptr && sptr < zptr; *sptr++ = *cptr++); for (cptr = "MAIL.MAI"; *cptr && sptr < zptr; *sptr++ = *cptr++); if (sptr > zptr) ErrorExit (SS$_BUGCHECK, FI_LI); *sptr = '\0'; if (Debug) fprintf (stdout, "|%s|\n", MailFileName); return (sptr - MailFileName); } MailFileName[0] = '\0'; return (0); } /*****************************************************************************/ /* Given the folder list (i.e. "FOLDER1\0FOLDER2\0\0") sort this into descending order and eliminate duplicate entries. Normally a folder list generated by callable Mail does not require this additional step but when a PMDF IMAP list is used in addition to callable Mail sorting and reduction is required. */ int CallMailPmdfImapSort (VMS_MAIL_USER *muptr) { int idx1, idx2, ListCount; char *cptr, *sptr, *zptr, *ListPtr; char **SortArray; /*********/ /* begin */ /*********/ if (Debug) fprintf (stdout, "CallMailPmdfImapSort()\n"); /* buffer this, to be disposed of shortly */ ListPtr = muptr->TempFolderList.ListPtr; /* generate an array of pointers to the folder names */ *(&SortArray) = CgiLibVeeMemCalloc (muptr->TempFolderList.ListCount * sizeof(char*)); if (!*(&SortArray)) ErrorExit (vaxc$errno, FI_LI); cptr = ListPtr; for (idx1 = 0; idx1 < muptr->TempFolderList.ListCount; idx1++) { SortArray[idx1] = cptr; while (*cptr) cptr++; cptr++; } /* sort the pointers (based on the strings they point to) */ qsort (*(&SortArray), muptr->TempFolderList.ListCount, sizeof(char*), &CallMailPmdfImapSortCmp); /* allocate space for the sorted strings */ muptr->TempFolderList.ListPtr = CgiLibVeeMemCalloc (muptr->TempFolderList.ListSize); if (!muptr->TempFolderList.ListPtr) return (vaxc$errno); /* copy the strings to new storage using the sorted pointers */ zptr = (sptr = muptr->TempFolderList.ListPtr) + muptr->TempFolderList.ListLength+1; idx1 = ListCount = 0; while (idx1 < muptr->TempFolderList.ListCount) { for (cptr = SortArray[idx1]; *cptr && sptr < zptr; *sptr++ = *cptr++); *sptr++ = '\0'; ListCount++; /* eliminate duplicates by comparing ahead */ for (idx2 = idx1 + 1; idx2 < muptr->TempFolderList.ListCount; idx2++) if (strcmp (SortArray[idx1], SortArray[idx2])) break; idx1 = idx2; } *sptr = '\0'; if (sptr >= zptr) ErrorExit (SS$_BUGCHECK, FI_LI); muptr->TempFolderList.ListLength = sptr - muptr->TempFolderList.ListPtr; muptr->TempFolderList.ListCount = ListCount; CgiLibVeeMemFree (ListPtr); CgiLibVeeMemFree (*(&SortArray)); return (SS$_NORMAL); } /*****************************************************************************/ /* Yes, it's a bit messy! The arguments are pointers to (pointers to char). */ int CallMailPmdfImapSortCmp ( const void *ptr1, const void *ptr2 ) { char *str1, *str2; /*********/ /* begin */ /*********/ str1 = *(char**)ptr1; str2 = *(char**)ptr2; /** if (Debug) fprintf (stdout, "CallMailPmdfImapSortCmp() |%s|%s|\n", str1, str2); **/ /* case-insensitive compare */ while (*str1 && *str2 && tolower(*str1) == tolower(*str2)) { str1++; str2++; } return (tolower(*str1) - tolower(*str2)); } /*****************************************************************************/