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 libraries 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) && !defined(CSRG_BASED)
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 #define LCL_SIG_HANDLER_SIGNATURE
108 #elif defined(__hpux)
109 #define LCL_SIG_HANDLER_SIGNATURE __harg
110 #elif defined(__aix) || defined(__alpha) || defined(linux) || defined(CSRG_BASED)
111 #define LCL_SIG_HANDLER_SIGNATURE int
115 // Debugging for RFCMailBox.
117 #if defined(DEBUG_RFCMailBox)
118 #define DEBUG_PRINTF(a) printf a
120 #define DEBUG_PRINTF(a)
124 // These macros define a method for executing statements
125 // with set group id privileges enabled
127 #if defined(MAILGROUP_REQUIRED)
129 #define PRIV_ENABLED_OPTIONAL(STATUSVARIABLE, STATEMENT) \
130 STATUSVARIABLE = STATEMENT;
132 #define PRIV_ENABLED(STATUSVARIABLE, STATEMENT) \
134 _session->enableGroupPrivileges(); \
135 STATUSVARIABLE = STATEMENT; \
136 _session->disableGroupPrivileges(); \
139 #define PRIV_ENABLED_OPEN(FILENAME, STATUSVARIABLE, STATEMENT) \
140 if (isSetMailGidNeeded(FILENAME)) { \
141 PRIV_ENABLED(STATUSVARIABLE, STATEMENT) \
143 STATUSVARIABLE = STATEMENT; \
148 #define PRIV_ENABLED_OPTIONAL(STATUSVARIABLE, STATEMENT) \
149 _session->enableGroupPrivileges(); \
150 STATUSVARIABLE = STATEMENT; \
151 _session->disableGroupPrivileges(); \
152 if (STATUSVARIABLE == -1) { \
153 STATUSVARIABLE = STATEMENT; \
156 #define PRIV_ENABLED PRIV_ENABLED_OPTIONAL
158 #define PRIV_ENABLED_OPEN(FILENAME, STATUSVARIABLE, STATEMENT) \
159 STATUSVARIABLE = STATEMENT;
163 #define GET_DUMPFILE_NAME(dfn) \
164 snprintf(dfn, sizeof(dfn), "%s/%s/dtmail.dump", getenv("HOME"), DtPERSONAL_TMP_DIRECTORY)
167 * Local Data Definitions
169 static const int RFCSignature = 0x448612e5;
171 static const int DEFAULT_FOLDER_SIZE = (32 << 10); // 32 KB
173 static int sigbus_env_valid = 0;
175 static jmp_buf sigbus_env;
178 * Local Function Declarations
180 static void SigBusHandler(LCL_SIG_HANDLER_SIGNATURE);
181 static int isMailGroupSystemMailbox(const char *mailboxPath);
182 static int isInboxMailbox(const char *mailboxPath);
183 static char *getInboxPath(DtMail::Session *session);
188 * Notified when an interesting (SIGBUS in this case) signal is raised.
189 * I wanted to throw a C++ exception at this point but that's not
190 * supported everywhere. So, we'll just have to use the thread-unsafe
191 * setjmp/longjmp combination.
194 void SigBusHandler(LCL_SIG_HANDLER_SIGNATURE)
196 if (sigbus_env_valid) {
197 longjmp(sigbus_env, 1);
202 void HexDump(FILE *pfp, char *pmsg, unsigned char *pbufr, int plen, int plimit)
204 unsigned char save[64];
205 long int x, y, z, word, cnt;
211 if (pfp_r == (FILE*) NULL) {
212 char dumpfilename[MAXPATHLEN+1];
213 _Xctimeparams ctime_buf;
215 GET_DUMPFILE_NAME(dumpfilename);
216 pfp_r = fopen(dumpfilename, "a");
217 const time_t clockTime = (const time_t) time((time_t *)0);
218 memset((void*) &ctime_buf, 0, sizeof(_Xctimeparams));
219 fprintf(pfp_r, "--------------------- pid=%ld %s",
220 (long)getpid(), _XCtime(&clockTime, ctime_buf));
223 (void) fprintf(pfp_r, "--> %s (%d bytes at %p):\n", pmsg, plen, pbufr);
225 memset((char *)save, 0, sizeof(save));
229 for (x = 0; cnt > 0; x++, z += 16)
231 (void) fprintf(pfp_r, "%p(+%6.6ld) ", pbufr + z, z);
232 for (y = 0; y < 16; y++)
234 save[y] = pbufr[x * 16 + y];
235 word = pbufr[x * 16 + y];
238 (void) fprintf(pfp_r, "%2.2lx%c", word, y == 7 ? '-' : ' ');
240 (void) fprintf(pfp_r, " ");
242 (void) fprintf(pfp_r, "%s", " *");
243 for (y = 0; y < 16; y++)
245 (void) fprintf(pfp_r, " ");
246 else if (pbufr[x * 16 + y] < ' ' || pbufr[x * 16 + y] > '~')
247 (void) fprintf(pfp_r, "%s", ".");
249 (void) fprintf(pfp_r, "%c", pbufr[x * 16 + y]);
250 (void) fprintf(pfp_r, "%s", "*\n");
253 if (plimit && (x >= (plimit-1)) && (cnt > (plimit*16))) {
254 while (cnt > (plimit*16)) {
259 fprintf(pfp_r, "...\n");
263 if (pfp == (FILE*) NULL) {
264 fprintf(pfp_r, "---------------------\n");
269 RFCMailBox::RFCMailBox(DtMailEnv & error,
270 DtMail::Session * session,
271 DtMailObjectSpace space,
275 const char * impl_name)
276 : DtMail::MailBox(error, session, space, arg, cb, client_data),
277 _msg_list(128), _mappings(4)
279 // We are using a condition to block any threads from
280 // trying to use this object until it is open. We will
281 // not set the object valid until we have a valid thread.
282 // Note that this will not be set to true until a stream
283 // has been successfully opened.
285 DtMailEnv localError;
288 _object_valid = new Condition;
290 _object_valid->setFalse();
292 _map_lock = MutexInit();
294 _impl_name = impl_name;
301 _mail_box_writable = DTM_FALSE;
302 _use_dot_lock = DTM_TRUE;
303 _long_lock_active = DTM_FALSE;
304 _dot_lock_active = DTM_FALSE;
305 _lockf_active = DTM_FALSE;
306 _uniqueLockId = generateUniqueLockId();
307 assert(_uniqueLockId != NULL);
308 _uniqueLockIdLength = strlen(_uniqueLockId);
309 _lockFileName = (char *)0;
312 _mr_allowed = DTM_TRUE;
314 _mra_serverpw = NULL;
316 createMailRetrievalAgent(NULL);
318 // Create a thread for getting new mail and expunging old mail.
319 // Of course we don't need threads for buffer objects.
321 if (_space != DtMailBufferObject) {
322 _thread_info = new NewMailData;
323 _thread_info->self = this;
324 _thread_info->object_valid = _object_valid;
325 _mbox_daemon = ThreadCreate(ThreadNewMailEntry, _thread_info);
328 // We will have to add a poll oriented method as well. We'll ignore
331 if (_space != DtMailBufferObject) {
332 _session->addEventRoutine(error, PollEntry, this, 15);
334 _last_poll = 0; // Causes first poll to fire right way.
337 // We need to figure out what type of locking to use. We use ToolTalk,
338 // when the user has explicitly turned it on via a property.
340 DtMail::MailRc * mailrc = _session->mailRc(error);
341 const char * value = NULL;
342 mailrc->getValue(localError, "cdetooltalklock", &value);
343 if (localError.isSet()) {
344 _tt_lock = DTM_FALSE;
352 // Determine if error logging is enabled
356 mailrc->getValue(localError, "errorlogging", &value);
357 _errorLogging = (localError.isSet() ?
358 (localError.clear(), DTM_FALSE) : DTM_TRUE);
363 _partialListCount = 0;
367 _object_signature = RFCSignature;
370 RFCMailBox::~RFCMailBox(void)
372 if (_object_signature != RFCSignature) {
376 MutexLock lock_scope(_obj_mutex);
377 if (_object_signature == RFCSignature) {
378 _object_valid->setFalse();
381 _session->removeEventRoutine(error, PollEntry, this);
383 // We need to copy the file if writable and dirty out.
384 // NOTE: the caller should really call writeMailBox() or expunge()
385 // (as appropriate) first before destroying this mailbox, as there
386 // is no way to pass an error indication back up from here to the
387 // caller. If an error happens let syslog handle it.
389 if (_mail_box_writable == DTM_TRUE) {
392 writeMailBox(error, DTM_FALSE);
395 // THE MAILBOX COULD NOT BE WRITTEN!! SHOULD DO SOMETHING
396 error.logError(DTM_TRUE,
397 "~RFCMailBox(): Failed to write mailbox: %s",
398 (const char *)error);
403 // Let's keep the map locked.
405 MutexLock lock_map(_map_lock);
407 // Next we tear down the message structures.
409 while (_msg_list.length()) {
410 MessageCache * mc = _msg_list[0];
413 _msg_list.remove(0); // Won't actually touch the object.
416 // Finally we need to get rid of the mapping. There are
417 // actually 3 conditions we need to deal with here.
419 // 1) We opened a file and mapped it. In that case, we unmap,
420 // and close the file.
422 // 2) We created a buffer. In that case the _mapped_region points,
423 // at the region owned by _buffer. We simply destroy _buffer,
424 // but we do not attempt to destroy the mapped region.
426 // 3) The caller asked us to open an existing buffer. In that
427 // case, do nothing (we didn't build it so we won't destroy it).
429 if (_fd >= 0) { // Option 1
430 for (int slot = 0; slot < _mappings.length(); slot++) {
431 munmap(_mappings[slot]->map_region,
432 (size_t) _mappings[slot]->map_size);
438 if (_buffer) { // Option 2
443 // Allocated using malloc and strdup, so free using free.
445 free((void*) _real_path);
446 free((void*) _uniqueLockId);
447 free((void*) _lockFileName);
450 _object_signature = 0;
452 if (NULL != _mra_command) delete _mra_command;
453 if (NULL != _mra_server) delete _mra_server;
454 if (NULL != _mra_serverpw) delete _mra_serverpw;
457 static int isMailGroupSystemMailbox(const char * mailboxPath)
460 #ifdef MAILGROUP_REQUIRED
461 static int oneTimeFlag = 0;
462 static char *cached_inbox_path = 0;
464 if (NULL == mailboxPath) return retval;
468 char *inbox_path = new char[MAXPATHLEN];
471 GetPasswordEntry(pw);
472 sprintf(inbox_path, MAIL_SPOOL_PATH, pw.pw_name);
473 cached_inbox_path = strdup(inbox_path);
475 delete [] inbox_path;
478 assert(cached_inbox_path);
479 retval = (!strcmp(mailboxPath, cached_inbox_path));
484 static char *getMailspoolPath(DtMail::Session *session)
487 DtMail::MailRc *mailrc = session->mailRc(error);
488 char *mailspoolpath = 0;
489 char *syspath = new char[MAXPATHLEN];
492 GetPasswordEntry(pw);
493 sprintf(syspath, MAIL_SPOOL_PATH, pw.pw_name);
494 mailspoolpath = strdup(syspath);
496 assert(NULL!=mailspoolpath);
498 return mailspoolpath;
501 static char *getInboxPath(DtMail::Session *session)
504 DtMail::MailRc *mailrc = session->mailRc(error);
507 mailrc->getValue(error, DTMAS_PROPKEY_INBOXPATH, (const char**) &inboxpath);
510 mailrc->getValue(error, "DT_MAIL", (const char**) &inboxpath);
513 mailrc->getValue(error, "MAIL", (const char**) &inboxpath);
516 inboxpath = getMailspoolPath(session);
527 if (inboxpath && 0 == (strcmp(inboxpath, "MAILSPOOL_FILE")))
530 inboxpath = getMailspoolPath(session);
533 assert(NULL!=inboxpath);
537 static int isInboxMailbox(DtMail::Session *session, const char * mailboxPath)
540 char *inbox_path = NULL;
542 inbox_path = getInboxPath(session);
543 assert(NULL!=inbox_path);
544 retval = (!strcmp(mailboxPath, inbox_path));
549 // Function: alterPageMappingAdvice - change OS mapping advice on selected map
551 // Give the operating system new advice on the referencing activity that
552 // should be expected for a region of file mapped pages.
554 // Spin through all map regions recorded in _mappings looking for the
555 // specified map (or all maps if -1 passed in as the map region), and
556 // if found issue an madvise call on the specified region of memory.
558 // map -- either a specific region that is part of _mappings,
559 // -- OR (MapRegion *)-1 to change ALL map regions in _mappings
560 // advice -- advice as per madvise(3) Operating System call
567 RFCMailBox::alterPageMappingAdvice(MapRegion *map, int advice)
569 int me = _mappings.length();
570 for (int m = 0; m < me; m++) {
571 MapRegion *map_t = _mappings[m];
573 #if !defined(linux) && !defined(sun)
574 // no madvise on these systems
575 if (map_t == map || map == (MapRegion *)-1)
576 madvise(map_t->map_region, (size_t) map_t->map_size, advice);
581 // Function: memoryPageSize - return hardware natural memory page size
583 // Compute and return the natural memory page size of the current hardware
585 // Use sysconf to query the operating system about the current hardware;
586 // cache the value so that repeated calls to this function do not generate
587 // repeated calls to the operating system.
593 // long -- natural page size of the current hardware
598 static long mach_page_size = -1; // -1 is "one time value"
600 if (mach_page_size == -1) {
601 mach_page_size = sysconf(_SC_PAGESIZE);
602 assert(mach_page_size != -1);
605 return(mach_page_size);
609 // Functions: addressIsMapped - check if address is in file mapped memory
611 // Check to see if a given memory address is within the bounds of any
612 // memory that may have been mapped from a mailbox file into memory,
613 // and return an indication of whether it is or not.
615 // Go through the list of file to memory mappings and check to see if
616 // the given address is within the bounds of one of the mappings.
618 // addressToCheck -- address in memory to check against mappings
622 // DTM_TRUE - memory address is in file mapped memory and can be accessed
623 // DTM_FALSE - memory address is not in file mapped memory - may not be valid
626 RFCMailBox::addressIsMapped(void *addressToCheck)
628 assert(addressToCheck != NULL);
629 int me = _mappings.length();
630 for (int m = 0; m < me; m++)
632 MapRegion *map = _mappings[m];
633 if ( (addressToCheck >= map->map_region)
634 && (addressToCheck < (map->map_region+map->file_size)) )
641 RFCMailBox::appendCB(DtMailEnv &error, char *buf, int len, void *clientData)
643 RFCMailBox *obj = (RFCMailBox*) clientData;
645 if (NULL == obj) return;
646 obj->append(error, buf, len);
650 RFCMailBox::append(DtMailEnv &error, char *buf, int len)
653 off_t end = lseek(_fd, 0, SEEK_END);
655 // Add a new-line at the end to distinguish separate messages.
656 status = SafeWrite(_fd, buf, len);
660 char *path = _session->expandPath(error, (char *)_arg);
665 DTME_AppendMailboxFile_FileTooBig, DTM_FALSE, NULL,
666 path, errno, error.errnoMessage(errno));
669 #if defined(__osf__) || defined(CSRG_BASED)
675 DTME_AppendMailboxFile_LinkLost, DTM_FALSE, NULL,
676 path, errno, error.errnoMessage(errno));
681 DTME_AppendMailboxFile_NoSpaceLeft, DTM_FALSE, NULL,
682 path, errno, error.errnoMessage(errno));
687 DTME_AppendMailboxFile_SystemError, DTM_FALSE, NULL,
688 path, errno, error.errnoMessage(errno));
693 if (_hide_access_events)
694 mailboxAccessHide("append");
697 time_t now = time((time_t) NULL);
698 mailboxAccessShow(now, "append");
704 RFCMailBox::create(DtMailEnv & error, mode_t create_mode)
708 MutexLock lock_scope(_obj_mutex);
709 MutexLock lock_map(_map_lock);
712 case DtMailBufferObject:
714 DtMailBuffer * buf = (DtMailBuffer *)_arg;
716 _buffer = (char *)malloc(DEFAULT_FOLDER_SIZE);
717 buf->buffer = _buffer;
718 buf->size = DEFAULT_FOLDER_SIZE;
720 MapRegion * map = new MapRegion;
721 map->file_region = map->map_region = _buffer;
722 map->file_size = map->map_size = DEFAULT_FOLDER_SIZE;
724 _mappings.append(map);
726 _mail_box_writable = DTM_TRUE;
731 case DtMailFileObject:
733 openRealFile(error, O_RDWR | O_CREAT, create_mode);
743 error.setError(DTME_NotSupported);
748 _object_valid->setTrue();
754 RFCMailBox::open(DtMailEnv & error,
755 DtMailBoolean auto_create,
758 DtMailBoolean lock_flag,
759 DtMailBoolean auto_parse
764 if (_tt_lock == DTM_TRUE && lock_flag == DTM_TRUE)
765 _lock_flag = DTM_TRUE;
767 _lock_flag = DTM_FALSE;
769 MutexLock lock_scope(_obj_mutex);
773 MutexLock lock_map(_map_lock);
776 case DtMailBufferObject:
778 DtMailBuffer * buf = (DtMailBuffer *)_arg;
780 MapRegion * map = new MapRegion;
781 map->file_region = map->map_region = (char *)buf->buffer;
782 map->file_size = map->map_size = buf->size;
784 _mappings.append(map);
786 _mail_box_writable = DTM_FALSE;
791 case DtMailFileObject:
796 _mail_box_writable = DTM_FALSE;
797 char * path = _session->expandPath(error, (char *)_arg);
798 PRIV_ENABLED_OPTIONAL(return_result, SafeAccess(path, W_OK));
799 if (return_result == 0) {
804 // We need to use the most restrictive mode that is possible
805 // on the file. If the caller has requested the file be open
806 // read-only, then we should do that, even if read-write is
807 // allowed. We don't want to try to open the file read-write
808 // if we don't have adequate permission however.
810 mode = open_mode == O_RDONLY ? open_mode : mode;
812 openRealFile(error, mode, create_mode);
814 if (auto_create == DTM_TRUE) {
828 error.setError(DTME_NotSupported);
831 if (error.isSet()) { // Can't parse this.
839 #if defined(POSIX_THREADS)
840 ThreadCreate(ThreadParseEntry, this);
845 _object_valid->setTrue(); // New mail watcher starts now.
855 _mail_box_writable = DTM_FALSE;
856 char * path = _session->expandPath(error, (char *)_arg);
857 PRIV_ENABLED_OPTIONAL(return_status, SafeAccess(path, W_OK));
858 if (return_status == 0) {
863 // We need to use the most restrictive mode that is possible
864 // on the file. If the caller has requested the file be open
865 // read-only, then we should do that, even if read-write is
866 // allowed. We don't want to try to open the file read-write
867 // if we don't have adequate permission however.
869 mode = open_mode == O_RDONLY ? open_mode : mode;
871 openRealFile(error, mode, create_mode);
873 if (auto_create == DTM_TRUE) {
877 // Isn't this wrong? Shouldn't create() be called
878 // before unlocking lock_scope?
886 // Validate this file if it's not empty
888 // When move/copy to a file, we want to make sure the first
889 // five characters are "From "
893 pread(_fd, (void *)inbuf, 5, 0);
895 lseek(_fd, (off_t) 0L, SEEK_SET);
896 if(-1 == read(_fd, (void *)inbuf, 5)) {
897 error.setError(DTME_NotMailBox);
900 lseek(_fd, (off_t) 0L, SEEK_SET);
904 if (strcmp(inbuf, "From ") != 0) {
905 error.setError(DTME_NotMailBox);
914 _object_valid->setTrue(); // New mail watcher starts now.
941 RFCMailBox::messageCount(DtMailEnv & error)
947 if (_object_valid->state() <= 0) {
948 error.setError(DTME_ObjectInvalid);
952 return(_msg_list.length());
954 #endif /* DEAD_WOOD */
957 RFCMailBox::getFirstMessageSummary(DtMailEnv & error,
958 const DtMailHeaderRequest & request,
959 DtMailHeaderLine & summary)
965 if (_object_valid->state() <= 0) {
966 error.setError(DTME_ObjectInvalid);
970 int slot = nextNotDel(0);
972 if (slot >= _msg_list.length()) {
976 makeHeaderLine(error, 0, request, summary);
978 return(_msg_list[0]);
982 RFCMailBox::getNextMessageSummary(DtMailEnv & error,
983 DtMailMessageHandle last,
984 const DtMailHeaderRequest & request,
985 DtMailHeaderLine & summary)
987 if (_object_valid->state() <= 0) {
988 error.setError(DTME_ObjectInvalid);
992 // Let's treat a null last as the start of the list.
995 return(getFirstMessageSummary(error, request, summary));
1000 int slot = _msg_list.indexof((MessageCache *)last);
1008 slot = nextNotDel(slot);
1010 if (slot >= _msg_list.length()) {
1014 makeHeaderLine(error, slot, request, summary);
1016 return(_msg_list[slot]);
1020 RFCMailBox::getMessageSummary(DtMailEnv & error,
1021 DtMailMessageHandle handle,
1022 const DtMailHeaderRequest & request,
1023 DtMailHeaderLine & summary)
1025 MutexLock lock_map(_map_lock);
1027 if (_object_valid->state() <= 0) {
1028 error.setError(DTME_ObjectInvalid);
1034 int slot = _msg_list.indexof((MessageCache *)handle);
1035 if (slot < 0 || slot >= _msg_list.length()) {
1036 error.setError(DTME_ObjectInvalid);
1040 makeHeaderLine(error, slot, request, summary);
1046 RFCMailBox::clearMessageSummary(DtMailHeaderLine & headers)
1048 if (NULL != headers.header_values)
1049 delete []headers.header_values;
1053 RFCMailBox::getMessage(DtMailEnv & error, DtMailMessageHandle hnd)
1055 if (_object_valid->state() <= 0) {
1056 error.setError(DTME_ObjectInvalid);
1062 int slot = _msg_list.indexof((MessageCache *)hnd);
1064 error.setError(DTME_ObjectInvalid);
1068 MessageCache * mc = _msg_list[slot];
1069 return(mc->message);
1073 RFCMailBox::getFirstMessage(DtMailEnv & error)
1079 if (_object_valid->state() <= 0) {
1080 error.setError(DTME_ObjectInvalid);
1084 int slot = nextNotDel(0);
1086 if (slot >= _msg_list.length()) {
1090 MessageCache * mc = _msg_list[slot];
1091 return(mc->message);
1095 RFCMailBox::getNextMessage(DtMailEnv & error,
1096 DtMail::Message * last)
1100 int slot = lookupByMsg((RFCMessage *)last);
1108 slot = nextNotDel(slot);
1110 if (slot >= _msg_list.length()) {
1114 MessageCache * mc = _msg_list[slot];
1115 return(mc->message);
1119 RFCMailBox::copyMessage(DtMailEnv & error,
1120 DtMail::Message * msg)
1122 #if defined(DEBUG_RFCMailBox)
1123 char *pname = "RFCMailBox::copyMessage";
1126 if (_object_valid->state() <= 0) {
1127 error.setError(DTME_ObjectInvalid);
1133 // The following is a hack for PAR0.5. In the future, we will use
1134 // this test for an optimization, but right now, we can only copy
1137 const char * msg_impl = msg->impl(error);
1138 if (strcmp(msg_impl, "Internet MIME") != 0 &&
1139 strcmp(msg_impl, "Sun Mail Tool") != 0) {
1140 error.setError(DTME_NotSupported);
1144 RFCMessage * rfc_msg = (RFCMessage *)msg;
1146 // We need to protect the file from access. Locking this will also
1147 // block any attempts by the new mail thread.
1151 DEBUG_PRINTF( ("%s: locking mailbox\n", pname) );
1153 if (error.isSet()) {
1154 DtMailEnv tmp_error;
1155 unlockFile(tmp_error, _fd);
1161 off_t end = lseek(_fd, 0, SEEK_END);
1162 status = SafeWrite(_fd, rfc_msg->_msg_start,
1163 rfc_msg->_msg_end - rfc_msg->_msg_start + 1);
1165 // We are going to put this at the real end of the file. We don't
1166 // really care what the current thought size is because new mail
1167 // will take care of that problem.
1170 // Add a new-line at the end.
1171 // It serves to distinguish separate messages.
1173 SafeWrite(_fd, "\n", 1);
1175 DEBUG_PRINTF( ("%s: unlocking mailbox\n", pname) );
1176 unlockFile(error, _fd);
1179 error.setError(DTME_ObjectCreationFailed);
1183 RFCMailBox::copyMailBox(DtMailEnv & error, DtMail::MailBox * mbox)
1187 for (DtMail::Message * msg = mbox->getFirstMessage(error);
1188 msg && error.isNotSet();
1189 msg = mbox->getNextMessage(error, msg)) {
1190 copyMessage(error, msg);
1191 if (error.isSet()) {
1198 RFCMailBox::checkForMail(
1200 const DtMailBoolean already_locked
1205 if (_space != DtMailFileObject) {
1206 error.setError(DTME_NotSupported);
1210 NewMailEvent(already_locked);
1214 RFCMailBox::expunge(DtMailEnv & error)
1218 for (int msg = 0; msg < _msg_list.length(); msg++) {
1219 MessageCache * mc = _msg_list[msg];
1220 if (mc->delete_pending == DTM_FALSE) {
1221 DtMail::Envelope * env = mc->message->getEnvelope(error);
1223 DtMailValueSeq value;
1224 env->getHeader(error, RFCDeleteHeader, DTM_FALSE, value);
1225 if (!error.isSet()) {
1226 mc->delete_pending = DTM_TRUE;
1235 writeMailBox(error, DTM_FALSE);
1236 if ((DTMailError_t)error ==
1237 DTME_CannotWriteToTemporaryMailboxFile_NoFreeSpaceLeft)
1239 // Need to do some thing here don't know now.
1243 // THE MAILBOX COULD NOT BE WRITTEN!! CALLER MUST DO SOMETHING
1246 "RFCMailBox::expunge(): Failed to write mailbox: %s",
1247 (const char *) error
1253 RFCMailBox::impl(DtMailEnv & error)
1260 RFCMailBox::markDirty(const int delta)
1264 // Make sure we really are dirty.
1270 RFCMailBox::callCallback(DtMailCallbackOp op, void * arg)
1272 if (_object_signature != RFCSignature || !_object_valid->state()) {
1277 if (_space == DtMailFileObject) {
1278 path = (char *)_arg;
1284 if (NULL != _callback)
1285 _callback(op, path, NULL, _cb_data, arg);
1291 RFCMailBox::newMessage(DtMailEnv & error)
1293 // RFC does not support a straightforward concept of adding
1294 // a message to a mailbox. We implement move/copy with a sort
1295 // of kludge, but we can only extend the kludge so far.
1297 error.setError(DTME_NotSupported);
1302 RFCMailBox::openRealFile(DtMailEnv & error, int open_mode, mode_t create_mode)
1304 // We first want to check the access modes we have on the
1305 // file. If we can write the file, then we will need to
1306 // root out the real path just to make sure we don't munge
1307 // a link someplace.
1310 char * path = _session->expandPath(error, (char *)_arg);
1311 if (error.isSet()) {
1312 return; // could not expand path
1315 if ((open_mode & O_RDWR) == O_RDONLY) {
1316 _real_path = strdup(path);
1317 SafeStat(_real_path, &buf);
1320 _real_path = (char *)malloc(MAXPATHLEN);
1321 char *link_path = new char[MAXPATHLEN];
1322 strcpy(link_path, path);
1323 strcpy(_real_path, path);
1325 if (SafeLStat(link_path, &buf) == 0 && (open_mode & O_CREAT) == 0) {
1326 while(S_ISLNK(buf.st_mode)) {
1327 int size = readlink(link_path, _real_path, sizeof(link_path));
1329 error.setError(DTME_NoSuchFile);
1331 _real_path = (char *)0;
1334 delete [] link_path;
1338 _real_path[size] = 0;
1339 if (_real_path[0] == '/') {
1340 strcpy(link_path, _real_path);
1343 char * last_slash = strrchr(link_path, '/');
1345 strcpy(last_slash + 1, _real_path);
1348 strcpy(link_path, "./");
1349 strcat(link_path, _real_path);
1353 strcpy(_real_path, link_path);
1355 if (SafeLStat(link_path, &buf)) {
1356 error.setError(DTME_NoSuchFile);
1358 _real_path = (char *)0;
1361 delete [] link_path;
1367 if ((open_mode & O_CREAT) == 0) {
1368 error.setError(DTME_NoSuchFile);
1370 _real_path = (char *)0;
1373 delete [] link_path;
1377 delete [] link_path;
1382 // We should now have a path we can open or create.
1383 // We must now make sure that if the file is being created that
1384 // it is created with the correct permissions and group owner.
1386 // If the mailbox to be created is NOT the system mailbox for this
1387 // user (e.g. the one that official delivery agents use), then the
1388 // permissions for the file should only let the current user have access.
1390 // If the mailbox to be created IS the system mailbox for this user,
1391 // and MAILGROUP_REQUIRED is defined, we must allow group read/write
1392 // and make sure the mailbox is owned by the correct group
1394 int requiresMailGroupCreation = 0;
1398 // delivery agent runs as specific non-root/wheel group
1399 requiresMailGroupCreation = isMailGroupSystemMailbox(_real_path);
1400 if ( (open_mode & O_CREAT) && requiresMailGroupCreation) {
1401 oldUmask = umask(DTMAIL_DEFAULT_CREATE_UMASK_MAILGROUP);
1402 create_mode = DTMAIL_DEFAULT_CREATE_MODE_MAILGROUP;
1405 oldUmask = umask(DTMAIL_DEFAULT_CREATE_UMASK);
1408 // We have 2 choices for locking an RFC file. The first is
1409 // to use the ToolTalk file scoping paradigm, and the second
1410 // is the normal lockf protocol. If we are using ToolTalk,
1411 // we need to initialize the ToolTalk locking object.
1413 // Unfortunately lockf() on an NFS mounted file system will
1414 // upset mmap(). A quick hack is to not use lockf() on any
1415 // remotely mounted file. Since mailx doesn't do tooltalk
1416 // locking now, it is possible to corrupt the spool file when
1417 // using mailx while mailtool has a NFS mounted mail file loaded.
1419 // We should find a solution where mailtool and mailx work
1422 // open the file before we obtain the lock can cause sync problem
1423 // if another owns the lock and has modified the mailbox
1425 DTMBX_LONGLOCK answer = DTMBX_LONGLOCK_SUCCESSFUL;
1427 if (_lock_flag == DTM_TRUE) {
1428 answer = longLock(error);
1429 if (answer == DTMBX_LONGLOCK_FAILED_CANCEL) {
1431 _real_path = (char *)0;
1435 // we have obtained the lock, go ahead and open the mailbox
1436 // Do not open the mailbox with privileges enabled, even if
1437 // creating the file
1439 PRIV_ENABLED_OPEN(_real_path, _fd,
1440 SafeOpen(_real_path, open_mode, create_mode));
1445 PRIV_ENABLED_OPEN(_real_path, _fd,
1446 SafeOpen(_real_path, open_mode, create_mode));
1449 #if !defined(SENDMAIL_LOCKS)
1450 // On some systems, sendmail uses lockf while delivering mail.
1451 // We can not hold a lockf style lock on those systems. Of course
1452 // if the user has turned off ToolTalk locking, they are playing
1455 if ( (_fd >= 0) && ( (open_mode & O_RDWR) == O_RDWR) )
1457 enum DtmFileLocality location;
1460 // Only attempt lockf() if file was opened for reading and writing
1462 location = DtMail::DetermineFileLocality(_real_path);
1463 #if defined(DEBUG_RFCMailBox)
1464 char *locstr = (location==Dtm_FL_UNKNOWN) ?
1466 ((location==Dtm_FL_LOCAL) ? "Local" : "Remote");
1467 DEBUG_PRINTF(("openRealFile: location is %s\n", locstr));
1473 case Dtm_FL_UNKNOWN:
1475 // locality unknown -- assume local and lock
1479 // locality local - apply lock
1481 assert(_lockf_active == DTM_FALSE);
1482 lseek(_fd, 0, SEEK_SET);
1483 PRIV_ENABLED_OPTIONAL(return_status,SafeLockf(_fd, F_TLOCK, 0));
1484 if (return_status == -1) {
1485 error.setError(DTME_OtherOwnsWrite);
1489 _lockf_active = DTM_TRUE;
1494 // locality otherwise -- assume remote dont lock
1503 (void) umask(oldUmask);
1508 switch (saveErrno) {
1510 error.setError(DTME_NoPermission);
1514 error.setError(DTME_IsDirectory);
1518 error.setError(DTME_NoSuchFile);
1522 error.setError(DTME_ObjectAccessFailed);
1526 _real_path = (char *)0;
1531 if ((open_mode & O_CREAT) && requiresMailGroupCreation) {
1533 // Make sure a newly created file has the correct group owner i.d.
1535 gid_t groupId = GetIdForGroupName(DTMAIL_DEFAULT_CREATE_MAILGROUP);
1537 (void) SafeFchown(_fd, (unsigned int) -1, groupId);
1540 if ((open_mode & 0x3) == O_RDWR) {
1541 if (answer == DTMBX_LONGLOCK_SUCCESSFUL ||
1542 answer == DTMBX_LONGLOCK_FAILED_READWRITE)
1543 _mail_box_writable = DTM_TRUE;
1545 _mail_box_writable = DTM_FALSE;
1548 _mail_box_writable = DTM_FALSE;
1552 _file_size = lseek(_fd, 0, SEEK_END);
1553 lseek(_fd, 0, SEEK_SET);
1554 _links = buf.st_nlink;
1556 _lockFileName = generateLockFileName();
1557 assert(_lockFileName != NULL);
1562 RFCMailBox::realFileSize(DtMailEnv & error, struct stat * stat_buf)
1566 // We have to deal with the problem of editors that will unlink
1567 // the file we are watching and rename it to a new file. This
1568 // will cause us to miss new mail, and eventually write data that
1569 // is out of sync with the real state of the file.
1571 // We do this by checking the link count first. If it has changed,
1572 // we will close, reopen, and then stat. Note that we could say if
1573 // the link count has gone down we will do this, but since we can't
1574 // be sure exactly what has happened to this file, we will consider
1575 // any change in the link count to require us to reopen the file.
1580 error_code = SafeFStat(_fd, &buf);
1581 if (error_code >= 0) {
1582 if (buf.st_nlink != _links) {
1585 DEBUG_PRINTF( ("realFileSize: (buf.st_nlink!=_links\n") );
1586 old_mode = fcntl(_fd, F_GETFL) & O_ACCMODE;
1589 PRIV_ENABLED_OPEN(_real_path,
1591 SafeOpen(_real_path, old_mode, 0666));
1593 error.setError(DTME_ObjectAccessFailed);
1597 transferLock(_fd, fd);
1601 if (SafeFStat(_fd, &buf) >= 0) {
1602 _links = buf.st_nlink;
1605 error.logError(DTM_FALSE,
1606 "realFileSize(): fstat(%d/%s) failed errno=%d\n",
1607 _fd, _real_path, errno);
1608 error.setError(DTME_ObjectAccessFailed);
1618 error.logError(DTM_FALSE,
1619 "realFileSize(): fstat(%d/%s) failed errno=%d\n",
1620 _fd, _real_path, errno);
1621 error.setError(DTME_ObjectAccessFailed);
1625 return(buf.st_size);
1629 RFCMailBox::mapFile(DtMailEnv & error,
1630 const DtMailBoolean already_locked,
1633 char *pname = "mapFile";
1636 if (already_locked == DTM_FALSE) {
1637 DEBUG_PRINTF( ("%s: locking mailbox\n", pname) );
1639 if (error.isSet()) {
1640 DtMailEnv tmp_error;
1641 unlockFile(tmp_error, _fd);
1646 // We must map in whole pages. This is done by rounding the
1647 // file size up to the next larger size.
1650 // Some notes on the singing and dancing that follows are in order.
1651 // As of 6/21/95 it has been determined that there is a bug in S494-17
1652 // and S495-25 whereby a mmap() of the mailbox can return sections that
1653 // have "nulls" where valid data should be found. To guard against this,
1654 // we check for nulls at the beginning and the end of a new mmap()ed region
1655 // and if they are found we retry the operation.
1657 // If NFS attribute caching is enabled (which is the default), a
1658 // stat/fstat of a NFS file may not return the correct true size of the
1659 // mailbox if it has been appended to since the last time it was
1660 // mmap()ed. To get around this problem, once it is noticed via
1661 // stat()/fstat() that the mailbox has changed, we must open the mailbox
1662 // on a separate file descriptor, read() in a byte, and then do a fstat()
1663 // to determine the true correct size of the mailbox.
1666 // fstat() currently open mailbox
1668 struct stat tempStatbuf;
1671 if (SafeFStat(_fd, &tempStatbuf) < 0) {
1675 "%s(%d): fstat(%d/%s) failed errno=%d\n",
1676 pname, err_phase, _fd, _real_path, errno);
1677 if (already_locked == DTM_FALSE) {
1678 DEBUG_PRINTF( ("%s: unlocking mailbox\n", pname) );
1679 unlockFile(error, _fd);
1681 error.setError(DTME_ObjectAccessFailed);
1685 // Obtain guaranteed current stat buf for mailbox object,
1686 // regarless of whether it is local or remote
1688 struct stat statbuf;
1691 if (SafeGuaranteedStat(_real_path, &statbuf) == -1) {
1695 "%s(%d): SafeGuaranteedStat(%s) failed errno=%d\n",
1696 pname, err_phase, _real_path, errno);
1697 if (already_locked == DTM_FALSE) {
1698 DEBUG_PRINTF( ("%s: unlocking mailbox\n", pname) );
1699 unlockFile(error, _fd);
1701 error.setError(DTME_ObjectAccessFailed);
1706 // statbuf -- contains the guaranteed stat struct for the mailbox
1707 // tempStatbuf -- contains fstat stat struct for original mailbox
1710 // See if the inode has changed - if so the file has been
1711 // modified out from under is
1714 if (statbuf.st_ino != tempStatbuf.st_ino) {
1716 error.logError(DTM_FALSE,
1717 "%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",
1718 pname, err_phase, _fd, _real_path, _real_path,
1719 statbuf.st_ino, statbuf.st_dev,
1720 statbuf.st_nlink, statbuf.st_size,
1721 tempStatbuf.st_ino, tempStatbuf.st_dev,
1722 tempStatbuf.st_nlink, tempStatbuf.st_size);
1723 if (already_locked == DTM_FALSE) {
1724 DEBUG_PRINTF( ("%s: unlocking mailbox\n", pname) );
1725 unlockFile(error, _fd);
1727 error.setError(DTME_ObjectInvalid);
1731 long pagesize = memoryPageSize();
1733 // We will only map any file space we haven't mapped so far.
1734 // We always map entire pages to make this easier. We must
1735 // remap the partial pages so we will get the real bits, not
1736 // the zero fill bits provided by mmap.
1738 // The arithmetic for partial mappings is a little odd, and
1739 // is worthy of explanation. The main issue is that we must
1740 // always start on a page boundary, and we must map page
1741 // size chunks. So, for any offset, we need to back up to
1742 // the start of a page in the file. This gives us the following
1743 // entries in MapRegion:
1745 // map_region - Address where the mapping starts.
1746 // map_size - Size of the mapping (will always be a multiple of pagesize).
1747 // file_region - Address where requested file offset begins within
1749 // file_size - Size of file within this mapping.
1750 // offset - Where the mapping begins (which is almost always different
1751 // than the offset where file_region begins.
1753 // So, the new offset begins where the last real file size ended.
1754 // We get this by adding the previous offset, to the file size mapped,
1755 // and then add any the difference between the mapped region and the
1756 // real file offset. This gets us an offset back to the old end of
1757 // file. Now if you are not confused, we need to adjust this new offset
1758 // back to the closest page boundary and begin the mapping from there!
1760 // File by messages:
1761 // v--------v------------v----v---------------------------v----------------v
1762 // | Msg1 | Msg2 |Msg3| Msg4 | Msg5 |
1763 // ^--------^------------^----^---------------------------^----------------^
1765 // +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
1766 // | Pg1 | Pg2 | Pg3 | Pg4 | Pg5 | Pg6 | Pg7 | Pg8 | Pg9 | Pg10| Pg11| Pg12|
1767 // +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
1772 // +--v--+-----+---v-+
1773 // | \\\\\\\\\\\\\\ |
1774 // +--^--+-----+---^-+
1785 // |->| offset_from_map
1798 // |---------------->| .offset
1800 // |-->| offset_from_map
1804 MapRegion * map = new MapRegion;
1805 long offset_from_map;
1807 if (_mappings.length()) {
1808 MapRegion * prev_map = _mappings[_mappings.length() - 1];
1809 map->offset = prev_map->offset + prev_map->file_size +
1810 (prev_map->file_region - prev_map->map_region);
1811 offset_from_map = map->offset % pagesize;
1812 map->offset -= offset_from_map;
1816 offset_from_map = 0;
1819 map->file_size = statbuf.st_size - map->offset - offset_from_map;
1820 map->map_size = statbuf.st_size - map->offset;
1821 map->map_size += (pagesize - (map->map_size % pagesize));
1823 int flags = MAP_PRIVATE;
1825 #if defined(MAP_NORESERVE)
1826 // We are not supposed to be writing to these pages. If
1827 // we don't specify MAP_NORESERVE however, the system will
1828 // reserve swap space equal to the file size to deal with
1829 // potential writes. This is wasteful to say the least.
1831 flags |= MAP_NORESERVE;
1834 // Need to obtain an mmap()ed region and one way or another
1835 // have the data from the mail file placed into that
1836 // region. There are two ways of accomplishing this:
1838 // Method 1: mmap() the data directly from the file
1839 // Method 2: mmap() /dev/zero and then read from the file to the region
1841 // We want to use method #1 if at all possible as it allows the
1842 // VM system to page the data in as it is accessed.
1844 // There is a potential problem with method #1 in that since
1845 // the region is a "view" into the real file data, if the file
1846 // itself is reduced in size behind our back by another
1847 // process, we have the potential for generating SIGBUS memory
1848 // reference errors if we try and access a byte that is no
1849 // longer within the file. This is true even is MAP_PRIVATE is used:
1851 // ** The behavior of PROT_WRITE can be influenced by setting
1852 // ** MAP_PRIVATE in the flags parameter. MAP_PRIVATE means
1853 // ** "Changes are private".
1855 // ** MAP_SHARED and MAP_PRIVATE describe the disposition of write
1856 // ** references to the memory object. If MAP_SHARED is
1857 // ** specified, write references will change the memory object.
1858 // ** If MAP_PRIVATE is specified, the initial write reference
1859 // ** will create a private copy of the memory object page and
1860 // ** redirect the mapping to the copy. Either MAP_SHARED or
1861 // ** MAP_PRIVATE must be specified, but not both. The mapping
1862 // ** type is retained across a fork(2).
1864 // ** Note that the private copy is not created until the first
1865 // ** write; until then, other users who have the object mapped
1866 // ** MAP_SHARED can change the object.
1868 // While this is always the case for a process that does not
1869 // abide by the advisory only locking protocols used to protect
1870 // against this, to guard against this method #1 is only used
1871 // if the mailbox is writable and we can obtain a short term
1872 // lock on the mailbox. This prevents the mailbox from changing
1873 // size at the moment we map it. Nonetheless, a SIGBUS
1874 // interrupt handler must be armed to handle the case where the
1875 // mailbox file is changed out from under us by devious means.
1877 // If this condition is not met, or the mmap() call for some
1878 // reason fails, then fall back to method #2.
1881 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";
1883 map->map_region = (char *)-1;
1885 if ( (_use_dot_lock == DTM_TRUE)
1886 && (_mail_box_writable == DTM_TRUE)
1887 #if defined(__osf__)
1888 && (_long_lock_active == DTM_TRUE)
1892 #if defined(__osf__) && OSMAJORVERSION < 4
1893 // This version of mmap does NOT allow requested length to be
1894 // greater than the file size ... in contradiction to the
1895 // documentation (don't round up).
1896 map->map_region = (char *) mmap(
1897 0, (statbuf.st_size - map->offset),
1898 PROT_READ, flags, _fd, (off_t) map->offset);
1900 map->map_region = (char *)mmap(
1901 0, (unsigned int) map->map_size,
1902 PROT_READ, flags, _fd, (off_t) map->offset);
1905 // ERROR/CONSISTENCY CHECKING: if the region was not mapped,
1906 // or the first or last byte of the region is a null, then
1907 // the mmap() is considered to have failed: write an entry
1908 // to the dump file, undo the damage, and bail to the caller
1911 if ( (map->map_region == (char *) -1) ||
1912 (map->file_size && (map->map_region[offset_from_map] == '\0')) ||
1914 (map->map_region[offset_from_map+map->file_size-1] == '\0'))
1918 ("mapFile: Error mmap(1) == %p, errno = %d\n", map->map_region, errno));
1924 map->map_size, PROT_READ, flags, _fd, _real_path, map->offset,
1925 map->map_region, errno);
1927 "%s(%d): statbuf: ino=%d, dev=%d, nlink=%d, size=%ld\n",
1929 statbuf.st_ino, statbuf.st_dev, statbuf.st_nlink, statbuf.st_size);
1931 if (map->map_region == (char *) -1)
1935 "%s(%d): mmap failed: errno = %d: %s\n",
1936 pname, err_phase, errno, strerror(errno));
1943 for (i=(int)offset_from_map, cnt=0;
1944 i<map->file_size+offset_from_map;
1946 if (map->map_region[i] == '\0') cnt++;
1949 "%s(%d): mmap failed: %d NULLs in map from byte %d to %d:\n",
1951 cnt, offset_from_map, map->file_size+offset_from_map);
1955 // Pass throught to attempt mapping through /dev/zero.
1957 munmap(map->map_region, (size_t) map->map_size);
1958 map->map_region = (char*) -1;
1960 if (_errorLogging) {
1964 (unsigned char *)map->map_region,
1966 _errorLogging ? 0 : 100);
1969 "Offset Region mapped in",
1970 (unsigned char *)map->map_region+offset_from_map,
1972 _errorLogging ? 0 : 100);
1975 if (already_locked == DTM_FALSE) {
1976 DEBUG_PRINTF( ("%s: unlocking mailbox\n", pname) );
1977 unlockFile(error, _fd);
1980 map->map_region != (char *)-1 ?
1981 DTME_ObjectAccessFailed :
1982 DTME_ObjectCreationFailed);
1990 ("mapFile: mmap(1) okay = %p, errno = %d\n", map->map_region, errno) );
1993 // If a region was mapped, cause OS to use sequential access paging rules
1995 if (map->map_region != (char *) -1)
1996 alterPageMappingAdvice(map);
1998 if (map->map_region == (char *) -1) {
1999 // Either the direct mmap failed, or we decided not to do a direct mmap
2000 // of the new data in the mailbox file. Now must create a mmap()ed
2001 // region against /dev/zero and then read the new data into that region
2003 char *devzero = "/dev/zero";
2005 #if defined(DO_ANONYMOUS_MAP)
2007 mode_t mode = create_mode;
2008 flags |= MAP_ANONYMOUS;
2010 int fd = SafeOpen(devzero, O_RDWR, create_mode);
2013 map->map_region = (char *)mmap(
2014 0, (unsigned int) map->map_size,
2015 PROT_READ|PROT_WRITE, flags, fd, 0);
2018 if (map->map_region == (char *)-1) {
2025 map->map_size, PROT_READ|PROT_WRITE, flags, fd, devzero, errno);
2029 map->map_size, PROT_READ|PROT_WRITE, flags, fd, devzero, errno);
2031 if (already_locked == DTM_FALSE) {
2032 DEBUG_PRINTF( ("%s: unlocking mailbox\n", pname) );
2033 unlockFile(error, _fd);
2035 error.setError(DTME_NoMemory);
2037 #if !defined(DO_ANONYMOUS_MAP)
2043 if (fd != -1 && (SafeClose(fd) < 0)) {
2044 // should deal with the error here (on /dev/zero??)
2047 // Have created a sufficiently large region mapped to /dev/zero
2048 // Read the "new bytes" into this region
2049 // Note that there is a suspicion that a window exists where even
2050 // though the file size appears to be increased by "n", that all
2051 // of the data may not be written in the file yet, and a read may
2052 // fall "short" in this case, especially if running over nfs. We
2053 // must be prepared to handle any condition whereby a read from
2054 // the file either yields less data than expected, or even though
2055 // a good return is received, the data may not be there, and thus
2056 // the check for "nulls" at the beginning and end of the buffer.
2059 // Copying messages between mailboxes often leaves a single
2060 // NULL character at the end of the file.
2061 // Check the last 2 bytes for NULL characters.
2064 lseek(_fd, (off_t) map->offset, SEEK_SET);
2065 size_t bytesToRead = (size_t)(statbuf.st_size - map->offset);
2066 ssize_t readResults = SafeRead(_fd, map->map_region, bytesToRead);
2067 if ( (readResults != bytesToRead) ||
2068 (readResults && (map->map_region[0] == '\0')) ||
2069 (readResults && (map->map_region[readResults-1] == '\0')) ||
2070 (readResults && (map->map_region[offset_from_map] == '\0')) ||
2072 (map->map_region[offset_from_map+map->file_size-1] == '\0'))
2077 "%s(%d): SafeRead(%d(%s), 0x%08lx, %d) == %d, errno == %d\n",
2078 pname, err_phase, _fd, _real_path, map->map_region, bytesToRead,
2079 readResults, errno);
2081 "%s(%d): stat buf: ino=%d, dev=%d, nlink=%d, size=%ld\n",
2082 pname, err_phase, statbuf.st_ino, statbuf.st_dev,
2083 statbuf.st_nlink, statbuf.st_size);
2085 if (readResults > 0) {
2090 (unsigned char *)map->map_region, readResults,
2091 _errorLogging ? 0 : 100);
2094 munmap(map->map_region, (size_t) map->map_size);
2096 if (already_locked == DTM_FALSE) {
2097 DEBUG_PRINTF( ("%s: unlocking mailbox\n", pname) );
2098 unlockFile(error, _fd);
2103 DTME_ObjectAccessFailed :
2104 DTME_ObjectCreationFailed);
2108 mprotect(map->map_region, (size_t) map->map_size, PROT_READ);
2109 alterPageMappingAdvice(map);
2112 map->file_region = map->map_region + offset_from_map;
2114 // Ok, we think we have got all of the new data that has been
2115 // appended to the mailbox - just to make absolutely sure, stat
2116 // the file again and make sure that the file size has remained
2117 // consistent throughout this operation. If it has changed, it
2118 // means some process ignored our lock on the file and appended
2119 // data anyway. In this case, throw away our current effort and
2120 // recursively call this function to try the attempt again.
2124 if (SafeGuaranteedStat(_real_path, &tempStatbuf) < 0) {
2128 "%s(%d): SafeGuaranteedStat(%s) failed errno=%d: %s\n",
2129 pname, err_phase, _real_path, errno, strerror(errno));
2130 munmap(map->map_region, (size_t) map->map_size);
2132 if (already_locked == DTM_FALSE) {
2133 DEBUG_PRINTF( ("%s: unlocking mailbox\n", pname) );
2134 unlockFile(error, _fd);
2136 error.setError(DTME_ObjectAccessFailed);
2141 if (tempStatbuf.st_size != statbuf.st_size) {
2145 "%s(%d): fstat(%d/%s) size changed %d/%d\n",
2147 _fd, _real_path, statbuf.st_size, tempStatbuf.st_size);
2148 munmap(map->map_region, (size_t) map->map_size);
2150 int mapResults = mapFile(error, DTM_TRUE);
2151 if (error.isSet()) {
2152 if (already_locked == DTM_FALSE) {
2153 DEBUG_PRINTF( ("%s: unlocking mailbox\n", pname) );
2154 unlockFile(error, _fd);
2161 if (already_locked == DTM_FALSE) {
2162 DEBUG_PRINTF( ("%s: unlocking mailbox\n", pname) );
2163 unlockFile(error, _fd);
2166 // We need to set _file_size here, because if we get the file size
2167 // when the mailbox is not locked, it is possible that we could be
2168 // stating the file while it is being written to. Since we have the
2169 // lock in this routine, we know the file size isn't changing, and it
2170 // is consistent with the amount being mapped in.
2172 _file_size = statbuf.st_size;
2174 if (_hide_access_events) mailboxAccessHide("mapFile");
2176 return(_mappings.append(map));
2180 RFCMailBox::nextNotDel(const int cur)
2183 for (del = cur; del < _msg_list.length(); del++) {
2184 MessageCache * mc = _msg_list[del];
2185 if (mc->delete_pending == DTM_FALSE) {
2194 RFCMailBox::prevNotDel(const int cur)
2197 for (del = cur; del >= 0; del--) {
2198 MessageCache * mc = _msg_list[del];
2199 if (mc->delete_pending == DTM_FALSE) {
2208 RFCMailBox::lookupByMsg(RFCMessage * msg)
2210 for (int slot = 0; slot < _msg_list.length(); slot++) {
2211 MessageCache * mc = _msg_list[slot];
2212 if (mc->message == msg) {
2221 RFCMailBox::ThreadParseEntry(void * client_data)
2223 RFCMailBox * self = (RFCMailBox *)client_data;
2226 MutexLock lock_map(self->_map_lock);
2228 self->parseFile(error, 0);
2237 RFCMailBox::parseFile(DtMailEnv & error, int map_slot)
2242 // We are not at the eof.
2246 const char * begin = _mappings[map_slot]->file_region;
2247 const char * end = begin + _mappings[map_slot]->file_size - 1;
2250 // Empty file. DO NOTHING OR IT WILL CRASH!
2256 // Parsing will always be a sequential access to the pages.
2257 // We will give the kernel a clue what we are up to and perhaps
2258 // help our parsing time in the process.
2260 unsigned long pagelimit = _mappings[map_slot]->map_size;
2262 #if !defined(linux) && !defined(sun)
2263 // no madvise; dont use optimization
2265 (char *)_mappings[map_slot]->map_region,
2266 (size_t) pagelimit, MADV_SEQUENTIAL);
2269 // We should always begin with a "From " if this is an RFC file,
2270 // with a valid offset. If we have something else, then look
2271 // forward until we find one.
2273 const char * parse_loc = begin;
2274 if (strncmp(parse_loc, "From ", 5)) {
2275 if (*parse_loc == ' ' || *parse_loc == '\n' || *parse_loc == '\t') {
2276 // We allow any number of white spaces before "From"
2277 // But "From" still has to be the first word in the line
2278 while ( (*parse_loc == ' ' || *parse_loc == '\n'
2279 || *parse_loc == '\t') && parse_loc <= end) {
2283 if (parse_loc >= end) {
2284 error.setError(DTME_NotMailBox);
2285 _at_eof.setTrue(); // We are, but so what.
2291 if (strncmp(parse_loc, "\nFrom ", 6)) {
2292 error.setError(DTME_NotMailBox);
2296 // This file does not start with either From or white space
2297 error.setError(DTME_NotMailBox);
2302 if (*parse_loc == '\n') {
2306 // We are sitting at the start of a message. We will build message
2307 // objects for each message in the list.
2310 MessageCache * cache = new MessageCache;
2311 cache->delete_pending = DTM_FALSE;
2313 cache->message = new RFCMessage(error, this, &parse_loc,
2315 if (error.isNotSet()) {
2317 //#ifdef MESSAGE_PARTIAL
2318 // message/partial processing is currently not working, so we are
2319 // taking out message/parital processing for now until we figure
2320 // out how to get it to work again. Only the following block of
2321 // code needs to be taken out in order to disable message/partial.
2322 // Also, when it was turned on, it was trying to combine partial
2323 // messages that were non-MIME (no MIME-VERSION header). This
2324 // caused even more problems. We should only check for partial
2325 // messages if it is MIME.
2327 if (_isPartial(error, cache->message)) {
2329 if (error.isNotSet()) {
2330 cache->message->setFlag(error, DtMailMessagePartial);
2332 if (error.isNotSet()) {
2333 cache->message = _assemblePartial(error, cache->message);
2335 if (error.isNotSet()) {
2336 _msg_list.append(cache);
2342 //#endif // MESSAGE_PARTIAL
2343 _msg_list.append(cache);
2348 } while (parse_loc <= end);
2350 // At this point we most likely will see random behavior. We will
2351 // tell the kernel to pull in the minimum number of extra pages.
2353 #if !defined(linux) && !defined(sun)
2354 // no madvise; dont use optimization
2356 (char *)_mappings[map_slot]->map_region,
2357 (size_t) pagelimit, MADV_RANDOM);
2359 // IBM code for message/partial vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
2360 // we need delete those messages if they are satisfied the
2361 // following two conditions
2362 // (1) marked for delete and
2363 // (2) it is a message/partial message
2365 // Otherwise, we will get segmentation error when the method
2366 // writeMailBox is invoked because a new message were
2367 // generated by assembling partial messages
2369 for (int msg = 0; msg < _msg_list.length(); msg++) {
2370 MessageCache * mc = _msg_list[msg];
2371 if (mc->delete_pending == DTM_FALSE) {
2372 DtMail::Envelope * env = mc->message->getEnvelope(error);
2374 DtMailValueSeq value;
2376 static const char * partial = "message/partial";
2377 static const char * contentType = "content-type";
2379 env->getHeader(error, contentType , DTM_FALSE, value);
2380 if (error.isNotSet()) {
2381 type = strdup(*(value[0]));
2387 if (error.isNotSet()) {
2388 env->getHeader(error, RFCDeleteHeader, DTM_FALSE, value);
2390 if (!error.isSet() && (strncasecmp(type, partial, 15) == 0)) {
2391 delete mc->message; // remove message storage
2392 delete mc; // remove message cache storage
2393 _msg_list.remove(msg); // remove message from message list
2394 msg -= 1; // next message is where we are at now
2405 //IBM code for message/partial ^^^^^^^^^^^^^^^^^^^^^^^^^^^
2412 RFCMailBox::ThreadNewMailEntry(void * client_data)
2414 NewMailData * info = (NewMailData *)client_data;
2416 // We need to wait for the object to become valid. We have
2417 // nothing to do until the mail box is open.
2418 info->object_valid->waitTrue();
2420 // Get the various configuration parameters from .mailrc. The
2421 // RFC_PING_INTERVAL controls how often we look for new mail.
2422 // RFC_CHECK is the number of pings between checks and
2423 // RFC_EXPUNGE is the number of pings between expunges.
2426 DtMail::MailRc * mailrc = info->self->session()->mailRc(error);
2430 time_t check_per_ping = 120;
2431 time_t expunge_per_ping = 240;
2433 const char * value = NULL;
2434 mailrc->getValue(error, "RFC_PING_INTERVAL", &value);
2435 if (error.isNotSet()) {
2436 ping = (time_t) strtol(value, NULL, 10);
2442 free((void*) value);
2445 mailrc->getValue(error, "RFC_CHECK", &value);
2446 if (error.isNotSet()) {
2447 check_per_ping = (time_t) strtol(value, NULL, 10);
2453 free((void*) value);
2456 mailrc->getValue(error, "RFC_EXPUNGE", &value);
2457 if (error.isNotSet()) {
2458 expunge_per_ping = (time_t) strtol(value, NULL, 10);
2464 free((void*) value);
2468 unsigned int unslept = 0;
2470 // Wait until the mail file is parsed.
2472 info->self->_at_eof.waitTrue();
2474 // We loop until the object tries to close, then we exit.
2476 while(info->object_valid->state()) {
2478 // The following sequence is a little weird, but here is why.
2479 // We need to sleep for the ping interval. We can be awaken
2480 // early however if the thread catches a signal. The main
2481 // thread will send a SIGTERM from the mail box destructor
2482 // to wake us up. If the object state is no longer valid, then
2483 // this is the cause, so we need to check it first and exit
2484 // if this is the case.
2486 // We can also be awaken by other signals that we don't care
2487 // about. In that case we need to go back through the loop
2488 // again and sleep any unslept time. We can't be sure however
2489 // that this additional sleep won't be awaken before it is
2490 // done so we have to keep looping and checking the object
2494 unslept = (unsigned int) ThreadSleep(unslept);
2495 continue; // We got rudely awaken!
2498 unslept = (unsigned int) ThreadSleep(ping);
2499 if (!info->object_valid->state()) {
2507 // We made here, we slept and are well rested, and
2508 // we should have a valid object. Run the events.
2513 if (check_per_ping && (pinged % check_per_ping) == 0) {
2515 // info->self->CheckPointEvent(error);
2516 info->self->CheckPointEvent();
2517 // if (error.isSet()) {
2518 // // MAILBOX COULD NOT BE CHECKPOINTED!!! MUST DO SOMETHING!!!
2523 if (expunge_per_ping && (pinged % expunge_per_ping) == 0) {
2524 info->self->ExpungeEvent();
2528 info->self->NewMailEvent();
2531 // We are responsible for cleaning up the condition variable.
2533 delete info->object_valid;
2543 RFCMailBox::PollEntry(void * client_data)
2545 RFCMailBox * self = (RFCMailBox *)client_data;
2547 // Get the various configuration parameters from .mailrc. The
2548 // RFC_PING_INTERVAL controls how often we look for new mail.
2549 // RFC_CHECK is the number of pings between checks and
2550 // RFC_EXPUNGE is the number of pings between expunges.
2555 DtMail::MailRc * mailrc = self->session()->mailRc(error);
2556 error.clear(); // IGNORING ERRORS FROM MAILRC CALL!!
2559 time_t ping = 60; // check for new mail every 60 seconds
2560 time_t save_interval = (30 * 60); // autosave every 30 minutes
2561 long minimumIdleTime = 30; // must have 30 seconds idle time to poll
2563 // Retrieve current setting for how often we are supposed to
2564 // look for new mail (retrieveinterval)
2569 mailrc->getValue(error, "retrieveinterval", &value);
2570 if (error.isNotSet() && value != NULL && *value != '\0')
2572 ping = (time_t) strtol(value, NULL, 10);
2579 if (NULL != value) free((void*) value);
2581 // Retrieve current setting for how often we are supposed to
2582 // perform an auto save of the mailbox
2585 mailrc->getValue(error, "dontautosave", &value);
2586 if (error.isSet()) {
2589 free((void*) value);
2592 mailrc->getValue(error, "saveinterval", &value);
2593 if (error.isNotSet() && value != NULL && *value != '\0') {
2594 save_interval = (time_t) strtol(value, NULL, 10) * 60;
2597 if (save_interval < 60)
2598 //save_interval = 60; // The minimum time is every minute
2599 save_interval = 15; // The minimum time is every minute
2601 else save_interval = -1;
2603 free((void*) value);
2605 // Get the current time in seconds, and compute how long it has
2606 // been since the last interactive input (keystroke, buttonpress)
2607 // was done by the user
2609 time_t now = time(NULL);
2610 time_t lastInteractive =
2611 (time_t) self->session()->lastInteractiveEventTime();
2612 time_t interactiveIdleTime = (now - lastInteractive);
2614 // If there has not been at least <<inactivityinterval> seconds of
2615 // interactive inactivity, skip processing until later so as not to
2616 // place the user in the horrible position of being "locked out" for
2617 // the duration of an auto save/and/or/new mail incorporation
2621 mailrc->getValue(error, "inactivityinterval", &value);
2622 if (error.isNotSet() && value != NULL && *value != '\0') {
2623 minimumIdleTime = strtol(value, NULL, 10);
2624 if (minimumIdleTime < 15)
2625 minimumIdleTime = 15; // at least 15 seconds must go by
2626 else if (minimumIdleTime > 600)
2627 minimumIdleTime = 600; // but not more than 10 minutes
2631 free((void*) value);
2633 if (interactiveIdleTime < minimumIdleTime)
2636 // See if time's up for doing a new mail incorporate
2638 if (ping > 0 && (now - self->_last_poll > ping))
2640 self->_last_poll = now;
2641 self->NewMailEvent();
2644 // See if time's up for doing an auto-save.
2645 // If time's up, check to see if the flag is clear for doing one.
2646 // Flag is not clear if say, the user is in the middle of composing
2649 if (save_interval >= 0 &&
2650 ((now - self->_last_check) > save_interval) &&
2651 (self->session()->getAutoSaveFlag())) {
2653 self->_last_check = (int) now;
2656 // self->CheckPointEvent(error);
2657 self->CheckPointEvent();
2658 // if (error.isSet()) {
2659 // // MAILBOX COULD NOT BE CHECKPOINTED!!! MUST DO SOMETHING!!!
2666 RFCMailBox::NewMailEvent(
2667 const DtMailBoolean already_locked
2670 DtMailEnv error, error1;
2672 struct stat tempStatbuf;
2673 struct stat statbuf;
2674 DtMailEventPacket event;
2675 DtMailCallbackOp op;
2677 if (!_object_valid->state()) return;
2678 if (!_mr_allowed) return;
2680 _session->setBusyState(error1, DtMailBusyState_NewMail);
2682 op = retrieveNewMail(error);
2685 const char *errmsg = NULL;
2686 errmsg = (const char *) error;
2689 event.target = DTM_TARGET_MAILBOX;
2690 event.target_object = this;
2691 event.operation = (void*) op;
2693 event.argument = strdup(errmsg);
2695 event.argument = NULL;
2696 event.event_time = time(NULL);
2698 // longUnlock(error);
2699 _session->writeEventData(error, &event, sizeof(event));
2700 _session->setBusyState(error1, DtMailBusyState_NotBusy);
2704 if ((SafeFStat(_fd, &tempStatbuf) < 0) ||
2705 (SafeGuaranteedStat(_real_path, &statbuf) == -1))
2708 _mail_box_writable = DTM_FALSE;
2711 event.target = DTM_TARGET_MAILBOX;
2712 event.target_object = this;
2713 event.operation = (void *)DTMC_ACCESSFAILED;
2714 event.argument = NULL;
2715 event.event_time = time(NULL);
2717 error.setError(DTME_ObjectAccessFailed);
2718 _session->writeEventData(error, &event, sizeof(event));
2719 _session->setBusyState(error1, DtMailBusyState_NotBusy);
2723 // See if the inode has changed - if so the file has been
2724 // modified out from under is
2726 if (statbuf.st_ino != tempStatbuf.st_ino)
2729 _mail_box_writable = DTM_FALSE;
2732 event.target = DTM_TARGET_MAILBOX;
2733 event.target_object = this;
2734 event.operation = (void *)DTMC_INODECHANGED;
2735 event.argument = NULL;
2736 event.event_time = time(NULL);
2738 error.setError(DTME_MailboxInodeChanged);
2739 _session->writeEventData(error, &event, sizeof(event));
2740 _session->setBusyState(error1, DtMailBusyState_NotBusy);
2744 // We need to compare the current file size to the last size we
2745 // knew about. If it has changed, we need to do something. There
2746 // are a few possibilities here that we must deal with:
2748 // 1. The file size has not changed. Boring. Simply return.
2750 // 2. The file size is larger, and the first byte begins with
2751 // either "From ", "\nFrom ", or "\r\nFrom ". In this case,
2752 // we need to parse the rest of the file, and notify the client
2753 // that new mail has arrived.
2755 // 3. The file size is either smaller, or it is larger and the first
2756 // few bytes dont match #2. This is the worse possible case. This
2757 // means that somebody has modified the object from beneath us.
2758 // We will turn on our "lost state" mode and refuse to do any
2759 // further processing. This thread exits immediately.
2761 // Even though we get the file size here, we can't use it to set the
2762 // new _file_size, because the file isn't currently locked, so another
2763 // process could be writing to it at this time. We rely on mapFile()
2764 // to get and set _file_size while it has the file locked.
2766 off_t size = realFileSize(error, &info);
2767 if (error.isSet()) {
2769 _mail_box_writable = DTM_FALSE;
2772 event.target = DTM_TARGET_MAILBOX;
2773 event.target_object = this;
2774 event.operation = (void *)DTMC_ACCESSFAILED;
2775 event.argument = NULL;
2776 event.event_time = time(NULL);
2778 _session->writeEventData(error, &event, sizeof(event));
2779 _session->setBusyState(error1, DtMailBusyState_NotBusy);
2783 MutexLock lock_object(_obj_mutex);
2785 if (size == _file_size)
2787 _session->setBusyState(error1, DtMailBusyState_NotBusy);
2788 if (_hide_access_events!=DTM_TRUE && info.st_atime<=info.st_mtime)
2789 mailboxAccessShow(info.st_mtime, "NewMailEvent: file_size unchanged");
2793 else if (size > _file_size)
2795 incorporate(error, already_locked);
2796 if (_hide_access_events!=DTM_TRUE && info.st_atime<=info.st_mtime)
2797 mailboxAccessShow(info.st_mtime, "NewMailEvent: file_size grew");
2802 _mail_box_writable = DTM_FALSE;
2803 *_object_valid = -1;
2806 event.target = DTM_TARGET_MAILBOX;
2807 event.target_object = this;
2808 event.operation = (void *)DTMC_BADSTATE;
2809 event.argument = NULL;
2810 event.event_time = time(NULL);
2812 _session->writeEventData(error, &event, sizeof(event));
2815 char *strbuf = new char[128];
2817 "NewMailEvent: File shrank %ld<%ld: already_locked is %s\n",
2818 (unsigned long)size, (unsigned long)_file_size,
2819 already_locked ? "TRUE" : "FALSE");
2824 // We need to bail right now if we have lost a valid mapping.
2826 if (_object_valid->state() < 0) {
2827 lock_object.unlock();
2831 _session->setBusyState(error1, DtMailBusyState_NotBusy);
2836 //RFCMailBox::CheckPointEvent(DtMailEnv & error)
2837 RFCMailBox::CheckPointEvent()
2839 if (!_object_valid->state() || _dirty == 0) {
2843 // Write the mail box out.
2848 // This is INCORRECT usage of the DtMailEnv class. It is against
2849 // standard policy to not clear the error token before passing
2850 // it to a function. We are basically breaking the error handling
2851 // model in order to percolate an error in the BE up to the FE.
2853 // setBusyState() does not modify the error token, it simply
2854 // uses it to report errors back to the user.
2855 _session->setBusyState(error, DtMailBusyState_AutoSave);
2857 writeMailBox(error, _hide_access_events);
2859 if ((DTMailError_t) error ==
2860 DTME_CannotWriteToTemporaryMailboxFile_NoFreeSpaceLeft)
2864 startAutoSave(error1,DTM_FALSE);
2865 showError((char *) error.getClient());
2866 error.setClient(NULL);
2867 startAutoSave(error1,DTM_TRUE);
2870 if (error.isSet()) {
2871 // MAILBOX COULD NOT BE CHECKPOINTED!!! setBusyState must handle error.
2872 error.logError(DTM_TRUE, "RFCMailBox::CheckPointEvent(): Failed to write mailbox: %s", (const char *)error);
2874 _session->setBusyState(error, DtMailBusyState_NotBusy);
2878 RFCMailBox::ExpungeEvent(DtMailBoolean closing)
2880 if (!_object_valid->state() && closing == DTM_FALSE) {
2884 // We need to get the expiration time for a message.
2888 DtMail::MailRc * mailrc = _session->mailRc(error);
2891 const char * expire_time = NULL;
2892 mailrc->getValue(error, "DTMAIL_EXPIRE_TIME", &expire_time);
2893 if (error.isNotSet()) {
2894 expire_days = strtol(expire_time, NULL, 10);
2900 if (NULL != expire_time)
2901 free((void*) expire_time);
2903 if (expire_days == 0 && closing == DTM_FALSE) {
2904 // Only scan on close. No timed delete in effect.
2909 if (expire_days > 0 && closing == DTM_TRUE) {
2910 // Timed delete is in effect so we don't destroy messages on
2915 time_t expire_secs = (time_t) expire_days * (24 * 3600);
2916 time_t now = time(NULL);
2918 for (int msg = 0; msg < _msg_list.length(); msg++) {
2919 MessageCache * mc = _msg_list[msg];
2920 if (mc->delete_pending == DTM_TRUE) {
2921 // Can only delete it so many times!
2926 DtMail::Envelope * env = mc->message->getEnvelope(error);
2928 DtMailValueSeq value;
2929 env->getHeader(error, RFCDeleteHeader, DTM_FALSE, value);
2930 if (!error.isSet()) {
2932 deleted = (time_t) strtol(*(value[0]), NULL, 16);
2933 if ((deleted + expire_secs) < now) {
2934 mc->delete_pending = DTM_TRUE;
2937 // We need to tell our client this message is really
2938 // gone. Of course, if they are closing, why bother
2941 if (closing == DTM_FALSE) {
2942 DtMailEventPacket event;
2944 event.target = DTM_TARGET_MAILBOX;
2945 event.target_object = this;
2946 event.operation = (void *)DTMC_DELETEMSG;
2947 event.argument = mc;
2948 event.event_time = time(NULL);
2949 _session->writeEventData(error, &event, sizeof(event));
2959 // Function: RFCMailBox::createTemporaryMailboxFile
2961 // Given the name for a temporary mailbox file, create a proper
2962 // temporary mailbox file and return a file descriptor opened on
2963 // the newly created file
2965 // . obtain information on current mailbox file
2966 // . create the new temporary file
2967 // . set the permissions, owner and group of the newly created file
2968 // to match those of the current mailbox
2970 // DtMailEnv & error - standard error structure used by caller
2971 // tmp_name -- -> name for temporary file
2973 // error.isset() will indicate if there were any errors encountered, in which
2974 // case the temporary file has not been created.
2976 // int file descriptor opened on the newly created temporary mailbox file
2977 // if no error encountered.
2980 RFCMailBox::createTemporaryMailboxFile(DtMailEnv & error, char *tmp_name)
2985 if (SafeFStat(_fd, &info) < 0) {
2988 error.logError(DTM_FALSE,
2989 "createTemporaryMailboxFile(): fstat(%d) failed errno=%d\n",
2991 error.vSetError(DTME_CannotObtainInformationOnOpenMailboxFile,
2992 DTM_FALSE, NULL, _real_path, error.errnoMessage(errno2));
2996 PRIV_ENABLED(fd,SafeOpen(tmp_name, O_RDWR | O_CREAT | O_TRUNC, info.st_mode));
3001 error.setError(DTME_CannotCreateTemporaryMailboxFile_NoPermission);
3005 error.setError(DTME_CannotCreateTemporaryMailboxFile_IsDirectory);
3009 error.setError(DTME_CannotCreateTemporaryMailboxFile_NoSuchFile);
3012 #if defined(__osf__) || defined(CSRG_BASED)
3017 error.setError(DTME_CannotCreateTemporaryMailboxFile_RemoteAccessLost);
3021 error.vSetError(DTME_CannotCreateTemporaryMailboxFile,
3022 DTM_FALSE, NULL, tmp_name, error.errnoMessage(errno2));
3027 PRIV_ENABLED_OPTIONAL(return_status,SafeFChmod(fd, info.st_mode & 07777));
3028 if (return_status == -1) {
3030 (void) SafeClose(fd);
3031 PRIV_ENABLED(return_status,SafeUnlink(tmp_name));
3032 error.vSetError(DTME_CannotSetPermissionsOfTemporaryMailboxFile,
3033 DTM_FALSE, NULL, tmp_name, info.st_mode & 07777,
3034 error.errnoMessage(errno2));
3038 PRIV_ENABLED(return_status, SafeFchown(fd, info.st_uid, (unsigned int) -1));
3040 // bug 1216914 - dtmail should be able to auto-save a mailbox which is not
3041 // opened [Read Only] if the user has write access to the original mailbox,
3042 // then the user should be able to overwrite the mailbox even if the owner
3043 // gets changes. A mailbox is only opened read-write if the "access()"
3044 // system call indicates the user has write permissions.
3046 if (return_status == -1) {
3048 (void) SafeClose(fd);
3049 PRIV_ENABLED(return_status,SafeUnlink(tmp_name));
3050 error.vSetError(DTME_CannotSetOwnerOfTemporaryMailboxFile,
3051 DTM_FALSE, NULL, tmp_name, info.st_uid,
3052 error.errnoMessage(errno2));
3057 PRIV_ENABLED(return_status, SafeFchown(fd, (unsigned int) -1, info.st_gid));
3061 // Function: RFCMailBox::writeMailBox - create new copy of complete mailbox
3063 // writeMailBox causes a complete copy of the current mailbox to be written
3064 // out to permanent storage. This is done by creating a temporary file, in
3065 // the same location the permanent file resides, and writing the new mailbox
3066 // contents to it. If this is successful, the temporary file is then renamed
3067 // over the old permanent file, taking its place. In this way, if there is
3068 // an error creating the new file, the old file still remains intact.
3070 // . make sure the current mailbox is writeable
3071 // . lock current mailbox
3072 // . check for new mail and incorporate if necessary
3073 // . cause the OS to begin mapping as much of the current mailbox into memory
3074 // as it can so it is available as soon as possible to be written
3075 // . create temporary file to hold new mailbox contents
3076 // . create message list fixing the location of all non-deleted messages
3077 // (dirty messages will be allocated temporary storage and created)
3078 // . build a vectored write array based upon the message list
3079 // . cause the new mailbox contents to be written via one call to SafeWritev
3080 // . delete all "deleted" messages from the message list
3081 // . map the new mailbox file into memory
3082 // . revise all message pointers based upon new mailbox contents
3083 // . remove the mappings and unmap all previous regions used
3084 // . add the one single new mailbox region to the mappings list
3085 // . transfer any lock on the old mailbox file to the new mailbox file
3086 // . rename the new mailbox file to the correct permanent path name
3087 // . set the modification and access times of the mailbox file properly
3089 // DtMailEnv & error - standard error structure used by caller
3091 // error.isset() will indicate if there were any errors encountered, in which
3092 // case the current contents of the mailbox have not been written.
3094 // At some point in the call chain, someone must check to see if this is
3095 // a FATAL ERROR (e.g. error.isFatal) and if so, display the returned
3096 // message and then immediately exit, as this means the mailbox could not
3097 // be written and the internal mailbox state is hopelessly munged..
3102 RFCMailBox::writeMailBox(DtMailEnv &error, DtMailBoolean hide_access)
3104 static char *pname = "writeMailBox";
3106 DtMailEnv error2; // When we need to preserve error during error cleanup
3107 int return_status; // for handling priv/unpriv operations
3108 #if defined(DEBUG_RFCMailBox)
3109 char *pname = "writeMailBox";
3112 MutexLock lock_map(_map_lock);
3115 if (_mail_box_writable == DTM_FALSE) {
3119 // Need to lock the file while we do this.
3121 DEBUG_PRINTF( ("%s: locking mailbox\n", pname) );
3123 if (error.isSet()) {
3124 DtMailEnv tmp_error;
3125 unlockFile(tmp_error, _fd);
3129 // Need to deal with any potentially new mail that
3130 // has arrived and we don't know about.
3132 if (_mra_server == NULL) checkForMail(error, DTM_TRUE);
3133 if (error.isSet()) {
3134 DEBUG_PRINTF( ("%s: locking mailbox\n", pname) );
3135 unlockFile(error2, _fd);
3139 // Create the new temporary file to hold the mailbox contents
3141 static char tmp_name[MAXPATHLEN+1];
3142 sprintf(tmp_name, "%s.tmp.%08lx", _real_path, (long)time(NULL));
3143 assert(strlen(tmp_name)<sizeof(tmp_name));
3145 int fd = createTemporaryMailboxFile(error, tmp_name);
3146 if (error.isSet()) {
3147 DEBUG_PRINTF( ("%s: locking mailbox\n", pname) );
3148 unlockFile(error2, _fd);
3153 // Got the new mailbox file all set up to be our friend.
3154 // zip through current message structure fixing the location of all
3155 // non-deleted messages in preparation of writing them all out to
3156 // the temporary file just opened. The act of "fixing" the location of
3157 // a message will cause dirty messages to be constructed in temporary
3158 // allocated areas which must later be deallocated. This deallocation
3159 // is done either by calling adjustMessageLocation() to make the location
3160 // of the message permanent, or by calling unfixMessageLocation) to
3161 // abandon the new message (e.g. if new mailbox cannot be created).
3163 int deletesPending = 0; // count # of delete_pending messages scanned
3166 struct tempMsgList {
3167 MessageCache *tmlMc; // -> mc of this message
3168 long tmlRealOffset; // byte offset from 0 where message is written
3169 long tmlBodyOffset; // byte offset from 0 of body parts of message
3170 char *tmlHeaderStart; // -> start of headers for this message
3171 long tmlHeaderLen; // length of headers
3172 char *tmlBodyStart; // -> start of bodies for this message
3173 long tmlBodyLen; // length of bodies
3174 int tmlTemporary; // ==0:permanent, !=0:temporary(must move)
3177 long tmlTotalSize = _msg_list.length()*4;
3178 tempMsgList *const tmlFirst = (tempMsgList*)
3179 malloc((size_t) (sizeof(tempMsgList)*tmlTotalSize));
3180 tempMsgList *tmlLast = tmlFirst;
3182 for (msg = 0; msg < _msg_list.length(); msg++) {
3183 MessageCache *mc = _msg_list[msg];
3184 if (mc->delete_pending == DTM_TRUE) { // ignore deleted messages
3185 deletesPending++; // but remember if any seen
3188 tmlLast->tmlMc = mc;
3189 mc->message->fixMessageLocation(&tmlLast->tmlHeaderStart, tmlLast->tmlHeaderLen,
3190 &tmlLast->tmlBodyStart, tmlLast->tmlBodyLen,
3191 tmlLast->tmlTemporary, tmlLast->tmlBodyOffset);
3193 fprintf(stdout, "msg %03d @ %08lx %08lx/%06d %08lx/%06d %04d %s\n",
3194 msg, mc, tmlLast->tmlHeaderStart, tmlLast->tmlHeaderLen,
3195 tmlLast->tmlBodyStart, tmlLast->tmlBodyLen,
3196 tmlLast->tmlBodyOffset, tmlLast->tmlTemporary ? "T" : "P");
3197 HexDump(stdout, "header", (unsigned char *)tmlLast->tmlHeaderStart, tmlLast->tmlHeaderLen, 10);
3198 HexDump(stdout, "body", (unsigned char *)tmlLast->tmlBodyStart, tmlLast->tmlBodyLen, 10);
3201 assert(tmlLast->tmlHeaderStart != NULL);
3202 assert(tmlLast->tmlHeaderLen);
3203 tmlLast->tmlRealOffset = -1;
3206 assert((tmlLast-tmlFirst) < tmlTotalSize);
3208 // we can now allocate the vectored write array and fill it according
3209 // to the data stored in the message list we just created
3211 long iovSize = ((tmlLast-tmlFirst)+2)*3;
3212 iovec *const iovFirst = (iovec *) malloc((size_t) (sizeof(iovec)*iovSize));
3213 iovec *iovLast = iovFirst;
3214 iovec *iovPrev = (iovec *)0;
3215 long iovCurrentOffset = 0;
3217 for (tempMsgList *tmlNdx = tmlFirst; tmlNdx < tmlLast; tmlNdx++) {
3218 // if this message happens to start on the first byte following the
3219 // last byte of the previous message, combine into a single vector,
3220 // else add this as another vector in the vector list
3222 tmlNdx->tmlRealOffset = iovCurrentOffset;
3223 if ( (iovPrev != (iovec *)0) && (!tmlNdx->tmlTemporary) &&
3224 ((char *)((size_t) iovPrev->iov_base+iovPrev->iov_len) ==
3225 tmlNdx->tmlHeaderStart)
3227 iovPrev->iov_len += (int)(tmlNdx->tmlHeaderLen+tmlNdx->tmlBodyLen);
3228 iovCurrentOffset += (int)(tmlNdx->tmlHeaderLen+tmlNdx->tmlBodyLen);
3230 else if (!tmlNdx->tmlTemporary) {
3231 iovLast->iov_base = (caddr_t)tmlNdx->tmlHeaderStart;
3232 iovLast->iov_len = (int)(tmlNdx->tmlHeaderLen+tmlNdx->tmlBodyLen);
3233 iovCurrentOffset += iovLast->iov_len;
3234 iovPrev = iovLast++;
3237 // Message is temporary - headers and bodies are in different areas,
3238 // and the headers are in a "temporary" area
3240 iovLast->iov_base = (caddr_t)tmlNdx->tmlHeaderStart;
3241 iovLast->iov_len = (int)tmlNdx->tmlHeaderLen;
3242 iovCurrentOffset += iovLast->iov_len;
3243 iovPrev = iovLast++;
3245 // Write out bodies only if the length is non-zero, otherwise,
3246 // optimize out the inclusion of a zero length write from the vector
3248 if (tmlNdx->tmlBodyLen > 0) {
3249 iovLast->iov_base = (caddr_t)tmlNdx->tmlBodyStart;
3250 iovLast->iov_len = (int)tmlNdx->tmlBodyLen;
3251 iovCurrentOffset += iovLast->iov_len;
3252 iovPrev = iovLast++;
3255 // The last two bytes of the message must be \n\n
3256 // If not then this must be forced
3257 // Obtain pointer tp to the last byte of the current message
3258 // Given this pointer we now have:
3259 // tp[-1] -- next to last byte in current message
3260 // tp[ 0] -- last byte in current message
3261 // tp[+1] -- first byte of next message (if any)
3262 // There must always be two \n characters between each message. If not,
3263 // we must insert sufficient \n characters into the message stream to
3264 // accomplish this. We want to avoid plopping these in, however, as each
3265 // one will add an extra 1- or 2-byte vector into the vectored write array,
3266 // which will affect the throughput of the overall write.
3267 // Irregardless of whether the current message is temporary, we check to see if the next
3268 // byte or two (as necessary) is in a mapped region; if so, we can then
3269 // peek ahead to see if there are the additional \ns we need.
3270 // If all this fails, we punt and put in a small 1- or 2-byte write
3274 char *tp = (char *) ((size_t) iovPrev->iov_base + (iovPrev->iov_len - 1));
3277 if ( ((addressIsMapped(tp+1) == DTM_TRUE) )
3278 && (*(tp+1) == '\n') ) {
3284 iovLast->iov_base = (caddr_t)"\n"; // add \n
3285 iovLast->iov_len = 1;
3287 iovPrev = iovLast++;
3291 if ( (( (addressIsMapped(tp+1) == DTM_TRUE)
3292 && (addressIsMapped(tp+2) == DTM_TRUE)))
3293 && (*(tp+1) == '\n') && (*(tp+2) == '\n')) {
3294 iovPrev->iov_len+= 2;
3295 iovCurrentOffset+= 2;
3299 iovLast->iov_base = (caddr_t)"\n\n"; // add \n\n
3300 iovLast->iov_len = 2;
3301 iovCurrentOffset += 2;
3302 iovPrev = iovLast++;
3307 assert((iovLast-iovFirst) < iovSize);
3309 // All of the messages are properly accounted for in the write vector;
3310 // cause the damage to be done by calling SafeWritev. After it returns,
3311 // Make absolutely sure that all of the mailbox data has made it to
3312 // the final destination, especially if the mailbox is not local -
3313 // this way if we run out of disk space or some other such problem,
3314 // it is caught here and now.
3316 unsigned long bytesWritten = SafeWritev(fd, iovFirst, iovLast-iovFirst);
3317 if (bytesWritten == (unsigned long)-1 || fsync(fd) == -1) {
3319 for (tempMsgList *tml = tmlFirst; tml < tmlLast; tml++) {
3320 MessageCache *mc = tml->tmlMc;
3322 mc->message->unfixMessageLocation(tml->tmlHeaderStart, tml->tmlTemporary);
3324 FileSystemSpace(tmp_name, 0,&fsname);
3325 (void) SafeClose(fd);
3326 DEBUG_PRINTF( ("%s: locking mailbox\n", pname) );
3327 unlockFile(error2, _fd);
3328 PRIV_ENABLED(return_status,SafeUnlink(tmp_name));
3334 DTME_CannotWriteToTemporaryMailboxFile_ProcessLimitsExceeded);
3337 #if defined(__osf__) || defined(CSRG_BASED)
3342 error.setError(DTME_CannotWriteToTemporaryMailboxFile_RemoteAccessLost);
3346 error.setClient(fsname);
3347 error.setError(DTME_CannotWriteToTemporaryMailboxFile_NoFreeSpaceLeft);
3351 error.vSetError(DTME_CannotWriteToTemporaryMailboxFile,
3352 DTM_FALSE, NULL, tmp_name, error.errnoMessage(errno2));
3357 // The current contents of the mailbox have successfully been written
3358 // to the temporary file. Cause the new mailbox file to be mapped
3361 MapRegion * map = mapNewRegion(error, fd, bytesWritten);
3362 if (error.isSet()) {
3363 for (tempMsgList *tml = tmlFirst; tml < tmlLast; tml++) {
3364 MessageCache *mc = tml->tmlMc;
3366 mc->message->unfixMessageLocation(tml->tmlHeaderStart, tml->tmlTemporary);
3368 (void) SafeClose(fd);
3369 DEBUG_PRINTF( ("%s: locking mailbox\n", pname) );
3370 unlockFile(error2, _fd);
3371 PRIV_ENABLED(return_status,SafeUnlink(tmp_name));
3377 // POINT OF NO RETURN -- NEW MAILBOX MUST BE SUCCESSFULLY LINKED UP WITH
3378 // BECAUSE THE MACHINATIONS AND POINTER MUNGING DONE BELOW CANNOT BE UNDONE
3381 // Flush all deleted messages (if any were previously detected)
3384 for (msg = 0; msg < _msg_list.length(); msg++) {
3385 MessageCache * mc = _msg_list[msg];
3386 if (mc->delete_pending == DTM_TRUE) {
3387 delete mc->message; // remove message storage
3388 delete mc; // remove message cache storage
3389 _msg_list.remove(msg); // remove message from message list
3390 msg -= 1; // next message is where we are at now
3394 // spin through all "written messages" and fixup their pointers so they
3395 // point into the new region
3398 // For this occasion advise the OS that we will be doing sequential access
3400 alterPageMappingAdvice(map);
3402 for (tempMsgList *tml = tmlFirst; tml < tmlLast; tml++) {
3403 MessageCache *mc = tml->tmlMc;
3405 assert(mc->delete_pending == DTM_FALSE);
3406 mc->message->adjustMessageLocation(tml->tmlHeaderStart, map->file_region+tml->tmlRealOffset, tml->tmlHeaderLen+tml->tmlBodyLen, tml->tmlTemporary, tml->tmlBodyOffset);
3409 // Loop through the current mappings, and unmap each.
3410 // Then make the new single large map the only mapping.
3412 while(_mappings.length()) {
3413 MapRegion * c_map = _mappings[0];
3414 munmap(c_map->map_region, (size_t) c_map->map_size);
3416 _mappings.remove(0);
3418 _mappings.append(map);
3420 // fix for cmvc defect 7912 - Queued mail lost upon closing dtmail
3421 // If we are using the .lock protocol, we are locking on a file name
3422 // basis, and therefore can rename the new mailbox over the old mailbox
3423 // without a worry about locks; however, if we are using another type
3424 // of locking which locks on a *file* basis, then as soon as the rename
3425 // is done if there is not a lock on the file a process like sendmail
3426 // could come in and complicate matters. After being properly locked,
3427 // rename the new mailbox file over the old mailbox file, and then
3428 // remove the old lock if applicable.
3430 lockNewMailboxFile(fd);
3431 PRIV_ENABLED(return_status,SafeRename(tmp_name, _real_path));
3432 if (return_status == -1) {
3433 // the rename failed -- we are in a world of hurt now.
3434 // We have successfully written the new mailbox out, unmapped the
3435 // old file, mapped in the new file, and bashed all of the various
3436 // pointers to point to the new mailbox; however, we cannot rename
3437 // the new mailbox over the old mailbox. We cannot continue, so return
3438 // this as a fatal error so that the caller can exit properly.
3440 error.vSetError(DTME_CannotRenameNewMailboxFileOverOld,
3441 DTM_TRUE, NULL, _real_path, tmp_name,
3442 error.errnoMessage());
3443 (void) SafeClose(fd);
3444 (void) SafeClose(_fd);
3445 return; // no complete cleanup necessary as we should exit real fast...
3448 assert(map->file_size == bytesWritten);
3450 DtMailBoolean file_grew;
3451 if (map->file_size > _file_size)
3452 file_grew = DTM_TRUE;
3454 file_grew = DTM_FALSE;
3456 _file_size = map->file_size;
3459 if (SafeFStat(fd, &info) < 0)
3462 error.logError(DTM_TRUE,
3463 "%s: fstat(%d/%s) failed errno=%d\n",
3464 pname, _fd, tmp_name, errno);
3465 error.setError(DTME_ObjectCreationFailed, DTM_TRUE, (Tt_message) 0);
3469 if (info.st_size != _file_size)
3471 error.logError(DTM_TRUE,
3472 "%s: new mailbox size not consistent with expected size = %d\nfstat: st_ino = %d, st_dev = %d, st_nlink = %d, st_size = %ld\n",
3473 pname, bytesWritten,
3474 info.st_ino, info.st_dev, info.st_nlink, info.st_size);
3475 error.setError(DTME_ObjectCreationFailed, DTM_TRUE, (Tt_message) 0);
3478 if (hide_access == DTM_FALSE && info.st_atime <= info.st_mtime)
3479 mailboxAccessShow(info.st_mtime, "writeMailBox");
3482 // order of unlocks is important here:
3483 // unlockOldMailboxFile checks the state of _long_lock_active
3484 // but does not alter it, whereas unlockFile does.
3486 unlockOldMailboxFile(_fd); // unlock old mailbox file first
3487 DEBUG_PRINTF( ("%s: locking mailbox\n", pname) );
3488 unlockFile(error2,fd); // then unlock new mailbox file
3489 if (SafeClose(_fd) < 0) {
3490 // should do something with the error here.
3493 _fd = fd; // new mailbox file now current one
3494 _dirty = 0; // mark mailbox as no longer dirty.
3503 RFCMailBox::incorporate(DtMailEnv & error, const DtMailBoolean already_locked)
3505 DtMailEventPacket event;
3507 if (already_locked == DTM_FALSE) {
3508 MutexLock lock_map(_map_lock);
3511 int slot = mapFile(error, already_locked);
3512 if (error.isSet()) {
3513 if (DTME_ObjectInvalid == (DTMailError_t) error)
3516 _mail_box_writable = DTM_FALSE;
3519 event.target = DTM_TARGET_MAILBOX;
3520 event.target_object = this;
3521 event.operation = (void *)DTMC_INODECHANGED;
3522 event.argument = NULL;
3523 event.event_time = time(NULL);
3524 _session->writeEventData(error, &event, sizeof(event));
3526 else if (DTME_ObjectAccessFailed == (DTMailError_t) error)
3529 _mail_box_writable = DTM_FALSE;
3532 event.target = DTM_TARGET_MAILBOX;
3533 event.target_object = this;
3534 event.operation = (void *)DTMC_ACCESSFAILED;
3535 event.argument = NULL;
3536 event.event_time = time(NULL);
3537 _session->writeEventData(error, &event, sizeof(event));
3544 MapRegion * map = _mappings[slot];
3545 const char * buf = map->file_region;
3547 // Let's accept any white space as okay in front of the
3550 for (; buf < (map->map_region + map->map_size) &&
3551 isspace(*buf); buf++) {
3556 DtMailBoolean done = DTM_FALSE;
3558 while (num_tries < 5 && !done) {
3559 if (strncmp(buf, "From ", 5) == 0) {
3560 DtMailMessageHandle last = NULL;
3562 // We can be here via either of two scenarios:
3563 // 1- Aside from incorporating new mail, there is no other
3564 // activity on the mail box. This is the "normal" case.
3565 // already_locked is its default value (FALSE).
3567 // 2- We are here because we were doing a Destroy Deleted Messages
3568 // when we noticed new mail that had to be first incorporated.
3569 // already_locked is TRUE (i.e., the Expunge method has lock...)
3570 // We cannot place the handle of the last entity in the array in
3571 // the stream because the entity may be marked for delete, and
3572 // the Destroy Deleted Messages operation will expunge the entity
3573 // shortly after it has been placed on the stream for the FE.
3575 // In both cases, we want to get the last UNDELETED message and
3576 // place it on the stream for the FE. Undeleted entities will
3577 // be valid even after a DDM while deleted entities will be invalid
3580 // Place the handle of the last entity in the array in the
3581 // callback stream. The FE will get it and can invoke
3582 // getNextMessageSummary() on the mailbox using the handle to
3583 // retrieve the new messages.
3585 if (_msg_list.length() > 0) {
3587 if (already_locked) {
3588 // already_locked is TRUE only in one case:
3589 // We were trying to destroy deleted messages when we
3590 // noticed new mail has come in and we need to incorporate
3592 // This will return the index of the last undeleted
3593 // message. That entity, we are assured, will remain
3594 // valid after a DMM that may appear just before the event
3595 // is received by the FE's callback method.
3597 int index = prevNotDel(_msg_list.length() - 1);
3598 last = _msg_list[index];
3601 // already_locked is FALSE
3603 // No possiblity of entities in _msg_list being expunged.
3604 // Give the current last one to the FE and let it retrieve
3605 // new messages via the getNext() method on that entity.
3606 last = _msg_list[_msg_list.length() - 1];
3612 parseFile(error, slot);
3615 event.target = DTM_TARGET_MAILBOX;
3616 event.target_object = this;
3617 event.operation = (void *)DTMC_NEWMAIL;
3618 event.argument = last;
3619 event.event_time = time(NULL);
3620 _session->writeEventData(error, &event, sizeof(event));
3625 _mail_box_writable = DTM_FALSE;
3626 *_object_valid = -1;
3629 event.target = DTM_TARGET_MAILBOX;
3630 event.target_object = this;
3631 event.operation = (void *)DTMC_BADSTATE;
3632 event.argument = NULL;
3633 event.event_time = time(NULL);
3635 error.setError(DTME_ObjectInvalid);
3636 _session->writeEventData(error, &event, sizeof(event));
3639 char *strbuf = new char[256];
3640 if (already_locked) {
3643 "Attempt #%d: RFCMailBox::incorporate(): No From: buf=<%.10s>, already_locked is TRUE\n", num_tries + 1, buf);
3647 "Attempt #%d: RFCMailBox::incorporate(): No From: buf=<%.10s>, already_locked is FALSE\n", num_tries + 1, buf);
3660 RFCMailBox::generateLockFileName(void)
3663 char *lock_path = new char[MAXPATHLEN+20];
3665 assert(_real_path != NULL);
3666 (void) sprintf(lock_path, "%s.lock", _real_path);
3667 s = strdup(lock_path);
3668 delete [] lock_path;
3672 // Function: RFCMailBox::generateUniqueLockId - create unique ID for this mailbox lock files
3674 // generateUniqueLockId creates a unique ID which is written into .lock files and
3675 // can then be checked to make sure that the lock file has not been compromised by
3678 // The ID generated consists of three parts:
3679 // <process id/%08d><current time in seconds/%08d><hardware serial number/%d>
3680 // Thus, a "typical" id would look like this:
3681 // 000018577971028681915751068
3682 // Which breaks down as:
3683 // 00001857 797102868 1915751068
3689 // char * -> allocated memory in which unique id has been created
3692 RFCMailBox::generateUniqueLockId(void)
3695 char hwserialbuf[64];
3697 #if !defined(__aix) && !defined(__hpux) && !defined(__osf__) && !defined(linux) && !defined(CSRG_BASED)
3698 if (sysinfo(SI_HW_SERIAL, (char *)hwserialbuf, sizeof(hwserialbuf)-1) == -1)
3700 strcpy(hwserialbuf, "dtmail");
3701 (void) sprintf(theId, "%08ld%08ld%s\0", (long)getpid(), (long)time((time_t *)0), hwserialbuf);
3702 assert(strlen(theId)<sizeof(theId));
3703 return(strdup(theId));
3707 RFCMailBox::checkLockFileOwnership(DtMailEnv & error)
3709 char *pname = "checkLockFileOwnership";
3711 assert(_lockFileName != NULL);
3713 if (SafeStat(_lockFileName, &info) < 0) {
3715 error.logError(DTM_FALSE,
3716 "%s: lock cannot be stat()ed: %s, errno = %d\n",
3717 pname, _lockFileName, errno);
3718 error.setError(DTME_ObjectInvalid);
3726 flags = O_RDONLY | O_SYNC;
3728 flags = O_RDONLY | O_RSYNC | O_SYNC;
3729 #endif /* O_RSYNC */
3731 #ifdef MAILGROUP_REQUIRED
3732 PRIV_ENABLED_OPEN(_lockFileName, lock_fd, SafeOpen(_lockFileName, flags, 0));
3734 PRIV_ENABLED_OPTIONAL(lock_fd, SafeOpen(_lockFileName, flags, 0));
3737 if (lock_fd == -1) {
3741 "%s: lock cannot be open()ed: %s, errno = %d\n",
3742 pname, _lockFileName, errno);
3743 error.setError(DTME_ObjectInvalid);
3747 char lockBuf[MAXPATHLEN];
3748 int status = SafeRead(lock_fd, lockBuf, sizeof(lockBuf)-1);
3753 "%s: lock cannot be read: %s, errno = %d\n",
3754 pname, _lockFileName, errno);
3755 (void) SafeClose(lock_fd);
3756 error.setError(DTME_ObjectInvalid);
3760 if ( (status < _uniqueLockIdLength+2)
3761 || (strncmp(lockBuf+2, _uniqueLockId, _uniqueLockIdLength) != 0) ) {
3764 "%s: dtmail lock file stolen by another process\n", pname);
3768 "lock file stolen - lock file contents",
3769 (unsigned char *)lockBuf, status, 0);
3770 (void) SafeClose(lock_fd);
3771 error.setError(DTME_ObjectInvalid);
3774 (void) SafeClose(lock_fd);
3778 // Function: RFCMailBox::linkLockFile - create and link temporary lock file to real lock file
3780 // Create a lock file for the current mailbox. Return success of failure.
3782 // . create a temporary lock file with a unique signature id of this process
3783 // . link the temporary lock file to the real lock file
3784 // . if the link is not successful, remove the temporary lock file and return the
3785 // time() in seconds on the remote system of when the temporary lock file was created
3786 // . if the link is successful, remove the temporary lock file (link) and return 0.
3788 // error -- standard error structure used by caller
3790 // If error.isSet() it is a fatal error from which the caller should return to its caller,
3791 // return value will always be time(0)
3792 // If !error.isSet() then check results of return value
3794 // time_t == 0 : indicates that the real lock file has been created and we own it
3795 // != 0 : could not create real lock file, return value is the time *on the remote system*
3796 // that the temporary lock file was created with (from comparison with existing
3797 // lock file to see how old it is)
3800 RFCMailBox::linkLockFile(DtMailEnv & error, char *tempLockFileName)
3806 // Create the temporary lock file. Failure to do so indicates lack of write permission
3807 // in the directory or some other fatal error
3811 int flags = O_RDWR | O_CREAT | O_EXCL | O_SYNC;
3813 int flags = O_RDWR | O_CREAT | O_EXCL | O_SYNC | O_RSYNC;
3815 PRIV_ENABLED(lock_fd,SafeOpen(tempLockFileName, flags, 0666));
3817 // We are not able to create the temporary lock file.
3818 // We will have to punt on trying to lock here from now on.
3822 error.setError(DTME_CannotCreateMailboxLockFile_NoPermission);
3826 error.setError(DTME_CannotCreateMailboxLockFile_IsDirectory);
3830 error.setError(DTME_CannotCreateMailboxLockFile_NoSuchFile);
3833 #if defined(__osf__) || defined(CSRG_BASED)
3838 error.setError(DTME_CannotCreateMailboxLockFile_RemoteAccessLost);
3842 error.vSetError(DTME_CannotCreateMailboxLockFile,
3843 DTM_FALSE, NULL, tempLockFileName, error.errnoMessage());
3849 // Get creation time of temporary file *on remote system*
3851 if (SafeFStat(lock_fd, &sbuf) == -1) {
3853 error.logError(DTM_FALSE,
3854 "linkLockFile(): temporary lock file cannot be stat()ed: %s, errno = %d\n",
3855 tempLockFileName, errno);
3856 error.vSetError(DTME_CannotCreateMailboxLockFile,
3857 DTM_FALSE, NULL, tempLockFileName, error.errnoMessage());
3858 (void) SafeClose(lock_fd);
3862 // Write proper contents to lock file:
3863 // Write the string "0" into the lock file to give us some
3864 // interoperability with SVR4 mailers. SVR4 mailers expect
3865 // a process ID to be written into the lock file and then
3866 // use kill() to see if the process is alive or not. We write
3867 // 0 into it so that SVR4 mailers will always think our lock file
3868 // is valid. In addition we include a unique ID so we can verify
3869 // if the lock file is stolen out from under us.
3871 ssize_t writeResults;
3872 writeResults = SafeWrite(lock_fd, "0\0", 2);
3873 if (writeResults == 2)
3874 writeResults += SafeWrite(lock_fd, _uniqueLockId, _uniqueLockIdLength);
3875 if ( (writeResults != _uniqueLockIdLength+2) ){
3877 error.logError(DTM_FALSE,
3878 "linkLockFile(): write to temporary lock file failed: %s, errno = %d\n",
3879 tempLockFileName, errno);
3880 error.vSetError(DTME_CannotCreateMailboxLockFile,
3881 DTM_FALSE, NULL, tempLockFileName, error.errnoMessage());
3882 (void) SafeClose(lock_fd);
3886 // sync up the lock file with the ultimate storage device
3888 if (fsync(lock_fd) == -1) {
3890 error.logError(DTM_FALSE,
3891 "linkLockFile(): fsync to temporary lock file failed: %s, errno = %d\n",
3892 tempLockFileName, errno);
3893 error.vSetError(DTME_CannotCreateMailboxLockFile,
3894 DTM_FALSE, NULL, tempLockFileName, error.errnoMessage());
3895 (void) SafeClose(lock_fd);
3901 if (SafeClose(lock_fd) == -1) {
3905 "linkLockFile(): close of temporary lock file failed: %s, errno = %d\n",
3906 tempLockFileName, errno);
3907 error.vSetError(DTME_CannotCreateMailboxLockFile,
3908 DTM_FALSE, NULL, tempLockFileName, error.errnoMessage());
3912 // The temporary lock file has been created - now try and link it to the
3913 // real lock file. Failure here is not fatal as we will retry and possible
3914 // try and remove the real lock file later on.
3916 PRIV_ENABLED(return_status,SafeLink(tempLockFileName, _lockFileName));
3917 if (return_status == -1) {
3918 PRIV_ENABLED(return_status,SafeRemove(tempLockFileName));
3919 return(sbuf.st_ctime);
3922 // We successfully linked the temp lock file to the real lock file name
3923 // This means we have the dot lock for our process - remove the temporary lock
3924 // file name (link) and return
3926 PRIV_ENABLED(return_status,SafeRemove(tempLockFileName));
3931 RFCMailBox::lockFile(DtMailEnv & error)
3933 #if defined(DEBUG_RFCMailBox)
3934 char *pname = "RFCMailBox::lockFile";
3936 int return_status = 0;
3938 // We will create a simple lock file to keep the file from
3939 // changing while we are doing critical work.
3942 // On some platforms, sendmail will place a lockf lock on the
3943 // file during mail delivery. If this is the case, then we
3944 // need to make sure we have the lock here.
3946 #if defined(SENDMAIL_LOCKS)
3947 assert(_lockf_active == DTM_FALSE);
3948 lseek(_fd, 0, SEEK_SET);
3949 PRIV_ENABLED_OPTIONAL(return_status,SafeLockf(_fd, F_LOCK, 0));
3950 if (return_status != -1)
3952 _lockf_active = DTM_TRUE;
3953 DEBUG_PRINTF( ("%s: lockf succeeded\n", pname) );
3957 if (_use_dot_lock == DTM_FALSE) {
3958 DEBUG_PRINTF( ("%s: not using dot lock\n", pname) );
3962 // Implement the .lock short term lock protocol
3963 // This code was "adapted" from Solaris 2.5 (SunOS 5.5)
3964 // usr/src/cmd/mail/maillock.c.
3966 assert(_dot_lock_active == DTM_FALSE);
3968 // Create the temporary mail lock file name
3969 // It has the form <_lockfilename><XXXXXX> or mailbox.lockXXXXXX
3970 // mktemp then creates a unique temporary file for the template
3972 assert(_lockFileName != NULL);
3973 char *tempLockFileName = new char[MAXPATHLEN];
3974 sprintf(tempLockFileName, "%sXXXXXX", _real_path);
3975 mktemp(tempLockFileName);
3976 PRIV_ENABLED(return_status,SafeRemove(tempLockFileName));
3978 // loop through attempting to create the temporary lock file,
3979 // and if successful attempt to link the temporary lock file
3980 // to the real lock file. If not successful, retry for up to 5
3981 // minutes, and remove the current lock file if it is more than
3988 // Attempt to create a temporary file and link it to the intended lock file
3989 // If it is successful, we have the lock and can return.
3990 // If it is not successful and error.isSet then it is a non-recoverable
3992 // in which case the mailbox is deemed not-writable any more.
3993 // If it is not successful and !error.isSet then it is a recoverable error,
3994 // in which case we spin and try again according to the retry rules.
3996 time_t t = linkLockFile(error, tempLockFileName);
3997 if (error.isSet()) {
3998 // hard error? -- something is wrong, assume read/only
3999 _use_dot_lock = DTM_FALSE;
4000 _mail_box_writable = DTM_FALSE;
4001 (void) SafeRemove(tempLockFileName);
4003 ("%s: failed to link dot_lock file %s\n", pname, tempLockFileName) );
4004 delete [] tempLockFileName;
4008 checkLockFileOwnership(error);
4011 _dot_lock_active = DTM_TRUE;
4012 DEBUG_PRINTF( ("%s: succeeded acquiring dot_lock file\n", pname) );
4014 delete [] tempLockFileName;
4018 // Could not link the temporary lock file to the intended real lock file
4019 // See if the lock file exists and if so if we can remove it because it
4020 // is > 5 mins old. If the stat fails it means the lock file disappeared
4021 // between our attempt to link to it and now - only allow this to go on
4022 // so many times before punting
4024 if (SafeStat(_lockFileName, &sbuf) == -1) {
4025 if (statFailed++ > 5) {
4026 error.vSetError(DTME_CannotCreateMailboxLockFile,
4027 DTM_FALSE, NULL, _lockFileName, error.errnoMessage());
4028 delete [] tempLockFileName;
4035 // The lock file already exists - compare the time of the temp
4036 // file with the time of the lock file, rather than with the
4037 // current time of day, since the files may reside on another
4038 // machine whose time of day differs from the one this program
4039 // is running on. If the lock file is less than 5 minutes old,
4040 // keep trying, otherwise, remove the lock file and try again.
4043 if (t < (sbuf.st_ctime + 300)) {
4048 error.logError(DTM_FALSE,
4049 "lockFile(): removing stale lock file %s ctime %08ld temp lock %s ctime %08ld diff %08ld\n",
4050 _lockFileName, sbuf.st_ctime, tempLockFileName, t, t-sbuf.st_ctime);
4052 ("%s: giving up; removing dot_lock file %s\n", pname, _lockFileName));
4054 PRIV_ENABLED(return_status,SafeRemove(_lockFileName));
4055 if (return_status == -1) {
4056 // We were not able to unlink the file. This means that
4057 // we do not have write access to the directory. We will
4058 // have to pass on taking long locks.
4060 _use_dot_lock = DTM_FALSE;
4061 _mail_box_writable = DTM_FALSE;
4062 error.vSetError(DTME_CannotRemoveStaleMailboxLockFile,
4063 DTM_FALSE, NULL, _lockFileName, error.errnoMessage());
4064 delete [] tempLockFileName;
4068 delete [] tempLockFileName;
4071 // lockNewMailboxFile -- before renaming a new mailbox file over an old
4072 // mailbox file, we need to establish a lock on the new mailbox file is
4073 // a lock was established on the old mailbox file. Since the .lock protocol
4074 // works on a file name basis we dont have to worry here, but if this
4075 // system uses lockf() to lock the files we need to establish that lock
4076 // on the new file first before renaming it over the old mailbox file.
4079 RFCMailBox::lockNewMailboxFile(int new_fd)
4081 int return_status = 0;
4082 if (_lockf_active == DTM_TRUE) {
4083 lseek(new_fd, 0, SEEK_SET);
4084 PRIV_ENABLED_OPTIONAL(return_status,SafeLockf(new_fd, F_LOCK, 0));
4088 // unlockOldMailboxFile -- after renaming a new mailbox file over an old
4089 // mailbox file, if a lockf() style lock was established on the old mailbox
4090 // file, it needs to be removed
4093 RFCMailBox::unlockOldMailboxFile(int old_fd)
4095 int return_status = 0;
4096 if (_lockf_active == DTM_TRUE) {
4097 lseek(old_fd, 0, SEEK_SET);
4098 PRIV_ENABLED_OPTIONAL(return_status,SafeLockf(old_fd, F_ULOCK, 0));
4103 RFCMailBox::transferLock(int old_fd, int new_fd)
4105 int return_status = 0;
4106 if (_lockf_active == DTM_TRUE) {
4107 lseek(new_fd, 0, SEEK_SET);
4108 PRIV_ENABLED_OPTIONAL(return_status,SafeLockf(new_fd, F_LOCK, 0));
4110 lseek(old_fd, F_ULOCK, 0);
4111 PRIV_ENABLED_OPTIONAL(return_status,SafeLockf(old_fd, F_ULOCK, 0));
4116 RFCMailBox::unlockFile(DtMailEnv & error, int fd)
4118 #if defined(DEBUG_RFCMailBox)
4119 char *pname = "RFCMailBox::unlockFile";
4123 // We will create a simple lock file to keep the file from
4124 // changing while we are doing critical work.
4127 if (_use_dot_lock == DTM_TRUE) {
4128 assert(_dot_lock_active == DTM_TRUE);
4129 assert(_lockFileName != NULL);
4130 _dot_lock_active = DTM_FALSE;
4131 checkLockFileOwnership(error);
4132 if (!error.isSet()) {
4133 DEBUG_PRINTF(("%s: unlinking dot_lock file\n", pname, _lockFileName));
4135 PRIV_ENABLED(return_status,SafeUnlink(_lockFileName));
4136 if (return_status == -1) {
4137 error.vSetError(DTME_CannotRemoveMailboxLockFile,
4138 DTM_FALSE, NULL, _lockFileName, error.errnoMessage());
4143 #if defined(SENDMAIL_LOCKS)
4144 if (_lockf_active == DTM_TRUE) {
4145 DEBUG_PRINTF(("%s: removing lockf\n", pname));
4147 lseek(fd, 0, SEEK_SET);
4148 PRIV_ENABLED_OPTIONAL(return_status,SafeLockf(fd, F_ULOCK, 0));
4149 _lockf_active = DTM_FALSE;
4154 #define DOT_DTMAIL_SUFFIX "dtmail"
4156 RFCMailBox::dotDtmailLockFile(char *filename, int)
4159 // Attempt to link the temporary lock file to the real lock file.
4160 assert(_real_path != NULL);
4161 (void) sprintf(filename, "%s.%s", _real_path, DOT_DTMAIL_SUFFIX);
4166 RFCMailBox::dotDtmailLock(DtMailEnv & error)
4168 char lockBuf[MAXPATHLEN];
4169 char dotDtmailPath[MAXPATHLEN+1];
4170 char *tempLockFileName = new char[MAXPATHLEN];
4172 int return_status = 0;
4175 int flags = O_RDWR | O_CREAT | O_EXCL | O_SYNC;
4177 int flags = O_RDWR | O_CREAT | O_EXCL | O_SYNC | O_RSYNC;
4180 // We will create a .dtmail file to prevent conflicts between dtmail's
4181 // operating on the same mailbox.
4183 // Create the temporary mail lock file name.
4184 sprintf(tempLockFileName, "%sXXXXXX", _real_path);
4185 mktemp(tempLockFileName);
4186 PRIV_ENABLED(return_status,SafeRemove(tempLockFileName));
4188 // Attempt to create the temporary file.
4189 PRIV_ENABLED(lock_fd,SafeOpen(tempLockFileName, flags, 0666));
4193 error.setError(DTME_CannotCreateMailboxLockFile_NoPermission);
4196 error.setError(DTME_CannotCreateMailboxLockFile_IsDirectory);
4199 error.setError(DTME_CannotCreateMailboxLockFile_NoSuchFile);
4201 #if defined(__osf__) || defined(CSRG_BASED)
4206 error.setError(DTME_CannotCreateMailboxLockFile_RemoteAccessLost);
4209 error.vSetError(DTME_CannotCreateMailboxLockFile,
4210 DTM_FALSE, NULL, tempLockFileName,
4211 error.errnoMessage());
4214 delete [] tempLockFileName;
4218 sprintf(lockBuf, "%d\n", getpid());
4219 len = strlen(lockBuf);
4220 len = SafeWrite(lock_fd, lockBuf, len);
4223 dotDtmailLockFile(dotDtmailPath, MAXPATHLEN);
4225 PRIV_ENABLED(return_status,SafeLink(tempLockFileName, dotDtmailPath));
4226 if (return_status == -1)
4227 error.vSetError(DTME_CannotCreateMailboxDotDtmailLockFile,
4228 DTM_FALSE, NULL, dotDtmailPath,
4229 error.errnoMessage());
4231 PRIV_ENABLED(return_status,SafeRemove(tempLockFileName));
4232 delete [] tempLockFileName;
4236 RFCMailBox::dotDtmailUnlock(DtMailEnv &)
4238 char dotDtmailPath[MAXPATHLEN+1];
4239 char lockBuf[MAXPATHLEN];
4241 int return_status = 0;
4244 int flags = O_RDONLY | O_EXCL | O_SYNC;
4246 int flags = O_RDONLY | O_EXCL | O_SYNC | O_RSYNC;
4249 dotDtmailLockFile(dotDtmailPath, MAXPATHLEN);
4250 PRIV_ENABLED(lock_fd,SafeOpen(dotDtmailPath, flags, 0666));
4254 return_status = SafeRead(lock_fd, lockBuf, sizeof(lockBuf)-1);
4255 if (return_status <= 0)
4257 (void) SafeClose(lock_fd);
4261 sscanf(lockBuf, "%d", &pid);
4262 if (pid != getpid())
4264 (void) SafeClose(lock_fd);
4268 (void) SafeClose(lock_fd);
4269 PRIV_ENABLED(return_status,SafeRemove(dotDtmailPath));
4273 // Possible return values,
4274 // DTMBX_LONGLOCK_FAILED_CANCEL - failed, cancel operation
4275 // DTMBX_LONGLOCK_FAILED_READONLY - failed, open read only
4276 // DTMBX_LONGLOCK_SUCCESSFUL - succeeded
4278 // This function will also manipulate "_mail_box_writable" and "_lock_flag"
4281 RFCMailBox::longLock(DtMailEnv & error)
4283 DTMBX_LONGLOCK rtn = DTMBX_LONGLOCK_FAILED_CANCEL;
4285 _long_lock_active = DTM_TRUE;
4286 _lock_obj = new FileShare(error,
4295 // TT is not available. Attempt to lock using the .dtmail lock.
4301 dotDtmailLock(error);
4304 _long_lock_active = DTM_FALSE;
4305 if (NULL != _callback)
4308 // .dtmail lock failed. The best we can do is read only.
4311 char dotDtmailPath[MAXPATHLEN+1];
4313 dotDtmailLockFile(dotDtmailPath, MAXPATHLEN);
4315 DTMC_DOTDTMAILLOCKFAILED, _real_path, NULL, _cb_data,
4316 (char*) dotDtmailPath, (const char*) error);
4317 if (DTM_TRUE == ans)
4319 rtn = DTMBX_LONGLOCK_FAILED_READONLY;
4324 rtn = DTMBX_LONGLOCK_FAILED_CANCEL;
4325 error.setError(DTME_UserInterrupted);
4330 rtn = DTMBX_LONGLOCK_SUCCESSFUL;
4335 // Ask TT to lock the file.
4337 _lock_obj->lockFile(error);
4341 // TT rejected our lock request. The best we can do is read only.
4344 if (_lock_obj->readOnly(error) == DTM_TRUE)
4346 rtn = DTMBX_LONGLOCK_FAILED_READONLY;
4351 rtn = DTMBX_LONGLOCK_FAILED_CANCEL;
4352 error.setError(DTME_UserInterrupted);
4357 _long_lock_active = DTM_FALSE;
4360 rtn = DTMBX_LONGLOCK_SUCCESSFUL;
4367 RFCMailBox::longUnlock(DtMailEnv & error)
4371 if (DTM_TRUE == _lock_flag)
4373 if (NULL != _lock_obj)
4378 else if (_long_lock_active == DTM_TRUE)
4379 dotDtmailUnlock(error);
4383 if (_mail_box_writable == DTM_FALSE)
4387 #if !defined(SENDMAIL_LOCKS)
4388 if (_lockf_active == DTM_TRUE)
4390 int return_status = 0;
4391 lseek(_fd, 0, SEEK_SET);
4392 PRIV_ENABLED_OPTIONAL(return_status,SafeLockf(_fd, F_ULOCK, 0));
4393 _lockf_active = DTM_FALSE;
4397 _long_lock_active = DTM_FALSE;
4400 RFCMailBox::MapRegion *
4401 RFCMailBox::mapNewRegion(DtMailEnv & error, int fd, unsigned long size)
4405 // Create the mapped region.
4407 MapRegion * map = new MapRegion;
4409 map->file_size = size;
4410 long page_size = memoryPageSize();
4412 #if defined(__osf__) && OSMAJORVERSION < 4
4413 // Problem with mapping. You can not round up to the nearest
4414 // memory block size when mapping a file. You need the exact
4415 // file size of less. - ff
4416 map->map_size = map->file_size;
4418 map->map_size = map->file_size + (page_size - (map->file_size % page_size)) + page_size;
4421 int flags = MAP_PRIVATE;
4423 #if defined(MMAP_NORESERVE)
4424 // We are not supposed to be writing to these pages. If
4425 // we don't specify MAP_NORESERVE however, the system will
4426 // reserve swap space equal to the file size to deal with
4427 // potential writes. This is wasteful to say the least.
4429 flags |= MAP_NORESERVE;
4432 map->map_region = (char *)mmap(0, (size_t) map->map_size,
4433 PROT_READ, flags, fd, 0);
4434 if (map->map_region == (char *)-1) {
4437 error.setError(DTME_CannotReadNewMailboxFile_OutOfMemory);
4441 error.vSetError(DTME_CannotReadNewMailboxFile,
4442 DTM_FALSE, NULL, error.errnoMessage());
4447 return((MapRegion *)NULL);
4450 map->file_region = map->map_region;
4456 RFCMailBox::makeHeaderLine(DtMailEnv & error,
4458 const DtMailHeaderRequest & request,
4459 DtMailHeaderLine & headers)
4461 MessageCache * mc = _msg_list[slot];
4462 DtMail::Envelope * env = mc->message->getEnvelope(error);
4464 // For each request, we need to retrieve the header values.
4466 headers.number_of_names = request.number_of_names;
4467 headers.header_values = new DtMailValueSeq[headers.number_of_names];
4469 for (int req = 0; req < request.number_of_names; req++) {
4470 // RFC Message::getHeader will pass abstract names through
4471 // as transport names if they can not be found in the abstract
4472 // table. Because of this we say all names are abstract and
4473 // rely on the specific implementation of RFCMessage. This
4474 // is potentially dangerous, but we are allowed to define
4475 // and require these semantics for RFCMessage (and this same
4476 // information appears in the appropriate place in getHeader.
4478 env->getHeader(error,
4479 request.header_name[req],
4481 headers.header_values[req]);
4482 if (error.isSet()) {
4483 headers.header_values[req].clear();
4492 RFCMailBox::waitForMsgs(int needed)
4494 while(_at_eof == 0 && needed >= _msg_list.length()) {
4501 RFCMailBox::writeToDumpFile(const char *format, ...)
4504 char dumpfilename[MAXPATHLEN+1];
4505 _Xctimeparams ctime_buf;
4507 GET_DUMPFILE_NAME(dumpfilename);
4508 FILE *df = fopen(dumpfilename, "a");
4510 const time_t clockTime = (const time_t) time((time_t *)0);
4511 memset((void*) &ctime_buf, 0, sizeof(_Xctimeparams));
4512 fprintf(df, "--------------------- pid=%ld %s",
4513 (long)getpid(), _XCtime(&clockTime, ctime_buf));
4516 va_start(var_args, format);
4517 vfprintf(df, format, var_args);
4520 fprintf(df, "---------------------\n");
4521 fprintf(df, "\n\n");
4527 RFCMailBox::startAutoSave(DtMailEnv & error,
4534 _session->addEventRoutine(error, PollEntry, this, 60);
4536 _last_poll = 0; // Causes first poll to fire right way.
4539 _session->removeEventRoutine(error, PollEntry, this);
4542 #if defined(reallyoldsun)
4543 #define SA_HANDLER_TYPE void (*)(void)
4545 #define SA_HANDLER_TYPE void (*)(int)
4549 RFCMailBox::dumpMaps(const char *str)
4553 off_t total_file_size = 0;
4554 struct sigaction sig_act, old_sig_act;
4555 char dumpfilename[MAXPATHLEN+1];
4557 GET_DUMPFILE_NAME(dumpfilename);
4558 FILE *df = fopen(dumpfilename, "a");
4560 if (df==NULL && _errorLogging)
4563 "dumpMaps(): Cant open dump file %s\n", dumpfilename);
4565 if (SafeFStat(_fd, &buf) < 0) {
4566 fprintf(df, "dumpMaps(): fstat(%d) failed errno=%d\n", _fd, errno);
4574 * Prepare signal handler for exception handling.
4576 (void) sigemptyset(&sig_act.sa_mask);
4577 sig_act.sa_flags = 0;
4578 sig_act.sa_handler = (SA_HANDLER_TYPE) SigBusHandler;
4579 sigaction(SIGBUS, &sig_act, &old_sig_act);
4580 sigbus_env_valid = 1;
4581 if (setjmp(sigbus_env) == 0) {
4582 const time_t clockTime = (const time_t) time((time_t *)0);
4583 _Xctimeparams ctime_buf;
4584 memset((void*) &ctime_buf, 0, sizeof(_Xctimeparams));
4585 fprintf(df, "--------------------- pid=%ld %s",
4586 (long)getpid(), _XCtime(&clockTime, ctime_buf));
4587 fprintf(df, "%s", str);
4588 fprintf(df, "---------------------\n");
4589 fprintf(df, "Mappings = %d\n", _mappings.length());
4590 fprintf(df, "Map Entries:\n");
4591 for (int m = 0; m < _mappings.length(); m++) {
4592 MapRegion *map = _mappings[m];
4593 fprintf(df, "map[%d]: map_region = %p, map_size = 0x%08lx(%08ld)\n",
4594 m, map->map_region, map->map_size, map->map_size);
4595 if (map->map_size % memoryPageSize()) {
4596 fprintf(df, "ERROR! map->map_size not mod %lu\n", memoryPageSize());
4598 HexDump(df, "map_region", (unsigned char *)map->map_region,
4599 (int) map->file_size, _errorLogging ? 0 : 10);
4601 "map[%d]: file_region = %p, file_size = 0x%08lx(%08ld)\n",
4602 m, map->file_region, map->file_size, map->file_size);
4603 fprintf(df, "map[%d]: offset = 0x%08lx(%08ld)\n",
4604 m, map->offset, map->offset);
4605 if (map->file_size == 0) {
4606 fprintf(df, "No data in file_region\n");
4609 if (strncasecmp(map->file_region, "From", 4)) {
4610 fprintf(df, "ERROR! map->file_region does not begin with From\n");
4614 (unsigned char *)map->file_region,
4615 (int) map->file_size, _errorLogging ? 0 : 10);
4617 total_file_size += (off_t) map->file_size;
4618 if ((total_file_size % 4096) == 0) {
4620 "Total file size falls on page boundary, totalsize = %lu\n",
4625 fprintf(df, "\nstat buffer entries: st_ino = %lu, st_dev = %lu, st_nlink = %lu, st_size = %ld\n",
4626 buf.st_ino, buf.st_dev, buf.st_nlink, buf.st_size);
4628 fprintf(df, "\n\n");
4631 fprintf(df, "\nSIGBUS received during output of file mappings.\n");
4632 fprintf(df, "This generally indicates a truncated or deleted file.\n");
4634 sigbus_env_valid = 0;
4635 sigaction(SIGBUS, &old_sig_act, NULL);
4639 // The following routines are required to bind this format specific driver
4640 // into the format neutral layer.
4642 // The first entry point is the capability query interface. This is used
4643 // by the client to determine what capabilities we support.
4646 RFCQueryImpl(DtMail::Session & session,
4648 const char * capability,
4653 if (strcmp(capability, DtMailCapabilityPropsSupported) == 0) {
4654 DtMailBoolean * resp = va_arg(args, DtMailBoolean *);
4659 if (strcmp(capability, DtMailCapabilityImplVersion) == 0) {
4660 char * version = va_arg(args, char *);
4661 strcpy(version, "1.0");
4665 if (strcmp(capability, DtMailCapabilityInboxName) == 0)
4667 DtMailObjectSpace *space = va_arg(args, DtMailObjectSpace *);
4668 void **inbox = va_arg(args, void **);
4670 *space = DtMailFileObject;
4671 *inbox = (void*) getInboxPath(&session);
4675 if (strcmp(capability, DtMailCapabilityMailspoolName) == 0)
4677 DtMailObjectSpace *space = va_arg(args, DtMailObjectSpace *);
4678 void **mailspool = va_arg(args, void **);
4680 *space = DtMailFileObject;
4681 *mailspool = (void*) getMailspoolPath(&session);
4685 if (strcmp(capability, DtMailCapabilityTransport) == 0) {
4686 DtMailBoolean * resp = va_arg(args, DtMailBoolean *);
4691 error.setError(DTME_NotSupported);
4695 // The QueryOpen entry point is used to determin if we can open the specified
4696 // path. If the name is of the form host:/path, or is a simple path, then we
4697 // will say we can. Additional work should be done for content typing here,
4698 // but we'll skip it for now.
4701 RFCQueryOpen(DtMail::Session &,
4703 DtMailObjectSpace space,
4708 // We can do buffers, so just say yes.
4710 if (space == DtMailBufferObject) {
4714 // If this isn't in the file name space, then give up now.
4716 if (space != DtMailFileObject) {
4720 char * path = (char *) arg;
4722 // First, is this of the form "host:/path". If so, it is probably
4723 // an RFC mail box, so we will say we can open it.
4725 for (const char * c = path; *c; c++) {
4727 // We hit a slash first, so colon's are not useful.
4731 if (*c == ':' && *(c+1) == '/') {
4732 // Looks like we have a host name. We should do a more
4733 // robust check at this point, but for now we will assume
4734 // that we have a valid host.
4739 // Okay, there defintely is not a host name. See if we can stat the
4740 // file. If we can, then assume we can open it, otherwise. If the file
4741 // doesn't exist, then we can create a new one.
4744 if (stat(path, &sbuf) < 0) {
4745 if (errno == ENOENT) {
4756 // The mail box construct entry point creates an instance of the RFC
4757 // mail box object. This object is then accessed through the virtual
4758 // DtMail::MailBox class API.
4761 RFCMailBoxConstruct(DtMail::Session & session,
4763 DtMailObjectSpace space,
4768 return(new RFCMailBox(error, &session, space, arg, cb, client_data, "Internet MIME"));
4772 RFCMessageQuery(DtMail::Session &,
4774 DtMailObjectSpace space,
4779 if (space != DtMailBufferObject) {
4787 RFCMessageConstruct(DtMail::Session & session,
4789 DtMailObjectSpace space,
4794 return(new RFCMessage(error, &session, space, arg, cb, client_data));
4798 RFCMIMETransportConstruct(DtMail::Session & session,
4800 DtMailStatusCallback cb,
4803 return(new RFCTransport(error, &session, cb, cb_data, "Internet MIME"));
4806 // The meta factory is responsible for returning the entry points
4807 // required for locating and creating various mail objects based for
4808 // the RFC implementation. It is essentially a switch table.
4812 RFCMetaFactory(const char * op)
4814 if (strcmp(op, QueryImplEntryOp) == 0) {
4815 return((void *)RFCQueryImpl);
4818 if (strcmp(op, QueryOpenEntryOp) == 0) {
4819 return((void *)RFCQueryOpen);
4822 if (strcmp(op, MailBoxConstructEntryOp) == 0) {
4823 return((void *)RFCMailBoxConstruct);
4826 if (strcmp(op, QueryMessageEntryOp) == 0) {
4827 return((void *)RFCMessageQuery);
4830 if (strcmp(op, MessageConstructEntryOp) == 0) {
4831 return((void *)RFCMessageConstruct);
4834 if (strcmp(op, TransportConstructEntryOp) == 0) {
4835 return((void *)RFCMIMETransportConstruct);
4841 // The mail box construct entry point creates an instance of the RFC
4842 // mail box object. This object is then accessed through the virtual
4843 // DtMail::MailBox class API.
4846 V3MailBoxConstruct(DtMail::Session & session,
4848 DtMailObjectSpace space,
4853 return(new RFCMailBox(error, &session, space, arg, cb, client_data,
4858 RFCV3TransportConstruct(DtMail::Session & session,
4860 DtMailStatusCallback cb,
4863 return(new RFCTransport(error, &session, cb, cb_data, "Sun Mail Tool"));
4866 // The meta factory is responsible for returning the entry points
4867 // required for locating and creating various mail objects based for
4868 // the RFC implementation. It is essentially a switch table.
4871 V3MetaFactory(const char * op)
4873 if (strcmp(op, QueryImplEntryOp) == 0) {
4874 return((void *)RFCQueryImpl);
4877 if (strcmp(op, QueryOpenEntryOp) == 0) {
4878 return((void *)RFCQueryOpen);
4881 if (strcmp(op, MailBoxConstructEntryOp) == 0) {
4882 return((void *)V3MailBoxConstruct);
4885 if (strcmp(op, QueryMessageEntryOp) == 0) {
4886 return((void *)RFCMessageQuery);
4889 if (strcmp(op, MessageConstructEntryOp) == 0) {
4890 return((void *)RFCMessageConstruct);
4893 if (strcmp(op, TransportConstructEntryOp) == 0) {
4894 return((void *)RFCV3TransportConstruct);
4902 RFCMailBox::createMailRetrievalAgent(char *password)
4904 DtMailEnv localError;
4905 char *path = _session->expandPath(localError, (char*) _arg);
4906 DtMailServer *server = NULL;
4909 if (! isInboxMailbox(_session, path))
4915 if (NULL != _mra_command) free(_mra_command);
4916 _mra_command = NULL;
4918 if (NULL != password)
4920 if (NULL != _mra_serverpw) free(_mra_serverpw);
4921 _mra_serverpw = strdup(password);
4924 if (NULL != _mra_server) delete _mra_server;
4927 if (True == DtMailServer::get_mailrc_value(
4928 _session, DTMAS_INBOX,
4929 DTMAS_PROPKEY_GETMAILVIASERVER,
4932 protocol = DtMailServer::get_mailrc_value(
4933 _session, DTMAS_INBOX,
4934 DTMAS_PROPKEY_PROTOCOL,
4937 if (! strcasecmp(protocol, DTMAS_PROTO_IMAP))
4938 _mra_server = (DtMailServer*) new IMAPServer(
4939 DTMAS_INBOX, _session, this,
4940 appendCB, (void*) this);
4941 else if (! strcasecmp(protocol, DTMAS_PROTO_APOP))
4942 _mra_server = (DtMailServer*) new APOPServer(
4943 DTMAS_INBOX, _session, this,
4944 appendCB, (void*) this);
4945 else if (! strcasecmp(protocol, DTMAS_PROTO_POP3))
4946 _mra_server = (DtMailServer*) new POP3Server(
4947 DTMAS_INBOX, _session, this,
4948 appendCB, (void*) this);
4949 else if (! strcasecmp(protocol, DTMAS_PROTO_POP2))
4950 _mra_server = (DtMailServer*) new POP2Server(
4951 DTMAS_INBOX, _session, this,
4952 appendCB, (void*) this);
4954 _mra_server = (DtMailServer*) new AUTOServer(
4955 DTMAS_INBOX, _session, this,
4956 appendCB, (void*) this);
4958 if (NULL != _mra_server) _mra_server->set_password(_mra_serverpw);
4961 else if (True == DtMailServer::get_mailrc_value(
4962 _session, DTMAS_INBOX,
4963 DTMAS_PROPKEY_GETMAILVIACOMMAND,
4966 _mra_command = DtMailServer::get_mailrc_value(
4967 _session, DTMAS_INBOX,
4968 DTMAS_PROPKEY_GETMAILCOMMAND,
4975 RFCMailBox::deleteMailRetrievalAgent()
4977 if (NULL != _mra_command) free(_mra_command);
4978 _mra_command = NULL;
4979 if (NULL != _mra_serverpw) free(_mra_serverpw);
4980 _mra_serverpw = NULL;
4981 if (NULL != _mra_server) delete _mra_server;
4986 RFCMailBox::updateMailRetrievalPassword(char *password)
4988 if (NULL == password || NULL == _mra_server) return;
4990 if (NULL != _mra_serverpw) delete _mra_serverpw;
4991 _mra_serverpw = strdup(password);
4992 _mra_server->set_password(_mra_serverpw);
4997 RFCMailBox::retrieveNewMail(DtMailEnv &error)
4999 if (NULL != _mra_server)
5001 _mra_server->retrieve_messages(error);
5003 if (DTME_MailServerAccess_MissingPassword == (DTMailError_t) error ||
5004 DTME_MailServerAccess_AuthorizationFailed == (DTMailError_t) error)
5005 return DTMC_SERVERPASSWORDNEEDED;
5006 else if (DTME_NoError != (DTMailError_t) error)
5007 return DTMC_SERVERACCESSFAILED;
5009 else if (NULL != _mra_command)
5011 int sysstatus = system(_mra_command);
5012 if (-1 == sysstatus)
5015 DTME_GetmailCommandRetrieval_SystemError,
5017 _mra_command, errno, error.errnoMessage(errno));
5018 if (_errorLogging) error.logError(DTM_FALSE, (const char*) error);
5019 return DTMC_GETMAILCOMMANDFAILED;
5021 if (0 == WIFEXITED(sysstatus))
5024 DTME_GetmailCommandRetrieval_AbnormalExit,
5027 if (_errorLogging) error.logError(DTM_FALSE, (const char*) error);
5028 return DTMC_GETMAILCOMMANDFAILED;
5030 else if (0 != WEXITSTATUS(sysstatus))
5035 "system('%s') returned %d\n",
5036 _mra_command, WEXITSTATUS(sysstatus));
5043 RFCMailBox::mailboxAccessShow(time_t mtime, char *prefix)
5047 new_time.modtime = mtime;
5048 new_time.actime = new_time.modtime+1;
5049 SafeUTime(_real_path, &new_time);
5050 #ifdef DEBUG_MAILBOX_ACCESS
5051 fprintf(stderr, "%s: forcing acctime>modtime\n", prefix);
5056 RFCMailBox::mailboxAccessHide(char *prefix)
5058 SafeUTime(_real_path, NULL);
5059 #ifdef DEBUG_MAILBOX_ACCESS
5060 fprintf(stderr, "%s: forcing modtime==acctime\n", prefix);