2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
6 * These libraries and programs are free software; you can
7 * redistribute them and/or modify them under the terms of the GNU
8 * Lesser General Public License as published by the Free Software
9 * Foundation; either version 2 of the License, or (at your option)
12 * These libraries and programs are distributed in the hope that
13 * they will be useful, but WITHOUT ANY WARRANTY; without even the
14 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU Lesser General Public License for more
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with these librararies and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
27 * $TOG: RFCMailBox.C /main/53 1998/12/10 17:22:41 mgreess $
29 * RESTRICTED CONFIDENTIAL INFORMATION:
31 * The information in this document is subject to special
32 * restrictions in a confidential disclosure agreement between
33 * HP, IBM, Sun, USL, SCO and Univel. Do not distribute this
34 * document outside HP, IBM, Sun, USL, SCO, or Univel without
35 * Sun's specific written approval. This document and all copies
36 * and derivative works thereof must be returned or destroyed at
39 * Copyright 1993, 1994, 1995 Sun Microsystems, Inc. All rights reserved.
45 #include <DtMail/Buffer.hh>
48 #define X_INCLUDE_TIME_H
49 #define XOS_USE_NO_LOCKING
50 #include <X11/Xos_r.h>
52 #include <EUSCompat.h>
64 #include <sys/param.h>
66 #if !defined(__aix) && !defined(__hpux) && !defined(linux)
67 #include <sys/systeminfo.h>
70 #include <Dt/DtPStrings.h>
76 int sysinfo(int, char *, long);
84 #if defined(NEED_MMAP_WRAPPER)
90 #if defined(NEED_MMAP_WRAPPER)
94 #include <DtMail/DtMail.hh>
95 #include <DtMail/DtMailServer.hh>
96 #include <DtMail/ImplDriver.hh>
97 #include <DtMail/Threads.hh>
98 #include <DtMail/IO.hh>
100 #include "str_utils.h"
102 #ifndef MAIL_SPOOL_PATH
103 #define MAIL_SPOOL_PATH "/var/mail/%s"
107 extern "C" int madvise(caddr_t, size_t, int);
108 extern "C" ssize_t pread(int, void *, size_t, off_t);
111 #if defined(sun) || defined(USL)
112 #define LCL_SIG_HANDLER_SIGNATURE
113 #elif defined(__hpux)
114 #define LCL_SIG_HANDLER_SIGNATURE __harg
115 #elif defined(__aix) || defined(__alpha) || defined(linux)
116 #define LCL_SIG_HANDLER_SIGNATURE int
117 #elif defined(__uxp__)
118 #define LCL_SIG_HANDLER_SIGNATURE
122 // Debugging for RFCMailBox.
124 #if defined(DEBUG_RFCMailBox)
125 #define DEBUG_PRINTF(a) printf a
127 #define DEBUG_PRINTF(a)
131 // These macros define a method for executing statements
132 // with set group id privileges enabled
134 #if defined(MAILGROUP_REQUIRED)
136 #define PRIV_ENABLED_OPTIONAL(STATUSVARIABLE, STATEMENT) \
137 STATUSVARIABLE = STATEMENT;
139 #define PRIV_ENABLED(STATUSVARIABLE, STATEMENT) \
141 _session->enableGroupPrivileges(); \
142 STATUSVARIABLE = STATEMENT; \
143 _session->disableGroupPrivileges(); \
146 #define PRIV_ENABLED_OPEN(FILENAME, STATUSVARIABLE, STATEMENT) \
147 if (isSetMailGidNeeded(FILENAME)) { \
148 PRIV_ENABLED(STATUSVARIABLE, STATEMENT) \
150 STATUSVARIABLE = STATEMENT; \
155 #define PRIV_ENABLED_OPTIONAL(STATUSVARIABLE, STATEMENT) \
156 _session->enableGroupPrivileges(); \
157 STATUSVARIABLE = STATEMENT; \
158 _session->disableGroupPrivileges(); \
159 if (STATUSVARIABLE == -1) { \
160 STATUSVARIABLE = STATEMENT; \
163 #define PRIV_ENABLED PRIV_ENABLED_OPTIONAL
165 #define PRIV_ENABLED_OPEN(FILENAME, STATUSVARIABLE, STATEMENT) \
166 STATUSVARIABLE = STATEMENT;
170 #define GET_DUMPFILE_NAME(dfn) \
171 sprintf(dfn, "%s/%s/dtmail.dump", getenv("HOME"), DtPERSONAL_TMP_DIRECTORY)
174 * Local Data Definitions
176 static const int RFCSignature = 0x448612e5;
178 static const int DEFAULT_FOLDER_SIZE = (32 << 10); // 32 KB
180 static int sigbus_env_valid = 0;
182 static jmp_buf sigbus_env;
185 * Local Function Declarations
187 static void SigBusHandler(LCL_SIG_HANDLER_SIGNATURE);
188 static int isMailGroupSystemMailbox(const char *mailboxPath);
189 static int isInboxMailbox(const char *mailboxPath);
190 static char *getInboxPath(DtMail::Session *session);
195 * Notified when an interesting (SIGBUS in this case) signal is raised.
196 * I wanted to throw a C++ exception at this point but that's not
197 * supported everywhere. So, we'll just have to use the thread-unsafe
198 * setjmp/longjmp combination.
201 void SigBusHandler(LCL_SIG_HANDLER_SIGNATURE)
203 if (sigbus_env_valid) {
204 longjmp(sigbus_env, 1);
209 void HexDump(FILE *pfp, char *pmsg, unsigned char *pbufr, int plen, int plimit)
211 unsigned char save[64];
212 long int x, y, z, word, cnt;
218 if (pfp_r == (FILE*) NULL) {
219 char *dumpfilename = new char [MAXPATHLEN+1];
220 _Xctimeparams ctime_buf;
222 GET_DUMPFILE_NAME(dumpfilename);
223 pfp_r = fopen(dumpfilename, "a");
224 const time_t clockTime = (const time_t) time((time_t *)0);
225 memset((void*) &ctime_buf, 0, sizeof(_Xctimeparams));
226 fprintf(pfp_r, "--------------------- pid=%ld %s",
227 (long)getpid(), _XCtime(&clockTime, ctime_buf));
228 delete [] dumpfilename;
231 (void) fprintf(pfp_r, "--> %s (%d bytes at 0x%08lx):\n", pmsg, plen, pbufr);
233 memset((char *)save, 0, sizeof(save));
237 for (x = 0; cnt > 0; x++, z += 16)
239 (void) fprintf(pfp_r, "0x%08lx(+%6.6ld) ", pbufr + z, z);
240 for (y = 0; y < 16; y++)
242 save[y] = pbufr[x * 16 + y];
243 word = pbufr[x * 16 + y];
246 (void) fprintf(pfp_r, "%2.2lx%c", word, y == 7 ? '-' : ' ');
248 (void) fprintf(pfp_r, " ");
250 (void) fprintf(pfp_r, "%s", " *");
251 for (y = 0; y < 16; y++)
253 (void) fprintf(pfp_r, " ");
254 else if (pbufr[x * 16 + y] < ' ' || pbufr[x * 16 + y] > '~')
255 (void) fprintf(pfp_r, "%s", ".");
257 (void) fprintf(pfp_r, "%c", pbufr[x * 16 + y]);
258 (void) fprintf(pfp_r, "%s", "*\n");
261 if (plimit && (x >= (plimit-1)) && (cnt > (plimit*16))) {
262 while (cnt > (plimit*16)) {
267 fprintf(pfp_r, "...\n");
271 if (pfp == (FILE*) NULL) {
272 fprintf(pfp_r, "---------------------\n");
277 RFCMailBox::RFCMailBox(DtMailEnv & error,
278 DtMail::Session * session,
279 DtMailObjectSpace space,
283 const char * impl_name)
284 : DtMail::MailBox(error, session, space, arg, cb, client_data),
285 _msg_list(128), _mappings(4)
287 // We are using a condition to block any threads from
288 // trying to use this object until it is open. We will
289 // not set the object valid until we have a valid thread.
290 // Note that this will not be set to true until a stream
291 // has been successfully opened.
293 DtMailEnv localError;
296 _object_valid = new Condition;
298 _object_valid->setFalse();
300 _map_lock = MutexInit();
302 _impl_name = impl_name;
309 _mail_box_writable = DTM_FALSE;
310 _use_dot_lock = DTM_TRUE;
311 _long_lock_active = DTM_FALSE;
312 _dot_lock_active = DTM_FALSE;
313 _lockf_active = DTM_FALSE;
314 _uniqueLockId = generateUniqueLockId();
315 assert(_uniqueLockId != NULL);
316 _uniqueLockIdLength = strlen(_uniqueLockId);
317 _lockFileName = (char *)0;
320 _mr_allowed = DTM_TRUE;
322 _mra_serverpw = NULL;
324 createMailRetrievalAgent(NULL);
326 // Create a thread for getting new mail and expunging old mail.
327 // Of course we don't need threads for buffer objects.
329 if (_space != DtMailBufferObject) {
330 _thread_info = new NewMailData;
331 _thread_info->self = this;
332 _thread_info->object_valid = _object_valid;
333 _mbox_daemon = ThreadCreate(ThreadNewMailEntry, _thread_info);
336 // We will have to add a poll oriented method as well. We'll ignore
339 if (_space != DtMailBufferObject) {
340 _session->addEventRoutine(error, PollEntry, this, 15);
342 _last_poll = 0; // Causes first poll to fire right way.
345 // We need to figure out what type of locking to use. We use ToolTalk,
346 // when the user has explicitly turned it on via a property.
348 DtMail::MailRc * mailrc = _session->mailRc(error);
349 const char * value = NULL;
350 mailrc->getValue(localError, "cdetooltalklock", &value);
351 if (localError.isSet()) {
352 _tt_lock = DTM_FALSE;
360 // Determine if error logging is enabled
364 mailrc->getValue(localError, "errorlogging", &value);
365 _errorLogging = (localError.isSet() ?
366 (localError.clear(), DTM_FALSE) : DTM_TRUE);
371 _partialListCount = 0;
375 _object_signature = RFCSignature;
378 RFCMailBox::~RFCMailBox(void)
380 if (_object_signature != RFCSignature) {
384 MutexLock lock_scope(_obj_mutex);
385 if (_object_signature == RFCSignature) {
386 _object_valid->setFalse();
389 _session->removeEventRoutine(error, PollEntry, this);
391 // We need to copy the file if writable and dirty out.
392 // NOTE: the caller should really call writeMailBox() or expunge()
393 // (as appropriate) first before destroying this mailbox, as there
394 // is no way to pass an error indication back up from here to the
395 // caller. If an error happens let syslog handle it.
397 if (_mail_box_writable == DTM_TRUE) {
400 writeMailBox(error, DTM_FALSE);
403 // THE MAILBOX COULD NOT BE WRITTEN!! SHOULD DO SOMETHING
404 error.logError(DTM_TRUE,
405 "~RFCMailBox(): Failed to write mailbox: %s",
406 (const char *)error);
411 // Let's keep the map locked.
413 MutexLock lock_map(_map_lock);
415 // Next we tear down the message structures.
417 while (_msg_list.length()) {
418 MessageCache * mc = _msg_list[0];
421 _msg_list.remove(0); // Won't actually touch the object.
424 // Finally we need to get rid of the mapping. There are
425 // actually 3 conditions we need to deal with here.
427 // 1) We opened a file and mapped it. In that case, we unmap,
428 // and close the file.
430 // 2) We created a buffer. In that case the _mapped_region points,
431 // at the region owned by _buffer. We simply destroy _buffer,
432 // but we do not attempt to destroy the mapped region.
434 // 3) The caller asked us to open an existing buffer. In that
435 // case, do nothing (we didn't build it so we won't destroy it).
437 if (_fd >= 0) { // Option 1
438 for (int slot = 0; slot < _mappings.length(); slot++) {
439 munmap(_mappings[slot]->map_region,
440 (size_t) _mappings[slot]->map_size);
446 if (_buffer) { // Option 2
451 // Allocated using malloc and strdup, so free using free.
453 free((void*) _real_path);
454 free((void*) _uniqueLockId);
455 free((void*) _lockFileName);
458 _object_signature = 0;
460 if (NULL != _mra_command) delete _mra_command;
461 if (NULL != _mra_server) delete _mra_server;
462 if (NULL != _mra_serverpw) delete _mra_serverpw;
465 static int isMailGroupSystemMailbox(const char * mailboxPath)
468 #ifdef MAILGROUP_REQUIRED
469 static int oneTimeFlag = 0;
470 static char *cached_inbox_path = 0;
472 if (NULL == mailboxPath) return retval;
476 char *inbox_path = new char[MAXPATHLEN];
479 GetPasswordEntry(pw);
480 sprintf(inbox_path, MAIL_SPOOL_PATH, pw.pw_name);
481 cached_inbox_path = strdup(inbox_path);
483 delete [] inbox_path;
486 assert(cached_inbox_path);
487 retval = (!strcmp(mailboxPath, cached_inbox_path));
492 static char *getMailspoolPath(DtMail::Session *session)
495 DtMail::MailRc *mailrc = session->mailRc(error);
496 char *mailspoolpath = 0;
497 char *syspath = new char[MAXPATHLEN];
500 GetPasswordEntry(pw);
501 sprintf(syspath, MAIL_SPOOL_PATH, pw.pw_name);
502 mailspoolpath = strdup(syspath);
504 assert(NULL!=mailspoolpath);
506 return mailspoolpath;
509 static char *getInboxPath(DtMail::Session *session)
512 DtMail::MailRc *mailrc = session->mailRc(error);
515 mailrc->getValue(error, DTMAS_PROPKEY_INBOXPATH, (const char**) &inboxpath);
518 mailrc->getValue(error, "DT_MAIL", (const char**) &inboxpath);
521 mailrc->getValue(error, "MAIL", (const char**) &inboxpath);
524 inboxpath = getMailspoolPath(session);
535 if (inboxpath && 0 == (strcmp(inboxpath, "MAILSPOOL_FILE")))
538 inboxpath = getMailspoolPath(session);
541 assert(NULL!=inboxpath);
545 static int isInboxMailbox(DtMail::Session *session, const char * mailboxPath)
548 char *inbox_path = NULL;
550 inbox_path = getInboxPath(session);
551 assert(NULL!=inbox_path);
552 retval = (!strcmp(mailboxPath, inbox_path));
557 // Function: alterPageMappingAdvice - change OS mapping advice on selected map
559 // Give the operating system new advice on the referencing activity that
560 // should be expected for a region of file mapped pages.
562 // Spin through all map regions recorded in _mappings looking for the
563 // specified map (or all maps if -1 passed in as the map region), and
564 // if found issue an madvise call on the specified region of memory.
566 // map -- either a specific region that is part of _mappings,
567 // -- OR (MapRegion *)-1 to change ALL map regions in _mappings
568 // advice -- advice as per madvise(3) Operating System call
575 RFCMailBox::alterPageMappingAdvice(MapRegion *map, int advice)
577 int me = _mappings.length();
578 for (int m = 0; m < me; m++) {
579 MapRegion *map_t = _mappings[m];
581 #if !defined(USL) && !defined(__uxp__) && !defined(linux)
582 // no madvise on these systems
583 if (map_t == map || map == (MapRegion *)-1)
584 madvise(map_t->map_region, (size_t) map_t->map_size, advice);
589 // Function: memoryPageSize - return hardware natural memory page size
591 // Compute and return the natural memory page size of the current hardware
593 // Use sysconf to query the operating system about the current hardware;
594 // cache the value so that repeated calls to this function do not generate
595 // repeated calls to the operating system.
601 // long -- natural page size of the current hardware
606 static long mach_page_size = -1; // -1 is "one time value"
608 if (mach_page_size == -1) {
609 mach_page_size = sysconf(_SC_PAGESIZE);
610 assert(mach_page_size != -1);
613 return(mach_page_size);
617 // Functions: addressIsMapped - check if address is in file mapped memory
619 // Check to see if a given memory address is within the bounds of any
620 // memory that may have been mapped from a mailbox file into memory,
621 // and return an indication of whether it is or not.
623 // Go through the list of file to memory mappings and check to see if
624 // the given address is within the bounds of one of the mappings.
626 // addressToCheck -- address in memory to check against mappings
630 // DTM_TRUE - memory address is in file mapped memory and can be accessed
631 // DTM_FALSE - memory address is not in file mapped memory - may not be valid
634 RFCMailBox::addressIsMapped(void *addressToCheck)
636 assert(addressToCheck != NULL);
637 int me = _mappings.length();
638 for (int m = 0; m < me; m++)
640 MapRegion *map = _mappings[m];
641 if ( (addressToCheck >= map->map_region)
642 && (addressToCheck < (map->map_region+map->file_size)) )
649 RFCMailBox::appendCB(DtMailEnv &error, char *buf, int len, void *clientData)
651 RFCMailBox *obj = (RFCMailBox*) clientData;
653 if (NULL == obj) return;
654 obj->append(error, buf, len);
658 RFCMailBox::append(DtMailEnv &error, char *buf, int len)
661 off_t end = lseek(_fd, 0, SEEK_END);
663 // Add a new-line at the end to distinguish separate messages.
664 status = SafeWrite(_fd, buf, len);
668 char *path = _session->expandPath(error, (char *)_arg);
673 DTME_AppendMailboxFile_FileTooBig, DTM_FALSE, NULL,
674 path, errno, error.errnoMessage(errno));
683 DTME_AppendMailboxFile_LinkLost, DTM_FALSE, NULL,
684 path, errno, error.errnoMessage(errno));
689 DTME_AppendMailboxFile_NoSpaceLeft, DTM_FALSE, NULL,
690 path, errno, error.errnoMessage(errno));
695 DTME_AppendMailboxFile_SystemError, DTM_FALSE, NULL,
696 path, errno, error.errnoMessage(errno));
701 if (_hide_access_events)
702 mailboxAccessHide("append");
705 time_t now = time((time_t) NULL);
706 mailboxAccessShow(now, "append");
712 RFCMailBox::create(DtMailEnv & error, mode_t create_mode)
716 MutexLock lock_scope(_obj_mutex);
717 MutexLock lock_map(_map_lock);
720 case DtMailBufferObject:
722 DtMailBuffer * buf = (DtMailBuffer *)_arg;
724 _buffer = (char *)malloc(DEFAULT_FOLDER_SIZE);
725 buf->buffer = _buffer;
726 buf->size = DEFAULT_FOLDER_SIZE;
728 MapRegion * map = new MapRegion;
729 map->file_region = map->map_region = _buffer;
730 map->file_size = map->map_size = DEFAULT_FOLDER_SIZE;
732 _mappings.append(map);
734 _mail_box_writable = DTM_TRUE;
739 case DtMailFileObject:
741 openRealFile(error, O_RDWR | O_CREAT, create_mode);
751 error.setError(DTME_NotSupported);
756 _object_valid->setTrue();
762 RFCMailBox::open(DtMailEnv & error,
763 DtMailBoolean auto_create,
766 DtMailBoolean lock_flag,
767 DtMailBoolean auto_parse
772 if (_tt_lock == DTM_TRUE && lock_flag == DTM_TRUE)
773 _lock_flag = DTM_TRUE;
775 _lock_flag = DTM_FALSE;
777 MutexLock lock_scope(_obj_mutex);
781 MutexLock lock_map(_map_lock);
784 case DtMailBufferObject:
786 DtMailBuffer * buf = (DtMailBuffer *)_arg;
788 MapRegion * map = new MapRegion;
789 map->file_region = map->map_region = (char *)buf->buffer;
790 map->file_size = map->map_size = buf->size;
792 _mappings.append(map);
794 _mail_box_writable = DTM_FALSE;
799 case DtMailFileObject:
804 _mail_box_writable = DTM_FALSE;
805 char * path = _session->expandPath(error, (char *)_arg);
806 PRIV_ENABLED_OPTIONAL(return_result, SafeAccess(path, W_OK));
807 if (return_result == 0) {
812 // We need to use the most restrictive mode that is possible
813 // on the file. If the caller has requested the file be open
814 // read-only, then we should do that, even if read-write is
815 // allowed. We don't want to try to open the file read-write
816 // if we don't have adequate permission however.
818 mode = open_mode == O_RDONLY ? open_mode : mode;
820 openRealFile(error, mode, create_mode);
822 if (auto_create == DTM_TRUE) {
836 error.setError(DTME_NotSupported);
839 if (error.isSet()) { // Can't parse this.
847 #if defined(POSIX_THREADS)
848 ThreadCreate(ThreadParseEntry, this);
853 _object_valid->setTrue(); // New mail watcher starts now.
863 _mail_box_writable = DTM_FALSE;
864 char * path = _session->expandPath(error, (char *)_arg);
865 PRIV_ENABLED_OPTIONAL(return_status, SafeAccess(path, W_OK));
866 if (return_status == 0) {
871 // We need to use the most restrictive mode that is possible
872 // on the file. If the caller has requested the file be open
873 // read-only, then we should do that, even if read-write is
874 // allowed. We don't want to try to open the file read-write
875 // if we don't have adequate permission however.
877 mode = open_mode == O_RDONLY ? open_mode : mode;
879 openRealFile(error, mode, create_mode);
881 if (auto_create == DTM_TRUE) {
885 // Isn't this wrong? Shouldn't create() be called
886 // before unlocking lock_scope?
894 // Validate this file if it's not empty
896 // When move/copy to a file, we want to make sure the first
897 // five characters are "From "
900 #if defined(sun) || defined(USL) || defined(__uxp__)
901 pread(_fd, (void *)inbuf, 5, 0);
903 lseek(_fd, (off_t) 0L, SEEK_SET);
904 read(_fd, (void *)inbuf, 5);
905 lseek(_fd, (off_t) 0L, SEEK_SET);
909 if (strcmp(inbuf, "From ") != 0) {
910 error.setError(DTME_NotMailBox);
919 _object_valid->setTrue(); // New mail watcher starts now.
946 RFCMailBox::messageCount(DtMailEnv & error)
952 if (_object_valid->state() <= 0) {
953 error.setError(DTME_ObjectInvalid);
957 return(_msg_list.length());
959 #endif /* DEAD_WOOD */
962 RFCMailBox::getFirstMessageSummary(DtMailEnv & error,
963 const DtMailHeaderRequest & request,
964 DtMailHeaderLine & summary)
970 if (_object_valid->state() <= 0) {
971 error.setError(DTME_ObjectInvalid);
975 int slot = nextNotDel(0);
977 if (slot >= _msg_list.length()) {
981 makeHeaderLine(error, 0, request, summary);
983 return(_msg_list[0]);
987 RFCMailBox::getNextMessageSummary(DtMailEnv & error,
988 DtMailMessageHandle last,
989 const DtMailHeaderRequest & request,
990 DtMailHeaderLine & summary)
992 if (_object_valid->state() <= 0) {
993 error.setError(DTME_ObjectInvalid);
997 // Let's treat a null last as the start of the list.
1000 return(getFirstMessageSummary(error, request, summary));
1005 int slot = _msg_list.indexof((MessageCache *)last);
1013 slot = nextNotDel(slot);
1015 if (slot >= _msg_list.length()) {
1019 makeHeaderLine(error, slot, request, summary);
1021 return(_msg_list[slot]);
1025 RFCMailBox::getMessageSummary(DtMailEnv & error,
1026 DtMailMessageHandle handle,
1027 const DtMailHeaderRequest & request,
1028 DtMailHeaderLine & summary)
1030 MutexLock lock_map(_map_lock);
1032 if (_object_valid->state() <= 0) {
1033 error.setError(DTME_ObjectInvalid);
1039 int slot = _msg_list.indexof((MessageCache *)handle);
1040 if (slot < 0 || slot >= _msg_list.length()) {
1041 error.setError(DTME_ObjectInvalid);
1045 makeHeaderLine(error, slot, request, summary);
1051 RFCMailBox::clearMessageSummary(DtMailHeaderLine & headers)
1053 if (NULL != headers.header_values)
1054 delete []headers.header_values;
1058 RFCMailBox::getMessage(DtMailEnv & error, DtMailMessageHandle hnd)
1060 if (_object_valid->state() <= 0) {
1061 error.setError(DTME_ObjectInvalid);
1067 int slot = _msg_list.indexof((MessageCache *)hnd);
1069 error.setError(DTME_ObjectInvalid);
1073 MessageCache * mc = _msg_list[slot];
1074 return(mc->message);
1078 RFCMailBox::getFirstMessage(DtMailEnv & error)
1084 if (_object_valid->state() <= 0) {
1085 error.setError(DTME_ObjectInvalid);
1089 int slot = nextNotDel(0);
1091 if (slot >= _msg_list.length()) {
1095 MessageCache * mc = _msg_list[slot];
1096 return(mc->message);
1100 RFCMailBox::getNextMessage(DtMailEnv & error,
1101 DtMail::Message * last)
1105 int slot = lookupByMsg((RFCMessage *)last);
1113 slot = nextNotDel(slot);
1115 if (slot >= _msg_list.length()) {
1119 MessageCache * mc = _msg_list[slot];
1120 return(mc->message);
1124 RFCMailBox::copyMessage(DtMailEnv & error,
1125 DtMail::Message * msg)
1127 #if defined(DEBUG_RFCMailBox)
1128 char *pname = "RFCMailBox::copyMessage";
1131 if (_object_valid->state() <= 0) {
1132 error.setError(DTME_ObjectInvalid);
1138 // The following is a hack for PAR0.5. In the future, we will use
1139 // this test for an optimization, but right now, we can only copy
1142 const char * msg_impl = msg->impl(error);
1143 if (strcmp(msg_impl, "Internet MIME") != 0 &&
1144 strcmp(msg_impl, "Sun Mail Tool") != 0) {
1145 error.setError(DTME_NotSupported);
1149 RFCMessage * rfc_msg = (RFCMessage *)msg;
1151 // We need to protect the file from access. Locking this will also
1152 // block any attempts by the new mail thread.
1156 DEBUG_PRINTF( ("%s: locking mailbox\n", pname) );
1158 if (error.isSet()) {
1159 DtMailEnv tmp_error;
1160 unlockFile(tmp_error, _fd);
1166 off_t end = lseek(_fd, 0, SEEK_END);
1167 status = SafeWrite(_fd, rfc_msg->_msg_start,
1168 rfc_msg->_msg_end - rfc_msg->_msg_start + 1);
1170 // We are going to put this at the real end of the file. We don't
1171 // really care what the current thought size is because new mail
1172 // will take care of that problem.
1175 // Add a new-line at the end.
1176 // It serves to distinguish separate messages.
1178 SafeWrite(_fd, "\n", 1);
1180 DEBUG_PRINTF( ("%s: unlocking mailbox\n", pname) );
1181 unlockFile(error, _fd);
1184 error.setError(DTME_ObjectCreationFailed);
1188 RFCMailBox::copyMailBox(DtMailEnv & error, DtMail::MailBox * mbox)
1192 for (DtMail::Message * msg = mbox->getFirstMessage(error);
1193 msg && error.isNotSet();
1194 msg = mbox->getNextMessage(error, msg)) {
1195 copyMessage(error, msg);
1196 if (error.isSet()) {
1203 RFCMailBox::checkForMail(
1205 const DtMailBoolean already_locked
1210 if (_space != DtMailFileObject) {
1211 error.setError(DTME_NotSupported);
1215 NewMailEvent(already_locked);
1219 RFCMailBox::expunge(DtMailEnv & error)
1223 for (int msg = 0; msg < _msg_list.length(); msg++) {
1224 MessageCache * mc = _msg_list[msg];
1225 if (mc->delete_pending == DTM_FALSE) {
1226 DtMail::Envelope * env = mc->message->getEnvelope(error);
1228 DtMailValueSeq value;
1229 env->getHeader(error, RFCDeleteHeader, DTM_FALSE, value);
1230 if (!error.isSet()) {
1231 mc->delete_pending = DTM_TRUE;
1240 writeMailBox(error, DTM_FALSE);
1241 if ((DTMailError_t)error ==
1242 DTME_CannotWriteToTemporaryMailboxFile_NoFreeSpaceLeft)
1244 // Need to do some thing here don't know now.
1248 // THE MAILBOX COULD NOT BE WRITTEN!! CALLER MUST DO SOMETHING
1251 "RFCMailBox::expunge(): Failed to write mailbox: %s",
1252 (const char *) error
1258 RFCMailBox::impl(DtMailEnv & error)
1265 RFCMailBox::markDirty(const int delta)
1269 // Make sure we really are dirty.
1275 RFCMailBox::callCallback(DtMailCallbackOp op, void * arg)
1277 if (_object_signature != RFCSignature || !_object_valid->state()) {
1282 if (_space == DtMailFileObject) {
1283 path = (char *)_arg;
1289 if (NULL != _callback)
1290 _callback(op, path, NULL, _cb_data, arg);
1296 RFCMailBox::newMessage(DtMailEnv & error)
1298 // RFC does not support a straightforward concept of adding
1299 // a message to a mailbox. We implement move/copy with a sort
1300 // of kludge, but we can only extend the kludge so far.
1302 error.setError(DTME_NotSupported);
1307 RFCMailBox::openRealFile(DtMailEnv & error, int open_mode, mode_t create_mode)
1309 // We first want to check the access modes we have on the
1310 // file. If we can write the file, then we will need to
1311 // root out the real path just to make sure we don't munge
1312 // a link someplace.
1315 char * path = _session->expandPath(error, (char *)_arg);
1316 if (error.isSet()) {
1317 return; // could not expand path
1320 if ((open_mode & O_RDWR) == O_RDONLY) {
1321 _real_path = strdup(path);
1322 SafeStat(_real_path, &buf);
1325 _real_path = (char *)malloc(MAXPATHLEN);
1326 char *link_path = new char[MAXPATHLEN];
1327 strcpy(link_path, path);
1328 strcpy(_real_path, path);
1330 if (SafeLStat(link_path, &buf) == 0 && (open_mode & O_CREAT) == 0) {
1331 while(S_ISLNK(buf.st_mode)) {
1332 int size = readlink(link_path, _real_path, sizeof(link_path));
1334 error.setError(DTME_NoSuchFile);
1336 _real_path = (char *)0;
1339 delete [] link_path;
1343 _real_path[size] = 0;
1344 if (_real_path[0] == '/') {
1345 strcpy(link_path, _real_path);
1348 char * last_slash = strrchr(link_path, '/');
1350 strcpy(last_slash + 1, _real_path);
1353 strcpy(link_path, "./");
1354 strcat(link_path, _real_path);
1358 strcpy(_real_path, link_path);
1360 if (SafeLStat(link_path, &buf)) {
1361 error.setError(DTME_NoSuchFile);
1363 _real_path = (char *)0;
1366 delete [] link_path;
1372 if ((open_mode & O_CREAT) == 0) {
1373 error.setError(DTME_NoSuchFile);
1375 _real_path = (char *)0;
1378 delete [] link_path;
1382 delete [] link_path;
1387 // We should now have a path we can open or create.
1388 // We must now make sure that if the file is being created that
1389 // it is created with the correct permissions and group owner.
1391 // If the mailbox to be created is NOT the system mailbox for this
1392 // user (e.g. the one that official delivery agents use), then the
1393 // permissions for the file should only let the current user have access.
1395 // If the mailbox to be created IS the system mailbox for this user,
1396 // and MAILGROUP_REQUIRED is defined, we must allow group read/write
1397 // and make sure the mailbox is owned by the correct group
1399 int requiresMailGroupCreation = 0;
1403 // delivery agent runs as specific non-root/wheel group
1404 requiresMailGroupCreation = isMailGroupSystemMailbox(_real_path);
1405 if ( (open_mode & O_CREAT) && requiresMailGroupCreation) {
1406 oldUmask = umask(DTMAIL_DEFAULT_CREATE_UMASK_MAILGROUP);
1407 create_mode = DTMAIL_DEFAULT_CREATE_MODE_MAILGROUP;
1410 oldUmask = umask(DTMAIL_DEFAULT_CREATE_UMASK);
1413 // We have 2 choices for locking an RFC file. The first is
1414 // to use the ToolTalk file scoping paradigm, and the second
1415 // is the normal lockf protocol. If we are using ToolTalk,
1416 // we need to initialize the ToolTalk locking object.
1418 // Unfortunately lockf() on an NFS mounted file system will
1419 // upset mmap(). A quick hack is to not use lockf() on any
1420 // remotely mounted file. Since mailx doesn't do tooltalk
1421 // locking now, it is possible to corrupt the spool file when
1422 // using mailx while mailtool has a NFS mounted mail file loaded.
1424 // We should find a solution where mailtool and mailx work
1427 // open the file before we obtain the lock can cause sync problem
1428 // if another owns the lock and has modified the mailbox
1430 DTMBX_LONGLOCK answer = DTMBX_LONGLOCK_SUCCESSFUL;
1432 if (_lock_flag == DTM_TRUE) {
1433 answer = longLock(error);
1434 if (answer == DTMBX_LONGLOCK_FAILED_CANCEL) {
1436 _real_path = (char *)0;
1440 // we have obtained the lock, go ahead and open the mailbox
1441 // Do not open the mailbox with privileges enabled, even if
1442 // creating the file
1444 PRIV_ENABLED_OPEN(_real_path, _fd,
1445 SafeOpen(_real_path, open_mode, create_mode));
1450 PRIV_ENABLED_OPEN(_real_path, _fd,
1451 SafeOpen(_real_path, open_mode, create_mode));
1454 #if !defined(SENDMAIL_LOCKS)
1455 // On some systems, sendmail uses lockf while delivering mail.
1456 // We can not hold a lockf style lock on those systems. Of course
1457 // if the user has turned off ToolTalk locking, they are playing
1460 if ( (_fd >= 0) && ( (open_mode & O_RDWR) == O_RDWR) )
1462 enum DtmFileLocality location;
1465 // Only attempt lockf() if file was opened for reading and writing
1467 location = DtMail::DetermineFileLocality(_real_path);
1468 #if defined(DEBUG_RFCMailBox)
1469 char *locstr = (location==Dtm_FL_UNKNOWN) ?
1471 ((location==Dtm_FL_LOCAL) ? "Local" : "Remote");
1472 DEBUG_PRINTF(("openRealFile: location is %s\n", locstr));
1478 case Dtm_FL_UNKNOWN:
1480 // locality unknown -- assume local and lock
1484 // locality local - apply lock
1486 assert(_lockf_active == DTM_FALSE);
1487 lseek(_fd, 0, SEEK_SET);
1488 PRIV_ENABLED_OPTIONAL(return_status,SafeLockf(_fd, F_TLOCK, 0));
1489 if (return_status == -1) {
1490 error.setError(DTME_OtherOwnsWrite);
1494 _lockf_active = DTM_TRUE;
1499 // locality otherwise -- assume remote dont lock
1508 (void) umask(oldUmask);
1513 switch (saveErrno) {
1515 error.setError(DTME_NoPermission);
1519 error.setError(DTME_IsDirectory);
1523 error.setError(DTME_NoSuchFile);
1527 error.setError(DTME_ObjectAccessFailed);
1531 _real_path = (char *)0;
1536 if ((open_mode & O_CREAT) && requiresMailGroupCreation) {
1538 // Make sure a newly created file has the correct group owner i.d.
1540 gid_t groupId = GetIdForGroupName(DTMAIL_DEFAULT_CREATE_MAILGROUP);
1542 (void) SafeFchown(_fd, (unsigned int) -1, groupId);
1545 if ((open_mode & 0x3) == O_RDWR) {
1546 if (answer == DTMBX_LONGLOCK_SUCCESSFUL ||
1547 answer == DTMBX_LONGLOCK_FAILED_READWRITE)
1548 _mail_box_writable = DTM_TRUE;
1550 _mail_box_writable = DTM_FALSE;
1553 _mail_box_writable = DTM_FALSE;
1557 _file_size = lseek(_fd, 0, SEEK_END);
1558 lseek(_fd, 0, SEEK_SET);
1559 _links = buf.st_nlink;
1561 _lockFileName = generateLockFileName();
1562 assert(_lockFileName != NULL);
1567 RFCMailBox::realFileSize(DtMailEnv & error, struct stat * stat_buf)
1571 // We have to deal with the problem of editors that will unlink
1572 // the file we are watching and rename it to a new file. This
1573 // will cause us to miss new mail, and eventually write data that
1574 // is out of sync with the real state of the file.
1576 // We do this by checking the link count first. If it has changed,
1577 // we will close, reopen, and then stat. Note that we could say if
1578 // the link count has gone down we will do this, but since we can't
1579 // be sure exactly what has happened to this file, we will consider
1580 // any change in the link count to require us to reopen the file.
1585 error_code = SafeFStat(_fd, &buf);
1586 if (error_code >= 0) {
1587 if (buf.st_nlink != _links) {
1590 DEBUG_PRINTF( ("realFileSize: (buf.st_nlink!=_links\n") );
1591 old_mode = fcntl(_fd, F_GETFL) & O_ACCMODE;
1594 PRIV_ENABLED_OPEN(_real_path,
1596 SafeOpen(_real_path, old_mode, 0666));
1598 error.setError(DTME_ObjectAccessFailed);
1602 transferLock(_fd, fd);
1606 if (SafeFStat(_fd, &buf) >= 0) {
1607 _links = buf.st_nlink;
1610 error.logError(DTM_FALSE,
1611 "realFileSize(): fstat(%d/%s) failed errno=%d\n",
1612 _fd, _real_path, errno);
1613 error.setError(DTME_ObjectAccessFailed);
1623 error.logError(DTM_FALSE,
1624 "realFileSize(): fstat(%d/%s) failed errno=%d\n",
1625 _fd, _real_path, errno);
1626 error.setError(DTME_ObjectAccessFailed);
1630 return(buf.st_size);
1634 RFCMailBox::mapFile(DtMailEnv & error,
1635 const DtMailBoolean already_locked,
1638 char *pname = "mapFile";
1641 if (already_locked == DTM_FALSE) {
1642 DEBUG_PRINTF( ("%s: locking mailbox\n", pname) );
1644 if (error.isSet()) {
1645 DtMailEnv tmp_error;
1646 unlockFile(tmp_error, _fd);
1651 // We must map in whole pages. This is done by rounding the
1652 // file size up to the next larger size.
1655 // Some notes on the singing and dancing that follows are in order.
1656 // As of 6/21/95 it has been determined that there is a bug in S494-17
1657 // and S495-25 whereby a mmap() of the mailbox can return sections that
1658 // have "nulls" where valid data should be found. To guard against this,
1659 // we check for nulls at the beginning and the end of a new mmap()ed region
1660 // and if they are found we retry the operation.
1662 // If NFS attribute caching is enabled (which is the default), a
1663 // stat/fstat of a NFS file may not return the correct true size of the
1664 // mailbox if it has been appended to since the last time it was
1665 // mmap()ed. To get around this problem, once it is noticed via
1666 // stat()/fstat() that the mailbox has changed, we must open the mailbox
1667 // on a separate file descriptor, read() in a byte, and then do a fstat()
1668 // to determine the true correct size of the mailbox.
1671 // fstat() currently open mailbox
1673 struct stat tempStatbuf;
1676 if (SafeFStat(_fd, &tempStatbuf) < 0) {
1680 "%s(%d): fstat(%d/%s) failed errno=%d\n",
1681 pname, err_phase, _fd, _real_path, errno);
1682 if (already_locked == DTM_FALSE) {
1683 DEBUG_PRINTF( ("%s: unlocking mailbox\n", pname) );
1684 unlockFile(error, _fd);
1686 error.setError(DTME_ObjectAccessFailed);
1690 // Obtain guaranteed current stat buf for mailbox object,
1691 // regarless of whether it is local or remote
1693 struct stat statbuf;
1696 if (SafeGuaranteedStat(_real_path, &statbuf) == -1) {
1700 "%s(%d): SafeGuaranteedStat(%s) failed errno=%d\n",
1701 pname, err_phase, _real_path, errno);
1702 if (already_locked == DTM_FALSE) {
1703 DEBUG_PRINTF( ("%s: unlocking mailbox\n", pname) );
1704 unlockFile(error, _fd);
1706 error.setError(DTME_ObjectAccessFailed);
1711 // statbuf -- contains the guaranteed stat struct for the mailbox
1712 // tempStatbuf -- contains fstat stat struct for original mailbox
1715 // See if the inode has changed - if so the file has been
1716 // modified out from under is
1719 if (statbuf.st_ino != tempStatbuf.st_ino) {
1721 error.logError(DTM_FALSE,
1722 "%s(%d): inode fstat(%d/%s) != stat(%s)\nfstat buffer entries: ino = %d, dev = %d, nlink = %d, size = %ld\n stat buffer entries: ino = %d, dev = %d, nlink = %d, size = %ld\n",
1723 pname, err_phase, _fd, _real_path, _real_path,
1724 statbuf.st_ino, statbuf.st_dev,
1725 statbuf.st_nlink, statbuf.st_size,
1726 tempStatbuf.st_ino, tempStatbuf.st_dev,
1727 tempStatbuf.st_nlink, tempStatbuf.st_size);
1728 if (already_locked == DTM_FALSE) {
1729 DEBUG_PRINTF( ("%s: unlocking mailbox\n", pname) );
1730 unlockFile(error, _fd);
1732 error.setError(DTME_ObjectInvalid);
1736 long pagesize = memoryPageSize();
1738 // We will only map any file space we haven't mapped so far.
1739 // We always map entire pages to make this easier. We must
1740 // remap the partial pages so we will get the real bits, not
1741 // the zero fill bits provided by mmap.
1743 // The arithmetic for partial mappings is a little odd, and
1744 // is worthy of explanation. The main issue is that we must
1745 // always start on a page boundary, and we must map page
1746 // size chunks. So, for any offset, we need to back up to
1747 // the start of a page in the file. This gives us the following
1748 // entries in MapRegion:
1750 // map_region - Address where the mapping starts.
1751 // map_size - Size of the mapping (will always be a multiple of pagesize).
1752 // file_region - Address where requested file offset begins within
1754 // file_size - Size of file within this mapping.
1755 // offset - Where the mapping begins (which is almost always different
1756 // than the offset where file_region begins.
1758 // So, the new offset begins where the last real file size ended.
1759 // We get this by adding the previous offset, to the file size mapped,
1760 // and then add any the difference between the mapped region and the
1761 // real file offset. This gets us an offset back to the old end of
1762 // file. Now if you are not confused, we need to adjust this new offset
1763 // back to the closest page boundary and begin the mapping from there!
1765 // File by messages:
1766 // v--------v------------v----v---------------------------v----------------v
1767 // | Msg1 | Msg2 |Msg3| Msg4 | Msg5 |
1768 // ^--------^------------^----^---------------------------^----------------^
1770 // +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
1771 // | Pg1 | Pg2 | Pg3 | Pg4 | Pg5 | Pg6 | Pg7 | Pg8 | Pg9 | Pg10| Pg11| Pg12|
1772 // +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
1777 // +--v--+-----+---v-+
1778 // | \\\\\\\\\\\\\\ |
1779 // +--^--+-----+---^-+
1790 // |->| offset_from_map
1803 // |---------------->| .offset
1805 // |-->| offset_from_map
1809 MapRegion * map = new MapRegion;
1810 long offset_from_map;
1812 if (_mappings.length()) {
1813 MapRegion * prev_map = _mappings[_mappings.length() - 1];
1814 map->offset = prev_map->offset + prev_map->file_size +
1815 (prev_map->file_region - prev_map->map_region);
1816 offset_from_map = map->offset % pagesize;
1817 map->offset -= offset_from_map;
1821 offset_from_map = 0;
1824 map->file_size = statbuf.st_size - map->offset - offset_from_map;
1825 map->map_size = statbuf.st_size - map->offset;
1826 map->map_size += (pagesize - (map->map_size % pagesize));
1828 int flags = MAP_PRIVATE;
1830 #if defined(MAP_NORESERVE)
1831 // We are not supposed to be writing to these pages. If
1832 // we don't specify MAP_NORESERVE however, the system will
1833 // reserve swap space equal to the file size to deal with
1834 // potential writes. This is wasteful to say the least.
1836 flags |= MAP_NORESERVE;
1839 // Need to obtain an mmap()ed region and one way or another
1840 // have the data from the mail file placed into that
1841 // region. There are two ways of accomplishing this:
1843 // Method 1: mmap() the data directly from the file
1844 // Method 2: mmap() /dev/zero and then read from the file to the region
1846 // We want to use method #1 if at all possible as it allows the
1847 // VM system to page the data in as it is accessed.
1849 // There is a potential problem with method #1 in that since
1850 // the region is a "view" into the real file data, if the file
1851 // itself is reduced in size behind our back by another
1852 // process, we have the potential for generating SIGBUS memory
1853 // reference errors if we try and access a byte that is no
1854 // longer within the file. This is true even is MAP_PRIVATE is used:
1856 // ** The behavior of PROT_WRITE can be influenced by setting
1857 // ** MAP_PRIVATE in the flags parameter. MAP_PRIVATE means
1858 // ** "Changes are private".
1860 // ** MAP_SHARED and MAP_PRIVATE describe the disposition of write
1861 // ** references to the memory object. If MAP_SHARED is
1862 // ** specified, write references will change the memory object.
1863 // ** If MAP_PRIVATE is specified, the initial write reference
1864 // ** will create a private copy of the memory object page and
1865 // ** redirect the mapping to the copy. Either MAP_SHARED or
1866 // ** MAP_PRIVATE must be specified, but not both. The mapping
1867 // ** type is retained across a fork(2).
1869 // ** Note that the private copy is not created until the first
1870 // ** write; until then, other users who have the object mapped
1871 // ** MAP_SHARED can change the object.
1873 // While this is always the case for a process that does not
1874 // abide by the advisory only locking protocols used to protect
1875 // against this, to guard against this method #1 is only used
1876 // if the mailbox is writable and we can obtain a short term
1877 // lock on the mailbox. This prevents the mailbox from changing
1878 // size at the moment we map it. Nonetheless, a SIGBUS
1879 // interrupt handler must be armed to handle the case where the
1880 // mailbox file is changed out from under us by devious means.
1882 // If this condition is not met, or the mmap() call for some
1883 // reason fails, then fall back to method #2.
1886 char *mmap_format_string = "%s(%d): mmap(0, map_size=%ld, prot=0x%04x, flags=0x%04x, fd=%d(%s), %x) == map_region=%x, errno == %d\n";
1888 map->map_region = (char *)-1;
1890 if ( (_use_dot_lock == DTM_TRUE)
1891 && (_mail_box_writable == DTM_TRUE)
1892 #if defined(__osf__)
1893 && (_long_lock_active == DTM_TRUE)
1897 #if defined(__osf__) && OSMAJORVERSION < 4
1898 // This version of mmap does NOT allow requested length to be
1899 // greater than the file size ... in contradiction to the
1900 // documentation (don't round up).
1901 map->map_region = (char *) mmap(
1902 0, (statbuf.st_size - map->offset),
1903 PROT_READ, flags, _fd, (off_t) map->offset);
1905 map->map_region = (char *)mmap(
1906 0, (unsigned int) map->map_size,
1907 PROT_READ, flags, _fd, (off_t) map->offset);
1910 // ERROR/CONSISTENCY CHECKING: if the region was not mapped,
1911 // or the first or last byte of the region is a null, then
1912 // the mmap() is considered to have failed: write an entry
1913 // to the dump file, undo the damage, and bail to the caller
1916 if ( (map->map_region == (char *) -1) ||
1917 (map->file_size && (map->map_region[offset_from_map] == '\0')) ||
1919 (map->map_region[offset_from_map+map->file_size-1] == '\0'))
1923 ("mapFile: Error mmap(1) == %p, errno = %d\n", map->map_region, errno));
1929 map->map_size, PROT_READ, flags, _fd, _real_path, map->offset,
1930 map->map_region, errno);
1932 "%s(%d): statbuf: ino=%d, dev=%d, nlink=%d, size=%ld\n",
1934 statbuf.st_ino, statbuf.st_dev, statbuf.st_nlink, statbuf.st_size);
1936 if (map->map_region == (char *) -1)
1940 "%s(%d): mmap failed: errno = %d: %s\n",
1941 pname, err_phase, errno, strerror(errno));
1948 for (i=(int)offset_from_map, cnt=0;
1949 i<map->file_size+offset_from_map;
1951 if (map->map_region[i] == '\0') cnt++;
1954 "%s(%d): mmap failed: %d NULLs in map from byte %d to %d:\n",
1956 cnt, offset_from_map, map->file_size+offset_from_map);
1960 // Pass throught to attempt mapping through /dev/zero.
1962 munmap(map->map_region, (size_t) map->map_size);
1963 map->map_region = (char*) -1;
1965 if (_errorLogging) {
1969 (unsigned char *)map->map_region,
1971 _errorLogging ? 0 : 100);
1974 "Offset Region mapped in",
1975 (unsigned char *)map->map_region+offset_from_map,
1977 _errorLogging ? 0 : 100);
1980 if (already_locked == DTM_FALSE) {
1981 DEBUG_PRINTF( ("%s: unlocking mailbox\n", pname) );
1982 unlockFile(error, _fd);
1985 map->map_region != (char *)-1 ?
1986 DTME_ObjectAccessFailed :
1987 DTME_ObjectCreationFailed);
1995 ("mapFile: mmap(1) okay = %p, errno = %d\n", map->map_region, errno) );
1998 // If a region was mapped, cause OS to use sequential access paging rules
2000 if (map->map_region != (char *) -1)
2001 alterPageMappingAdvice(map);
2003 if (map->map_region == (char *) -1) {
2004 // Either the direct mmap failed, or we decided not to do a direct mmap
2005 // of the new data in the mailbox file. Now must create a mmap()ed
2006 // region against /dev/zero and then read the new data into that region
2008 char *devzero = "/dev/zero";
2010 #if defined(DO_ANONYMOUS_MAP)
2012 mode_t mode = create_mode;
2013 flags |= MAP_ANONYMOUS;
2015 int fd = SafeOpen(devzero, O_RDWR, create_mode);
2018 map->map_region = (char *)mmap(
2019 0, (unsigned int) map->map_size,
2020 PROT_READ|PROT_WRITE, flags, fd, 0);
2023 if (map->map_region == (char *)-1) {
2030 map->map_size, PROT_READ|PROT_WRITE, flags, fd, devzero, errno);
2034 map->map_size, PROT_READ|PROT_WRITE, flags, fd, devzero, errno);
2036 if (already_locked == DTM_FALSE) {
2037 DEBUG_PRINTF( ("%s: unlocking mailbox\n", pname) );
2038 unlockFile(error, _fd);
2040 error.setError(DTME_NoMemory);
2045 if (fd != -1 && (SafeClose(fd) < 0)) {
2046 // should deal with the error here (on /dev/zero??)
2049 // Have created a sufficiently large region mapped to /dev/zero
2050 // Read the "new bytes" into this region
2051 // Note that there is a suspicion that a window exists where even
2052 // though the file size appears to be increased by "n", that all
2053 // of the data may not be written in the file yet, and a read may
2054 // fall "short" in this case, especially if running over nfs. We
2055 // must be prepared to handle any condition whereby a read from
2056 // the file either yields less data than expected, or even though
2057 // a good return is received, the data may not be there, and thus
2058 // the check for "nulls" at the beginning and end of the buffer.
2061 // Copying messages between mailboxes often leaves a single
2062 // NULL character at the end of the file.
2063 // Check the last 2 bytes for NULL characters.
2066 lseek(_fd, (off_t) map->offset, SEEK_SET);
2067 size_t bytesToRead = (size_t)(statbuf.st_size - map->offset);
2068 ssize_t readResults = SafeRead(_fd, map->map_region, bytesToRead);
2069 if ( (readResults != bytesToRead) ||
2070 (readResults && (map->map_region[0] == '\0')) ||
2071 (readResults && (map->map_region[readResults-1] == '\0')) ||
2072 (readResults && (map->map_region[offset_from_map] == '\0')) ||
2074 (map->map_region[offset_from_map+map->file_size-1] == '\0'))
2079 "%s(%d): SafeRead(%d(%s), 0x%08lx, %d) == %d, errno == %d\n",
2080 pname, err_phase, _fd, _real_path, map->map_region, bytesToRead,
2081 readResults, errno);
2083 "%s(%d): stat buf: ino=%d, dev=%d, nlink=%d, size=%ld\n",
2084 pname, err_phase, statbuf.st_ino, statbuf.st_dev,
2085 statbuf.st_nlink, statbuf.st_size);
2087 if (readResults > 0) {
2092 (unsigned char *)map->map_region, readResults,
2093 _errorLogging ? 0 : 100);
2096 munmap(map->map_region, (size_t) map->map_size);
2098 if (already_locked == DTM_FALSE) {
2099 DEBUG_PRINTF( ("%s: unlocking mailbox\n", pname) );
2100 unlockFile(error, _fd);
2105 DTME_ObjectAccessFailed :
2106 DTME_ObjectCreationFailed);
2110 mprotect(map->map_region, (size_t) map->map_size, PROT_READ);
2111 alterPageMappingAdvice(map);
2114 map->file_region = map->map_region + offset_from_map;
2116 // Ok, we think we have got all of the new data that has been
2117 // appended to the mailbox - just to make absolutely sure, stat
2118 // the file again and make sure that the file size has remained
2119 // consistent throughout this operation. If it has changed, it
2120 // means some process ignored our lock on the file and appended
2121 // data anyway. In this case, throw away our current effort and
2122 // recursively call this function to try the attempt again.
2126 if (SafeGuaranteedStat(_real_path, &tempStatbuf) < 0) {
2130 "%s(%d): SafeGuaranteedStat(%s) failed errno=%d: %s\n",
2131 pname, err_phase, _real_path, errno, strerror(errno));
2132 munmap(map->map_region, (size_t) map->map_size);
2134 if (already_locked == DTM_FALSE) {
2135 DEBUG_PRINTF( ("%s: unlocking mailbox\n", pname) );
2136 unlockFile(error, _fd);
2138 error.setError(DTME_ObjectAccessFailed);
2143 if (tempStatbuf.st_size != statbuf.st_size) {
2147 "%s(%d): fstat(%d/%s) size changed %d/%d\n",
2149 _fd, _real_path, statbuf.st_size, tempStatbuf.st_size);
2150 munmap(map->map_region, (size_t) map->map_size);
2152 int mapResults = mapFile(error, DTM_TRUE);
2153 if (error.isSet()) {
2154 if (already_locked == DTM_FALSE) {
2155 DEBUG_PRINTF( ("%s: unlocking mailbox\n", pname) );
2156 unlockFile(error, _fd);
2163 if (already_locked == DTM_FALSE) {
2164 DEBUG_PRINTF( ("%s: unlocking mailbox\n", pname) );
2165 unlockFile(error, _fd);
2168 // We need to set _file_size here, because if we get the file size
2169 // when the mailbox is not locked, it is possible that we could be
2170 // stating the file while it is being written to. Since we have the
2171 // lock in this routine, we know the file size isn't changing, and it
2172 // is consistent with the amount being mapped in.
2174 _file_size = statbuf.st_size;
2176 if (_hide_access_events) mailboxAccessHide("mapFile");
2178 return(_mappings.append(map));
2182 RFCMailBox::nextNotDel(const int cur)
2185 for (del = cur; del < _msg_list.length(); del++) {
2186 MessageCache * mc = _msg_list[del];
2187 if (mc->delete_pending == DTM_FALSE) {
2196 RFCMailBox::prevNotDel(const int cur)
2199 for (del = cur; del >= 0; del--) {
2200 MessageCache * mc = _msg_list[del];
2201 if (mc->delete_pending == DTM_FALSE) {
2210 RFCMailBox::lookupByMsg(RFCMessage * msg)
2212 for (int slot = 0; slot < _msg_list.length(); slot++) {
2213 MessageCache * mc = _msg_list[slot];
2214 if (mc->message == msg) {
2223 RFCMailBox::ThreadParseEntry(void * client_data)
2225 RFCMailBox * self = (RFCMailBox *)client_data;
2228 MutexLock lock_map(self->_map_lock);
2230 self->parseFile(error, 0);
2239 RFCMailBox::parseFile(DtMailEnv & error, int map_slot)
2244 // We are not at the eof.
2248 const char * begin = _mappings[map_slot]->file_region;
2249 const char * end = begin + _mappings[map_slot]->file_size - 1;
2252 // Empty file. DO NOTHING OR IT WILL CRASH!
2258 // Parsing will always be a sequential access to the pages.
2259 // We will give the kernel a clue what we are up to and perhaps
2260 // help our parsing time in the process.
2262 unsigned long pagelimit = _mappings[map_slot]->map_size;
2264 #if !defined(USL) && !defined(__uxp__) && !defined(linux)
2265 // no madvise; dont use optimization
2267 (char *)_mappings[map_slot]->map_region,
2268 (size_t) pagelimit, MADV_SEQUENTIAL);
2271 // We should always begin with a "From " if this is an RFC file,
2272 // with a valid offset. If we have something else, then look
2273 // forward until we find one.
2275 const char * parse_loc = begin;
2276 if (strncmp(parse_loc, "From ", 5)) {
2277 if (*parse_loc == ' ' || *parse_loc == '\n' || *parse_loc == '\t') {
2278 // We allow any number of white spaces before "From"
2279 // But "From" still has to be the first word in the line
2280 while ( (*parse_loc == ' ' || *parse_loc == '\n'
2281 || *parse_loc == '\t') && parse_loc <= end) {
2285 if (parse_loc >= end) {
2286 error.setError(DTME_NotMailBox);
2287 _at_eof.setTrue(); // We are, but so what.
2293 if (strncmp(parse_loc, "\nFrom ", 6)) {
2294 error.setError(DTME_NotMailBox);
2298 // This file does not start with either From or white space
2299 error.setError(DTME_NotMailBox);
2304 if (*parse_loc == '\n') {
2308 // We are sitting at the start of a message. We will build message
2309 // objects for each message in the list.
2312 MessageCache * cache = new MessageCache;
2313 cache->delete_pending = DTM_FALSE;
2315 cache->message = new RFCMessage(error, this, &parse_loc,
2317 if (error.isNotSet()) {
2319 //#ifdef MESSAGE_PARTIAL
2320 // message/partial processing is currently not working, so we are
2321 // taking out message/parital processing for now until we figure
2322 // out how to get it to work again. Only the following block of
2323 // code needs to be taken out in order to disable message/partial.
2324 // Also, when it was turned on, it was trying to combine partial
2325 // messages that were non-MIME (no MIME-VERSION header). This
2326 // caused even more problems. We should only check for partial
2327 // messages if it is MIME.
2329 if (_isPartial(error, cache->message)) {
2331 if (error.isNotSet()) {
2332 cache->message->setFlag(error, DtMailMessagePartial);
2334 if (error.isNotSet()) {
2335 cache->message = _assemblePartial(error, cache->message);
2337 if (error.isNotSet()) {
2338 _msg_list.append(cache);
2344 //#endif // MESSAGE_PARTIAL
2345 _msg_list.append(cache);
2350 } while (parse_loc <= end);
2352 // At this point we most likely will see random behavior. We will
2353 // tell the kernel to pull in the minimum number of extra pages.
2355 #if !defined(USL) && !defined(__uxp__) && !defined(linux)
2356 // no madvise; dont use optimization
2358 (char *)_mappings[map_slot]->map_region,
2359 (size_t) pagelimit, MADV_RANDOM);
2361 // IBM code for message/partial vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
2362 // we need delete those messages if they are satisfied the
2363 // following two conditions
2364 // (1) marked for delete and
2365 // (2) it is a message/partial message
2367 // Otherwise, we will get segmentation error when the method
2368 // writeMailBox is invoked because a new message were
2369 // generated by assembling partial messages
2371 for (int msg = 0; msg < _msg_list.length(); msg++) {
2372 MessageCache * mc = _msg_list[msg];
2373 if (mc->delete_pending == DTM_FALSE) {
2374 DtMail::Envelope * env = mc->message->getEnvelope(error);
2376 DtMailValueSeq value;
2378 static const char * partial = "message/partial";
2379 static const char * contentType = "content-type";
2381 env->getHeader(error, contentType , DTM_FALSE, value);
2382 if (error.isNotSet()) {
2383 type = strdup(*(value[0]));
2389 if (error.isNotSet()) {
2390 env->getHeader(error, RFCDeleteHeader, DTM_FALSE, value);
2392 if (!error.isSet() && (strncasecmp(type, partial, 15) == 0)) {
2393 delete mc->message; // remove message storage
2394 delete mc; // remove message cache storage
2395 _msg_list.remove(msg); // remove message from message list
2396 msg -= 1; // next message is where we are at now
2407 //IBM code for message/partial ^^^^^^^^^^^^^^^^^^^^^^^^^^^
2414 RFCMailBox::ThreadNewMailEntry(void * client_data)
2416 NewMailData * info = (NewMailData *)client_data;
2418 // We need to wait for the object to become valid. We have
2419 // nothing to do until the mail box is open.
2420 info->object_valid->waitTrue();
2422 // Get the various configuration parameters from .mailrc. The
2423 // RFC_PING_INTERVAL controls how often we look for new mail.
2424 // RFC_CHECK is the number of pings between checks and
2425 // RFC_EXPUNGE is the number of pings between expunges.
2428 DtMail::MailRc * mailrc = info->self->session()->mailRc(error);
2432 time_t check_per_ping = 120;
2433 time_t expunge_per_ping = 240;
2435 const char * value = NULL;
2436 mailrc->getValue(error, "RFC_PING_INTERVAL", &value);
2437 if (error.isNotSet()) {
2438 ping = (time_t) strtol(value, NULL, 10);
2444 free((void*) value);
2447 mailrc->getValue(error, "RFC_CHECK", &value);
2448 if (error.isNotSet()) {
2449 check_per_ping = (time_t) strtol(value, NULL, 10);
2455 free((void*) value);
2458 mailrc->getValue(error, "RFC_EXPUNGE", &value);
2459 if (error.isNotSet()) {
2460 expunge_per_ping = (time_t) strtol(value, NULL, 10);
2466 free((void*) value);
2470 unsigned int unslept = 0;
2472 // Wait until the mail file is parsed.
2474 info->self->_at_eof.waitTrue();
2476 // We loop until the object tries to close, then we exit.
2478 while(info->object_valid->state()) {
2480 // The following sequence is a little wierd, but here is why.
2481 // We need to sleep for the ping interval. We can be awaken
2482 // early however if the thread catches a signal. The main
2483 // thread will send a SIGTERM from the mail box destructor
2484 // to wake us up. If the object state is no longer valid, then
2485 // this is the cause, so we need to check it first and exit
2486 // if this is the case.
2488 // We can also be awaken by other signals that we don't care
2489 // about. In that case we need to go back through the loop
2490 // again and sleep any unslept time. We can't be sure however
2491 // that this additional sleep won't be awaken before it is
2492 // done so we have to keep looping and checking the object
2496 unslept = (unsigned int) ThreadSleep(unslept);
2497 continue; // We got rudely awaken!
2500 unslept = (unsigned int) ThreadSleep(ping);
2501 if (!info->object_valid->state()) {
2509 // We made here, we slept and are well rested, and
2510 // we should have a valid object. Run the events.
2515 if (check_per_ping && (pinged % check_per_ping) == 0) {
2517 // info->self->CheckPointEvent(error);
2518 info->self->CheckPointEvent();
2519 // if (error.isSet()) {
2520 // // MAILBOX COULD NOT BE CHECKPOINTED!!! MUST DO SOMETHING!!!
2525 if (expunge_per_ping && (pinged % expunge_per_ping) == 0) {
2526 info->self->ExpungeEvent();
2530 info->self->NewMailEvent();
2533 // We are responsible for cleaning up the condition variable.
2535 delete info->object_valid;
2545 RFCMailBox::PollEntry(void * client_data)
2547 RFCMailBox * self = (RFCMailBox *)client_data;
2549 // Get the various configuration parameters from .mailrc. The
2550 // RFC_PING_INTERVAL controls how often we look for new mail.
2551 // RFC_CHECK is the number of pings between checks and
2552 // RFC_EXPUNGE is the number of pings between expunges.
2557 DtMail::MailRc * mailrc = self->session()->mailRc(error);
2558 error.clear(); // IGNORING ERRORS FROM MAILRC CALL!!
2561 time_t ping = 60; // check for new mail every 60 seconds
2562 time_t save_interval = (30 * 60); // autosave every 30 minutes
2563 long minimumIdleTime = 30; // must have 30 seconds idle time to poll
2565 // Retrieve current setting for how often we are supposed to
2566 // look for new mail (retrieveinterval)
2571 mailrc->getValue(error, "retrieveinterval", &value);
2572 if (error.isNotSet() && value != NULL && *value != '\0')
2574 ping = (time_t) strtol(value, NULL, 10);
2581 if (NULL != value) free((void*) value);
2583 // Retrieve current setting for how often we are supposed to
2584 // perform an auto save of the mailbox
2587 mailrc->getValue(error, "dontautosave", &value);
2588 if (error.isSet()) {
2591 free((void*) value);
2594 mailrc->getValue(error, "saveinterval", &value);
2595 if (error.isNotSet() && value != NULL && *value != '\0') {
2596 save_interval = (time_t) strtol(value, NULL, 10) * 60;
2599 if (save_interval < 60)
2600 //save_interval = 60; // The minimum time is every minute
2601 save_interval = 15; // The minimum time is every minute
2603 else save_interval = -1;
2605 free((void*) value);
2607 // Get the current time in seconds, and compute how long it has
2608 // been since the last interactive input (keystroke, buttonpress)
2609 // was done by the user
2611 time_t now = time(NULL);
2612 time_t lastInteractive =
2613 (time_t) self->session()->lastInteractiveEventTime();
2614 time_t interactiveIdleTime = (now - lastInteractive);
2616 // If there has not been at least <<inactivityinterval> seconds of
2617 // interactive inactivity, skip processing until later so as not to
2618 // place the user in the horrible position of being "locked out" for
2619 // the duration of an auto save/and/or/new mail incorporation
2623 mailrc->getValue(error, "inactivityinterval", &value);
2624 if (error.isNotSet() && value != NULL && *value != '\0') {
2625 minimumIdleTime = strtol(value, NULL, 10);
2626 if (minimumIdleTime < 15)
2627 minimumIdleTime = 15; // at least 15 seconds must go by
2628 else if (minimumIdleTime > 600)
2629 minimumIdleTime = 600; // but not more than 10 minutes
2633 free((void*) value);
2635 if (interactiveIdleTime < minimumIdleTime)
2638 // See if time's up for doing a new mail incorporate
2640 if (ping > 0 && (now - self->_last_poll > ping))
2642 self->_last_poll = now;
2643 self->NewMailEvent();
2646 // See if time's up for doing an auto-save.
2647 // If time's up, check to see if the flag is clear for doing one.
2648 // Flag is not clear if say, the user is in the middle of composing
2651 if (save_interval >= 0 &&
2652 ((now - self->_last_check) > save_interval) &&
2653 (self->session()->getAutoSaveFlag())) {
2655 self->_last_check = (int) now;
2658 // self->CheckPointEvent(error);
2659 self->CheckPointEvent();
2660 // if (error.isSet()) {
2661 // // MAILBOX COULD NOT BE CHECKPOINTED!!! MUST DO SOMETHING!!!
2668 RFCMailBox::NewMailEvent(
2669 const DtMailBoolean already_locked
2672 DtMailEnv error, error1;
2674 struct stat tempStatbuf;
2675 struct stat statbuf;
2676 DtMailEventPacket event;
2677 DtMailCallbackOp op;
2679 if (!_object_valid->state()) return;
2680 if (!_mr_allowed) return;
2682 _session->setBusyState(error1, DtMailBusyState_NewMail);
2684 op = retrieveNewMail(error);
2687 const char *errmsg = NULL;
2688 errmsg = (const char *) error;
2691 event.target = DTM_TARGET_MAILBOX;
2692 event.target_object = this;
2693 event.operation = (void*) op;
2695 event.argument = strdup(errmsg);
2697 event.argument = NULL;
2698 event.event_time = time(NULL);
2700 // longUnlock(error);
2701 _session->writeEventData(error, &event, sizeof(event));
2702 _session->setBusyState(error1, DtMailBusyState_NotBusy);
2706 if ((SafeFStat(_fd, &tempStatbuf) < 0) ||
2707 (SafeGuaranteedStat(_real_path, &statbuf) == -1))
2710 _mail_box_writable = DTM_FALSE;
2713 event.target = DTM_TARGET_MAILBOX;
2714 event.target_object = this;
2715 event.operation = (void *)DTMC_ACCESSFAILED;
2716 event.argument = NULL;
2717 event.event_time = time(NULL);
2719 error.setError(DTME_ObjectAccessFailed);
2720 _session->writeEventData(error, &event, sizeof(event));
2721 _session->setBusyState(error1, DtMailBusyState_NotBusy);
2725 // See if the inode has changed - if so the file has been
2726 // modified out from under is
2728 if (statbuf.st_ino != tempStatbuf.st_ino)
2731 _mail_box_writable = DTM_FALSE;
2734 event.target = DTM_TARGET_MAILBOX;
2735 event.target_object = this;
2736 event.operation = (void *)DTMC_INODECHANGED;
2737 event.argument = NULL;
2738 event.event_time = time(NULL);
2740 error.setError(DTME_MailboxInodeChanged);
2741 _session->writeEventData(error, &event, sizeof(event));
2742 _session->setBusyState(error1, DtMailBusyState_NotBusy);
2746 // We need to compare the current file size to the last size we
2747 // knew about. If it has changed, we need to do something. There
2748 // are a few possibilities here that we must deal with:
2750 // 1. The file size has not changed. Boring. Simply return.
2752 // 2. The file size is larger, and the first byte begins with
2753 // either "From ", "\nFrom ", or "\r\nFrom ". In this case,
2754 // we need to parse the rest of the file, and notify the client
2755 // that new mail has arrived.
2757 // 3. The file size is either smaller, or it is larger and the first
2758 // few bytes dont match #2. This is the worse possible case. This
2759 // means that somebody has modified the object from beneath us.
2760 // We will turn on our "lost state" mode and refuse to do any
2761 // further processing. This thread exits immediately.
2763 // Even though we get the file size here, we can't use it to set the
2764 // new _file_size, because the file isn't currently locked, so another
2765 // process could be writing to it at this time. We rely on mapFile()
2766 // to get and set _file_size while it has the file locked.
2768 off_t size = realFileSize(error, &info);
2769 if (error.isSet()) {
2771 _mail_box_writable = DTM_FALSE;
2774 event.target = DTM_TARGET_MAILBOX;
2775 event.target_object = this;
2776 event.operation = (void *)DTMC_ACCESSFAILED;
2777 event.argument = NULL;
2778 event.event_time = time(NULL);
2780 _session->writeEventData(error, &event, sizeof(event));
2781 _session->setBusyState(error1, DtMailBusyState_NotBusy);
2785 MutexLock lock_object(_obj_mutex);
2787 if (size == _file_size)
2789 _session->setBusyState(error1, DtMailBusyState_NotBusy);
2790 if (_hide_access_events!=DTM_TRUE && info.st_atime<=info.st_mtime)
2791 mailboxAccessShow(info.st_mtime, "NewMailEvent: file_size unchanged");
2795 else if (size > _file_size)
2797 incorporate(error, already_locked);
2798 if (_hide_access_events!=DTM_TRUE && info.st_atime<=info.st_mtime)
2799 mailboxAccessShow(info.st_mtime, "NewMailEvent: file_size grew");
2804 _mail_box_writable = DTM_FALSE;
2805 *_object_valid = -1;
2808 event.target = DTM_TARGET_MAILBOX;
2809 event.target_object = this;
2810 event.operation = (void *)DTMC_BADSTATE;
2811 event.argument = NULL;
2812 event.event_time = time(NULL);
2814 _session->writeEventData(error, &event, sizeof(event));
2817 char *strbuf = new char[128];
2819 "NewMailEvent: File shrank %ld<%ld: already_locked is %s\n",
2820 (unsigned long)size, (unsigned long)_file_size,
2821 already_locked ? "TRUE" : "FALSE");
2826 // We need to bail right now if we have lost a valid mapping.
2828 if (_object_valid->state() < 0) {
2829 lock_object.unlock();
2833 _session->setBusyState(error1, DtMailBusyState_NotBusy);
2838 //RFCMailBox::CheckPointEvent(DtMailEnv & error)
2839 RFCMailBox::CheckPointEvent()
2841 if (!_object_valid->state() || _dirty == 0) {
2845 // Write the mail box out.
2850 // This is INCORRECT usage of the DtMailEnv class. It is against
2851 // standard policy to not clear the error token before passing
2852 // it to a function. We are basically breaking the error handling
2853 // model in order to percolate an error in the BE up to the FE.
2855 // setBusyState() does not modify the error token, it simply
2856 // uses it to report errors back to the user.
2857 _session->setBusyState(error, DtMailBusyState_AutoSave);
2859 writeMailBox(error, _hide_access_events);
2861 if ((DTMailError_t) error ==
2862 DTME_CannotWriteToTemporaryMailboxFile_NoFreeSpaceLeft)
2866 startAutoSave(error1,DTM_FALSE);
2867 showError((char *) error.getClient());
2868 error.setClient(NULL);
2869 startAutoSave(error1,DTM_TRUE);
2872 if (error.isSet()) {
2873 // MAILBOX COULD NOT BE CHECKPOINTED!!! setBusyState must handle error.
2874 error.logError(DTM_TRUE, "RFCMailBox::CheckPointEvent(): Failed to write mailbox: %s", (const char *)error);
2876 _session->setBusyState(error, DtMailBusyState_NotBusy);
2880 RFCMailBox::ExpungeEvent(DtMailBoolean closing)
2882 if (!_object_valid->state() && closing == DTM_FALSE) {
2886 // We need to get the expiration time for a message.
2890 DtMail::MailRc * mailrc = _session->mailRc(error);
2893 const char * expire_time = NULL;
2894 mailrc->getValue(error, "DTMAIL_EXPIRE_TIME", &expire_time);
2895 if (error.isNotSet()) {
2896 expire_days = strtol(expire_time, NULL, 10);
2902 if (NULL != expire_time)
2903 free((void*) expire_time);
2905 if (expire_days == 0 && closing == DTM_FALSE) {
2906 // Only scan on close. No timed delete in effect.
2911 if (expire_days > 0 && closing == DTM_TRUE) {
2912 // Timed delete is in effect so we don't destroy messages on
2917 time_t expire_secs = (time_t) expire_days * (24 * 3600);
2918 time_t now = time(NULL);
2920 for (int msg = 0; msg < _msg_list.length(); msg++) {
2921 MessageCache * mc = _msg_list[msg];
2922 if (mc->delete_pending == DTM_TRUE) {
2923 // Can only delete it so many times!
2928 DtMail::Envelope * env = mc->message->getEnvelope(error);
2930 DtMailValueSeq value;
2931 env->getHeader(error, RFCDeleteHeader, DTM_FALSE, value);
2932 if (!error.isSet()) {
2934 deleted = (time_t) strtol(*(value[0]), NULL, 16);
2935 if ((deleted + expire_secs) < now) {
2936 mc->delete_pending = DTM_TRUE;
2939 // We need to tell our client this message is really
2940 // gone. Of course, if they are closing, why bother
2943 if (closing == DTM_FALSE) {
2944 DtMailEventPacket event;
2946 event.target = DTM_TARGET_MAILBOX;
2947 event.target_object = this;
2948 event.operation = (void *)DTMC_DELETEMSG;
2949 event.argument = mc;
2950 event.event_time = time(NULL);
2951 _session->writeEventData(error, &event, sizeof(event));
2961 // Function: RFCMailBox::createTemporaryMailboxFile
2963 // Given the name for a temporary mailbox file, create a proper
2964 // temporary mailbox file and return a file descriptor opened on
2965 // the newly created file
2967 // . obtain information on current mailbox file
2968 // . create the new temporary file
2969 // . set the permissions, owner and group of the newly created file
2970 // to match those of the current mailbox
2972 // DtMailEnv & error - standard error structure used by caller
2973 // tmp_name -- -> name for temporary file
2975 // error.isset() will indicate if there were any errors encountered, in which
2976 // case the temporary file has not been created.
2978 // int file descriptor opened on the newly created temporary mailbox file
2979 // if no error encountered.
2982 RFCMailBox::createTemporaryMailboxFile(DtMailEnv & error, char *tmp_name)
2987 if (SafeFStat(_fd, &info) < 0) {
2990 error.logError(DTM_FALSE,
2991 "createTemporaryMailboxFile(): fstat(%d) failed errno=%d\n",
2993 error.vSetError(DTME_CannotObtainInformationOnOpenMailboxFile,
2994 DTM_FALSE, NULL, _real_path, error.errnoMessage(errno2));
2998 PRIV_ENABLED(fd,SafeOpen(tmp_name, O_RDWR | O_CREAT | O_TRUNC, info.st_mode));
3003 error.setError(DTME_CannotCreateTemporaryMailboxFile_NoPermission);
3007 error.setError(DTME_CannotCreateTemporaryMailboxFile_IsDirectory);
3011 error.setError(DTME_CannotCreateTemporaryMailboxFile_NoSuchFile);
3014 #if defined(__osf__)
3019 error.setError(DTME_CannotCreateTemporaryMailboxFile_RemoteAccessLost);
3023 error.vSetError(DTME_CannotCreateTemporaryMailboxFile,
3024 DTM_FALSE, NULL, tmp_name, error.errnoMessage(errno2));
3029 PRIV_ENABLED_OPTIONAL(return_status,SafeFChmod(fd, info.st_mode & 07777));
3030 if (return_status == -1) {
3032 (void) SafeClose(fd);
3033 PRIV_ENABLED(return_status,SafeUnlink(tmp_name));
3034 error.vSetError(DTME_CannotSetPermissionsOfTemporaryMailboxFile,
3035 DTM_FALSE, NULL, tmp_name, info.st_mode & 07777,
3036 error.errnoMessage(errno2));
3040 PRIV_ENABLED(return_status, SafeFchown(fd, info.st_uid, (unsigned int) -1));
3042 // bug 1216914 - dtmail should be able to auto-save a mailbox which is not
3043 // opened [Read Only] if the user has write access to the original mailbox,
3044 // then the user should be able to overwrite the mailbox even if the owner
3045 // gets changes. A mailbox is only opened read-write if the "access()"
3046 // system call indicates the user has write permissions.
3048 if (return_status == -1) {
3050 (void) SafeClose(fd);
3051 PRIV_ENABLED(return_status,SafeUnlink(tmp_name));
3052 error.vSetError(DTME_CannotSetOwnerOfTemporaryMailboxFile,
3053 DTM_FALSE, NULL, tmp_name, info.st_uid,
3054 error.errnoMessage(errno2));
3059 PRIV_ENABLED(return_status, SafeFchown(fd, (unsigned int) -1, info.st_gid));
3063 // Function: RFCMailBox::writeMailBox - create new copy of complete mailbox
3065 // writeMailBox causes a complete copy of the current mailbox to be written
3066 // out to permanent storage. This is done by creating a temporary file, in
3067 // the same location the permanent file resides, and writing the new mailbox
3068 // contents to it. If this is successful, the temporary file is then renamed
3069 // over the old permanent file, taking its place. In this way, if there is
3070 // an error creating the new file, the old file still remains intact.
3072 // . make sure the current mailbox is writeable
3073 // . lock current mailbox
3074 // . check for new mail and incorporate if necessary
3075 // . cause the OS to begin mapping as much of the current mailbox into memory
3076 // as it can so it is available as soon as possible to be written
3077 // . create temporary file to hold new mailbox contents
3078 // . create message list fixing the location of all non-deleted messages
3079 // (dirty messages will be allocated temporary storage and created)
3080 // . build a vectored write array based upon the message list
3081 // . cause the new mailbox contents to be written via one call to SafeWritev
3082 // . delete all "deleted" messages from the message list
3083 // . map the new mailbox file into memory
3084 // . revise all message pointers based upon new mailbox contents
3085 // . remove the mappings and unmap all previous regions used
3086 // . add the one single new mailbox region to the mappings list
3087 // . transfer any lock on the old mailbox file to the new mailbox file
3088 // . rename the new mailbox file to the correct permanent path name
3089 // . set the modification and access times of the mailbox file properly
3091 // DtMailEnv & error - standard error structure used by caller
3093 // error.isset() will indicate if there were any errors encountered, in which
3094 // case the current contents of the mailbox have not been written.
3096 // At some point in the call chain, someone must check to see if this is
3097 // a FATAL ERROR (e.g. error.isFatal) and if so, display the returned
3098 // message and then immediately exit, as this means the mailbox could not
3099 // be written and the internal mailbox state is hopelessly munged..
3104 RFCMailBox::writeMailBox(DtMailEnv &error, DtMailBoolean hide_access)
3106 static char *pname = "writeMailBox";
3108 DtMailEnv error2; // When we need to preserve error during error cleanup
3109 int return_status; // for handling priv/unpriv operations
3110 #if defined(DEBUG_RFCMailBox)
3111 char *pname = "writeMailBox";
3114 MutexLock lock_map(_map_lock);
3117 if (_mail_box_writable == DTM_FALSE) {
3121 // Need to lock the file while we do this.
3123 DEBUG_PRINTF( ("%s: locking mailbox\n", pname) );
3125 if (error.isSet()) {
3126 DtMailEnv tmp_error;
3127 unlockFile(tmp_error, _fd);
3131 // Need to deal with any potentially new mail that
3132 // has arrived and we don't know about.
3134 if (_mra_server == NULL) checkForMail(error, DTM_TRUE);
3135 if (error.isSet()) {
3136 DEBUG_PRINTF( ("%s: locking mailbox\n", pname) );
3137 unlockFile(error2, _fd);
3141 // Create the new temporary file to hold the mailbox contents
3143 static char tmp_name[MAXPATHLEN+1];
3144 sprintf(tmp_name, "%s.tmp.%08lx", _real_path, (long)time(NULL));
3145 assert(strlen(tmp_name)<sizeof(tmp_name));
3147 int fd = createTemporaryMailboxFile(error, tmp_name);
3148 if (error.isSet()) {
3149 DEBUG_PRINTF( ("%s: locking mailbox\n", pname) );
3150 unlockFile(error2, _fd);
3155 // Got the new mailbox file all set up to be our friend.
3156 // zip through current message structure fixing the location of all
3157 // non-deleted messages in preparation of writing them all out to
3158 // the temporary file just opened. The act of "fixing" the location of
3159 // a message will cause dirty messages to be constructed in temporary
3160 // allocated areas which must later be deallocated. This deallocation
3161 // is done either by calling adjustMessageLocation() to make the location
3162 // of the message permanent, or by calling unfixMessageLocation) to
3163 // abandon the new message (e.g. if new mailbox cannot be created).
3165 int deletesPending = 0; // count # of delete_pending messages scanned
3168 struct tempMsgList {
3169 MessageCache *tmlMc; // -> mc of this message
3170 long tmlRealOffset; // byte offset from 0 where message is written
3171 long tmlBodyOffset; // byte offset from 0 of body parts of message
3172 char *tmlHeaderStart; // -> start of headers for this message
3173 long tmlHeaderLen; // length of headers
3174 char *tmlBodyStart; // -> start of bodies for this message
3175 long tmlBodyLen; // length of bodies
3176 int tmlTemporary; // ==0:permanent, !=0:temporary(must move)
3179 long tmlTotalSize = _msg_list.length()*4;
3180 tempMsgList *const tmlFirst = (tempMsgList*)
3181 malloc((size_t) (sizeof(tempMsgList)*tmlTotalSize));
3182 tempMsgList *tmlLast = tmlFirst;
3184 for (msg = 0; msg < _msg_list.length(); msg++) {
3185 MessageCache *mc = _msg_list[msg];
3186 if (mc->delete_pending == DTM_TRUE) { // ignore deleted messages
3187 deletesPending++; // but remember if any seen
3190 tmlLast->tmlMc = mc;
3191 mc->message->fixMessageLocation(&tmlLast->tmlHeaderStart, tmlLast->tmlHeaderLen,
3192 &tmlLast->tmlBodyStart, tmlLast->tmlBodyLen,
3193 tmlLast->tmlTemporary, tmlLast->tmlBodyOffset);
3195 fprintf(stdout, "msg %03d @ %08lx %08lx/%06d %08lx/%06d %04d %s\n",
3196 msg, mc, tmlLast->tmlHeaderStart, tmlLast->tmlHeaderLen,
3197 tmlLast->tmlBodyStart, tmlLast->tmlBodyLen,
3198 tmlLast->tmlBodyOffset, tmlLast->tmlTemporary ? "T" : "P");
3199 HexDump(stdout, "header", (unsigned char *)tmlLast->tmlHeaderStart, tmlLast->tmlHeaderLen, 10);
3200 HexDump(stdout, "body", (unsigned char *)tmlLast->tmlBodyStart, tmlLast->tmlBodyLen, 10);
3203 assert(tmlLast->tmlHeaderStart != NULL);
3204 assert(tmlLast->tmlHeaderLen);
3205 tmlLast->tmlRealOffset = -1;
3208 assert((tmlLast-tmlFirst) < tmlTotalSize);
3210 // we can now allocate the vectored write array and fill it according
3211 // to the data stored in the message list we just created
3213 long iovSize = ((tmlLast-tmlFirst)+2)*3;
3214 iovec *const iovFirst = (iovec *) malloc((size_t) (sizeof(iovec)*iovSize));
3215 iovec *iovLast = iovFirst;
3216 iovec *iovPrev = (iovec *)0;
3217 long iovCurrentOffset = 0;
3219 for (tempMsgList *tmlNdx = tmlFirst; tmlNdx < tmlLast; tmlNdx++) {
3220 // if this message happens to start on the first byte following the
3221 // last byte of the previous message, combine into a single vector,
3222 // else add this as another vector in the vector list
3224 tmlNdx->tmlRealOffset = iovCurrentOffset;
3225 if ( (iovPrev != (iovec *)0) && (!tmlNdx->tmlTemporary) &&
3226 ((char *)((size_t) iovPrev->iov_base+iovPrev->iov_len) ==
3227 tmlNdx->tmlHeaderStart)
3229 iovPrev->iov_len += (int)(tmlNdx->tmlHeaderLen+tmlNdx->tmlBodyLen);
3230 iovCurrentOffset += (int)(tmlNdx->tmlHeaderLen+tmlNdx->tmlBodyLen);
3232 else if (!tmlNdx->tmlTemporary) {
3233 iovLast->iov_base = (caddr_t)tmlNdx->tmlHeaderStart;
3234 iovLast->iov_len = (int)(tmlNdx->tmlHeaderLen+tmlNdx->tmlBodyLen);
3235 iovCurrentOffset += iovLast->iov_len;
3236 iovPrev = iovLast++;
3239 // Message is temporary - headers and bodies are in different areas,
3240 // and the headers are in a "temporary" area
3242 iovLast->iov_base = (caddr_t)tmlNdx->tmlHeaderStart;
3243 iovLast->iov_len = (int)tmlNdx->tmlHeaderLen;
3244 iovCurrentOffset += iovLast->iov_len;
3245 iovPrev = iovLast++;
3247 // Write out bodies only if the length is non-zero, otherwise,
3248 // optimize out the inclusion of a zero length write from the vector
3250 if (tmlNdx->tmlBodyLen > 0) {
3251 iovLast->iov_base = (caddr_t)tmlNdx->tmlBodyStart;
3252 iovLast->iov_len = (int)tmlNdx->tmlBodyLen;
3253 iovCurrentOffset += iovLast->iov_len;
3254 iovPrev = iovLast++;
3257 // The last two bytes of the message must be \n\n
3258 // If not then this must be forced
3259 // Obtain pointer tp to the last byte of the current message
3260 // Given this pointer we now have:
3261 // tp[-1] -- next to last byte in current message
3262 // tp[ 0] -- last byte in current message
3263 // tp[+1] -- first byte of next message (if any)
3264 // There must always be two \n characters between each message. If not,
3265 // we must insert sufficient \n characters into the message stream to
3266 // accomplish this. We want to avoid plopping these in, however, as each
3267 // one will add an extra 1- or 2-byte vector into the vectored write array,
3268 // which will affect the throughput of the overall write.
3269 // Irregardless of whether the current message is temporary, we check to see if the next
3270 // byte or two (as necessary) is in a mapped region; if so, we can then
3271 // peek ahead to see if there are the additional \ns we need.
3272 // If all this fails, we punt and put in a small 1- or 2-byte write
3276 char *tp = (char *) ((size_t) iovPrev->iov_base + (iovPrev->iov_len - 1));
3279 if ( ((addressIsMapped(tp+1) == DTM_TRUE) )
3280 && (*(tp+1) == '\n') ) {
3286 iovLast->iov_base = (caddr_t)"\n"; // add \n
3287 iovLast->iov_len = 1;
3289 iovPrev = iovLast++;
3293 if ( (( (addressIsMapped(tp+1) == DTM_TRUE)
3294 && (addressIsMapped(tp+2) == DTM_TRUE)))
3295 && (*(tp+1) == '\n') && (*(tp+2) == '\n')) {
3296 iovPrev->iov_len+= 2;
3297 iovCurrentOffset+= 2;
3301 iovLast->iov_base = (caddr_t)"\n\n"; // add \n\n
3302 iovLast->iov_len = 2;
3303 iovCurrentOffset += 2;
3304 iovPrev = iovLast++;
3309 assert((iovLast-iovFirst) < iovSize);
3311 // All of the messages are properly accounted for in the write vector;
3312 // cause the damage to be done by calling SafeWritev. After it returns,
3313 // Make absolutely sure that all of the mailbox data has made it to
3314 // the final destination, especially if the mailbox is not local -
3315 // this way if we run out of disk space or some other such problem,
3316 // it is caught here and now.
3318 unsigned long bytesWritten = SafeWritev(fd, iovFirst, iovLast-iovFirst);
3319 if (bytesWritten == (unsigned long)-1 || fsync(fd) == -1) {
3321 for (tempMsgList *tml = tmlFirst; tml < tmlLast; tml++) {
3322 MessageCache *mc = tml->tmlMc;
3324 mc->message->unfixMessageLocation(tml->tmlHeaderStart, tml->tmlTemporary);
3326 FileSystemSpace(tmp_name, 0,&fsname);
3327 (void) SafeClose(fd);
3328 DEBUG_PRINTF( ("%s: locking mailbox\n", pname) );
3329 unlockFile(error2, _fd);
3330 PRIV_ENABLED(return_status,SafeUnlink(tmp_name));
3336 DTME_CannotWriteToTemporaryMailboxFile_ProcessLimitsExceeded);
3339 #if defined(__osf__)
3344 error.setError(DTME_CannotWriteToTemporaryMailboxFile_RemoteAccessLost);
3348 error.setClient(fsname);
3349 error.setError(DTME_CannotWriteToTemporaryMailboxFile_NoFreeSpaceLeft);
3353 error.vSetError(DTME_CannotWriteToTemporaryMailboxFile,
3354 DTM_FALSE, NULL, tmp_name, error.errnoMessage(errno2));
3359 // The current contents of the mailbox have successfully been written
3360 // to the temporary file. Cause the new mailbox file to be mapped
3363 MapRegion * map = mapNewRegion(error, fd, bytesWritten);
3364 if (error.isSet()) {
3365 for (tempMsgList *tml = tmlFirst; tml < tmlLast; tml++) {
3366 MessageCache *mc = tml->tmlMc;
3368 mc->message->unfixMessageLocation(tml->tmlHeaderStart, tml->tmlTemporary);
3370 (void) SafeClose(fd);
3371 DEBUG_PRINTF( ("%s: locking mailbox\n", pname) );
3372 unlockFile(error2, _fd);
3373 PRIV_ENABLED(return_status,SafeUnlink(tmp_name));
3379 // POINT OF NO RETURN -- NEW MAILBOX MUST BE SUCCESSFULLY LINKED UP WITH
3380 // BECAUSE THE MACHINATIONS AND POINTER MUNGING DONE BELOW CANNOT BE UNDONE
3383 // Flush all deleted messages (if any were previously detected)
3386 for (msg = 0; msg < _msg_list.length(); msg++) {
3387 MessageCache * mc = _msg_list[msg];
3388 if (mc->delete_pending == DTM_TRUE) {
3389 delete mc->message; // remove message storage
3390 delete mc; // remove message cache storage
3391 _msg_list.remove(msg); // remove message from message list
3392 msg -= 1; // next message is where we are at now
3396 // spin through all "written messages" and fixup their pointers so they
3397 // point into the new region
3400 // For this occasion advise the OS that we will be doing sequential access
3402 alterPageMappingAdvice(map);
3404 for (tempMsgList *tml = tmlFirst; tml < tmlLast; tml++) {
3405 MessageCache *mc = tml->tmlMc;
3407 assert(mc->delete_pending == DTM_FALSE);
3408 mc->message->adjustMessageLocation(tml->tmlHeaderStart, map->file_region+tml->tmlRealOffset, tml->tmlHeaderLen+tml->tmlBodyLen, tml->tmlTemporary, tml->tmlBodyOffset);
3411 // Loop through the current mappings, and unmap each.
3412 // Then make the new single large map the only mapping.
3414 while(_mappings.length()) {
3415 MapRegion * c_map = _mappings[0];
3416 munmap(c_map->map_region, (size_t) c_map->map_size);
3418 _mappings.remove(0);
3420 _mappings.append(map);
3422 // fix for cmvc defect 7912 - Queued mail lost upon closing dtmail
3423 // If we are using the .lock protocol, we are locking on a file name
3424 // basis, and therefore can rename the new mailbox over the old mailbox
3425 // without a worry about locks; however, if we are using another type
3426 // of locking which locks on a *file* basis, then as soon as the rename
3427 // is done if there is not a lock on the file a process like sendmail
3428 // could come in and complicate matters. After being properly locked,
3429 // rename the new mailbox file over the old mailbox file, and then
3430 // remove the old lock if applicable.
3432 lockNewMailboxFile(fd);
3433 PRIV_ENABLED(return_status,SafeRename(tmp_name, _real_path));
3434 if (return_status == -1) {
3435 // the rename failed -- we are in a world of hurt now.
3436 // We have successfully written the new mailbox out, unmapped the
3437 // old file, mapped in the new file, and bashed all of the various
3438 // pointers to point to the new mailbox; however, we cannot rename
3439 // the new mailbox over the old mailbox. We cannot continue, so return
3440 // this as a fatal error so that the caller can exit properly.
3442 error.vSetError(DTME_CannotRenameNewMailboxFileOverOld,
3443 DTM_TRUE, NULL, _real_path, tmp_name,
3444 error.errnoMessage());
3445 (void) SafeClose(fd);
3446 (void) SafeClose(_fd);
3447 return; // no complete cleanup necessary as we should exit real fast...
3450 assert(map->file_size == bytesWritten);
3452 DtMailBoolean file_grew;
3453 if (map->file_size > _file_size)
3454 file_grew = DTM_TRUE;
3456 file_grew = DTM_FALSE;
3458 _file_size = map->file_size;
3461 if (SafeFStat(fd, &info) < 0)
3464 error.logError(DTM_TRUE,
3465 "%s: fstat(%d/%s) failed errno=%d\n",
3466 pname, _fd, tmp_name, errno);
3467 error.setError(DTME_ObjectCreationFailed, DTM_TRUE, (Tt_message) 0);
3471 if (info.st_size != _file_size)
3473 error.logError(DTM_TRUE,
3474 "%s: new mailbox size not consistent with expected size = %d\nfstat: st_ino = %d, st_dev = %d, st_nlink = %d, st_size = %ld\n",
3475 pname, bytesWritten,
3476 info.st_ino, info.st_dev, info.st_nlink, info.st_size);
3477 error.setError(DTME_ObjectCreationFailed, DTM_TRUE, (Tt_message) 0);
3480 if (hide_access == DTM_FALSE && info.st_atime <= info.st_mtime)
3481 mailboxAccessShow(info.st_mtime, "writeMailBox");
3484 // order of unlocks is important here:
3485 // unlockOldMailboxFile checks the state of _long_lock_active
3486 // but does not alter it, whereas unlockFile does.
3488 unlockOldMailboxFile(_fd); // unlock old mailbox file first
3489 DEBUG_PRINTF( ("%s: locking mailbox\n", pname) );
3490 unlockFile(error2,fd); // then unlock new mailbox file
3491 if (SafeClose(_fd) < 0) {
3492 // should do something with the error here.
3495 _fd = fd; // new mailbox file now current one
3496 _dirty = 0; // mark mailbox as no longer dirty.
3505 RFCMailBox::incorporate(DtMailEnv & error, const DtMailBoolean already_locked)
3507 DtMailEventPacket event;
3509 if (already_locked == DTM_FALSE) {
3510 MutexLock lock_map(_map_lock);
3513 int slot = mapFile(error, already_locked);
3514 if (error.isSet()) {
3515 if (DTME_ObjectInvalid == (DTMailError_t) error)
3518 _mail_box_writable = DTM_FALSE;
3521 event.target = DTM_TARGET_MAILBOX;
3522 event.target_object = this;
3523 event.operation = (void *)DTMC_INODECHANGED;
3524 event.argument = NULL;
3525 event.event_time = time(NULL);
3526 _session->writeEventData(error, &event, sizeof(event));
3528 else if (DTME_ObjectAccessFailed == (DTMailError_t) error)
3531 _mail_box_writable = DTM_FALSE;
3534 event.target = DTM_TARGET_MAILBOX;
3535 event.target_object = this;
3536 event.operation = (void *)DTMC_ACCESSFAILED;
3537 event.argument = NULL;
3538 event.event_time = time(NULL);
3539 _session->writeEventData(error, &event, sizeof(event));
3546 MapRegion * map = _mappings[slot];
3547 const char * buf = map->file_region;
3549 // Let's accept any white space as okay in front of the
3552 for (; buf < (map->map_region + map->map_size) &&
3553 isspace(*buf); buf++) {
3558 DtMailBoolean done = DTM_FALSE;
3560 while (num_tries < 5 && !done) {
3561 if (strncmp(buf, "From ", 5) == 0) {
3562 DtMailMessageHandle last = NULL;
3564 // We can be here via either of two scenarios:
3565 // 1- Aside from incorporating new mail, there is no other
3566 // activity on the mail box. This is the "normal" case.
3567 // already_locked is its default value (FALSE).
3569 // 2- We are here because we were doing a Destroy Deleted Messages
3570 // when we noticed new mail that had to be first incorporated.
3571 // already_locked is TRUE (i.e., the Expunge method has lock...)
3572 // We cannot place the handle of the last entity in the array in
3573 // the stream because the entity may be marked for delete, and
3574 // the Destroy Deleted Messages operation will expunge the entity
3575 // shortly after it has been placed on the stream for the FE.
3577 // In both cases, we want to get the last UNDELETED message and
3578 // place it on the stream for the FE. Undeleted entities will
3579 // be valid even after a DDM while deleted entities will be invalid
3582 // Place the handle of the last entity in the array in the
3583 // callback stream. The FE will get it and can invoke
3584 // getNextMessageSummary() on the mailbox using the handle to
3585 // retrieve the new messages.
3587 if (_msg_list.length() > 0) {
3589 if (already_locked) {
3590 // already_locked is TRUE only in one case:
3591 // We were trying to destroy deleted messages when we
3592 // noticed new mail has come in and we need to incorporate
3594 // This will return the index of the last undeleted
3595 // message. That entity, we are assured, will remain
3596 // valid after a DMM that may appear just before the event
3597 // is received by the FE's callback method.
3599 int index = prevNotDel(_msg_list.length() - 1);
3600 last = _msg_list[index];
3603 // already_locked is FALSE
3605 // No possiblity of entities in _msg_list being expunged.
3606 // Give the current last one to the FE and let it retrieve
3607 // new messages via the getNext() method on that entity.
3608 last = _msg_list[_msg_list.length() - 1];
3614 parseFile(error, slot);
3617 event.target = DTM_TARGET_MAILBOX;
3618 event.target_object = this;
3619 event.operation = (void *)DTMC_NEWMAIL;
3620 event.argument = last;
3621 event.event_time = time(NULL);
3622 _session->writeEventData(error, &event, sizeof(event));
3627 _mail_box_writable = DTM_FALSE;
3628 *_object_valid = -1;
3631 event.target = DTM_TARGET_MAILBOX;
3632 event.target_object = this;
3633 event.operation = (void *)DTMC_BADSTATE;
3634 event.argument = NULL;
3635 event.event_time = time(NULL);
3637 error.setError(DTME_ObjectInvalid);
3638 _session->writeEventData(error, &event, sizeof(event));
3641 char *strbuf = new char[256];
3642 if (already_locked) {
3645 "Attempt #%d: RFCMailBox::incorporate(): No From: buf=<%.10s>, already_locked is TRUE\n", num_tries + 1, buf);
3649 "Attempt #%d: RFCMailBox::incorporate(): No From: buf=<%.10s>, already_locked is FALSE\n", num_tries + 1, buf);
3662 RFCMailBox::generateLockFileName(void)
3665 char *lock_path = new char[MAXPATHLEN+20];
3667 assert(_real_path != NULL);
3668 (void) sprintf(lock_path, "%s.lock", _real_path);
3669 s = strdup(lock_path);
3670 delete [] lock_path;
3674 // Function: RFCMailBox::generateUniqueLockId - create unique ID for this mailbox lock files
3676 // generateUniqueLockId creates a unique ID which is written into .lock files and
3677 // can then be checked to make sure that the lock file has not been compromised by
3680 // The ID generated consists of three parts:
3681 // <process id/%08d><current time in seconds/%08d><hardware serial number/%d>
3682 // Thus, a "typical" id would look like this:
3683 // 000018577971028681915751068
3684 // Which breaks down as:
3685 // 00001857 797102868 1915751068
3691 // char * -> allocated memory in which unique id has been created
3694 RFCMailBox::generateUniqueLockId(void)
3697 char hwserialbuf[64];
3699 #if !defined(__aix) && !defined(__hpux) && !defined(__osf__) && !defined(linux)
3700 if (sysinfo(SI_HW_SERIAL, (char *)hwserialbuf, sizeof(hwserialbuf)-1) == -1)
3702 strcpy(hwserialbuf, "dtmail");
3703 (void) sprintf(theId, "%08ld%08ld%s\0", (long)getpid(), (long)time((time_t *)0), hwserialbuf);
3704 assert(strlen(theId)<sizeof(theId));
3705 return(strdup(theId));
3709 RFCMailBox::checkLockFileOwnership(DtMailEnv & error)
3711 char *pname = "checkLockFileOwnership";
3713 assert(_lockFileName != NULL);
3715 if (SafeStat(_lockFileName, &info) < 0) {
3717 error.logError(DTM_FALSE,
3718 "%s: lock cannot be stat()ed: %s, errno = %d\n",
3719 pname, _lockFileName, errno);
3720 error.setError(DTME_ObjectInvalid);
3728 flags = O_RDONLY | O_SYNC;
3730 flags = O_RDONLY | O_RSYNC | O_SYNC;
3731 #endif /* O_RSYNC */
3733 #ifdef MAILGROUP_REQUIRED
3734 PRIV_ENABLED_OPEN(_lockFileName, lock_fd, SafeOpen(_lockFileName, flags, 0));
3736 PRIV_ENABLED_OPTIONAL(lock_fd, SafeOpen(_lockFileName, flags, 0));
3739 if (lock_fd == -1) {
3743 "%s: lock cannot be open()ed: %s, errno = %d\n",
3744 pname, _lockFileName, errno);
3745 error.setError(DTME_ObjectInvalid);
3749 char lockBuf[MAXPATHLEN];
3750 int status = SafeRead(lock_fd, lockBuf, sizeof(lockBuf)-1);
3755 "%s: lock cannot be read: %s, errno = %d\n",
3756 pname, _lockFileName, errno);
3757 (void) SafeClose(lock_fd);
3758 error.setError(DTME_ObjectInvalid);
3762 if ( (status < _uniqueLockIdLength+2)
3763 || (strncmp(lockBuf+2, _uniqueLockId, _uniqueLockIdLength) != 0) ) {
3766 "%s: dtmail lock file stolen by another process\n", pname);
3770 "lock file stolen - lock file contents",
3771 (unsigned char *)lockBuf, status, 0);
3772 (void) SafeClose(lock_fd);
3773 error.setError(DTME_ObjectInvalid);
3776 (void) SafeClose(lock_fd);
3780 // Function: RFCMailBox::linkLockFile - create and link temporary lock file to real lock file
3782 // Create a lock file for the current mailbox. Return success of failure.
3784 // . create a temporary lock file with a unique signature id of this process
3785 // . link the temporary lock file to the real lock file
3786 // . if the link is not successful, remove the temporary lock file and return the
3787 // time() in seconds on the remote system of when the temporary lock file was created
3788 // . if the link is successful, remove the temporary lock file (link) and return 0.
3790 // error -- standard error structure used by caller
3792 // If error.isSet() it is a fatal error from which the caller should return to its caller,
3793 // return value will always be time(0)
3794 // If !error.isSet() then check results of return value
3796 // time_t == 0 : indicates that the real lock file has been created and we own it
3797 // != 0 : could not create real lock file, return value is the time *on the remote system*
3798 // that the temporary lock file was created with (from comparison with existing
3799 // lock file to see how old it is)
3802 RFCMailBox::linkLockFile(DtMailEnv & error, char *tempLockFileName)
3808 // Create the temporary lock file. Failure to do so indicates lack of write permission
3809 // in the directory or some other fatal error
3813 int flags = O_RDWR | O_CREAT | O_EXCL | O_SYNC;
3815 int flags = O_RDWR | O_CREAT | O_EXCL | O_SYNC | O_RSYNC;
3817 PRIV_ENABLED(lock_fd,SafeOpen(tempLockFileName, flags, 0666));
3819 // We are not able to create the temporary lock file.
3820 // We will have to punt on trying to lock here from now on.
3824 error.setError(DTME_CannotCreateMailboxLockFile_NoPermission);
3828 error.setError(DTME_CannotCreateMailboxLockFile_IsDirectory);
3832 error.setError(DTME_CannotCreateMailboxLockFile_NoSuchFile);
3835 #if defined(__osf__)
3840 error.setError(DTME_CannotCreateMailboxLockFile_RemoteAccessLost);
3844 error.vSetError(DTME_CannotCreateMailboxLockFile,
3845 DTM_FALSE, NULL, tempLockFileName, error.errnoMessage());
3851 // Get creation time of temporary file *on remote system*
3853 if (SafeFStat(lock_fd, &sbuf) == -1) {
3855 error.logError(DTM_FALSE,
3856 "linkLockFile(): temporary lock file cannot be stat()ed: %s, errno = %d\n",
3857 tempLockFileName, errno);
3858 error.vSetError(DTME_CannotCreateMailboxLockFile,
3859 DTM_FALSE, NULL, tempLockFileName, error.errnoMessage());
3860 (void) SafeClose(lock_fd);
3864 // Write proper contents to lock file:
3865 // Write the string "0" into the lock file to give us some
3866 // interoperability with SVR4 mailers. SVR4 mailers expect
3867 // a process ID to be written into the lock file and then
3868 // use kill() to see if the process is alive or not. We write
3869 // 0 into it so that SVR4 mailers will always think our lock file
3870 // is valid. In addition we include a unique ID so we can verify
3871 // if the lock file is stolen out from under us.
3873 ssize_t writeResults;
3874 writeResults = SafeWrite(lock_fd, "0\0", 2);
3875 if (writeResults == 2)
3876 writeResults += SafeWrite(lock_fd, _uniqueLockId, _uniqueLockIdLength);
3877 if ( (writeResults != _uniqueLockIdLength+2) ){
3879 error.logError(DTM_FALSE,
3880 "linkLockFile(): write to temporary lock file failed: %s, errno = %d\n",
3881 tempLockFileName, errno);
3882 error.vSetError(DTME_CannotCreateMailboxLockFile,
3883 DTM_FALSE, NULL, tempLockFileName, error.errnoMessage());
3884 (void) SafeClose(lock_fd);
3888 // sync up the lock file with the ultimate storage device
3890 if (fsync(lock_fd) == -1) {
3892 error.logError(DTM_FALSE,
3893 "linkLockFile(): fsync to temporary lock file failed: %s, errno = %d\n",
3894 tempLockFileName, errno);
3895 error.vSetError(DTME_CannotCreateMailboxLockFile,
3896 DTM_FALSE, NULL, tempLockFileName, error.errnoMessage());
3897 (void) SafeClose(lock_fd);
3903 if (SafeClose(lock_fd) == -1) {
3907 "linkLockFile(): close of temporary lock file failed: %s, errno = %d\n",
3908 tempLockFileName, errno);
3909 error.vSetError(DTME_CannotCreateMailboxLockFile,
3910 DTM_FALSE, NULL, tempLockFileName, error.errnoMessage());
3914 // The temporary lock file has been created - now try and link it to the
3915 // real lock file. Failure here is not fatal as we will retry and possible
3916 // try and remove the real lock file later on.
3918 PRIV_ENABLED(return_status,SafeLink(tempLockFileName, _lockFileName));
3919 if (return_status == -1) {
3920 PRIV_ENABLED(return_status,SafeRemove(tempLockFileName));
3921 return(sbuf.st_ctime);
3924 // We successfully linked the temp lock file to the real lock file name
3925 // This means we have the dot lock for our process - remove the temporary lock
3926 // file name (link) and return
3928 PRIV_ENABLED(return_status,SafeRemove(tempLockFileName));
3933 RFCMailBox::lockFile(DtMailEnv & error)
3935 #if defined(DEBUG_RFCMailBox)
3936 char *pname = "RFCMailBox::lockFile";
3938 int return_status = 0;
3940 // We will create a simple lock file to keep the file from
3941 // changing while we are doing critical work.
3944 // On some platforms, sendmail will place a lockf lock on the
3945 // file during mail delivery. If this is the case, then we
3946 // need to make sure we have the lock here.
3948 #if defined(SENDMAIL_LOCKS)
3949 assert(_lockf_active == DTM_FALSE);
3950 lseek(_fd, 0, SEEK_SET);
3951 PRIV_ENABLED_OPTIONAL(return_status,SafeLockf(_fd, F_LOCK, 0));
3952 if (return_status != -1)
3954 _lockf_active = DTM_TRUE;
3955 DEBUG_PRINTF( ("%s: lockf succeeded\n", pname) );
3959 if (_use_dot_lock == DTM_FALSE) {
3960 DEBUG_PRINTF( ("%s: not using dot lock\n", pname) );
3964 // Implement the .lock short term lock protocol
3965 // This code was "adapted" from Solaris 2.5 (SunOS 5.5)
3966 // usr/src/cmd/mail/maillock.c.
3968 assert(_dot_lock_active == DTM_FALSE);
3970 // Create the temporary mail lock file name
3971 // It has the form <_lockfilename><XXXXXX> or mailbox.lockXXXXXX
3972 // mktemp then creates a unique temporary file for the template
3974 assert(_lockFileName != NULL);
3975 char *tempLockFileName = new char[MAXPATHLEN];
3976 sprintf(tempLockFileName, "%sXXXXXX", _real_path);
3977 mktemp(tempLockFileName);
3978 PRIV_ENABLED(return_status,SafeRemove(tempLockFileName));
3980 // loop through attempting to create the temporary lock file,
3981 // and if successful attempt to link the temporary lock file
3982 // to the real lock file. If not successful, retry for up to 5
3983 // minutes, and remove the current lock file if it is more than
3990 // Attempt to create a temporary file and link it to the intended lock file
3991 // If it is successful, we have the lock and can return.
3992 // If it is not successful and error.isSet then it is a non-recoverable
3994 // in which case the mailbox is deemed not-writable any more.
3995 // If it is not successful and !error.isSet then it is a recoverable error,
3996 // in which case we spin and try again according to the retry rules.
3998 time_t t = linkLockFile(error, tempLockFileName);
3999 if (error.isSet()) {
4000 // hard error? -- something is wrong, assume read/only
4001 _use_dot_lock = DTM_FALSE;
4002 _mail_box_writable = DTM_FALSE;
4003 (void) SafeRemove(tempLockFileName);
4005 ("%s: failed to link dot_lock file %s\n", pname, tempLockFileName) );
4006 delete [] tempLockFileName;
4010 checkLockFileOwnership(error);
4013 _dot_lock_active = DTM_TRUE;
4014 DEBUG_PRINTF( ("%s: succeeded acquiring dot_lock file\n", pname) );
4016 delete [] tempLockFileName;
4020 // Could not link the temporary lock file to the intended real lock file
4021 // See if the lock file exists and if so if we can remove it because it
4022 // is > 5 mins old. If the stat fails it means the lock file disappeared
4023 // between our attempt to link to it and now - only allow this to go on
4024 // so many times before punting
4026 if (SafeStat(_lockFileName, &sbuf) == -1) {
4027 if (statFailed++ > 5) {
4028 error.vSetError(DTME_CannotCreateMailboxLockFile,
4029 DTM_FALSE, NULL, _lockFileName, error.errnoMessage());
4030 delete [] tempLockFileName;
4037 // The lock file already exists - compare the time of the temp
4038 // file with the time of the lock file, rather than with the
4039 // current time of day, since the files may reside on another
4040 // machine whose time of day differs from the one this program
4041 // is running on. If the lock file is less than 5 minutes old,
4042 // keep trying, otherwise, remove the lock file and try again.
4045 if (t < (sbuf.st_ctime + 300)) {
4050 error.logError(DTM_FALSE,
4051 "lockFile(): removing stale lock file %s ctime %08ld temp lock %s ctime %08ld diff %08ld\n",
4052 _lockFileName, sbuf.st_ctime, tempLockFileName, t, t-sbuf.st_ctime);
4054 ("%s: giving up; removing dot_lock file %s\n", pname, _lockFileName));
4056 PRIV_ENABLED(return_status,SafeRemove(_lockFileName));
4057 if (return_status == -1) {
4058 // We were not able to unlink the file. This means that
4059 // we do not have write access to the directory. We will
4060 // have to pass on taking long locks.
4062 _use_dot_lock = DTM_FALSE;
4063 _mail_box_writable = DTM_FALSE;
4064 error.vSetError(DTME_CannotRemoveStaleMailboxLockFile,
4065 DTM_FALSE, NULL, _lockFileName, error.errnoMessage());
4066 delete [] tempLockFileName;
4070 delete [] tempLockFileName;
4073 // lockNewMailboxFile -- before renaming a new mailbox file over an old
4074 // mailbox file, we need to establish a lock on the new mailbox file is
4075 // a lock was established on the old mailbox file. Since the .lock protocol
4076 // works on a file name basis we dont have to worry here, but if this
4077 // system uses lockf() to lock the files we need to establish that lock
4078 // on the new file first before renaming it over the old mailbox file.
4081 RFCMailBox::lockNewMailboxFile(int new_fd)
4083 int return_status = 0;
4084 if (_lockf_active == DTM_TRUE) {
4085 lseek(new_fd, 0, SEEK_SET);
4086 PRIV_ENABLED_OPTIONAL(return_status,SafeLockf(new_fd, F_LOCK, 0));
4090 // unlockOldMailboxFile -- after renaming a new mailbox file over an old
4091 // mailbox file, if a lockf() style lock was established on the old mailbox
4092 // file, it needs to be removed
4095 RFCMailBox::unlockOldMailboxFile(int old_fd)
4097 int return_status = 0;
4098 if (_lockf_active == DTM_TRUE) {
4099 lseek(old_fd, 0, SEEK_SET);
4100 PRIV_ENABLED_OPTIONAL(return_status,SafeLockf(old_fd, F_ULOCK, 0));
4105 RFCMailBox::transferLock(int old_fd, int new_fd)
4107 int return_status = 0;
4108 if (_lockf_active == DTM_TRUE) {
4109 lseek(new_fd, 0, SEEK_SET);
4110 PRIV_ENABLED_OPTIONAL(return_status,SafeLockf(new_fd, F_LOCK, 0));
4112 lseek(old_fd, F_ULOCK, 0);
4113 PRIV_ENABLED_OPTIONAL(return_status,SafeLockf(old_fd, F_ULOCK, 0));
4118 RFCMailBox::unlockFile(DtMailEnv & error, int fd)
4120 #if defined(DEBUG_RFCMailBox)
4121 char *pname = "RFCMailBox::unlockFile";
4125 // We will create a simple lock file to keep the file from
4126 // changing while we are doing critical work.
4129 if (_use_dot_lock == DTM_TRUE) {
4130 assert(_dot_lock_active == DTM_TRUE);
4131 assert(_lockFileName != NULL);
4132 _dot_lock_active = DTM_FALSE;
4133 checkLockFileOwnership(error);
4134 if (!error.isSet()) {
4135 DEBUG_PRINTF(("%s: unlinking dot_lock file\n", pname, _lockFileName));
4137 PRIV_ENABLED(return_status,SafeUnlink(_lockFileName));
4138 if (return_status == -1) {
4139 error.vSetError(DTME_CannotRemoveMailboxLockFile,
4140 DTM_FALSE, NULL, _lockFileName, error.errnoMessage());
4145 #if defined(SENDMAIL_LOCKS)
4146 if (_lockf_active == DTM_TRUE) {
4147 DEBUG_PRINTF(("%s: removing lockf\n", pname));
4149 lseek(fd, 0, SEEK_SET);
4150 PRIV_ENABLED_OPTIONAL(return_status,SafeLockf(fd, F_ULOCK, 0));
4151 _lockf_active = DTM_FALSE;
4156 #define DOT_DTMAIL_SUFFIX "dtmail"
4158 RFCMailBox::dotDtmailLockFile(char *filename, int)
4161 // Attempt to link the temporary lock file to the real lock file.
4162 assert(_real_path != NULL);
4163 (void) sprintf(filename, "%s.%s", _real_path, DOT_DTMAIL_SUFFIX);
4168 RFCMailBox::dotDtmailLock(DtMailEnv & error)
4170 char lockBuf[MAXPATHLEN];
4171 char dotDtmailPath[MAXPATHLEN+1];
4172 char *tempLockFileName = new char[MAXPATHLEN];
4174 int return_status = 0;
4177 int flags = O_RDWR | O_CREAT | O_EXCL | O_SYNC;
4179 int flags = O_RDWR | O_CREAT | O_EXCL | O_SYNC | O_RSYNC;
4182 // We will create a .dtmail file to prevent conflicts between dtmail's
4183 // operating on the same mailbox.
4185 // Create the temporary mail lock file name.
4186 sprintf(tempLockFileName, "%sXXXXXX", _real_path);
4187 mktemp(tempLockFileName);
4188 PRIV_ENABLED(return_status,SafeRemove(tempLockFileName));
4190 // Attempt to create the temporary file.
4191 PRIV_ENABLED(lock_fd,SafeOpen(tempLockFileName, flags, 0666));
4195 error.setError(DTME_CannotCreateMailboxLockFile_NoPermission);
4198 error.setError(DTME_CannotCreateMailboxLockFile_IsDirectory);
4201 error.setError(DTME_CannotCreateMailboxLockFile_NoSuchFile);
4203 #if defined(__osf__)
4208 error.setError(DTME_CannotCreateMailboxLockFile_RemoteAccessLost);
4211 error.vSetError(DTME_CannotCreateMailboxLockFile,
4212 DTM_FALSE, NULL, tempLockFileName,
4213 error.errnoMessage());
4216 delete [] tempLockFileName;
4220 sprintf(lockBuf, "%d\n", getpid());
4221 len = strlen(lockBuf);
4222 len = SafeWrite(lock_fd, lockBuf, len);
4225 dotDtmailLockFile(dotDtmailPath, MAXPATHLEN);
4227 PRIV_ENABLED(return_status,SafeLink(tempLockFileName, dotDtmailPath));
4228 if (return_status == -1)
4229 error.vSetError(DTME_CannotCreateMailboxDotDtmailLockFile,
4230 DTM_FALSE, NULL, dotDtmailPath,
4231 error.errnoMessage());
4233 PRIV_ENABLED(return_status,SafeRemove(tempLockFileName));
4234 delete [] tempLockFileName;
4238 RFCMailBox::dotDtmailUnlock(DtMailEnv &)
4240 char dotDtmailPath[MAXPATHLEN+1];
4241 char lockBuf[MAXPATHLEN];
4243 int return_status = 0;
4246 int flags = O_RDONLY | O_EXCL | O_SYNC;
4248 int flags = O_RDONLY | O_EXCL | O_SYNC | O_RSYNC;
4251 dotDtmailLockFile(dotDtmailPath, MAXPATHLEN);
4252 PRIV_ENABLED(lock_fd,SafeOpen(dotDtmailPath, flags, 0666));
4256 return_status = SafeRead(lock_fd, lockBuf, sizeof(lockBuf)-1);
4257 if (return_status <= 0)
4259 (void) SafeClose(lock_fd);
4263 sscanf(lockBuf, "%d", &pid);
4264 if (pid != getpid())
4266 (void) SafeClose(lock_fd);
4270 (void) SafeClose(lock_fd);
4271 PRIV_ENABLED(return_status,SafeRemove(dotDtmailPath));
4275 // Possible return values,
4276 // DTMBX_LONGLOCK_FAILED_CANCEL - failed, cancel operation
4277 // DTMBX_LONGLOCK_FAILED_READONLY - failed, open read only
4278 // DTMBX_LONGLOCK_SUCCESSFUL - succeeded
4280 // This function will also manipulate "_mail_box_writable" and "_lock_flag"
4283 RFCMailBox::longLock(DtMailEnv & error)
4285 DTMBX_LONGLOCK rtn = DTMBX_LONGLOCK_FAILED_CANCEL;
4287 _long_lock_active = DTM_TRUE;
4288 _lock_obj = new FileShare(error,
4297 // TT is not available. Attempt to lock using the .dtmail lock.
4303 dotDtmailLock(error);
4306 _long_lock_active = DTM_FALSE;
4307 if (NULL != _callback)
4310 // .dtmail lock failed. The best we can do is read only.
4313 char dotDtmailPath[MAXPATHLEN+1];
4315 dotDtmailLockFile(dotDtmailPath, MAXPATHLEN);
4317 DTMC_DOTDTMAILLOCKFAILED, _real_path, NULL, _cb_data,
4318 (char*) dotDtmailPath, (const char*) error);
4319 if (DTM_TRUE == ans)
4321 rtn = DTMBX_LONGLOCK_FAILED_READONLY;
4326 rtn = DTMBX_LONGLOCK_FAILED_CANCEL;
4327 error.setError(DTME_UserInterrupted);
4332 rtn = DTMBX_LONGLOCK_SUCCESSFUL;
4337 // Ask TT to lock the file.
4339 _lock_obj->lockFile(error);
4343 // TT rejected our lock request. The best we can do is read only.
4346 if (_lock_obj->readOnly(error) == DTM_TRUE)
4348 rtn = DTMBX_LONGLOCK_FAILED_READONLY;
4353 rtn = DTMBX_LONGLOCK_FAILED_CANCEL;
4354 error.setError(DTME_UserInterrupted);
4359 _long_lock_active = DTM_FALSE;
4362 rtn = DTMBX_LONGLOCK_SUCCESSFUL;
4369 RFCMailBox::longUnlock(DtMailEnv & error)
4373 if (DTM_TRUE == _lock_flag)
4375 if (NULL != _lock_obj)
4380 else if (_long_lock_active == DTM_TRUE)
4381 dotDtmailUnlock(error);
4385 if (_mail_box_writable == DTM_FALSE)
4389 #if !defined(SENDMAIL_LOCKS)
4390 if (_lockf_active == DTM_TRUE)
4392 int return_status = 0;
4393 lseek(_fd, 0, SEEK_SET);
4394 PRIV_ENABLED_OPTIONAL(return_status,SafeLockf(_fd, F_ULOCK, 0));
4395 _lockf_active = DTM_FALSE;
4399 _long_lock_active = DTM_FALSE;
4402 RFCMailBox::MapRegion *
4403 RFCMailBox::mapNewRegion(DtMailEnv & error, int fd, unsigned long size)
4407 // Create the mapped region.
4409 MapRegion * map = new MapRegion;
4411 map->file_size = size;
4412 long page_size = memoryPageSize();
4414 #if defined(__osf__) && OSMAJORVERSION < 4
4415 // Problem with mapping. You can not round up to the nearest
4416 // memory block size when mapping a file. You need the exact
4417 // file size of less. - ff
4418 map->map_size = map->file_size;
4420 map->map_size = map->file_size + (page_size - (map->file_size % page_size)) + page_size;
4423 int flags = MAP_PRIVATE;
4425 #if defined(MMAP_NORESERVE)
4426 // We are not supposed to be writing to these pages. If
4427 // we don't specify MAP_NORESERVE however, the system will
4428 // reserve swap space equal to the file size to deal with
4429 // potential writes. This is wasteful to say the least.
4431 flags |= MAP_NORESERVE;
4434 map->map_region = (char *)mmap(0, (size_t) map->map_size,
4435 PROT_READ, flags, fd, 0);
4436 if (map->map_region == (char *)-1) {
4439 error.setError(DTME_CannotReadNewMailboxFile_OutOfMemory);
4443 error.vSetError(DTME_CannotReadNewMailboxFile,
4444 DTM_FALSE, NULL, error.errnoMessage());
4449 return((MapRegion *)NULL);
4452 map->file_region = map->map_region;
4458 RFCMailBox::makeHeaderLine(DtMailEnv & error,
4460 const DtMailHeaderRequest & request,
4461 DtMailHeaderLine & headers)
4463 MessageCache * mc = _msg_list[slot];
4464 DtMail::Envelope * env = mc->message->getEnvelope(error);
4466 // For each request, we need to retrieve the header values.
4468 headers.number_of_names = request.number_of_names;
4469 headers.header_values = new DtMailValueSeq[headers.number_of_names];
4471 for (int req = 0; req < request.number_of_names; req++) {
4472 // RFC Message::getHeader will pass abstract names through
4473 // as transport names if they can not be found in the abstract
4474 // table. Because of this we say all names are abstract and
4475 // rely on the specific implementation of RFCMessage. This
4476 // is potentially dangerous, but we are allowed to define
4477 // and require these semantics for RFCMessage (and this same
4478 // information appears in the appropriate place in getHeader.
4480 env->getHeader(error,
4481 request.header_name[req],
4483 headers.header_values[req]);
4484 if (error.isSet()) {
4485 headers.header_values[req].clear();
4494 RFCMailBox::waitForMsgs(int needed)
4496 while(_at_eof == 0 && needed >= _msg_list.length()) {
4503 RFCMailBox::writeToDumpFile(const char *format, ...)
4506 char *dumpfilename = new char[MAXPATHLEN+1];
4507 _Xctimeparams ctime_buf;
4509 GET_DUMPFILE_NAME(dumpfilename);
4510 FILE *df = fopen(dumpfilename, "a");
4512 const time_t clockTime = (const time_t) time((time_t *)0);
4513 memset((void*) &ctime_buf, 0, sizeof(_Xctimeparams));
4514 fprintf(df, "--------------------- pid=%ld %s",
4515 (long)getpid(), _XCtime(&clockTime, ctime_buf));
4518 va_start(var_args, format);
4519 vfprintf(df, format, var_args);
4522 fprintf(df, "---------------------\n");
4523 fprintf(df, "\n\n");
4525 delete [] dumpfilename;
4530 RFCMailBox::startAutoSave(DtMailEnv & error,
4537 _session->addEventRoutine(error, PollEntry, this, 60);
4539 _last_poll = 0; // Causes first poll to fire right way.
4542 _session->removeEventRoutine(error, PollEntry, this);
4546 RFCMailBox::dumpMaps(const char *str)
4550 off_t total_file_size = 0;
4551 struct sigaction sig_act, old_sig_act;
4552 char *dumpfilename = new char[MAXPATHLEN+1];
4554 GET_DUMPFILE_NAME(dumpfilename);
4555 FILE *df = fopen(dumpfilename, "a");
4557 if (df==NULL && _errorLogging)
4560 "dumpMaps(): Cant open dump file %s\n", dumpfilename);
4561 delete [] dumpfilename;
4563 if (SafeFStat(_fd, &buf) < 0) {
4564 fprintf(df, "dumpMaps(): fstat(%d) failed errno=%d\n", _fd, errno);
4572 * Prepare signal handler for exception handling.
4574 (void) sigemptyset(&sig_act.sa_mask);
4575 sig_act.sa_flags = 0;
4577 sig_act.sa_handler = (void(*)())SigBusHandler;
4579 sig_act.sa_handler = SigBusHandler;
4581 sigaction(SIGBUS, &sig_act, &old_sig_act);
4582 sigbus_env_valid = 1;
4583 if (setjmp(sigbus_env) == 0) {
4584 const time_t clockTime = (const time_t) time((time_t *)0);
4585 _Xctimeparams ctime_buf;
4586 memset((void*) &ctime_buf, 0, sizeof(_Xctimeparams));
4587 fprintf(df, "--------------------- pid=%ld %s",
4588 (long)getpid(), _XCtime(&clockTime, ctime_buf));
4590 fprintf(df, "---------------------\n");
4591 fprintf(df, "Mappings = %d\n", _mappings.length());
4592 fprintf(df, "Map Entries:\n");
4593 for (int m = 0; m < _mappings.length(); m++) {
4594 MapRegion *map = _mappings[m];
4595 fprintf(df, "map[%d]: map_region = 0x%08lx, map_size = 0x%08lx(%08ld)\n",
4596 m, map->map_region, map->map_size, map->map_size);
4597 if (map->map_size % memoryPageSize()) {
4598 fprintf(df, "ERROR! map->map_size not mod %d\n", memoryPageSize());
4600 HexDump(df, "map_region", (unsigned char *)map->map_region,
4601 (int) map->file_size, _errorLogging ? 0 : 10);
4603 "map[%d]: file_region = 0x%08lx, file_size = 0x%08lx(%08ld)\n",
4604 m, map->file_region, map->file_size, map->file_size);
4605 fprintf(df, "map[%d]: offset = 0x%08lx(%08ld)\n",
4606 m, map->offset, map->offset);
4607 if (map->file_size == 0) {
4608 fprintf(df, "No data in file_region\n");
4611 if (strncasecmp(map->file_region, "From", 4)) {
4612 fprintf(df, "ERROR! map->file_region does not begin with From\n");
4616 (unsigned char *)map->file_region,
4617 (int) map->file_size, _errorLogging ? 0 : 10);
4619 total_file_size += (off_t) map->file_size;
4620 if ((total_file_size % 4096) == 0) {
4622 "Total file size falls on page boundary, totalsize = %d\n",
4627 fprintf(df, "\nstat buffer entries: st_ino = %d, st_dev = %d, st_nlink = %d, st_size = %ld\n",
4628 buf.st_ino, buf.st_dev, buf.st_nlink, buf.st_size);
4630 fprintf(df, "\n\n");
4633 fprintf(df, "\nSIGBUS received during output of file mappings.\n");
4634 fprintf(df, "This generally indicates a truncated or deleted file.\n");
4636 sigbus_env_valid = 0;
4637 sigaction(SIGBUS, &old_sig_act, NULL);
4641 // The following routines are required to bind this format specific driver
4642 // into the format neutral layer.
4644 // The first entry point is the capability query interface. This is used
4645 // by the client to determine what capabilities we support.
4648 RFCQueryImpl(DtMail::Session & session,
4650 const char * capability,
4655 if (strcmp(capability, DtMailCapabilityPropsSupported) == 0) {
4656 DtMailBoolean * resp = va_arg(args, DtMailBoolean *);
4661 if (strcmp(capability, DtMailCapabilityImplVersion) == 0) {
4662 char * version = va_arg(args, char *);
4663 strcpy(version, "1.0");
4667 if (strcmp(capability, DtMailCapabilityInboxName) == 0)
4669 DtMailObjectSpace *space = va_arg(args, DtMailObjectSpace *);
4670 void **inbox = va_arg(args, void **);
4672 *space = DtMailFileObject;
4673 *inbox = (void*) getInboxPath(&session);
4677 if (strcmp(capability, DtMailCapabilityMailspoolName) == 0)
4679 DtMailObjectSpace *space = va_arg(args, DtMailObjectSpace *);
4680 void **mailspool = va_arg(args, void **);
4682 *space = DtMailFileObject;
4683 *mailspool = (void*) getMailspoolPath(&session);
4687 if (strcmp(capability, DtMailCapabilityTransport) == 0) {
4688 DtMailBoolean * resp = va_arg(args, DtMailBoolean *);
4693 error.setError(DTME_NotSupported);
4697 // The QueryOpen entry point is used to determin if we can open the specified
4698 // path. If the name is of the form host:/path, or is a simple path, then we
4699 // will say we can. Additional work should be done for content typing here,
4700 // but we'll skip it for now.
4703 RFCQueryOpen(DtMail::Session &,
4705 DtMailObjectSpace space,
4710 // We can do buffers, so just say yes.
4712 if (space == DtMailBufferObject) {
4716 // If this isn't in the file name space, then give up now.
4718 if (space != DtMailFileObject) {
4722 char * path = (char *) arg;
4724 // First, is this of the form "host:/path". If so, it is probably
4725 // an RFC mail box, so we will say we can open it.
4727 for (const char * c = path; *c; c++) {
4729 // We hit a slash first, so colon's are not useful.
4733 if (*c == ':' && *(c+1) == '/') {
4734 // Looks like we have a host name. We should do a more
4735 // robust check at this point, but for now we will assume
4736 // that we have a valid host.
4741 // Okay, there defintely is not a host name. See if we can stat the
4742 // file. If we can, then assume we can open it, otherwise. If the file
4743 // doesn't exist, then we can create a new one.
4746 if (stat(path, &sbuf) < 0) {
4747 if (errno == ENOENT) {
4758 // The mail box construct entry point creates an instance of the RFC
4759 // mail box object. This object is then accessed through the virtual
4760 // DtMail::MailBox class API.
4763 RFCMailBoxConstruct(DtMail::Session & session,
4765 DtMailObjectSpace space,
4770 return(new RFCMailBox(error, &session, space, arg, cb, client_data, "Internet MIME"));
4774 RFCMessageQuery(DtMail::Session &,
4776 DtMailObjectSpace space,
4781 if (space != DtMailBufferObject) {
4789 RFCMessageConstruct(DtMail::Session & session,
4791 DtMailObjectSpace space,
4796 return(new RFCMessage(error, &session, space, arg, cb, client_data));
4800 RFCMIMETransportConstruct(DtMail::Session & session,
4802 DtMailStatusCallback cb,
4805 return(new RFCTransport(error, &session, cb, cb_data, "Internet MIME"));
4808 // The meta factory is responsible for returning the entry points
4809 // required for locating and creating various mail objects based for
4810 // the RFC implementation. It is essentially a switch table.
4814 RFCMetaFactory(const char * op)
4816 if (strcmp(op, QueryImplEntryOp) == 0) {
4817 return(RFCQueryImpl);
4820 if (strcmp(op, QueryOpenEntryOp) == 0) {
4821 return(RFCQueryOpen);
4824 if (strcmp(op, MailBoxConstructEntryOp) == 0) {
4825 return(RFCMailBoxConstruct);
4828 if (strcmp(op, QueryMessageEntryOp) == 0) {
4829 return(RFCMessageQuery);
4832 if (strcmp(op, MessageConstructEntryOp) == 0) {
4833 return(RFCMessageConstruct);
4836 if (strcmp(op, TransportConstructEntryOp) == 0) {
4837 return(RFCMIMETransportConstruct);
4843 // The mail box construct entry point creates an instance of the RFC
4844 // mail box object. This object is then accessed through the virtual
4845 // DtMail::MailBox class API.
4848 V3MailBoxConstruct(DtMail::Session & session,
4850 DtMailObjectSpace space,
4855 return(new RFCMailBox(error, &session, space, arg, cb, client_data,
4860 RFCV3TransportConstruct(DtMail::Session & session,
4862 DtMailStatusCallback cb,
4865 return(new RFCTransport(error, &session, cb, cb_data, "Sun Mail Tool"));
4868 // The meta factory is responsible for returning the entry points
4869 // required for locating and creating various mail objects based for
4870 // the RFC implementation. It is essentially a switch table.
4873 V3MetaFactory(const char * op)
4875 if (strcmp(op, QueryImplEntryOp) == 0) {
4876 return(RFCQueryImpl);
4879 if (strcmp(op, QueryOpenEntryOp) == 0) {
4880 return(RFCQueryOpen);
4883 if (strcmp(op, MailBoxConstructEntryOp) == 0) {
4884 return(V3MailBoxConstruct);
4887 if (strcmp(op, QueryMessageEntryOp) == 0) {
4888 return(RFCMessageQuery);
4891 if (strcmp(op, MessageConstructEntryOp) == 0) {
4892 return(RFCMessageConstruct);
4895 if (strcmp(op, TransportConstructEntryOp) == 0) {
4896 return(RFCV3TransportConstruct);
4904 RFCMailBox::createMailRetrievalAgent(char *password)
4906 DtMailEnv localError;
4907 char *path = _session->expandPath(localError, (char*) _arg);
4908 DtMailServer *server = NULL;
4911 if (! isInboxMailbox(_session, path))
4917 if (NULL != _mra_command) free(_mra_command);
4918 _mra_command = NULL;
4920 if (NULL != password)
4922 if (NULL != _mra_serverpw) free(_mra_serverpw);
4923 _mra_serverpw = strdup(password);
4926 if (NULL != _mra_server) delete _mra_server;
4929 if (True == DtMailServer::get_mailrc_value(
4930 _session, DTMAS_INBOX,
4931 DTMAS_PROPKEY_GETMAILVIASERVER,
4934 protocol = DtMailServer::get_mailrc_value(
4935 _session, DTMAS_INBOX,
4936 DTMAS_PROPKEY_PROTOCOL,
4939 if (! strcasecmp(protocol, DTMAS_PROTO_IMAP))
4940 _mra_server = (DtMailServer*) new IMAPServer(
4941 DTMAS_INBOX, _session, this,
4942 appendCB, (void*) this);
4943 else if (! strcasecmp(protocol, DTMAS_PROTO_APOP))
4944 _mra_server = (DtMailServer*) new APOPServer(
4945 DTMAS_INBOX, _session, this,
4946 appendCB, (void*) this);
4947 else if (! strcasecmp(protocol, DTMAS_PROTO_POP3))
4948 _mra_server = (DtMailServer*) new POP3Server(
4949 DTMAS_INBOX, _session, this,
4950 appendCB, (void*) this);
4951 else if (! strcasecmp(protocol, DTMAS_PROTO_POP2))
4952 _mra_server = (DtMailServer*) new POP2Server(
4953 DTMAS_INBOX, _session, this,
4954 appendCB, (void*) this);
4956 _mra_server = (DtMailServer*) new AUTOServer(
4957 DTMAS_INBOX, _session, this,
4958 appendCB, (void*) this);
4960 if (NULL != _mra_server) _mra_server->set_password(_mra_serverpw);
4961 if (NULL != protocol) free(protocol);
4963 else if (True == DtMailServer::get_mailrc_value(
4964 _session, DTMAS_INBOX,
4965 DTMAS_PROPKEY_GETMAILVIACOMMAND,
4968 _mra_command = DtMailServer::get_mailrc_value(
4969 _session, DTMAS_INBOX,
4970 DTMAS_PROPKEY_GETMAILCOMMAND,
4977 RFCMailBox::deleteMailRetrievalAgent()
4979 if (NULL != _mra_command) free(_mra_command);
4980 _mra_command = NULL;
4981 if (NULL != _mra_serverpw) free(_mra_serverpw);
4982 _mra_serverpw = NULL;
4983 if (NULL != _mra_server) delete _mra_server;
4988 RFCMailBox::updateMailRetrievalPassword(char *password)
4990 if (NULL == password || NULL == _mra_server) return;
4992 if (NULL != _mra_serverpw) delete _mra_serverpw;
4993 _mra_serverpw = strdup(password);
4994 _mra_server->set_password(_mra_serverpw);
4999 RFCMailBox::retrieveNewMail(DtMailEnv &error)
5001 if (NULL != _mra_server)
5003 _mra_server->retrieve_messages(error);
5005 if (DTME_MailServerAccess_MissingPassword == (DTMailError_t) error ||
5006 DTME_MailServerAccess_AuthorizationFailed == (DTMailError_t) error)
5007 return DTMC_SERVERPASSWORDNEEDED;
5008 else if (DTME_NoError != (DTMailError_t) error)
5009 return DTMC_SERVERACCESSFAILED;
5011 else if (NULL != _mra_command)
5013 int sysstatus = system(_mra_command);
5014 if (-1 == sysstatus)
5017 DTME_GetmailCommandRetrieval_SystemError,
5019 _mra_command, errno, error.errnoMessage(errno));
5020 if (_errorLogging) error.logError(DTM_FALSE, (const char*) error);
5021 return DTMC_GETMAILCOMMANDFAILED;
5023 if (0 == WIFEXITED(sysstatus))
5026 DTME_GetmailCommandRetrieval_AbnormalExit,
5029 if (_errorLogging) error.logError(DTM_FALSE, (const char*) error);
5030 return DTMC_GETMAILCOMMANDFAILED;
5032 else if (0 != WEXITSTATUS(sysstatus))
5037 "system('%s') returned %d\n",
5038 _mra_command, WEXITSTATUS(sysstatus));
5045 RFCMailBox::mailboxAccessShow(time_t mtime, char *prefix)
5049 new_time.modtime = mtime;
5050 new_time.actime = new_time.modtime+1;
5051 SafeUTime(_real_path, &new_time);
5052 #ifdef DEBUG_MAILBOX_ACCESS
5053 fprintf(stderr, "%s: forcing acctime>modtime\n", prefix);
5058 RFCMailBox::mailboxAccessHide(char *prefix)
5060 SafeUTime(_real_path, NULL);
5061 #ifdef DEBUG_MAILBOX_ACCESS
5062 fprintf(stderr, "%s: forcing modtime==acctime\n", prefix);