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 extern "C" int madvise(caddr_t, size_t, int);
108 extern "C" ssize_t pread(int, void *, size_t, off_t);
111 #if defined(sun) || defined(USL)
112 #define LCL_SIG_HANDLER_SIGNATURE
113 #elif defined(__hpux)
114 #define LCL_SIG_HANDLER_SIGNATURE __harg
115 #elif defined(__aix) || defined(__alpha) || defined(linux) || defined(CSRG_BASED)
116 #define LCL_SIG_HANDLER_SIGNATURE int
117 #elif defined(__uxp__)
118 #define LCL_SIG_HANDLER_SIGNATURE
122 // Debugging for RFCMailBox.
124 #if defined(DEBUG_RFCMailBox)
125 #define DEBUG_PRINTF(a) printf a
127 #define DEBUG_PRINTF(a)
131 // These macros define a method for executing statements
132 // with set group id privileges enabled
134 #if defined(MAILGROUP_REQUIRED)
136 #define PRIV_ENABLED_OPTIONAL(STATUSVARIABLE, STATEMENT) \
137 STATUSVARIABLE = STATEMENT;
139 #define PRIV_ENABLED(STATUSVARIABLE, STATEMENT) \
141 _session->enableGroupPrivileges(); \
142 STATUSVARIABLE = STATEMENT; \
143 _session->disableGroupPrivileges(); \
146 #define PRIV_ENABLED_OPEN(FILENAME, STATUSVARIABLE, STATEMENT) \
147 if (isSetMailGidNeeded(FILENAME)) { \
148 PRIV_ENABLED(STATUSVARIABLE, STATEMENT) \
150 STATUSVARIABLE = STATEMENT; \
155 #define PRIV_ENABLED_OPTIONAL(STATUSVARIABLE, STATEMENT) \
156 _session->enableGroupPrivileges(); \
157 STATUSVARIABLE = STATEMENT; \
158 _session->disableGroupPrivileges(); \
159 if (STATUSVARIABLE == -1) { \
160 STATUSVARIABLE = STATEMENT; \
163 #define PRIV_ENABLED PRIV_ENABLED_OPTIONAL
165 #define PRIV_ENABLED_OPEN(FILENAME, STATUSVARIABLE, STATEMENT) \
166 STATUSVARIABLE = STATEMENT;
170 #define GET_DUMPFILE_NAME(dfn) \
171 snprintf(dfn, sizeof(dfn), "%s/%s/dtmail.dump", getenv("HOME"), DtPERSONAL_TMP_DIRECTORY)
174 * Local Data Definitions
176 static const int RFCSignature = 0x448612e5;
178 static const int DEFAULT_FOLDER_SIZE = (32 << 10); // 32 KB
180 static int sigbus_env_valid = 0;
182 static jmp_buf sigbus_env;
185 * Local Function Declarations
187 static void SigBusHandler(LCL_SIG_HANDLER_SIGNATURE);
188 static int isMailGroupSystemMailbox(const char *mailboxPath);
189 static int isInboxMailbox(const char *mailboxPath);
190 static char *getInboxPath(DtMail::Session *session);
195 * Notified when an interesting (SIGBUS in this case) signal is raised.
196 * I wanted to throw a C++ exception at this point but that's not
197 * supported everywhere. So, we'll just have to use the thread-unsafe
198 * setjmp/longjmp combination.
201 void SigBusHandler(LCL_SIG_HANDLER_SIGNATURE)
203 if (sigbus_env_valid) {
204 longjmp(sigbus_env, 1);
209 void HexDump(FILE *pfp, char *pmsg, unsigned char *pbufr, int plen, int plimit)
211 unsigned char save[64];
212 long int x, y, z, word, cnt;
218 if (pfp_r == (FILE*) NULL) {
219 char dumpfilename[MAXPATHLEN+1];
220 _Xctimeparams ctime_buf;
222 GET_DUMPFILE_NAME(dumpfilename);
223 pfp_r = fopen(dumpfilename, "a");
224 const time_t clockTime = (const time_t) time((time_t *)0);
225 memset((void*) &ctime_buf, 0, sizeof(_Xctimeparams));
226 fprintf(pfp_r, "--------------------- pid=%ld %s",
227 (long)getpid(), _XCtime(&clockTime, ctime_buf));
230 (void) fprintf(pfp_r, "--> %s (%d bytes at %p):\n", pmsg, plen, pbufr);
232 memset((char *)save, 0, sizeof(save));
236 for (x = 0; cnt > 0; x++, z += 16)
238 (void) fprintf(pfp_r, "%p(+%6.6ld) ", pbufr + z, z);
239 for (y = 0; y < 16; y++)
241 save[y] = pbufr[x * 16 + y];
242 word = pbufr[x * 16 + y];
245 (void) fprintf(pfp_r, "%2.2lx%c", word, y == 7 ? '-' : ' ');
247 (void) fprintf(pfp_r, " ");
249 (void) fprintf(pfp_r, "%s", " *");
250 for (y = 0; y < 16; y++)
252 (void) fprintf(pfp_r, " ");
253 else if (pbufr[x * 16 + y] < ' ' || pbufr[x * 16 + y] > '~')
254 (void) fprintf(pfp_r, "%s", ".");
256 (void) fprintf(pfp_r, "%c", pbufr[x * 16 + y]);
257 (void) fprintf(pfp_r, "%s", "*\n");
260 if (plimit && (x >= (plimit-1)) && (cnt > (plimit*16))) {
261 while (cnt > (plimit*16)) {
266 fprintf(pfp_r, "...\n");
270 if (pfp == (FILE*) NULL) {
271 fprintf(pfp_r, "---------------------\n");
276 RFCMailBox::RFCMailBox(DtMailEnv & error,
277 DtMail::Session * session,
278 DtMailObjectSpace space,
282 const char * impl_name)
283 : DtMail::MailBox(error, session, space, arg, cb, client_data),
284 _msg_list(128), _mappings(4)
286 // We are using a condition to block any threads from
287 // trying to use this object until it is open. We will
288 // not set the object valid until we have a valid thread.
289 // Note that this will not be set to true until a stream
290 // has been successfully opened.
292 DtMailEnv localError;
295 _object_valid = new Condition;
297 _object_valid->setFalse();
299 _map_lock = MutexInit();
301 _impl_name = impl_name;
308 _mail_box_writable = DTM_FALSE;
309 _use_dot_lock = DTM_TRUE;
310 _long_lock_active = DTM_FALSE;
311 _dot_lock_active = DTM_FALSE;
312 _lockf_active = DTM_FALSE;
313 _uniqueLockId = generateUniqueLockId();
314 assert(_uniqueLockId != NULL);
315 _uniqueLockIdLength = strlen(_uniqueLockId);
316 _lockFileName = (char *)0;
319 _mr_allowed = DTM_TRUE;
321 _mra_serverpw = NULL;
323 createMailRetrievalAgent(NULL);
325 // Create a thread for getting new mail and expunging old mail.
326 // Of course we don't need threads for buffer objects.
328 if (_space != DtMailBufferObject) {
329 _thread_info = new NewMailData;
330 _thread_info->self = this;
331 _thread_info->object_valid = _object_valid;
332 _mbox_daemon = ThreadCreate(ThreadNewMailEntry, _thread_info);
335 // We will have to add a poll oriented method as well. We'll ignore
338 if (_space != DtMailBufferObject) {
339 _session->addEventRoutine(error, PollEntry, this, 15);
341 _last_poll = 0; // Causes first poll to fire right way.
344 // We need to figure out what type of locking to use. We use ToolTalk,
345 // when the user has explicitly turned it on via a property.
347 DtMail::MailRc * mailrc = _session->mailRc(error);
348 const char * value = NULL;
349 mailrc->getValue(localError, "cdetooltalklock", &value);
350 if (localError.isSet()) {
351 _tt_lock = DTM_FALSE;
359 // Determine if error logging is enabled
363 mailrc->getValue(localError, "errorlogging", &value);
364 _errorLogging = (localError.isSet() ?
365 (localError.clear(), DTM_FALSE) : DTM_TRUE);
370 _partialListCount = 0;
374 _object_signature = RFCSignature;
377 RFCMailBox::~RFCMailBox(void)
379 if (_object_signature != RFCSignature) {
383 MutexLock lock_scope(_obj_mutex);
384 if (_object_signature == RFCSignature) {
385 _object_valid->setFalse();
388 _session->removeEventRoutine(error, PollEntry, this);
390 // We need to copy the file if writable and dirty out.
391 // NOTE: the caller should really call writeMailBox() or expunge()
392 // (as appropriate) first before destroying this mailbox, as there
393 // is no way to pass an error indication back up from here to the
394 // caller. If an error happens let syslog handle it.
396 if (_mail_box_writable == DTM_TRUE) {
399 writeMailBox(error, DTM_FALSE);
402 // THE MAILBOX COULD NOT BE WRITTEN!! SHOULD DO SOMETHING
403 error.logError(DTM_TRUE,
404 "~RFCMailBox(): Failed to write mailbox: %s",
405 (const char *)error);
410 // Let's keep the map locked.
412 MutexLock lock_map(_map_lock);
414 // Next we tear down the message structures.
416 while (_msg_list.length()) {
417 MessageCache * mc = _msg_list[0];
420 _msg_list.remove(0); // Won't actually touch the object.
423 // Finally we need to get rid of the mapping. There are
424 // actually 3 conditions we need to deal with here.
426 // 1) We opened a file and mapped it. In that case, we unmap,
427 // and close the file.
429 // 2) We created a buffer. In that case the _mapped_region points,
430 // at the region owned by _buffer. We simply destroy _buffer,
431 // but we do not attempt to destroy the mapped region.
433 // 3) The caller asked us to open an existing buffer. In that
434 // case, do nothing (we didn't build it so we won't destroy it).
436 if (_fd >= 0) { // Option 1
437 for (int slot = 0; slot < _mappings.length(); slot++) {
438 munmap(_mappings[slot]->map_region,
439 (size_t) _mappings[slot]->map_size);
445 if (_buffer) { // Option 2
450 // Allocated using malloc and strdup, so free using free.
452 free((void*) _real_path);
453 free((void*) _uniqueLockId);
454 free((void*) _lockFileName);
457 _object_signature = 0;
459 if (NULL != _mra_command) delete _mra_command;
460 if (NULL != _mra_server) delete _mra_server;
461 if (NULL != _mra_serverpw) delete _mra_serverpw;
464 static int isMailGroupSystemMailbox(const char * mailboxPath)
467 #ifdef MAILGROUP_REQUIRED
468 static int oneTimeFlag = 0;
469 static char *cached_inbox_path = 0;
471 if (NULL == mailboxPath) return retval;
475 char *inbox_path = new char[MAXPATHLEN];
478 GetPasswordEntry(pw);
479 sprintf(inbox_path, MAIL_SPOOL_PATH, pw.pw_name);
480 cached_inbox_path = strdup(inbox_path);
482 delete [] inbox_path;
485 assert(cached_inbox_path);
486 retval = (!strcmp(mailboxPath, cached_inbox_path));
491 static char *getMailspoolPath(DtMail::Session *session)
494 DtMail::MailRc *mailrc = session->mailRc(error);
495 char *mailspoolpath = 0;
496 char *syspath = new char[MAXPATHLEN];
499 GetPasswordEntry(pw);
500 sprintf(syspath, MAIL_SPOOL_PATH, pw.pw_name);
501 mailspoolpath = strdup(syspath);
503 assert(NULL!=mailspoolpath);
505 return mailspoolpath;
508 static char *getInboxPath(DtMail::Session *session)
511 DtMail::MailRc *mailrc = session->mailRc(error);
514 mailrc->getValue(error, DTMAS_PROPKEY_INBOXPATH, (const char**) &inboxpath);
517 mailrc->getValue(error, "DT_MAIL", (const char**) &inboxpath);
520 mailrc->getValue(error, "MAIL", (const char**) &inboxpath);
523 inboxpath = getMailspoolPath(session);
534 if (inboxpath && 0 == (strcmp(inboxpath, "MAILSPOOL_FILE")))
537 inboxpath = getMailspoolPath(session);
540 assert(NULL!=inboxpath);
544 static int isInboxMailbox(DtMail::Session *session, const char * mailboxPath)
547 char *inbox_path = NULL;
549 inbox_path = getInboxPath(session);
550 assert(NULL!=inbox_path);
551 retval = (!strcmp(mailboxPath, inbox_path));
556 // Function: alterPageMappingAdvice - change OS mapping advice on selected map
558 // Give the operating system new advice on the referencing activity that
559 // should be expected for a region of file mapped pages.
561 // Spin through all map regions recorded in _mappings looking for the
562 // specified map (or all maps if -1 passed in as the map region), and
563 // if found issue an madvise call on the specified region of memory.
565 // map -- either a specific region that is part of _mappings,
566 // -- OR (MapRegion *)-1 to change ALL map regions in _mappings
567 // advice -- advice as per madvise(3) Operating System call
574 RFCMailBox::alterPageMappingAdvice(MapRegion *map, int advice)
576 int me = _mappings.length();
577 for (int m = 0; m < me; m++) {
578 MapRegion *map_t = _mappings[m];
580 #if !defined(USL) && !defined(__uxp__) && !defined(linux) && !defined(sun)
581 // no madvise on these systems
582 if (map_t == map || map == (MapRegion *)-1)
583 madvise(map_t->map_region, (size_t) map_t->map_size, advice);
588 // Function: memoryPageSize - return hardware natural memory page size
590 // Compute and return the natural memory page size of the current hardware
592 // Use sysconf to query the operating system about the current hardware;
593 // cache the value so that repeated calls to this function do not generate
594 // repeated calls to the operating system.
600 // long -- natural page size of the current hardware
605 static long mach_page_size = -1; // -1 is "one time value"
607 if (mach_page_size == -1) {
608 mach_page_size = sysconf(_SC_PAGESIZE);
609 assert(mach_page_size != -1);
612 return(mach_page_size);
616 // Functions: addressIsMapped - check if address is in file mapped memory
618 // Check to see if a given memory address is within the bounds of any
619 // memory that may have been mapped from a mailbox file into memory,
620 // and return an indication of whether it is or not.
622 // Go through the list of file to memory mappings and check to see if
623 // the given address is within the bounds of one of the mappings.
625 // addressToCheck -- address in memory to check against mappings
629 // DTM_TRUE - memory address is in file mapped memory and can be accessed
630 // DTM_FALSE - memory address is not in file mapped memory - may not be valid
633 RFCMailBox::addressIsMapped(void *addressToCheck)
635 assert(addressToCheck != NULL);
636 int me = _mappings.length();
637 for (int m = 0; m < me; m++)
639 MapRegion *map = _mappings[m];
640 if ( (addressToCheck >= map->map_region)
641 && (addressToCheck < (map->map_region+map->file_size)) )
648 RFCMailBox::appendCB(DtMailEnv &error, char *buf, int len, void *clientData)
650 RFCMailBox *obj = (RFCMailBox*) clientData;
652 if (NULL == obj) return;
653 obj->append(error, buf, len);
657 RFCMailBox::append(DtMailEnv &error, char *buf, int len)
660 off_t end = lseek(_fd, 0, SEEK_END);
662 // Add a new-line at the end to distinguish separate messages.
663 status = SafeWrite(_fd, buf, len);
667 char *path = _session->expandPath(error, (char *)_arg);
672 DTME_AppendMailboxFile_FileTooBig, DTM_FALSE, NULL,
673 path, errno, error.errnoMessage(errno));
676 #if defined(__osf__) || defined(CSRG_BASED)
682 DTME_AppendMailboxFile_LinkLost, DTM_FALSE, NULL,
683 path, errno, error.errnoMessage(errno));
688 DTME_AppendMailboxFile_NoSpaceLeft, DTM_FALSE, NULL,
689 path, errno, error.errnoMessage(errno));
694 DTME_AppendMailboxFile_SystemError, DTM_FALSE, NULL,
695 path, errno, error.errnoMessage(errno));
700 if (_hide_access_events)
701 mailboxAccessHide("append");
704 time_t now = time((time_t) NULL);
705 mailboxAccessShow(now, "append");
711 RFCMailBox::create(DtMailEnv & error, mode_t create_mode)
715 MutexLock lock_scope(_obj_mutex);
716 MutexLock lock_map(_map_lock);
719 case DtMailBufferObject:
721 DtMailBuffer * buf = (DtMailBuffer *)_arg;
723 _buffer = (char *)malloc(DEFAULT_FOLDER_SIZE);
724 buf->buffer = _buffer;
725 buf->size = DEFAULT_FOLDER_SIZE;
727 MapRegion * map = new MapRegion;
728 map->file_region = map->map_region = _buffer;
729 map->file_size = map->map_size = DEFAULT_FOLDER_SIZE;
731 _mappings.append(map);
733 _mail_box_writable = DTM_TRUE;
738 case DtMailFileObject:
740 openRealFile(error, O_RDWR | O_CREAT, create_mode);
750 error.setError(DTME_NotSupported);
755 _object_valid->setTrue();
761 RFCMailBox::open(DtMailEnv & error,
762 DtMailBoolean auto_create,
765 DtMailBoolean lock_flag,
766 DtMailBoolean auto_parse
771 if (_tt_lock == DTM_TRUE && lock_flag == DTM_TRUE)
772 _lock_flag = DTM_TRUE;
774 _lock_flag = DTM_FALSE;
776 MutexLock lock_scope(_obj_mutex);
780 MutexLock lock_map(_map_lock);
783 case DtMailBufferObject:
785 DtMailBuffer * buf = (DtMailBuffer *)_arg;
787 MapRegion * map = new MapRegion;
788 map->file_region = map->map_region = (char *)buf->buffer;
789 map->file_size = map->map_size = buf->size;
791 _mappings.append(map);
793 _mail_box_writable = DTM_FALSE;
798 case DtMailFileObject:
803 _mail_box_writable = DTM_FALSE;
804 char * path = _session->expandPath(error, (char *)_arg);
805 PRIV_ENABLED_OPTIONAL(return_result, SafeAccess(path, W_OK));
806 if (return_result == 0) {
811 // We need to use the most restrictive mode that is possible
812 // on the file. If the caller has requested the file be open
813 // read-only, then we should do that, even if read-write is
814 // allowed. We don't want to try to open the file read-write
815 // if we don't have adequate permission however.
817 mode = open_mode == O_RDONLY ? open_mode : mode;
819 openRealFile(error, mode, create_mode);
821 if (auto_create == DTM_TRUE) {
835 error.setError(DTME_NotSupported);
838 if (error.isSet()) { // Can't parse this.
846 #if defined(POSIX_THREADS)
847 ThreadCreate(ThreadParseEntry, this);
852 _object_valid->setTrue(); // New mail watcher starts now.
862 _mail_box_writable = DTM_FALSE;
863 char * path = _session->expandPath(error, (char *)_arg);
864 PRIV_ENABLED_OPTIONAL(return_status, SafeAccess(path, W_OK));
865 if (return_status == 0) {
870 // We need to use the most restrictive mode that is possible
871 // on the file. If the caller has requested the file be open
872 // read-only, then we should do that, even if read-write is
873 // allowed. We don't want to try to open the file read-write
874 // if we don't have adequate permission however.
876 mode = open_mode == O_RDONLY ? open_mode : mode;
878 openRealFile(error, mode, create_mode);
880 if (auto_create == DTM_TRUE) {
884 // Isn't this wrong? Shouldn't create() be called
885 // before unlocking lock_scope?
893 // Validate this file if it's not empty
895 // When move/copy to a file, we want to make sure the first
896 // five characters are "From "
899 #if defined(sun) || defined(USL) || defined(__uxp__)
900 pread(_fd, (void *)inbuf, 5, 0);
902 lseek(_fd, (off_t) 0L, SEEK_SET);
903 if(-1 == read(_fd, (void *)inbuf, 5)) {
904 error.setError(DTME_NotMailBox);
907 lseek(_fd, (off_t) 0L, SEEK_SET);
911 if (strcmp(inbuf, "From ") != 0) {
912 error.setError(DTME_NotMailBox);
921 _object_valid->setTrue(); // New mail watcher starts now.
948 RFCMailBox::messageCount(DtMailEnv & error)
954 if (_object_valid->state() <= 0) {
955 error.setError(DTME_ObjectInvalid);
959 return(_msg_list.length());
961 #endif /* DEAD_WOOD */
964 RFCMailBox::getFirstMessageSummary(DtMailEnv & error,
965 const DtMailHeaderRequest & request,
966 DtMailHeaderLine & summary)
972 if (_object_valid->state() <= 0) {
973 error.setError(DTME_ObjectInvalid);
977 int slot = nextNotDel(0);
979 if (slot >= _msg_list.length()) {
983 makeHeaderLine(error, 0, request, summary);
985 return(_msg_list[0]);
989 RFCMailBox::getNextMessageSummary(DtMailEnv & error,
990 DtMailMessageHandle last,
991 const DtMailHeaderRequest & request,
992 DtMailHeaderLine & summary)
994 if (_object_valid->state() <= 0) {
995 error.setError(DTME_ObjectInvalid);
999 // Let's treat a null last as the start of the list.
1002 return(getFirstMessageSummary(error, request, summary));
1007 int slot = _msg_list.indexof((MessageCache *)last);
1015 slot = nextNotDel(slot);
1017 if (slot >= _msg_list.length()) {
1021 makeHeaderLine(error, slot, request, summary);
1023 return(_msg_list[slot]);
1027 RFCMailBox::getMessageSummary(DtMailEnv & error,
1028 DtMailMessageHandle handle,
1029 const DtMailHeaderRequest & request,
1030 DtMailHeaderLine & summary)
1032 MutexLock lock_map(_map_lock);
1034 if (_object_valid->state() <= 0) {
1035 error.setError(DTME_ObjectInvalid);
1041 int slot = _msg_list.indexof((MessageCache *)handle);
1042 if (slot < 0 || slot >= _msg_list.length()) {
1043 error.setError(DTME_ObjectInvalid);
1047 makeHeaderLine(error, slot, request, summary);
1053 RFCMailBox::clearMessageSummary(DtMailHeaderLine & headers)
1055 if (NULL != headers.header_values)
1056 delete []headers.header_values;
1060 RFCMailBox::getMessage(DtMailEnv & error, DtMailMessageHandle hnd)
1062 if (_object_valid->state() <= 0) {
1063 error.setError(DTME_ObjectInvalid);
1069 int slot = _msg_list.indexof((MessageCache *)hnd);
1071 error.setError(DTME_ObjectInvalid);
1075 MessageCache * mc = _msg_list[slot];
1076 return(mc->message);
1080 RFCMailBox::getFirstMessage(DtMailEnv & error)
1086 if (_object_valid->state() <= 0) {
1087 error.setError(DTME_ObjectInvalid);
1091 int slot = nextNotDel(0);
1093 if (slot >= _msg_list.length()) {
1097 MessageCache * mc = _msg_list[slot];
1098 return(mc->message);
1102 RFCMailBox::getNextMessage(DtMailEnv & error,
1103 DtMail::Message * last)
1107 int slot = lookupByMsg((RFCMessage *)last);
1115 slot = nextNotDel(slot);
1117 if (slot >= _msg_list.length()) {
1121 MessageCache * mc = _msg_list[slot];
1122 return(mc->message);
1126 RFCMailBox::copyMessage(DtMailEnv & error,
1127 DtMail::Message * msg)
1129 #if defined(DEBUG_RFCMailBox)
1130 char *pname = "RFCMailBox::copyMessage";
1133 if (_object_valid->state() <= 0) {
1134 error.setError(DTME_ObjectInvalid);
1140 // The following is a hack for PAR0.5. In the future, we will use
1141 // this test for an optimization, but right now, we can only copy
1144 const char * msg_impl = msg->impl(error);
1145 if (strcmp(msg_impl, "Internet MIME") != 0 &&
1146 strcmp(msg_impl, "Sun Mail Tool") != 0) {
1147 error.setError(DTME_NotSupported);
1151 RFCMessage * rfc_msg = (RFCMessage *)msg;
1153 // We need to protect the file from access. Locking this will also
1154 // block any attempts by the new mail thread.
1158 DEBUG_PRINTF( ("%s: locking mailbox\n", pname) );
1160 if (error.isSet()) {
1161 DtMailEnv tmp_error;
1162 unlockFile(tmp_error, _fd);
1168 off_t end = lseek(_fd, 0, SEEK_END);
1169 status = SafeWrite(_fd, rfc_msg->_msg_start,
1170 rfc_msg->_msg_end - rfc_msg->_msg_start + 1);
1172 // We are going to put this at the real end of the file. We don't
1173 // really care what the current thought size is because new mail
1174 // will take care of that problem.
1177 // Add a new-line at the end.
1178 // It serves to distinguish separate messages.
1180 SafeWrite(_fd, "\n", 1);
1182 DEBUG_PRINTF( ("%s: unlocking mailbox\n", pname) );
1183 unlockFile(error, _fd);
1186 error.setError(DTME_ObjectCreationFailed);
1190 RFCMailBox::copyMailBox(DtMailEnv & error, DtMail::MailBox * mbox)
1194 for (DtMail::Message * msg = mbox->getFirstMessage(error);
1195 msg && error.isNotSet();
1196 msg = mbox->getNextMessage(error, msg)) {
1197 copyMessage(error, msg);
1198 if (error.isSet()) {
1205 RFCMailBox::checkForMail(
1207 const DtMailBoolean already_locked
1212 if (_space != DtMailFileObject) {
1213 error.setError(DTME_NotSupported);
1217 NewMailEvent(already_locked);
1221 RFCMailBox::expunge(DtMailEnv & error)
1225 for (int msg = 0; msg < _msg_list.length(); msg++) {
1226 MessageCache * mc = _msg_list[msg];
1227 if (mc->delete_pending == DTM_FALSE) {
1228 DtMail::Envelope * env = mc->message->getEnvelope(error);
1230 DtMailValueSeq value;
1231 env->getHeader(error, RFCDeleteHeader, DTM_FALSE, value);
1232 if (!error.isSet()) {
1233 mc->delete_pending = DTM_TRUE;
1242 writeMailBox(error, DTM_FALSE);
1243 if ((DTMailError_t)error ==
1244 DTME_CannotWriteToTemporaryMailboxFile_NoFreeSpaceLeft)
1246 // Need to do some thing here don't know now.
1250 // THE MAILBOX COULD NOT BE WRITTEN!! CALLER MUST DO SOMETHING
1253 "RFCMailBox::expunge(): Failed to write mailbox: %s",
1254 (const char *) error
1260 RFCMailBox::impl(DtMailEnv & error)
1267 RFCMailBox::markDirty(const int delta)
1271 // Make sure we really are dirty.
1277 RFCMailBox::callCallback(DtMailCallbackOp op, void * arg)
1279 if (_object_signature != RFCSignature || !_object_valid->state()) {
1284 if (_space == DtMailFileObject) {
1285 path = (char *)_arg;
1291 if (NULL != _callback)
1292 _callback(op, path, NULL, _cb_data, arg);
1298 RFCMailBox::newMessage(DtMailEnv & error)
1300 // RFC does not support a straightforward concept of adding
1301 // a message to a mailbox. We implement move/copy with a sort
1302 // of kludge, but we can only extend the kludge so far.
1304 error.setError(DTME_NotSupported);
1309 RFCMailBox::openRealFile(DtMailEnv & error, int open_mode, mode_t create_mode)
1311 // We first want to check the access modes we have on the
1312 // file. If we can write the file, then we will need to
1313 // root out the real path just to make sure we don't munge
1314 // a link someplace.
1317 char * path = _session->expandPath(error, (char *)_arg);
1318 if (error.isSet()) {
1319 return; // could not expand path
1322 if ((open_mode & O_RDWR) == O_RDONLY) {
1323 _real_path = strdup(path);
1324 SafeStat(_real_path, &buf);
1327 _real_path = (char *)malloc(MAXPATHLEN);
1328 char *link_path = new char[MAXPATHLEN];
1329 strcpy(link_path, path);
1330 strcpy(_real_path, path);
1332 if (SafeLStat(link_path, &buf) == 0 && (open_mode & O_CREAT) == 0) {
1333 while(S_ISLNK(buf.st_mode)) {
1334 int size = readlink(link_path, _real_path, sizeof(link_path));
1336 error.setError(DTME_NoSuchFile);
1338 _real_path = (char *)0;
1341 delete [] link_path;
1345 _real_path[size] = 0;
1346 if (_real_path[0] == '/') {
1347 strcpy(link_path, _real_path);
1350 char * last_slash = strrchr(link_path, '/');
1352 strcpy(last_slash + 1, _real_path);
1355 strcpy(link_path, "./");
1356 strcat(link_path, _real_path);
1360 strcpy(_real_path, link_path);
1362 if (SafeLStat(link_path, &buf)) {
1363 error.setError(DTME_NoSuchFile);
1365 _real_path = (char *)0;
1368 delete [] link_path;
1374 if ((open_mode & O_CREAT) == 0) {
1375 error.setError(DTME_NoSuchFile);
1377 _real_path = (char *)0;
1380 delete [] link_path;
1384 delete [] link_path;
1389 // We should now have a path we can open or create.
1390 // We must now make sure that if the file is being created that
1391 // it is created with the correct permissions and group owner.
1393 // If the mailbox to be created is NOT the system mailbox for this
1394 // user (e.g. the one that official delivery agents use), then the
1395 // permissions for the file should only let the current user have access.
1397 // If the mailbox to be created IS the system mailbox for this user,
1398 // and MAILGROUP_REQUIRED is defined, we must allow group read/write
1399 // and make sure the mailbox is owned by the correct group
1401 int requiresMailGroupCreation = 0;
1405 // delivery agent runs as specific non-root/wheel group
1406 requiresMailGroupCreation = isMailGroupSystemMailbox(_real_path);
1407 if ( (open_mode & O_CREAT) && requiresMailGroupCreation) {
1408 oldUmask = umask(DTMAIL_DEFAULT_CREATE_UMASK_MAILGROUP);
1409 create_mode = DTMAIL_DEFAULT_CREATE_MODE_MAILGROUP;
1412 oldUmask = umask(DTMAIL_DEFAULT_CREATE_UMASK);
1415 // We have 2 choices for locking an RFC file. The first is
1416 // to use the ToolTalk file scoping paradigm, and the second
1417 // is the normal lockf protocol. If we are using ToolTalk,
1418 // we need to initialize the ToolTalk locking object.
1420 // Unfortunately lockf() on an NFS mounted file system will
1421 // upset mmap(). A quick hack is to not use lockf() on any
1422 // remotely mounted file. Since mailx doesn't do tooltalk
1423 // locking now, it is possible to corrupt the spool file when
1424 // using mailx while mailtool has a NFS mounted mail file loaded.
1426 // We should find a solution where mailtool and mailx work
1429 // open the file before we obtain the lock can cause sync problem
1430 // if another owns the lock and has modified the mailbox
1432 DTMBX_LONGLOCK answer = DTMBX_LONGLOCK_SUCCESSFUL;
1434 if (_lock_flag == DTM_TRUE) {
1435 answer = longLock(error);
1436 if (answer == DTMBX_LONGLOCK_FAILED_CANCEL) {
1438 _real_path = (char *)0;
1442 // we have obtained the lock, go ahead and open the mailbox
1443 // Do not open the mailbox with privileges enabled, even if
1444 // creating the file
1446 PRIV_ENABLED_OPEN(_real_path, _fd,
1447 SafeOpen(_real_path, open_mode, create_mode));
1452 PRIV_ENABLED_OPEN(_real_path, _fd,
1453 SafeOpen(_real_path, open_mode, create_mode));
1456 #if !defined(SENDMAIL_LOCKS)
1457 // On some systems, sendmail uses lockf while delivering mail.
1458 // We can not hold a lockf style lock on those systems. Of course
1459 // if the user has turned off ToolTalk locking, they are playing
1462 if ( (_fd >= 0) && ( (open_mode & O_RDWR) == O_RDWR) )
1464 enum DtmFileLocality location;
1467 // Only attempt lockf() if file was opened for reading and writing
1469 location = DtMail::DetermineFileLocality(_real_path);
1470 #if defined(DEBUG_RFCMailBox)
1471 char *locstr = (location==Dtm_FL_UNKNOWN) ?
1473 ((location==Dtm_FL_LOCAL) ? "Local" : "Remote");
1474 DEBUG_PRINTF(("openRealFile: location is %s\n", locstr));
1480 case Dtm_FL_UNKNOWN:
1482 // locality unknown -- assume local and lock
1486 // locality local - apply lock
1488 assert(_lockf_active == DTM_FALSE);
1489 lseek(_fd, 0, SEEK_SET);
1490 PRIV_ENABLED_OPTIONAL(return_status,SafeLockf(_fd, F_TLOCK, 0));
1491 if (return_status == -1) {
1492 error.setError(DTME_OtherOwnsWrite);
1496 _lockf_active = DTM_TRUE;
1501 // locality otherwise -- assume remote dont lock
1510 (void) umask(oldUmask);
1515 switch (saveErrno) {
1517 error.setError(DTME_NoPermission);
1521 error.setError(DTME_IsDirectory);
1525 error.setError(DTME_NoSuchFile);
1529 error.setError(DTME_ObjectAccessFailed);
1533 _real_path = (char *)0;
1538 if ((open_mode & O_CREAT) && requiresMailGroupCreation) {
1540 // Make sure a newly created file has the correct group owner i.d.
1542 gid_t groupId = GetIdForGroupName(DTMAIL_DEFAULT_CREATE_MAILGROUP);
1544 (void) SafeFchown(_fd, (unsigned int) -1, groupId);
1547 if ((open_mode & 0x3) == O_RDWR) {
1548 if (answer == DTMBX_LONGLOCK_SUCCESSFUL ||
1549 answer == DTMBX_LONGLOCK_FAILED_READWRITE)
1550 _mail_box_writable = DTM_TRUE;
1552 _mail_box_writable = DTM_FALSE;
1555 _mail_box_writable = DTM_FALSE;
1559 _file_size = lseek(_fd, 0, SEEK_END);
1560 lseek(_fd, 0, SEEK_SET);
1561 _links = buf.st_nlink;
1563 _lockFileName = generateLockFileName();
1564 assert(_lockFileName != NULL);
1569 RFCMailBox::realFileSize(DtMailEnv & error, struct stat * stat_buf)
1573 // We have to deal with the problem of editors that will unlink
1574 // the file we are watching and rename it to a new file. This
1575 // will cause us to miss new mail, and eventually write data that
1576 // is out of sync with the real state of the file.
1578 // We do this by checking the link count first. If it has changed,
1579 // we will close, reopen, and then stat. Note that we could say if
1580 // the link count has gone down we will do this, but since we can't
1581 // be sure exactly what has happened to this file, we will consider
1582 // any change in the link count to require us to reopen the file.
1587 error_code = SafeFStat(_fd, &buf);
1588 if (error_code >= 0) {
1589 if (buf.st_nlink != _links) {
1592 DEBUG_PRINTF( ("realFileSize: (buf.st_nlink!=_links\n") );
1593 old_mode = fcntl(_fd, F_GETFL) & O_ACCMODE;
1596 PRIV_ENABLED_OPEN(_real_path,
1598 SafeOpen(_real_path, old_mode, 0666));
1600 error.setError(DTME_ObjectAccessFailed);
1604 transferLock(_fd, fd);
1608 if (SafeFStat(_fd, &buf) >= 0) {
1609 _links = buf.st_nlink;
1612 error.logError(DTM_FALSE,
1613 "realFileSize(): fstat(%d/%s) failed errno=%d\n",
1614 _fd, _real_path, errno);
1615 error.setError(DTME_ObjectAccessFailed);
1625 error.logError(DTM_FALSE,
1626 "realFileSize(): fstat(%d/%s) failed errno=%d\n",
1627 _fd, _real_path, errno);
1628 error.setError(DTME_ObjectAccessFailed);
1632 return(buf.st_size);
1636 RFCMailBox::mapFile(DtMailEnv & error,
1637 const DtMailBoolean already_locked,
1640 char *pname = "mapFile";
1643 if (already_locked == DTM_FALSE) {
1644 DEBUG_PRINTF( ("%s: locking mailbox\n", pname) );
1646 if (error.isSet()) {
1647 DtMailEnv tmp_error;
1648 unlockFile(tmp_error, _fd);
1653 // We must map in whole pages. This is done by rounding the
1654 // file size up to the next larger size.
1657 // Some notes on the singing and dancing that follows are in order.
1658 // As of 6/21/95 it has been determined that there is a bug in S494-17
1659 // and S495-25 whereby a mmap() of the mailbox can return sections that
1660 // have "nulls" where valid data should be found. To guard against this,
1661 // we check for nulls at the beginning and the end of a new mmap()ed region
1662 // and if they are found we retry the operation.
1664 // If NFS attribute caching is enabled (which is the default), a
1665 // stat/fstat of a NFS file may not return the correct true size of the
1666 // mailbox if it has been appended to since the last time it was
1667 // mmap()ed. To get around this problem, once it is noticed via
1668 // stat()/fstat() that the mailbox has changed, we must open the mailbox
1669 // on a separate file descriptor, read() in a byte, and then do a fstat()
1670 // to determine the true correct size of the mailbox.
1673 // fstat() currently open mailbox
1675 struct stat tempStatbuf;
1678 if (SafeFStat(_fd, &tempStatbuf) < 0) {
1682 "%s(%d): fstat(%d/%s) failed errno=%d\n",
1683 pname, err_phase, _fd, _real_path, errno);
1684 if (already_locked == DTM_FALSE) {
1685 DEBUG_PRINTF( ("%s: unlocking mailbox\n", pname) );
1686 unlockFile(error, _fd);
1688 error.setError(DTME_ObjectAccessFailed);
1692 // Obtain guaranteed current stat buf for mailbox object,
1693 // regarless of whether it is local or remote
1695 struct stat statbuf;
1698 if (SafeGuaranteedStat(_real_path, &statbuf) == -1) {
1702 "%s(%d): SafeGuaranteedStat(%s) failed errno=%d\n",
1703 pname, err_phase, _real_path, errno);
1704 if (already_locked == DTM_FALSE) {
1705 DEBUG_PRINTF( ("%s: unlocking mailbox\n", pname) );
1706 unlockFile(error, _fd);
1708 error.setError(DTME_ObjectAccessFailed);
1713 // statbuf -- contains the guaranteed stat struct for the mailbox
1714 // tempStatbuf -- contains fstat stat struct for original mailbox
1717 // See if the inode has changed - if so the file has been
1718 // modified out from under is
1721 if (statbuf.st_ino != tempStatbuf.st_ino) {
1723 error.logError(DTM_FALSE,
1724 "%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",
1725 pname, err_phase, _fd, _real_path, _real_path,
1726 statbuf.st_ino, statbuf.st_dev,
1727 statbuf.st_nlink, statbuf.st_size,
1728 tempStatbuf.st_ino, tempStatbuf.st_dev,
1729 tempStatbuf.st_nlink, tempStatbuf.st_size);
1730 if (already_locked == DTM_FALSE) {
1731 DEBUG_PRINTF( ("%s: unlocking mailbox\n", pname) );
1732 unlockFile(error, _fd);
1734 error.setError(DTME_ObjectInvalid);
1738 long pagesize = memoryPageSize();
1740 // We will only map any file space we haven't mapped so far.
1741 // We always map entire pages to make this easier. We must
1742 // remap the partial pages so we will get the real bits, not
1743 // the zero fill bits provided by mmap.
1745 // The arithmetic for partial mappings is a little odd, and
1746 // is worthy of explanation. The main issue is that we must
1747 // always start on a page boundary, and we must map page
1748 // size chunks. So, for any offset, we need to back up to
1749 // the start of a page in the file. This gives us the following
1750 // entries in MapRegion:
1752 // map_region - Address where the mapping starts.
1753 // map_size - Size of the mapping (will always be a multiple of pagesize).
1754 // file_region - Address where requested file offset begins within
1756 // file_size - Size of file within this mapping.
1757 // offset - Where the mapping begins (which is almost always different
1758 // than the offset where file_region begins.
1760 // So, the new offset begins where the last real file size ended.
1761 // We get this by adding the previous offset, to the file size mapped,
1762 // and then add any the difference between the mapped region and the
1763 // real file offset. This gets us an offset back to the old end of
1764 // file. Now if you are not confused, we need to adjust this new offset
1765 // back to the closest page boundary and begin the mapping from there!
1767 // File by messages:
1768 // v--------v------------v----v---------------------------v----------------v
1769 // | Msg1 | Msg2 |Msg3| Msg4 | Msg5 |
1770 // ^--------^------------^----^---------------------------^----------------^
1772 // +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
1773 // | Pg1 | Pg2 | Pg3 | Pg4 | Pg5 | Pg6 | Pg7 | Pg8 | Pg9 | Pg10| Pg11| Pg12|
1774 // +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
1779 // +--v--+-----+---v-+
1780 // | \\\\\\\\\\\\\\ |
1781 // +--^--+-----+---^-+
1792 // |->| offset_from_map
1805 // |---------------->| .offset
1807 // |-->| offset_from_map
1811 MapRegion * map = new MapRegion;
1812 long offset_from_map;
1814 if (_mappings.length()) {
1815 MapRegion * prev_map = _mappings[_mappings.length() - 1];
1816 map->offset = prev_map->offset + prev_map->file_size +
1817 (prev_map->file_region - prev_map->map_region);
1818 offset_from_map = map->offset % pagesize;
1819 map->offset -= offset_from_map;
1823 offset_from_map = 0;
1826 map->file_size = statbuf.st_size - map->offset - offset_from_map;
1827 map->map_size = statbuf.st_size - map->offset;
1828 map->map_size += (pagesize - (map->map_size % pagesize));
1830 int flags = MAP_PRIVATE;
1832 #if defined(MAP_NORESERVE)
1833 // We are not supposed to be writing to these pages. If
1834 // we don't specify MAP_NORESERVE however, the system will
1835 // reserve swap space equal to the file size to deal with
1836 // potential writes. This is wasteful to say the least.
1838 flags |= MAP_NORESERVE;
1841 // Need to obtain an mmap()ed region and one way or another
1842 // have the data from the mail file placed into that
1843 // region. There are two ways of accomplishing this:
1845 // Method 1: mmap() the data directly from the file
1846 // Method 2: mmap() /dev/zero and then read from the file to the region
1848 // We want to use method #1 if at all possible as it allows the
1849 // VM system to page the data in as it is accessed.
1851 // There is a potential problem with method #1 in that since
1852 // the region is a "view" into the real file data, if the file
1853 // itself is reduced in size behind our back by another
1854 // process, we have the potential for generating SIGBUS memory
1855 // reference errors if we try and access a byte that is no
1856 // longer within the file. This is true even is MAP_PRIVATE is used:
1858 // ** The behavior of PROT_WRITE can be influenced by setting
1859 // ** MAP_PRIVATE in the flags parameter. MAP_PRIVATE means
1860 // ** "Changes are private".
1862 // ** MAP_SHARED and MAP_PRIVATE describe the disposition of write
1863 // ** references to the memory object. If MAP_SHARED is
1864 // ** specified, write references will change the memory object.
1865 // ** If MAP_PRIVATE is specified, the initial write reference
1866 // ** will create a private copy of the memory object page and
1867 // ** redirect the mapping to the copy. Either MAP_SHARED or
1868 // ** MAP_PRIVATE must be specified, but not both. The mapping
1869 // ** type is retained across a fork(2).
1871 // ** Note that the private copy is not created until the first
1872 // ** write; until then, other users who have the object mapped
1873 // ** MAP_SHARED can change the object.
1875 // While this is always the case for a process that does not
1876 // abide by the advisory only locking protocols used to protect
1877 // against this, to guard against this method #1 is only used
1878 // if the mailbox is writable and we can obtain a short term
1879 // lock on the mailbox. This prevents the mailbox from changing
1880 // size at the moment we map it. Nonetheless, a SIGBUS
1881 // interrupt handler must be armed to handle the case where the
1882 // mailbox file is changed out from under us by devious means.
1884 // If this condition is not met, or the mmap() call for some
1885 // reason fails, then fall back to method #2.
1888 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";
1890 map->map_region = (char *)-1;
1892 if ( (_use_dot_lock == DTM_TRUE)
1893 && (_mail_box_writable == DTM_TRUE)
1894 #if defined(__osf__)
1895 && (_long_lock_active == DTM_TRUE)
1899 #if defined(__osf__) && OSMAJORVERSION < 4
1900 // This version of mmap does NOT allow requested length to be
1901 // greater than the file size ... in contradiction to the
1902 // documentation (don't round up).
1903 map->map_region = (char *) mmap(
1904 0, (statbuf.st_size - map->offset),
1905 PROT_READ, flags, _fd, (off_t) map->offset);
1907 map->map_region = (char *)mmap(
1908 0, (unsigned int) map->map_size,
1909 PROT_READ, flags, _fd, (off_t) map->offset);
1912 // ERROR/CONSISTENCY CHECKING: if the region was not mapped,
1913 // or the first or last byte of the region is a null, then
1914 // the mmap() is considered to have failed: write an entry
1915 // to the dump file, undo the damage, and bail to the caller
1918 if ( (map->map_region == (char *) -1) ||
1919 (map->file_size && (map->map_region[offset_from_map] == '\0')) ||
1921 (map->map_region[offset_from_map+map->file_size-1] == '\0'))
1925 ("mapFile: Error mmap(1) == %p, errno = %d\n", map->map_region, errno));
1931 map->map_size, PROT_READ, flags, _fd, _real_path, map->offset,
1932 map->map_region, errno);
1934 "%s(%d): statbuf: ino=%d, dev=%d, nlink=%d, size=%ld\n",
1936 statbuf.st_ino, statbuf.st_dev, statbuf.st_nlink, statbuf.st_size);
1938 if (map->map_region == (char *) -1)
1942 "%s(%d): mmap failed: errno = %d: %s\n",
1943 pname, err_phase, errno, strerror(errno));
1950 for (i=(int)offset_from_map, cnt=0;
1951 i<map->file_size+offset_from_map;
1953 if (map->map_region[i] == '\0') cnt++;
1956 "%s(%d): mmap failed: %d NULLs in map from byte %d to %d:\n",
1958 cnt, offset_from_map, map->file_size+offset_from_map);
1962 // Pass throught to attempt mapping through /dev/zero.
1964 munmap(map->map_region, (size_t) map->map_size);
1965 map->map_region = (char*) -1;
1967 if (_errorLogging) {
1971 (unsigned char *)map->map_region,
1973 _errorLogging ? 0 : 100);
1976 "Offset Region mapped in",
1977 (unsigned char *)map->map_region+offset_from_map,
1979 _errorLogging ? 0 : 100);
1982 if (already_locked == DTM_FALSE) {
1983 DEBUG_PRINTF( ("%s: unlocking mailbox\n", pname) );
1984 unlockFile(error, _fd);
1987 map->map_region != (char *)-1 ?
1988 DTME_ObjectAccessFailed :
1989 DTME_ObjectCreationFailed);
1997 ("mapFile: mmap(1) okay = %p, errno = %d\n", map->map_region, errno) );
2000 // If a region was mapped, cause OS to use sequential access paging rules
2002 if (map->map_region != (char *) -1)
2003 alterPageMappingAdvice(map);
2005 if (map->map_region == (char *) -1) {
2006 // Either the direct mmap failed, or we decided not to do a direct mmap
2007 // of the new data in the mailbox file. Now must create a mmap()ed
2008 // region against /dev/zero and then read the new data into that region
2010 char *devzero = "/dev/zero";
2012 #if defined(DO_ANONYMOUS_MAP)
2014 mode_t mode = create_mode;
2015 flags |= MAP_ANONYMOUS;
2017 int fd = SafeOpen(devzero, O_RDWR, create_mode);
2020 map->map_region = (char *)mmap(
2021 0, (unsigned int) map->map_size,
2022 PROT_READ|PROT_WRITE, flags, fd, 0);
2025 if (map->map_region == (char *)-1) {
2032 map->map_size, PROT_READ|PROT_WRITE, flags, fd, devzero, errno);
2036 map->map_size, PROT_READ|PROT_WRITE, flags, fd, devzero, errno);
2038 if (already_locked == DTM_FALSE) {
2039 DEBUG_PRINTF( ("%s: unlocking mailbox\n", pname) );
2040 unlockFile(error, _fd);
2042 error.setError(DTME_NoMemory);
2044 #if !defined(DO_ANONYMOUS_MAP)
2050 if (fd != -1 && (SafeClose(fd) < 0)) {
2051 // should deal with the error here (on /dev/zero??)
2054 // Have created a sufficiently large region mapped to /dev/zero
2055 // Read the "new bytes" into this region
2056 // Note that there is a suspicion that a window exists where even
2057 // though the file size appears to be increased by "n", that all
2058 // of the data may not be written in the file yet, and a read may
2059 // fall "short" in this case, especially if running over nfs. We
2060 // must be prepared to handle any condition whereby a read from
2061 // the file either yields less data than expected, or even though
2062 // a good return is received, the data may not be there, and thus
2063 // the check for "nulls" at the beginning and end of the buffer.
2066 // Copying messages between mailboxes often leaves a single
2067 // NULL character at the end of the file.
2068 // Check the last 2 bytes for NULL characters.
2071 lseek(_fd, (off_t) map->offset, SEEK_SET);
2072 size_t bytesToRead = (size_t)(statbuf.st_size - map->offset);
2073 ssize_t readResults = SafeRead(_fd, map->map_region, bytesToRead);
2074 if ( (readResults != bytesToRead) ||
2075 (readResults && (map->map_region[0] == '\0')) ||
2076 (readResults && (map->map_region[readResults-1] == '\0')) ||
2077 (readResults && (map->map_region[offset_from_map] == '\0')) ||
2079 (map->map_region[offset_from_map+map->file_size-1] == '\0'))
2084 "%s(%d): SafeRead(%d(%s), 0x%08lx, %d) == %d, errno == %d\n",
2085 pname, err_phase, _fd, _real_path, map->map_region, bytesToRead,
2086 readResults, errno);
2088 "%s(%d): stat buf: ino=%d, dev=%d, nlink=%d, size=%ld\n",
2089 pname, err_phase, statbuf.st_ino, statbuf.st_dev,
2090 statbuf.st_nlink, statbuf.st_size);
2092 if (readResults > 0) {
2097 (unsigned char *)map->map_region, readResults,
2098 _errorLogging ? 0 : 100);
2101 munmap(map->map_region, (size_t) map->map_size);
2103 if (already_locked == DTM_FALSE) {
2104 DEBUG_PRINTF( ("%s: unlocking mailbox\n", pname) );
2105 unlockFile(error, _fd);
2110 DTME_ObjectAccessFailed :
2111 DTME_ObjectCreationFailed);
2115 mprotect(map->map_region, (size_t) map->map_size, PROT_READ);
2116 alterPageMappingAdvice(map);
2119 map->file_region = map->map_region + offset_from_map;
2121 // Ok, we think we have got all of the new data that has been
2122 // appended to the mailbox - just to make absolutely sure, stat
2123 // the file again and make sure that the file size has remained
2124 // consistent throughout this operation. If it has changed, it
2125 // means some process ignored our lock on the file and appended
2126 // data anyway. In this case, throw away our current effort and
2127 // recursively call this function to try the attempt again.
2131 if (SafeGuaranteedStat(_real_path, &tempStatbuf) < 0) {
2135 "%s(%d): SafeGuaranteedStat(%s) failed errno=%d: %s\n",
2136 pname, err_phase, _real_path, errno, strerror(errno));
2137 munmap(map->map_region, (size_t) map->map_size);
2139 if (already_locked == DTM_FALSE) {
2140 DEBUG_PRINTF( ("%s: unlocking mailbox\n", pname) );
2141 unlockFile(error, _fd);
2143 error.setError(DTME_ObjectAccessFailed);
2148 if (tempStatbuf.st_size != statbuf.st_size) {
2152 "%s(%d): fstat(%d/%s) size changed %d/%d\n",
2154 _fd, _real_path, statbuf.st_size, tempStatbuf.st_size);
2155 munmap(map->map_region, (size_t) map->map_size);
2157 int mapResults = mapFile(error, DTM_TRUE);
2158 if (error.isSet()) {
2159 if (already_locked == DTM_FALSE) {
2160 DEBUG_PRINTF( ("%s: unlocking mailbox\n", pname) );
2161 unlockFile(error, _fd);
2168 if (already_locked == DTM_FALSE) {
2169 DEBUG_PRINTF( ("%s: unlocking mailbox\n", pname) );
2170 unlockFile(error, _fd);
2173 // We need to set _file_size here, because if we get the file size
2174 // when the mailbox is not locked, it is possible that we could be
2175 // stating the file while it is being written to. Since we have the
2176 // lock in this routine, we know the file size isn't changing, and it
2177 // is consistent with the amount being mapped in.
2179 _file_size = statbuf.st_size;
2181 if (_hide_access_events) mailboxAccessHide("mapFile");
2183 return(_mappings.append(map));
2187 RFCMailBox::nextNotDel(const int cur)
2190 for (del = cur; del < _msg_list.length(); del++) {
2191 MessageCache * mc = _msg_list[del];
2192 if (mc->delete_pending == DTM_FALSE) {
2201 RFCMailBox::prevNotDel(const int cur)
2204 for (del = cur; del >= 0; del--) {
2205 MessageCache * mc = _msg_list[del];
2206 if (mc->delete_pending == DTM_FALSE) {
2215 RFCMailBox::lookupByMsg(RFCMessage * msg)
2217 for (int slot = 0; slot < _msg_list.length(); slot++) {
2218 MessageCache * mc = _msg_list[slot];
2219 if (mc->message == msg) {
2228 RFCMailBox::ThreadParseEntry(void * client_data)
2230 RFCMailBox * self = (RFCMailBox *)client_data;
2233 MutexLock lock_map(self->_map_lock);
2235 self->parseFile(error, 0);
2244 RFCMailBox::parseFile(DtMailEnv & error, int map_slot)
2249 // We are not at the eof.
2253 const char * begin = _mappings[map_slot]->file_region;
2254 const char * end = begin + _mappings[map_slot]->file_size - 1;
2257 // Empty file. DO NOTHING OR IT WILL CRASH!
2263 // Parsing will always be a sequential access to the pages.
2264 // We will give the kernel a clue what we are up to and perhaps
2265 // help our parsing time in the process.
2267 unsigned long pagelimit = _mappings[map_slot]->map_size;
2269 #if !defined(USL) && !defined(__uxp__) && !defined(linux) && !defined(sun)
2270 // no madvise; dont use optimization
2272 (char *)_mappings[map_slot]->map_region,
2273 (size_t) pagelimit, MADV_SEQUENTIAL);
2276 // We should always begin with a "From " if this is an RFC file,
2277 // with a valid offset. If we have something else, then look
2278 // forward until we find one.
2280 const char * parse_loc = begin;
2281 if (strncmp(parse_loc, "From ", 5)) {
2282 if (*parse_loc == ' ' || *parse_loc == '\n' || *parse_loc == '\t') {
2283 // We allow any number of white spaces before "From"
2284 // But "From" still has to be the first word in the line
2285 while ( (*parse_loc == ' ' || *parse_loc == '\n'
2286 || *parse_loc == '\t') && parse_loc <= end) {
2290 if (parse_loc >= end) {
2291 error.setError(DTME_NotMailBox);
2292 _at_eof.setTrue(); // We are, but so what.
2298 if (strncmp(parse_loc, "\nFrom ", 6)) {
2299 error.setError(DTME_NotMailBox);
2303 // This file does not start with either From or white space
2304 error.setError(DTME_NotMailBox);
2309 if (*parse_loc == '\n') {
2313 // We are sitting at the start of a message. We will build message
2314 // objects for each message in the list.
2317 MessageCache * cache = new MessageCache;
2318 cache->delete_pending = DTM_FALSE;
2320 cache->message = new RFCMessage(error, this, &parse_loc,
2322 if (error.isNotSet()) {
2324 //#ifdef MESSAGE_PARTIAL
2325 // message/partial processing is currently not working, so we are
2326 // taking out message/parital processing for now until we figure
2327 // out how to get it to work again. Only the following block of
2328 // code needs to be taken out in order to disable message/partial.
2329 // Also, when it was turned on, it was trying to combine partial
2330 // messages that were non-MIME (no MIME-VERSION header). This
2331 // caused even more problems. We should only check for partial
2332 // messages if it is MIME.
2334 if (_isPartial(error, cache->message)) {
2336 if (error.isNotSet()) {
2337 cache->message->setFlag(error, DtMailMessagePartial);
2339 if (error.isNotSet()) {
2340 cache->message = _assemblePartial(error, cache->message);
2342 if (error.isNotSet()) {
2343 _msg_list.append(cache);
2349 //#endif // MESSAGE_PARTIAL
2350 _msg_list.append(cache);
2355 } while (parse_loc <= end);
2357 // At this point we most likely will see random behavior. We will
2358 // tell the kernel to pull in the minimum number of extra pages.
2360 #if !defined(USL) && !defined(__uxp__) && !defined(linux) && !defined(sun)
2361 // no madvise; dont use optimization
2363 (char *)_mappings[map_slot]->map_region,
2364 (size_t) pagelimit, MADV_RANDOM);
2366 // IBM code for message/partial vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
2367 // we need delete those messages if they are satisfied the
2368 // following two conditions
2369 // (1) marked for delete and
2370 // (2) it is a message/partial message
2372 // Otherwise, we will get segmentation error when the method
2373 // writeMailBox is invoked because a new message were
2374 // generated by assembling partial messages
2376 for (int msg = 0; msg < _msg_list.length(); msg++) {
2377 MessageCache * mc = _msg_list[msg];
2378 if (mc->delete_pending == DTM_FALSE) {
2379 DtMail::Envelope * env = mc->message->getEnvelope(error);
2381 DtMailValueSeq value;
2383 static const char * partial = "message/partial";
2384 static const char * contentType = "content-type";
2386 env->getHeader(error, contentType , DTM_FALSE, value);
2387 if (error.isNotSet()) {
2388 type = strdup(*(value[0]));
2394 if (error.isNotSet()) {
2395 env->getHeader(error, RFCDeleteHeader, DTM_FALSE, value);
2397 if (!error.isSet() && (strncasecmp(type, partial, 15) == 0)) {
2398 delete mc->message; // remove message storage
2399 delete mc; // remove message cache storage
2400 _msg_list.remove(msg); // remove message from message list
2401 msg -= 1; // next message is where we are at now
2412 //IBM code for message/partial ^^^^^^^^^^^^^^^^^^^^^^^^^^^
2419 RFCMailBox::ThreadNewMailEntry(void * client_data)
2421 NewMailData * info = (NewMailData *)client_data;
2423 // We need to wait for the object to become valid. We have
2424 // nothing to do until the mail box is open.
2425 info->object_valid->waitTrue();
2427 // Get the various configuration parameters from .mailrc. The
2428 // RFC_PING_INTERVAL controls how often we look for new mail.
2429 // RFC_CHECK is the number of pings between checks and
2430 // RFC_EXPUNGE is the number of pings between expunges.
2433 DtMail::MailRc * mailrc = info->self->session()->mailRc(error);
2437 time_t check_per_ping = 120;
2438 time_t expunge_per_ping = 240;
2440 const char * value = NULL;
2441 mailrc->getValue(error, "RFC_PING_INTERVAL", &value);
2442 if (error.isNotSet()) {
2443 ping = (time_t) strtol(value, NULL, 10);
2449 free((void*) value);
2452 mailrc->getValue(error, "RFC_CHECK", &value);
2453 if (error.isNotSet()) {
2454 check_per_ping = (time_t) strtol(value, NULL, 10);
2460 free((void*) value);
2463 mailrc->getValue(error, "RFC_EXPUNGE", &value);
2464 if (error.isNotSet()) {
2465 expunge_per_ping = (time_t) strtol(value, NULL, 10);
2471 free((void*) value);
2475 unsigned int unslept = 0;
2477 // Wait until the mail file is parsed.
2479 info->self->_at_eof.waitTrue();
2481 // We loop until the object tries to close, then we exit.
2483 while(info->object_valid->state()) {
2485 // The following sequence is a little weird, but here is why.
2486 // We need to sleep for the ping interval. We can be awaken
2487 // early however if the thread catches a signal. The main
2488 // thread will send a SIGTERM from the mail box destructor
2489 // to wake us up. If the object state is no longer valid, then
2490 // this is the cause, so we need to check it first and exit
2491 // if this is the case.
2493 // We can also be awaken by other signals that we don't care
2494 // about. In that case we need to go back through the loop
2495 // again and sleep any unslept time. We can't be sure however
2496 // that this additional sleep won't be awaken before it is
2497 // done so we have to keep looping and checking the object
2501 unslept = (unsigned int) ThreadSleep(unslept);
2502 continue; // We got rudely awaken!
2505 unslept = (unsigned int) ThreadSleep(ping);
2506 if (!info->object_valid->state()) {
2514 // We made here, we slept and are well rested, and
2515 // we should have a valid object. Run the events.
2520 if (check_per_ping && (pinged % check_per_ping) == 0) {
2522 // info->self->CheckPointEvent(error);
2523 info->self->CheckPointEvent();
2524 // if (error.isSet()) {
2525 // // MAILBOX COULD NOT BE CHECKPOINTED!!! MUST DO SOMETHING!!!
2530 if (expunge_per_ping && (pinged % expunge_per_ping) == 0) {
2531 info->self->ExpungeEvent();
2535 info->self->NewMailEvent();
2538 // We are responsible for cleaning up the condition variable.
2540 delete info->object_valid;
2550 RFCMailBox::PollEntry(void * client_data)
2552 RFCMailBox * self = (RFCMailBox *)client_data;
2554 // Get the various configuration parameters from .mailrc. The
2555 // RFC_PING_INTERVAL controls how often we look for new mail.
2556 // RFC_CHECK is the number of pings between checks and
2557 // RFC_EXPUNGE is the number of pings between expunges.
2562 DtMail::MailRc * mailrc = self->session()->mailRc(error);
2563 error.clear(); // IGNORING ERRORS FROM MAILRC CALL!!
2566 time_t ping = 60; // check for new mail every 60 seconds
2567 time_t save_interval = (30 * 60); // autosave every 30 minutes
2568 long minimumIdleTime = 30; // must have 30 seconds idle time to poll
2570 // Retrieve current setting for how often we are supposed to
2571 // look for new mail (retrieveinterval)
2576 mailrc->getValue(error, "retrieveinterval", &value);
2577 if (error.isNotSet() && value != NULL && *value != '\0')
2579 ping = (time_t) strtol(value, NULL, 10);
2586 if (NULL != value) free((void*) value);
2588 // Retrieve current setting for how often we are supposed to
2589 // perform an auto save of the mailbox
2592 mailrc->getValue(error, "dontautosave", &value);
2593 if (error.isSet()) {
2596 free((void*) value);
2599 mailrc->getValue(error, "saveinterval", &value);
2600 if (error.isNotSet() && value != NULL && *value != '\0') {
2601 save_interval = (time_t) strtol(value, NULL, 10) * 60;
2604 if (save_interval < 60)
2605 //save_interval = 60; // The minimum time is every minute
2606 save_interval = 15; // The minimum time is every minute
2608 else save_interval = -1;
2610 free((void*) value);
2612 // Get the current time in seconds, and compute how long it has
2613 // been since the last interactive input (keystroke, buttonpress)
2614 // was done by the user
2616 time_t now = time(NULL);
2617 time_t lastInteractive =
2618 (time_t) self->session()->lastInteractiveEventTime();
2619 time_t interactiveIdleTime = (now - lastInteractive);
2621 // If there has not been at least <<inactivityinterval> seconds of
2622 // interactive inactivity, skip processing until later so as not to
2623 // place the user in the horrible position of being "locked out" for
2624 // the duration of an auto save/and/or/new mail incorporation
2628 mailrc->getValue(error, "inactivityinterval", &value);
2629 if (error.isNotSet() && value != NULL && *value != '\0') {
2630 minimumIdleTime = strtol(value, NULL, 10);
2631 if (minimumIdleTime < 15)
2632 minimumIdleTime = 15; // at least 15 seconds must go by
2633 else if (minimumIdleTime > 600)
2634 minimumIdleTime = 600; // but not more than 10 minutes
2638 free((void*) value);
2640 if (interactiveIdleTime < minimumIdleTime)
2643 // See if time's up for doing a new mail incorporate
2645 if (ping > 0 && (now - self->_last_poll > ping))
2647 self->_last_poll = now;
2648 self->NewMailEvent();
2651 // See if time's up for doing an auto-save.
2652 // If time's up, check to see if the flag is clear for doing one.
2653 // Flag is not clear if say, the user is in the middle of composing
2656 if (save_interval >= 0 &&
2657 ((now - self->_last_check) > save_interval) &&
2658 (self->session()->getAutoSaveFlag())) {
2660 self->_last_check = (int) now;
2663 // self->CheckPointEvent(error);
2664 self->CheckPointEvent();
2665 // if (error.isSet()) {
2666 // // MAILBOX COULD NOT BE CHECKPOINTED!!! MUST DO SOMETHING!!!
2673 RFCMailBox::NewMailEvent(
2674 const DtMailBoolean already_locked
2677 DtMailEnv error, error1;
2679 struct stat tempStatbuf;
2680 struct stat statbuf;
2681 DtMailEventPacket event;
2682 DtMailCallbackOp op;
2684 if (!_object_valid->state()) return;
2685 if (!_mr_allowed) return;
2687 _session->setBusyState(error1, DtMailBusyState_NewMail);
2689 op = retrieveNewMail(error);
2692 const char *errmsg = NULL;
2693 errmsg = (const char *) error;
2696 event.target = DTM_TARGET_MAILBOX;
2697 event.target_object = this;
2698 event.operation = (void*) op;
2700 event.argument = strdup(errmsg);
2702 event.argument = NULL;
2703 event.event_time = time(NULL);
2705 // longUnlock(error);
2706 _session->writeEventData(error, &event, sizeof(event));
2707 _session->setBusyState(error1, DtMailBusyState_NotBusy);
2711 if ((SafeFStat(_fd, &tempStatbuf) < 0) ||
2712 (SafeGuaranteedStat(_real_path, &statbuf) == -1))
2715 _mail_box_writable = DTM_FALSE;
2718 event.target = DTM_TARGET_MAILBOX;
2719 event.target_object = this;
2720 event.operation = (void *)DTMC_ACCESSFAILED;
2721 event.argument = NULL;
2722 event.event_time = time(NULL);
2724 error.setError(DTME_ObjectAccessFailed);
2725 _session->writeEventData(error, &event, sizeof(event));
2726 _session->setBusyState(error1, DtMailBusyState_NotBusy);
2730 // See if the inode has changed - if so the file has been
2731 // modified out from under is
2733 if (statbuf.st_ino != tempStatbuf.st_ino)
2736 _mail_box_writable = DTM_FALSE;
2739 event.target = DTM_TARGET_MAILBOX;
2740 event.target_object = this;
2741 event.operation = (void *)DTMC_INODECHANGED;
2742 event.argument = NULL;
2743 event.event_time = time(NULL);
2745 error.setError(DTME_MailboxInodeChanged);
2746 _session->writeEventData(error, &event, sizeof(event));
2747 _session->setBusyState(error1, DtMailBusyState_NotBusy);
2751 // We need to compare the current file size to the last size we
2752 // knew about. If it has changed, we need to do something. There
2753 // are a few possibilities here that we must deal with:
2755 // 1. The file size has not changed. Boring. Simply return.
2757 // 2. The file size is larger, and the first byte begins with
2758 // either "From ", "\nFrom ", or "\r\nFrom ". In this case,
2759 // we need to parse the rest of the file, and notify the client
2760 // that new mail has arrived.
2762 // 3. The file size is either smaller, or it is larger and the first
2763 // few bytes dont match #2. This is the worse possible case. This
2764 // means that somebody has modified the object from beneath us.
2765 // We will turn on our "lost state" mode and refuse to do any
2766 // further processing. This thread exits immediately.
2768 // Even though we get the file size here, we can't use it to set the
2769 // new _file_size, because the file isn't currently locked, so another
2770 // process could be writing to it at this time. We rely on mapFile()
2771 // to get and set _file_size while it has the file locked.
2773 off_t size = realFileSize(error, &info);
2774 if (error.isSet()) {
2776 _mail_box_writable = DTM_FALSE;
2779 event.target = DTM_TARGET_MAILBOX;
2780 event.target_object = this;
2781 event.operation = (void *)DTMC_ACCESSFAILED;
2782 event.argument = NULL;
2783 event.event_time = time(NULL);
2785 _session->writeEventData(error, &event, sizeof(event));
2786 _session->setBusyState(error1, DtMailBusyState_NotBusy);
2790 MutexLock lock_object(_obj_mutex);
2792 if (size == _file_size)
2794 _session->setBusyState(error1, DtMailBusyState_NotBusy);
2795 if (_hide_access_events!=DTM_TRUE && info.st_atime<=info.st_mtime)
2796 mailboxAccessShow(info.st_mtime, "NewMailEvent: file_size unchanged");
2800 else if (size > _file_size)
2802 incorporate(error, already_locked);
2803 if (_hide_access_events!=DTM_TRUE && info.st_atime<=info.st_mtime)
2804 mailboxAccessShow(info.st_mtime, "NewMailEvent: file_size grew");
2809 _mail_box_writable = DTM_FALSE;
2810 *_object_valid = -1;
2813 event.target = DTM_TARGET_MAILBOX;
2814 event.target_object = this;
2815 event.operation = (void *)DTMC_BADSTATE;
2816 event.argument = NULL;
2817 event.event_time = time(NULL);
2819 _session->writeEventData(error, &event, sizeof(event));
2822 char *strbuf = new char[128];
2824 "NewMailEvent: File shrank %ld<%ld: already_locked is %s\n",
2825 (unsigned long)size, (unsigned long)_file_size,
2826 already_locked ? "TRUE" : "FALSE");
2831 // We need to bail right now if we have lost a valid mapping.
2833 if (_object_valid->state() < 0) {
2834 lock_object.unlock();
2838 _session->setBusyState(error1, DtMailBusyState_NotBusy);
2843 //RFCMailBox::CheckPointEvent(DtMailEnv & error)
2844 RFCMailBox::CheckPointEvent()
2846 if (!_object_valid->state() || _dirty == 0) {
2850 // Write the mail box out.
2855 // This is INCORRECT usage of the DtMailEnv class. It is against
2856 // standard policy to not clear the error token before passing
2857 // it to a function. We are basically breaking the error handling
2858 // model in order to percolate an error in the BE up to the FE.
2860 // setBusyState() does not modify the error token, it simply
2861 // uses it to report errors back to the user.
2862 _session->setBusyState(error, DtMailBusyState_AutoSave);
2864 writeMailBox(error, _hide_access_events);
2866 if ((DTMailError_t) error ==
2867 DTME_CannotWriteToTemporaryMailboxFile_NoFreeSpaceLeft)
2871 startAutoSave(error1,DTM_FALSE);
2872 showError((char *) error.getClient());
2873 error.setClient(NULL);
2874 startAutoSave(error1,DTM_TRUE);
2877 if (error.isSet()) {
2878 // MAILBOX COULD NOT BE CHECKPOINTED!!! setBusyState must handle error.
2879 error.logError(DTM_TRUE, "RFCMailBox::CheckPointEvent(): Failed to write mailbox: %s", (const char *)error);
2881 _session->setBusyState(error, DtMailBusyState_NotBusy);
2885 RFCMailBox::ExpungeEvent(DtMailBoolean closing)
2887 if (!_object_valid->state() && closing == DTM_FALSE) {
2891 // We need to get the expiration time for a message.
2895 DtMail::MailRc * mailrc = _session->mailRc(error);
2898 const char * expire_time = NULL;
2899 mailrc->getValue(error, "DTMAIL_EXPIRE_TIME", &expire_time);
2900 if (error.isNotSet()) {
2901 expire_days = strtol(expire_time, NULL, 10);
2907 if (NULL != expire_time)
2908 free((void*) expire_time);
2910 if (expire_days == 0 && closing == DTM_FALSE) {
2911 // Only scan on close. No timed delete in effect.
2916 if (expire_days > 0 && closing == DTM_TRUE) {
2917 // Timed delete is in effect so we don't destroy messages on
2922 time_t expire_secs = (time_t) expire_days * (24 * 3600);
2923 time_t now = time(NULL);
2925 for (int msg = 0; msg < _msg_list.length(); msg++) {
2926 MessageCache * mc = _msg_list[msg];
2927 if (mc->delete_pending == DTM_TRUE) {
2928 // Can only delete it so many times!
2933 DtMail::Envelope * env = mc->message->getEnvelope(error);
2935 DtMailValueSeq value;
2936 env->getHeader(error, RFCDeleteHeader, DTM_FALSE, value);
2937 if (!error.isSet()) {
2939 deleted = (time_t) strtol(*(value[0]), NULL, 16);
2940 if ((deleted + expire_secs) < now) {
2941 mc->delete_pending = DTM_TRUE;
2944 // We need to tell our client this message is really
2945 // gone. Of course, if they are closing, why bother
2948 if (closing == DTM_FALSE) {
2949 DtMailEventPacket event;
2951 event.target = DTM_TARGET_MAILBOX;
2952 event.target_object = this;
2953 event.operation = (void *)DTMC_DELETEMSG;
2954 event.argument = mc;
2955 event.event_time = time(NULL);
2956 _session->writeEventData(error, &event, sizeof(event));
2966 // Function: RFCMailBox::createTemporaryMailboxFile
2968 // Given the name for a temporary mailbox file, create a proper
2969 // temporary mailbox file and return a file descriptor opened on
2970 // the newly created file
2972 // . obtain information on current mailbox file
2973 // . create the new temporary file
2974 // . set the permissions, owner and group of the newly created file
2975 // to match those of the current mailbox
2977 // DtMailEnv & error - standard error structure used by caller
2978 // tmp_name -- -> name for temporary file
2980 // error.isset() will indicate if there were any errors encountered, in which
2981 // case the temporary file has not been created.
2983 // int file descriptor opened on the newly created temporary mailbox file
2984 // if no error encountered.
2987 RFCMailBox::createTemporaryMailboxFile(DtMailEnv & error, char *tmp_name)
2992 if (SafeFStat(_fd, &info) < 0) {
2995 error.logError(DTM_FALSE,
2996 "createTemporaryMailboxFile(): fstat(%d) failed errno=%d\n",
2998 error.vSetError(DTME_CannotObtainInformationOnOpenMailboxFile,
2999 DTM_FALSE, NULL, _real_path, error.errnoMessage(errno2));
3003 PRIV_ENABLED(fd,SafeOpen(tmp_name, O_RDWR | O_CREAT | O_TRUNC, info.st_mode));
3008 error.setError(DTME_CannotCreateTemporaryMailboxFile_NoPermission);
3012 error.setError(DTME_CannotCreateTemporaryMailboxFile_IsDirectory);
3016 error.setError(DTME_CannotCreateTemporaryMailboxFile_NoSuchFile);
3019 #if defined(__osf__) || defined(CSRG_BASED)
3024 error.setError(DTME_CannotCreateTemporaryMailboxFile_RemoteAccessLost);
3028 error.vSetError(DTME_CannotCreateTemporaryMailboxFile,
3029 DTM_FALSE, NULL, tmp_name, error.errnoMessage(errno2));
3034 PRIV_ENABLED_OPTIONAL(return_status,SafeFChmod(fd, info.st_mode & 07777));
3035 if (return_status == -1) {
3037 (void) SafeClose(fd);
3038 PRIV_ENABLED(return_status,SafeUnlink(tmp_name));
3039 error.vSetError(DTME_CannotSetPermissionsOfTemporaryMailboxFile,
3040 DTM_FALSE, NULL, tmp_name, info.st_mode & 07777,
3041 error.errnoMessage(errno2));
3045 PRIV_ENABLED(return_status, SafeFchown(fd, info.st_uid, (unsigned int) -1));
3047 // bug 1216914 - dtmail should be able to auto-save a mailbox which is not
3048 // opened [Read Only] if the user has write access to the original mailbox,
3049 // then the user should be able to overwrite the mailbox even if the owner
3050 // gets changes. A mailbox is only opened read-write if the "access()"
3051 // system call indicates the user has write permissions.
3053 if (return_status == -1) {
3055 (void) SafeClose(fd);
3056 PRIV_ENABLED(return_status,SafeUnlink(tmp_name));
3057 error.vSetError(DTME_CannotSetOwnerOfTemporaryMailboxFile,
3058 DTM_FALSE, NULL, tmp_name, info.st_uid,
3059 error.errnoMessage(errno2));
3064 PRIV_ENABLED(return_status, SafeFchown(fd, (unsigned int) -1, info.st_gid));
3068 // Function: RFCMailBox::writeMailBox - create new copy of complete mailbox
3070 // writeMailBox causes a complete copy of the current mailbox to be written
3071 // out to permanent storage. This is done by creating a temporary file, in
3072 // the same location the permanent file resides, and writing the new mailbox
3073 // contents to it. If this is successful, the temporary file is then renamed
3074 // over the old permanent file, taking its place. In this way, if there is
3075 // an error creating the new file, the old file still remains intact.
3077 // . make sure the current mailbox is writeable
3078 // . lock current mailbox
3079 // . check for new mail and incorporate if necessary
3080 // . cause the OS to begin mapping as much of the current mailbox into memory
3081 // as it can so it is available as soon as possible to be written
3082 // . create temporary file to hold new mailbox contents
3083 // . create message list fixing the location of all non-deleted messages
3084 // (dirty messages will be allocated temporary storage and created)
3085 // . build a vectored write array based upon the message list
3086 // . cause the new mailbox contents to be written via one call to SafeWritev
3087 // . delete all "deleted" messages from the message list
3088 // . map the new mailbox file into memory
3089 // . revise all message pointers based upon new mailbox contents
3090 // . remove the mappings and unmap all previous regions used
3091 // . add the one single new mailbox region to the mappings list
3092 // . transfer any lock on the old mailbox file to the new mailbox file
3093 // . rename the new mailbox file to the correct permanent path name
3094 // . set the modification and access times of the mailbox file properly
3096 // DtMailEnv & error - standard error structure used by caller
3098 // error.isset() will indicate if there were any errors encountered, in which
3099 // case the current contents of the mailbox have not been written.
3101 // At some point in the call chain, someone must check to see if this is
3102 // a FATAL ERROR (e.g. error.isFatal) and if so, display the returned
3103 // message and then immediately exit, as this means the mailbox could not
3104 // be written and the internal mailbox state is hopelessly munged..
3109 RFCMailBox::writeMailBox(DtMailEnv &error, DtMailBoolean hide_access)
3111 static char *pname = "writeMailBox";
3113 DtMailEnv error2; // When we need to preserve error during error cleanup
3114 int return_status; // for handling priv/unpriv operations
3115 #if defined(DEBUG_RFCMailBox)
3116 char *pname = "writeMailBox";
3119 MutexLock lock_map(_map_lock);
3122 if (_mail_box_writable == DTM_FALSE) {
3126 // Need to lock the file while we do this.
3128 DEBUG_PRINTF( ("%s: locking mailbox\n", pname) );
3130 if (error.isSet()) {
3131 DtMailEnv tmp_error;
3132 unlockFile(tmp_error, _fd);
3136 // Need to deal with any potentially new mail that
3137 // has arrived and we don't know about.
3139 if (_mra_server == NULL) checkForMail(error, DTM_TRUE);
3140 if (error.isSet()) {
3141 DEBUG_PRINTF( ("%s: locking mailbox\n", pname) );
3142 unlockFile(error2, _fd);
3146 // Create the new temporary file to hold the mailbox contents
3148 static char tmp_name[MAXPATHLEN+1];
3149 sprintf(tmp_name, "%s.tmp.%08lx", _real_path, (long)time(NULL));
3150 assert(strlen(tmp_name)<sizeof(tmp_name));
3152 int fd = createTemporaryMailboxFile(error, tmp_name);
3153 if (error.isSet()) {
3154 DEBUG_PRINTF( ("%s: locking mailbox\n", pname) );
3155 unlockFile(error2, _fd);
3160 // Got the new mailbox file all set up to be our friend.
3161 // zip through current message structure fixing the location of all
3162 // non-deleted messages in preparation of writing them all out to
3163 // the temporary file just opened. The act of "fixing" the location of
3164 // a message will cause dirty messages to be constructed in temporary
3165 // allocated areas which must later be deallocated. This deallocation
3166 // is done either by calling adjustMessageLocation() to make the location
3167 // of the message permanent, or by calling unfixMessageLocation) to
3168 // abandon the new message (e.g. if new mailbox cannot be created).
3170 int deletesPending = 0; // count # of delete_pending messages scanned
3173 struct tempMsgList {
3174 MessageCache *tmlMc; // -> mc of this message
3175 long tmlRealOffset; // byte offset from 0 where message is written
3176 long tmlBodyOffset; // byte offset from 0 of body parts of message
3177 char *tmlHeaderStart; // -> start of headers for this message
3178 long tmlHeaderLen; // length of headers
3179 char *tmlBodyStart; // -> start of bodies for this message
3180 long tmlBodyLen; // length of bodies
3181 int tmlTemporary; // ==0:permanent, !=0:temporary(must move)
3184 long tmlTotalSize = _msg_list.length()*4;
3185 tempMsgList *const tmlFirst = (tempMsgList*)
3186 malloc((size_t) (sizeof(tempMsgList)*tmlTotalSize));
3187 tempMsgList *tmlLast = tmlFirst;
3189 for (msg = 0; msg < _msg_list.length(); msg++) {
3190 MessageCache *mc = _msg_list[msg];
3191 if (mc->delete_pending == DTM_TRUE) { // ignore deleted messages
3192 deletesPending++; // but remember if any seen
3195 tmlLast->tmlMc = mc;
3196 mc->message->fixMessageLocation(&tmlLast->tmlHeaderStart, tmlLast->tmlHeaderLen,
3197 &tmlLast->tmlBodyStart, tmlLast->tmlBodyLen,
3198 tmlLast->tmlTemporary, tmlLast->tmlBodyOffset);
3200 fprintf(stdout, "msg %03d @ %08lx %08lx/%06d %08lx/%06d %04d %s\n",
3201 msg, mc, tmlLast->tmlHeaderStart, tmlLast->tmlHeaderLen,
3202 tmlLast->tmlBodyStart, tmlLast->tmlBodyLen,
3203 tmlLast->tmlBodyOffset, tmlLast->tmlTemporary ? "T" : "P");
3204 HexDump(stdout, "header", (unsigned char *)tmlLast->tmlHeaderStart, tmlLast->tmlHeaderLen, 10);
3205 HexDump(stdout, "body", (unsigned char *)tmlLast->tmlBodyStart, tmlLast->tmlBodyLen, 10);
3208 assert(tmlLast->tmlHeaderStart != NULL);
3209 assert(tmlLast->tmlHeaderLen);
3210 tmlLast->tmlRealOffset = -1;
3213 assert((tmlLast-tmlFirst) < tmlTotalSize);
3215 // we can now allocate the vectored write array and fill it according
3216 // to the data stored in the message list we just created
3218 long iovSize = ((tmlLast-tmlFirst)+2)*3;
3219 iovec *const iovFirst = (iovec *) malloc((size_t) (sizeof(iovec)*iovSize));
3220 iovec *iovLast = iovFirst;
3221 iovec *iovPrev = (iovec *)0;
3222 long iovCurrentOffset = 0;
3224 for (tempMsgList *tmlNdx = tmlFirst; tmlNdx < tmlLast; tmlNdx++) {
3225 // if this message happens to start on the first byte following the
3226 // last byte of the previous message, combine into a single vector,
3227 // else add this as another vector in the vector list
3229 tmlNdx->tmlRealOffset = iovCurrentOffset;
3230 if ( (iovPrev != (iovec *)0) && (!tmlNdx->tmlTemporary) &&
3231 ((char *)((size_t) iovPrev->iov_base+iovPrev->iov_len) ==
3232 tmlNdx->tmlHeaderStart)
3234 iovPrev->iov_len += (int)(tmlNdx->tmlHeaderLen+tmlNdx->tmlBodyLen);
3235 iovCurrentOffset += (int)(tmlNdx->tmlHeaderLen+tmlNdx->tmlBodyLen);
3237 else if (!tmlNdx->tmlTemporary) {
3238 iovLast->iov_base = (caddr_t)tmlNdx->tmlHeaderStart;
3239 iovLast->iov_len = (int)(tmlNdx->tmlHeaderLen+tmlNdx->tmlBodyLen);
3240 iovCurrentOffset += iovLast->iov_len;
3241 iovPrev = iovLast++;
3244 // Message is temporary - headers and bodies are in different areas,
3245 // and the headers are in a "temporary" area
3247 iovLast->iov_base = (caddr_t)tmlNdx->tmlHeaderStart;
3248 iovLast->iov_len = (int)tmlNdx->tmlHeaderLen;
3249 iovCurrentOffset += iovLast->iov_len;
3250 iovPrev = iovLast++;
3252 // Write out bodies only if the length is non-zero, otherwise,
3253 // optimize out the inclusion of a zero length write from the vector
3255 if (tmlNdx->tmlBodyLen > 0) {
3256 iovLast->iov_base = (caddr_t)tmlNdx->tmlBodyStart;
3257 iovLast->iov_len = (int)tmlNdx->tmlBodyLen;
3258 iovCurrentOffset += iovLast->iov_len;
3259 iovPrev = iovLast++;
3262 // The last two bytes of the message must be \n\n
3263 // If not then this must be forced
3264 // Obtain pointer tp to the last byte of the current message
3265 // Given this pointer we now have:
3266 // tp[-1] -- next to last byte in current message
3267 // tp[ 0] -- last byte in current message
3268 // tp[+1] -- first byte of next message (if any)
3269 // There must always be two \n characters between each message. If not,
3270 // we must insert sufficient \n characters into the message stream to
3271 // accomplish this. We want to avoid plopping these in, however, as each
3272 // one will add an extra 1- or 2-byte vector into the vectored write array,
3273 // which will affect the throughput of the overall write.
3274 // Irregardless of whether the current message is temporary, we check to see if the next
3275 // byte or two (as necessary) is in a mapped region; if so, we can then
3276 // peek ahead to see if there are the additional \ns we need.
3277 // If all this fails, we punt and put in a small 1- or 2-byte write
3281 char *tp = (char *) ((size_t) iovPrev->iov_base + (iovPrev->iov_len - 1));
3284 if ( ((addressIsMapped(tp+1) == DTM_TRUE) )
3285 && (*(tp+1) == '\n') ) {
3291 iovLast->iov_base = (caddr_t)"\n"; // add \n
3292 iovLast->iov_len = 1;
3294 iovPrev = iovLast++;
3298 if ( (( (addressIsMapped(tp+1) == DTM_TRUE)
3299 && (addressIsMapped(tp+2) == DTM_TRUE)))
3300 && (*(tp+1) == '\n') && (*(tp+2) == '\n')) {
3301 iovPrev->iov_len+= 2;
3302 iovCurrentOffset+= 2;
3306 iovLast->iov_base = (caddr_t)"\n\n"; // add \n\n
3307 iovLast->iov_len = 2;
3308 iovCurrentOffset += 2;
3309 iovPrev = iovLast++;
3314 assert((iovLast-iovFirst) < iovSize);
3316 // All of the messages are properly accounted for in the write vector;
3317 // cause the damage to be done by calling SafeWritev. After it returns,
3318 // Make absolutely sure that all of the mailbox data has made it to
3319 // the final destination, especially if the mailbox is not local -
3320 // this way if we run out of disk space or some other such problem,
3321 // it is caught here and now.
3323 unsigned long bytesWritten = SafeWritev(fd, iovFirst, iovLast-iovFirst);
3324 if (bytesWritten == (unsigned long)-1 || fsync(fd) == -1) {
3326 for (tempMsgList *tml = tmlFirst; tml < tmlLast; tml++) {
3327 MessageCache *mc = tml->tmlMc;
3329 mc->message->unfixMessageLocation(tml->tmlHeaderStart, tml->tmlTemporary);
3331 FileSystemSpace(tmp_name, 0,&fsname);
3332 (void) SafeClose(fd);
3333 DEBUG_PRINTF( ("%s: locking mailbox\n", pname) );
3334 unlockFile(error2, _fd);
3335 PRIV_ENABLED(return_status,SafeUnlink(tmp_name));
3341 DTME_CannotWriteToTemporaryMailboxFile_ProcessLimitsExceeded);
3344 #if defined(__osf__) || defined(CSRG_BASED)
3349 error.setError(DTME_CannotWriteToTemporaryMailboxFile_RemoteAccessLost);
3353 error.setClient(fsname);
3354 error.setError(DTME_CannotWriteToTemporaryMailboxFile_NoFreeSpaceLeft);
3358 error.vSetError(DTME_CannotWriteToTemporaryMailboxFile,
3359 DTM_FALSE, NULL, tmp_name, error.errnoMessage(errno2));
3364 // The current contents of the mailbox have successfully been written
3365 // to the temporary file. Cause the new mailbox file to be mapped
3368 MapRegion * map = mapNewRegion(error, fd, bytesWritten);
3369 if (error.isSet()) {
3370 for (tempMsgList *tml = tmlFirst; tml < tmlLast; tml++) {
3371 MessageCache *mc = tml->tmlMc;
3373 mc->message->unfixMessageLocation(tml->tmlHeaderStart, tml->tmlTemporary);
3375 (void) SafeClose(fd);
3376 DEBUG_PRINTF( ("%s: locking mailbox\n", pname) );
3377 unlockFile(error2, _fd);
3378 PRIV_ENABLED(return_status,SafeUnlink(tmp_name));
3384 // POINT OF NO RETURN -- NEW MAILBOX MUST BE SUCCESSFULLY LINKED UP WITH
3385 // BECAUSE THE MACHINATIONS AND POINTER MUNGING DONE BELOW CANNOT BE UNDONE
3388 // Flush all deleted messages (if any were previously detected)
3391 for (msg = 0; msg < _msg_list.length(); msg++) {
3392 MessageCache * mc = _msg_list[msg];
3393 if (mc->delete_pending == DTM_TRUE) {
3394 delete mc->message; // remove message storage
3395 delete mc; // remove message cache storage
3396 _msg_list.remove(msg); // remove message from message list
3397 msg -= 1; // next message is where we are at now
3401 // spin through all "written messages" and fixup their pointers so they
3402 // point into the new region
3405 // For this occasion advise the OS that we will be doing sequential access
3407 alterPageMappingAdvice(map);
3409 for (tempMsgList *tml = tmlFirst; tml < tmlLast; tml++) {
3410 MessageCache *mc = tml->tmlMc;
3412 assert(mc->delete_pending == DTM_FALSE);
3413 mc->message->adjustMessageLocation(tml->tmlHeaderStart, map->file_region+tml->tmlRealOffset, tml->tmlHeaderLen+tml->tmlBodyLen, tml->tmlTemporary, tml->tmlBodyOffset);
3416 // Loop through the current mappings, and unmap each.
3417 // Then make the new single large map the only mapping.
3419 while(_mappings.length()) {
3420 MapRegion * c_map = _mappings[0];
3421 munmap(c_map->map_region, (size_t) c_map->map_size);
3423 _mappings.remove(0);
3425 _mappings.append(map);
3427 // fix for cmvc defect 7912 - Queued mail lost upon closing dtmail
3428 // If we are using the .lock protocol, we are locking on a file name
3429 // basis, and therefore can rename the new mailbox over the old mailbox
3430 // without a worry about locks; however, if we are using another type
3431 // of locking which locks on a *file* basis, then as soon as the rename
3432 // is done if there is not a lock on the file a process like sendmail
3433 // could come in and complicate matters. After being properly locked,
3434 // rename the new mailbox file over the old mailbox file, and then
3435 // remove the old lock if applicable.
3437 lockNewMailboxFile(fd);
3438 PRIV_ENABLED(return_status,SafeRename(tmp_name, _real_path));
3439 if (return_status == -1) {
3440 // the rename failed -- we are in a world of hurt now.
3441 // We have successfully written the new mailbox out, unmapped the
3442 // old file, mapped in the new file, and bashed all of the various
3443 // pointers to point to the new mailbox; however, we cannot rename
3444 // the new mailbox over the old mailbox. We cannot continue, so return
3445 // this as a fatal error so that the caller can exit properly.
3447 error.vSetError(DTME_CannotRenameNewMailboxFileOverOld,
3448 DTM_TRUE, NULL, _real_path, tmp_name,
3449 error.errnoMessage());
3450 (void) SafeClose(fd);
3451 (void) SafeClose(_fd);
3452 return; // no complete cleanup necessary as we should exit real fast...
3455 assert(map->file_size == bytesWritten);
3457 DtMailBoolean file_grew;
3458 if (map->file_size > _file_size)
3459 file_grew = DTM_TRUE;
3461 file_grew = DTM_FALSE;
3463 _file_size = map->file_size;
3466 if (SafeFStat(fd, &info) < 0)
3469 error.logError(DTM_TRUE,
3470 "%s: fstat(%d/%s) failed errno=%d\n",
3471 pname, _fd, tmp_name, errno);
3472 error.setError(DTME_ObjectCreationFailed, DTM_TRUE, (Tt_message) 0);
3476 if (info.st_size != _file_size)
3478 error.logError(DTM_TRUE,
3479 "%s: new mailbox size not consistent with expected size = %d\nfstat: st_ino = %d, st_dev = %d, st_nlink = %d, st_size = %ld\n",
3480 pname, bytesWritten,
3481 info.st_ino, info.st_dev, info.st_nlink, info.st_size);
3482 error.setError(DTME_ObjectCreationFailed, DTM_TRUE, (Tt_message) 0);
3485 if (hide_access == DTM_FALSE && info.st_atime <= info.st_mtime)
3486 mailboxAccessShow(info.st_mtime, "writeMailBox");
3489 // order of unlocks is important here:
3490 // unlockOldMailboxFile checks the state of _long_lock_active
3491 // but does not alter it, whereas unlockFile does.
3493 unlockOldMailboxFile(_fd); // unlock old mailbox file first
3494 DEBUG_PRINTF( ("%s: locking mailbox\n", pname) );
3495 unlockFile(error2,fd); // then unlock new mailbox file
3496 if (SafeClose(_fd) < 0) {
3497 // should do something with the error here.
3500 _fd = fd; // new mailbox file now current one
3501 _dirty = 0; // mark mailbox as no longer dirty.
3510 RFCMailBox::incorporate(DtMailEnv & error, const DtMailBoolean already_locked)
3512 DtMailEventPacket event;
3514 if (already_locked == DTM_FALSE) {
3515 MutexLock lock_map(_map_lock);
3518 int slot = mapFile(error, already_locked);
3519 if (error.isSet()) {
3520 if (DTME_ObjectInvalid == (DTMailError_t) error)
3523 _mail_box_writable = DTM_FALSE;
3526 event.target = DTM_TARGET_MAILBOX;
3527 event.target_object = this;
3528 event.operation = (void *)DTMC_INODECHANGED;
3529 event.argument = NULL;
3530 event.event_time = time(NULL);
3531 _session->writeEventData(error, &event, sizeof(event));
3533 else if (DTME_ObjectAccessFailed == (DTMailError_t) error)
3536 _mail_box_writable = DTM_FALSE;
3539 event.target = DTM_TARGET_MAILBOX;
3540 event.target_object = this;
3541 event.operation = (void *)DTMC_ACCESSFAILED;
3542 event.argument = NULL;
3543 event.event_time = time(NULL);
3544 _session->writeEventData(error, &event, sizeof(event));
3551 MapRegion * map = _mappings[slot];
3552 const char * buf = map->file_region;
3554 // Let's accept any white space as okay in front of the
3557 for (; buf < (map->map_region + map->map_size) &&
3558 isspace(*buf); buf++) {
3563 DtMailBoolean done = DTM_FALSE;
3565 while (num_tries < 5 && !done) {
3566 if (strncmp(buf, "From ", 5) == 0) {
3567 DtMailMessageHandle last = NULL;
3569 // We can be here via either of two scenarios:
3570 // 1- Aside from incorporating new mail, there is no other
3571 // activity on the mail box. This is the "normal" case.
3572 // already_locked is its default value (FALSE).
3574 // 2- We are here because we were doing a Destroy Deleted Messages
3575 // when we noticed new mail that had to be first incorporated.
3576 // already_locked is TRUE (i.e., the Expunge method has lock...)
3577 // We cannot place the handle of the last entity in the array in
3578 // the stream because the entity may be marked for delete, and
3579 // the Destroy Deleted Messages operation will expunge the entity
3580 // shortly after it has been placed on the stream for the FE.
3582 // In both cases, we want to get the last UNDELETED message and
3583 // place it on the stream for the FE. Undeleted entities will
3584 // be valid even after a DDM while deleted entities will be invalid
3587 // Place the handle of the last entity in the array in the
3588 // callback stream. The FE will get it and can invoke
3589 // getNextMessageSummary() on the mailbox using the handle to
3590 // retrieve the new messages.
3592 if (_msg_list.length() > 0) {
3594 if (already_locked) {
3595 // already_locked is TRUE only in one case:
3596 // We were trying to destroy deleted messages when we
3597 // noticed new mail has come in and we need to incorporate
3599 // This will return the index of the last undeleted
3600 // message. That entity, we are assured, will remain
3601 // valid after a DMM that may appear just before the event
3602 // is received by the FE's callback method.
3604 int index = prevNotDel(_msg_list.length() - 1);
3605 last = _msg_list[index];
3608 // already_locked is FALSE
3610 // No possiblity of entities in _msg_list being expunged.
3611 // Give the current last one to the FE and let it retrieve
3612 // new messages via the getNext() method on that entity.
3613 last = _msg_list[_msg_list.length() - 1];
3619 parseFile(error, slot);
3622 event.target = DTM_TARGET_MAILBOX;
3623 event.target_object = this;
3624 event.operation = (void *)DTMC_NEWMAIL;
3625 event.argument = last;
3626 event.event_time = time(NULL);
3627 _session->writeEventData(error, &event, sizeof(event));
3632 _mail_box_writable = DTM_FALSE;
3633 *_object_valid = -1;
3636 event.target = DTM_TARGET_MAILBOX;
3637 event.target_object = this;
3638 event.operation = (void *)DTMC_BADSTATE;
3639 event.argument = NULL;
3640 event.event_time = time(NULL);
3642 error.setError(DTME_ObjectInvalid);
3643 _session->writeEventData(error, &event, sizeof(event));
3646 char *strbuf = new char[256];
3647 if (already_locked) {
3650 "Attempt #%d: RFCMailBox::incorporate(): No From: buf=<%.10s>, already_locked is TRUE\n", num_tries + 1, buf);
3654 "Attempt #%d: RFCMailBox::incorporate(): No From: buf=<%.10s>, already_locked is FALSE\n", num_tries + 1, buf);
3667 RFCMailBox::generateLockFileName(void)
3670 char *lock_path = new char[MAXPATHLEN+20];
3672 assert(_real_path != NULL);
3673 (void) sprintf(lock_path, "%s.lock", _real_path);
3674 s = strdup(lock_path);
3675 delete [] lock_path;
3679 // Function: RFCMailBox::generateUniqueLockId - create unique ID for this mailbox lock files
3681 // generateUniqueLockId creates a unique ID which is written into .lock files and
3682 // can then be checked to make sure that the lock file has not been compromised by
3685 // The ID generated consists of three parts:
3686 // <process id/%08d><current time in seconds/%08d><hardware serial number/%d>
3687 // Thus, a "typical" id would look like this:
3688 // 000018577971028681915751068
3689 // Which breaks down as:
3690 // 00001857 797102868 1915751068
3696 // char * -> allocated memory in which unique id has been created
3699 RFCMailBox::generateUniqueLockId(void)
3702 char hwserialbuf[64];
3704 #if !defined(__aix) && !defined(__hpux) && !defined(__osf__) && !defined(linux) && !defined(CSRG_BASED)
3705 if (sysinfo(SI_HW_SERIAL, (char *)hwserialbuf, sizeof(hwserialbuf)-1) == -1)
3707 strcpy(hwserialbuf, "dtmail");
3708 (void) sprintf(theId, "%08ld%08ld%s\0", (long)getpid(), (long)time((time_t *)0), hwserialbuf);
3709 assert(strlen(theId)<sizeof(theId));
3710 return(strdup(theId));
3714 RFCMailBox::checkLockFileOwnership(DtMailEnv & error)
3716 char *pname = "checkLockFileOwnership";
3718 assert(_lockFileName != NULL);
3720 if (SafeStat(_lockFileName, &info) < 0) {
3722 error.logError(DTM_FALSE,
3723 "%s: lock cannot be stat()ed: %s, errno = %d\n",
3724 pname, _lockFileName, errno);
3725 error.setError(DTME_ObjectInvalid);
3733 flags = O_RDONLY | O_SYNC;
3735 flags = O_RDONLY | O_RSYNC | O_SYNC;
3736 #endif /* O_RSYNC */
3738 #ifdef MAILGROUP_REQUIRED
3739 PRIV_ENABLED_OPEN(_lockFileName, lock_fd, SafeOpen(_lockFileName, flags, 0));
3741 PRIV_ENABLED_OPTIONAL(lock_fd, SafeOpen(_lockFileName, flags, 0));
3744 if (lock_fd == -1) {
3748 "%s: lock cannot be open()ed: %s, errno = %d\n",
3749 pname, _lockFileName, errno);
3750 error.setError(DTME_ObjectInvalid);
3754 char lockBuf[MAXPATHLEN];
3755 int status = SafeRead(lock_fd, lockBuf, sizeof(lockBuf)-1);
3760 "%s: lock cannot be read: %s, errno = %d\n",
3761 pname, _lockFileName, errno);
3762 (void) SafeClose(lock_fd);
3763 error.setError(DTME_ObjectInvalid);
3767 if ( (status < _uniqueLockIdLength+2)
3768 || (strncmp(lockBuf+2, _uniqueLockId, _uniqueLockIdLength) != 0) ) {
3771 "%s: dtmail lock file stolen by another process\n", pname);
3775 "lock file stolen - lock file contents",
3776 (unsigned char *)lockBuf, status, 0);
3777 (void) SafeClose(lock_fd);
3778 error.setError(DTME_ObjectInvalid);
3781 (void) SafeClose(lock_fd);
3785 // Function: RFCMailBox::linkLockFile - create and link temporary lock file to real lock file
3787 // Create a lock file for the current mailbox. Return success of failure.
3789 // . create a temporary lock file with a unique signature id of this process
3790 // . link the temporary lock file to the real lock file
3791 // . if the link is not successful, remove the temporary lock file and return the
3792 // time() in seconds on the remote system of when the temporary lock file was created
3793 // . if the link is successful, remove the temporary lock file (link) and return 0.
3795 // error -- standard error structure used by caller
3797 // If error.isSet() it is a fatal error from which the caller should return to its caller,
3798 // return value will always be time(0)
3799 // If !error.isSet() then check results of return value
3801 // time_t == 0 : indicates that the real lock file has been created and we own it
3802 // != 0 : could not create real lock file, return value is the time *on the remote system*
3803 // that the temporary lock file was created with (from comparison with existing
3804 // lock file to see how old it is)
3807 RFCMailBox::linkLockFile(DtMailEnv & error, char *tempLockFileName)
3813 // Create the temporary lock file. Failure to do so indicates lack of write permission
3814 // in the directory or some other fatal error
3818 int flags = O_RDWR | O_CREAT | O_EXCL | O_SYNC;
3820 int flags = O_RDWR | O_CREAT | O_EXCL | O_SYNC | O_RSYNC;
3822 PRIV_ENABLED(lock_fd,SafeOpen(tempLockFileName, flags, 0666));
3824 // We are not able to create the temporary lock file.
3825 // We will have to punt on trying to lock here from now on.
3829 error.setError(DTME_CannotCreateMailboxLockFile_NoPermission);
3833 error.setError(DTME_CannotCreateMailboxLockFile_IsDirectory);
3837 error.setError(DTME_CannotCreateMailboxLockFile_NoSuchFile);
3840 #if defined(__osf__) || defined(CSRG_BASED)
3845 error.setError(DTME_CannotCreateMailboxLockFile_RemoteAccessLost);
3849 error.vSetError(DTME_CannotCreateMailboxLockFile,
3850 DTM_FALSE, NULL, tempLockFileName, error.errnoMessage());
3856 // Get creation time of temporary file *on remote system*
3858 if (SafeFStat(lock_fd, &sbuf) == -1) {
3860 error.logError(DTM_FALSE,
3861 "linkLockFile(): temporary lock file cannot be stat()ed: %s, errno = %d\n",
3862 tempLockFileName, errno);
3863 error.vSetError(DTME_CannotCreateMailboxLockFile,
3864 DTM_FALSE, NULL, tempLockFileName, error.errnoMessage());
3865 (void) SafeClose(lock_fd);
3869 // Write proper contents to lock file:
3870 // Write the string "0" into the lock file to give us some
3871 // interoperability with SVR4 mailers. SVR4 mailers expect
3872 // a process ID to be written into the lock file and then
3873 // use kill() to see if the process is alive or not. We write
3874 // 0 into it so that SVR4 mailers will always think our lock file
3875 // is valid. In addition we include a unique ID so we can verify
3876 // if the lock file is stolen out from under us.
3878 ssize_t writeResults;
3879 writeResults = SafeWrite(lock_fd, "0\0", 2);
3880 if (writeResults == 2)
3881 writeResults += SafeWrite(lock_fd, _uniqueLockId, _uniqueLockIdLength);
3882 if ( (writeResults != _uniqueLockIdLength+2) ){
3884 error.logError(DTM_FALSE,
3885 "linkLockFile(): write to temporary lock file failed: %s, errno = %d\n",
3886 tempLockFileName, errno);
3887 error.vSetError(DTME_CannotCreateMailboxLockFile,
3888 DTM_FALSE, NULL, tempLockFileName, error.errnoMessage());
3889 (void) SafeClose(lock_fd);
3893 // sync up the lock file with the ultimate storage device
3895 if (fsync(lock_fd) == -1) {
3897 error.logError(DTM_FALSE,
3898 "linkLockFile(): fsync to temporary lock file failed: %s, errno = %d\n",
3899 tempLockFileName, errno);
3900 error.vSetError(DTME_CannotCreateMailboxLockFile,
3901 DTM_FALSE, NULL, tempLockFileName, error.errnoMessage());
3902 (void) SafeClose(lock_fd);
3908 if (SafeClose(lock_fd) == -1) {
3912 "linkLockFile(): close of temporary lock file failed: %s, errno = %d\n",
3913 tempLockFileName, errno);
3914 error.vSetError(DTME_CannotCreateMailboxLockFile,
3915 DTM_FALSE, NULL, tempLockFileName, error.errnoMessage());
3919 // The temporary lock file has been created - now try and link it to the
3920 // real lock file. Failure here is not fatal as we will retry and possible
3921 // try and remove the real lock file later on.
3923 PRIV_ENABLED(return_status,SafeLink(tempLockFileName, _lockFileName));
3924 if (return_status == -1) {
3925 PRIV_ENABLED(return_status,SafeRemove(tempLockFileName));
3926 return(sbuf.st_ctime);
3929 // We successfully linked the temp lock file to the real lock file name
3930 // This means we have the dot lock for our process - remove the temporary lock
3931 // file name (link) and return
3933 PRIV_ENABLED(return_status,SafeRemove(tempLockFileName));
3938 RFCMailBox::lockFile(DtMailEnv & error)
3940 #if defined(DEBUG_RFCMailBox)
3941 char *pname = "RFCMailBox::lockFile";
3943 int return_status = 0;
3945 // We will create a simple lock file to keep the file from
3946 // changing while we are doing critical work.
3949 // On some platforms, sendmail will place a lockf lock on the
3950 // file during mail delivery. If this is the case, then we
3951 // need to make sure we have the lock here.
3953 #if defined(SENDMAIL_LOCKS)
3954 assert(_lockf_active == DTM_FALSE);
3955 lseek(_fd, 0, SEEK_SET);
3956 PRIV_ENABLED_OPTIONAL(return_status,SafeLockf(_fd, F_LOCK, 0));
3957 if (return_status != -1)
3959 _lockf_active = DTM_TRUE;
3960 DEBUG_PRINTF( ("%s: lockf succeeded\n", pname) );
3964 if (_use_dot_lock == DTM_FALSE) {
3965 DEBUG_PRINTF( ("%s: not using dot lock\n", pname) );
3969 // Implement the .lock short term lock protocol
3970 // This code was "adapted" from Solaris 2.5 (SunOS 5.5)
3971 // usr/src/cmd/mail/maillock.c.
3973 assert(_dot_lock_active == DTM_FALSE);
3975 // Create the temporary mail lock file name
3976 // It has the form <_lockfilename><XXXXXX> or mailbox.lockXXXXXX
3977 // mktemp then creates a unique temporary file for the template
3979 assert(_lockFileName != NULL);
3980 char *tempLockFileName = new char[MAXPATHLEN];
3981 sprintf(tempLockFileName, "%sXXXXXX", _real_path);
3982 mktemp(tempLockFileName);
3983 PRIV_ENABLED(return_status,SafeRemove(tempLockFileName));
3985 // loop through attempting to create the temporary lock file,
3986 // and if successful attempt to link the temporary lock file
3987 // to the real lock file. If not successful, retry for up to 5
3988 // minutes, and remove the current lock file if it is more than
3995 // Attempt to create a temporary file and link it to the intended lock file
3996 // If it is successful, we have the lock and can return.
3997 // If it is not successful and error.isSet then it is a non-recoverable
3999 // in which case the mailbox is deemed not-writable any more.
4000 // If it is not successful and !error.isSet then it is a recoverable error,
4001 // in which case we spin and try again according to the retry rules.
4003 time_t t = linkLockFile(error, tempLockFileName);
4004 if (error.isSet()) {
4005 // hard error? -- something is wrong, assume read/only
4006 _use_dot_lock = DTM_FALSE;
4007 _mail_box_writable = DTM_FALSE;
4008 (void) SafeRemove(tempLockFileName);
4010 ("%s: failed to link dot_lock file %s\n", pname, tempLockFileName) );
4011 delete [] tempLockFileName;
4015 checkLockFileOwnership(error);
4018 _dot_lock_active = DTM_TRUE;
4019 DEBUG_PRINTF( ("%s: succeeded acquiring dot_lock file\n", pname) );
4021 delete [] tempLockFileName;
4025 // Could not link the temporary lock file to the intended real lock file
4026 // See if the lock file exists and if so if we can remove it because it
4027 // is > 5 mins old. If the stat fails it means the lock file disappeared
4028 // between our attempt to link to it and now - only allow this to go on
4029 // so many times before punting
4031 if (SafeStat(_lockFileName, &sbuf) == -1) {
4032 if (statFailed++ > 5) {
4033 error.vSetError(DTME_CannotCreateMailboxLockFile,
4034 DTM_FALSE, NULL, _lockFileName, error.errnoMessage());
4035 delete [] tempLockFileName;
4042 // The lock file already exists - compare the time of the temp
4043 // file with the time of the lock file, rather than with the
4044 // current time of day, since the files may reside on another
4045 // machine whose time of day differs from the one this program
4046 // is running on. If the lock file is less than 5 minutes old,
4047 // keep trying, otherwise, remove the lock file and try again.
4050 if (t < (sbuf.st_ctime + 300)) {
4055 error.logError(DTM_FALSE,
4056 "lockFile(): removing stale lock file %s ctime %08ld temp lock %s ctime %08ld diff %08ld\n",
4057 _lockFileName, sbuf.st_ctime, tempLockFileName, t, t-sbuf.st_ctime);
4059 ("%s: giving up; removing dot_lock file %s\n", pname, _lockFileName));
4061 PRIV_ENABLED(return_status,SafeRemove(_lockFileName));
4062 if (return_status == -1) {
4063 // We were not able to unlink the file. This means that
4064 // we do not have write access to the directory. We will
4065 // have to pass on taking long locks.
4067 _use_dot_lock = DTM_FALSE;
4068 _mail_box_writable = DTM_FALSE;
4069 error.vSetError(DTME_CannotRemoveStaleMailboxLockFile,
4070 DTM_FALSE, NULL, _lockFileName, error.errnoMessage());
4071 delete [] tempLockFileName;
4075 delete [] tempLockFileName;
4078 // lockNewMailboxFile -- before renaming a new mailbox file over an old
4079 // mailbox file, we need to establish a lock on the new mailbox file is
4080 // a lock was established on the old mailbox file. Since the .lock protocol
4081 // works on a file name basis we dont have to worry here, but if this
4082 // system uses lockf() to lock the files we need to establish that lock
4083 // on the new file first before renaming it over the old mailbox file.
4086 RFCMailBox::lockNewMailboxFile(int new_fd)
4088 int return_status = 0;
4089 if (_lockf_active == DTM_TRUE) {
4090 lseek(new_fd, 0, SEEK_SET);
4091 PRIV_ENABLED_OPTIONAL(return_status,SafeLockf(new_fd, F_LOCK, 0));
4095 // unlockOldMailboxFile -- after renaming a new mailbox file over an old
4096 // mailbox file, if a lockf() style lock was established on the old mailbox
4097 // file, it needs to be removed
4100 RFCMailBox::unlockOldMailboxFile(int old_fd)
4102 int return_status = 0;
4103 if (_lockf_active == DTM_TRUE) {
4104 lseek(old_fd, 0, SEEK_SET);
4105 PRIV_ENABLED_OPTIONAL(return_status,SafeLockf(old_fd, F_ULOCK, 0));
4110 RFCMailBox::transferLock(int old_fd, int new_fd)
4112 int return_status = 0;
4113 if (_lockf_active == DTM_TRUE) {
4114 lseek(new_fd, 0, SEEK_SET);
4115 PRIV_ENABLED_OPTIONAL(return_status,SafeLockf(new_fd, F_LOCK, 0));
4117 lseek(old_fd, F_ULOCK, 0);
4118 PRIV_ENABLED_OPTIONAL(return_status,SafeLockf(old_fd, F_ULOCK, 0));
4123 RFCMailBox::unlockFile(DtMailEnv & error, int fd)
4125 #if defined(DEBUG_RFCMailBox)
4126 char *pname = "RFCMailBox::unlockFile";
4130 // We will create a simple lock file to keep the file from
4131 // changing while we are doing critical work.
4134 if (_use_dot_lock == DTM_TRUE) {
4135 assert(_dot_lock_active == DTM_TRUE);
4136 assert(_lockFileName != NULL);
4137 _dot_lock_active = DTM_FALSE;
4138 checkLockFileOwnership(error);
4139 if (!error.isSet()) {
4140 DEBUG_PRINTF(("%s: unlinking dot_lock file\n", pname, _lockFileName));
4142 PRIV_ENABLED(return_status,SafeUnlink(_lockFileName));
4143 if (return_status == -1) {
4144 error.vSetError(DTME_CannotRemoveMailboxLockFile,
4145 DTM_FALSE, NULL, _lockFileName, error.errnoMessage());
4150 #if defined(SENDMAIL_LOCKS)
4151 if (_lockf_active == DTM_TRUE) {
4152 DEBUG_PRINTF(("%s: removing lockf\n", pname));
4154 lseek(fd, 0, SEEK_SET);
4155 PRIV_ENABLED_OPTIONAL(return_status,SafeLockf(fd, F_ULOCK, 0));
4156 _lockf_active = DTM_FALSE;
4161 #define DOT_DTMAIL_SUFFIX "dtmail"
4163 RFCMailBox::dotDtmailLockFile(char *filename, int)
4166 // Attempt to link the temporary lock file to the real lock file.
4167 assert(_real_path != NULL);
4168 (void) sprintf(filename, "%s.%s", _real_path, DOT_DTMAIL_SUFFIX);
4173 RFCMailBox::dotDtmailLock(DtMailEnv & error)
4175 char lockBuf[MAXPATHLEN];
4176 char dotDtmailPath[MAXPATHLEN+1];
4177 char *tempLockFileName = new char[MAXPATHLEN];
4179 int return_status = 0;
4182 int flags = O_RDWR | O_CREAT | O_EXCL | O_SYNC;
4184 int flags = O_RDWR | O_CREAT | O_EXCL | O_SYNC | O_RSYNC;
4187 // We will create a .dtmail file to prevent conflicts between dtmail's
4188 // operating on the same mailbox.
4190 // Create the temporary mail lock file name.
4191 sprintf(tempLockFileName, "%sXXXXXX", _real_path);
4192 mktemp(tempLockFileName);
4193 PRIV_ENABLED(return_status,SafeRemove(tempLockFileName));
4195 // Attempt to create the temporary file.
4196 PRIV_ENABLED(lock_fd,SafeOpen(tempLockFileName, flags, 0666));
4200 error.setError(DTME_CannotCreateMailboxLockFile_NoPermission);
4203 error.setError(DTME_CannotCreateMailboxLockFile_IsDirectory);
4206 error.setError(DTME_CannotCreateMailboxLockFile_NoSuchFile);
4208 #if defined(__osf__) || defined(CSRG_BASED)
4213 error.setError(DTME_CannotCreateMailboxLockFile_RemoteAccessLost);
4216 error.vSetError(DTME_CannotCreateMailboxLockFile,
4217 DTM_FALSE, NULL, tempLockFileName,
4218 error.errnoMessage());
4221 delete [] tempLockFileName;
4225 sprintf(lockBuf, "%d\n", getpid());
4226 len = strlen(lockBuf);
4227 len = SafeWrite(lock_fd, lockBuf, len);
4230 dotDtmailLockFile(dotDtmailPath, MAXPATHLEN);
4232 PRIV_ENABLED(return_status,SafeLink(tempLockFileName, dotDtmailPath));
4233 if (return_status == -1)
4234 error.vSetError(DTME_CannotCreateMailboxDotDtmailLockFile,
4235 DTM_FALSE, NULL, dotDtmailPath,
4236 error.errnoMessage());
4238 PRIV_ENABLED(return_status,SafeRemove(tempLockFileName));
4239 delete [] tempLockFileName;
4243 RFCMailBox::dotDtmailUnlock(DtMailEnv &)
4245 char dotDtmailPath[MAXPATHLEN+1];
4246 char lockBuf[MAXPATHLEN];
4248 int return_status = 0;
4251 int flags = O_RDONLY | O_EXCL | O_SYNC;
4253 int flags = O_RDONLY | O_EXCL | O_SYNC | O_RSYNC;
4256 dotDtmailLockFile(dotDtmailPath, MAXPATHLEN);
4257 PRIV_ENABLED(lock_fd,SafeOpen(dotDtmailPath, flags, 0666));
4261 return_status = SafeRead(lock_fd, lockBuf, sizeof(lockBuf)-1);
4262 if (return_status <= 0)
4264 (void) SafeClose(lock_fd);
4268 sscanf(lockBuf, "%d", &pid);
4269 if (pid != getpid())
4271 (void) SafeClose(lock_fd);
4275 (void) SafeClose(lock_fd);
4276 PRIV_ENABLED(return_status,SafeRemove(dotDtmailPath));
4280 // Possible return values,
4281 // DTMBX_LONGLOCK_FAILED_CANCEL - failed, cancel operation
4282 // DTMBX_LONGLOCK_FAILED_READONLY - failed, open read only
4283 // DTMBX_LONGLOCK_SUCCESSFUL - succeeded
4285 // This function will also manipulate "_mail_box_writable" and "_lock_flag"
4288 RFCMailBox::longLock(DtMailEnv & error)
4290 DTMBX_LONGLOCK rtn = DTMBX_LONGLOCK_FAILED_CANCEL;
4292 _long_lock_active = DTM_TRUE;
4293 _lock_obj = new FileShare(error,
4302 // TT is not available. Attempt to lock using the .dtmail lock.
4308 dotDtmailLock(error);
4311 _long_lock_active = DTM_FALSE;
4312 if (NULL != _callback)
4315 // .dtmail lock failed. The best we can do is read only.
4318 char dotDtmailPath[MAXPATHLEN+1];
4320 dotDtmailLockFile(dotDtmailPath, MAXPATHLEN);
4322 DTMC_DOTDTMAILLOCKFAILED, _real_path, NULL, _cb_data,
4323 (char*) dotDtmailPath, (const char*) error);
4324 if (DTM_TRUE == ans)
4326 rtn = DTMBX_LONGLOCK_FAILED_READONLY;
4331 rtn = DTMBX_LONGLOCK_FAILED_CANCEL;
4332 error.setError(DTME_UserInterrupted);
4337 rtn = DTMBX_LONGLOCK_SUCCESSFUL;
4342 // Ask TT to lock the file.
4344 _lock_obj->lockFile(error);
4348 // TT rejected our lock request. The best we can do is read only.
4351 if (_lock_obj->readOnly(error) == DTM_TRUE)
4353 rtn = DTMBX_LONGLOCK_FAILED_READONLY;
4358 rtn = DTMBX_LONGLOCK_FAILED_CANCEL;
4359 error.setError(DTME_UserInterrupted);
4364 _long_lock_active = DTM_FALSE;
4367 rtn = DTMBX_LONGLOCK_SUCCESSFUL;
4374 RFCMailBox::longUnlock(DtMailEnv & error)
4378 if (DTM_TRUE == _lock_flag)
4380 if (NULL != _lock_obj)
4385 else if (_long_lock_active == DTM_TRUE)
4386 dotDtmailUnlock(error);
4390 if (_mail_box_writable == DTM_FALSE)
4394 #if !defined(SENDMAIL_LOCKS)
4395 if (_lockf_active == DTM_TRUE)
4397 int return_status = 0;
4398 lseek(_fd, 0, SEEK_SET);
4399 PRIV_ENABLED_OPTIONAL(return_status,SafeLockf(_fd, F_ULOCK, 0));
4400 _lockf_active = DTM_FALSE;
4404 _long_lock_active = DTM_FALSE;
4407 RFCMailBox::MapRegion *
4408 RFCMailBox::mapNewRegion(DtMailEnv & error, int fd, unsigned long size)
4412 // Create the mapped region.
4414 MapRegion * map = new MapRegion;
4416 map->file_size = size;
4417 long page_size = memoryPageSize();
4419 #if defined(__osf__) && OSMAJORVERSION < 4
4420 // Problem with mapping. You can not round up to the nearest
4421 // memory block size when mapping a file. You need the exact
4422 // file size of less. - ff
4423 map->map_size = map->file_size;
4425 map->map_size = map->file_size + (page_size - (map->file_size % page_size)) + page_size;
4428 int flags = MAP_PRIVATE;
4430 #if defined(MMAP_NORESERVE)
4431 // We are not supposed to be writing to these pages. If
4432 // we don't specify MAP_NORESERVE however, the system will
4433 // reserve swap space equal to the file size to deal with
4434 // potential writes. This is wasteful to say the least.
4436 flags |= MAP_NORESERVE;
4439 map->map_region = (char *)mmap(0, (size_t) map->map_size,
4440 PROT_READ, flags, fd, 0);
4441 if (map->map_region == (char *)-1) {
4444 error.setError(DTME_CannotReadNewMailboxFile_OutOfMemory);
4448 error.vSetError(DTME_CannotReadNewMailboxFile,
4449 DTM_FALSE, NULL, error.errnoMessage());
4454 return((MapRegion *)NULL);
4457 map->file_region = map->map_region;
4463 RFCMailBox::makeHeaderLine(DtMailEnv & error,
4465 const DtMailHeaderRequest & request,
4466 DtMailHeaderLine & headers)
4468 MessageCache * mc = _msg_list[slot];
4469 DtMail::Envelope * env = mc->message->getEnvelope(error);
4471 // For each request, we need to retrieve the header values.
4473 headers.number_of_names = request.number_of_names;
4474 headers.header_values = new DtMailValueSeq[headers.number_of_names];
4476 for (int req = 0; req < request.number_of_names; req++) {
4477 // RFC Message::getHeader will pass abstract names through
4478 // as transport names if they can not be found in the abstract
4479 // table. Because of this we say all names are abstract and
4480 // rely on the specific implementation of RFCMessage. This
4481 // is potentially dangerous, but we are allowed to define
4482 // and require these semantics for RFCMessage (and this same
4483 // information appears in the appropriate place in getHeader.
4485 env->getHeader(error,
4486 request.header_name[req],
4488 headers.header_values[req]);
4489 if (error.isSet()) {
4490 headers.header_values[req].clear();
4499 RFCMailBox::waitForMsgs(int needed)
4501 while(_at_eof == 0 && needed >= _msg_list.length()) {
4508 RFCMailBox::writeToDumpFile(const char *format, ...)
4511 char dumpfilename[MAXPATHLEN+1];
4512 _Xctimeparams ctime_buf;
4514 GET_DUMPFILE_NAME(dumpfilename);
4515 FILE *df = fopen(dumpfilename, "a");
4517 const time_t clockTime = (const time_t) time((time_t *)0);
4518 memset((void*) &ctime_buf, 0, sizeof(_Xctimeparams));
4519 fprintf(df, "--------------------- pid=%ld %s",
4520 (long)getpid(), _XCtime(&clockTime, ctime_buf));
4523 va_start(var_args, format);
4524 vfprintf(df, format, var_args);
4527 fprintf(df, "---------------------\n");
4528 fprintf(df, "\n\n");
4534 RFCMailBox::startAutoSave(DtMailEnv & error,
4541 _session->addEventRoutine(error, PollEntry, this, 60);
4543 _last_poll = 0; // Causes first poll to fire right way.
4546 _session->removeEventRoutine(error, PollEntry, this);
4549 #if defined(reallyoldsun) || defined(USL)
4550 #define SA_HANDLER_TYPE void (*)(void)
4552 #define SA_HANDLER_TYPE void (*)(int)
4556 RFCMailBox::dumpMaps(const char *str)
4560 off_t total_file_size = 0;
4561 struct sigaction sig_act, old_sig_act;
4562 char dumpfilename[MAXPATHLEN+1];
4564 GET_DUMPFILE_NAME(dumpfilename);
4565 FILE *df = fopen(dumpfilename, "a");
4567 if (df==NULL && _errorLogging)
4570 "dumpMaps(): Cant open dump file %s\n", dumpfilename);
4572 if (SafeFStat(_fd, &buf) < 0) {
4573 fprintf(df, "dumpMaps(): fstat(%d) failed errno=%d\n", _fd, errno);
4581 * Prepare signal handler for exception handling.
4583 (void) sigemptyset(&sig_act.sa_mask);
4584 sig_act.sa_flags = 0;
4585 sig_act.sa_handler = (SA_HANDLER_TYPE) SigBusHandler;
4586 sigaction(SIGBUS, &sig_act, &old_sig_act);
4587 sigbus_env_valid = 1;
4588 if (setjmp(sigbus_env) == 0) {
4589 const time_t clockTime = (const time_t) time((time_t *)0);
4590 _Xctimeparams ctime_buf;
4591 memset((void*) &ctime_buf, 0, sizeof(_Xctimeparams));
4592 fprintf(df, "--------------------- pid=%ld %s",
4593 (long)getpid(), _XCtime(&clockTime, ctime_buf));
4594 fprintf(df, "%s", str);
4595 fprintf(df, "---------------------\n");
4596 fprintf(df, "Mappings = %d\n", _mappings.length());
4597 fprintf(df, "Map Entries:\n");
4598 for (int m = 0; m < _mappings.length(); m++) {
4599 MapRegion *map = _mappings[m];
4600 fprintf(df, "map[%d]: map_region = %p, map_size = 0x%08lx(%08ld)\n",
4601 m, map->map_region, map->map_size, map->map_size);
4602 if (map->map_size % memoryPageSize()) {
4603 fprintf(df, "ERROR! map->map_size not mod %lu\n", memoryPageSize());
4605 HexDump(df, "map_region", (unsigned char *)map->map_region,
4606 (int) map->file_size, _errorLogging ? 0 : 10);
4608 "map[%d]: file_region = %p, file_size = 0x%08lx(%08ld)\n",
4609 m, map->file_region, map->file_size, map->file_size);
4610 fprintf(df, "map[%d]: offset = 0x%08lx(%08ld)\n",
4611 m, map->offset, map->offset);
4612 if (map->file_size == 0) {
4613 fprintf(df, "No data in file_region\n");
4616 if (strncasecmp(map->file_region, "From", 4)) {
4617 fprintf(df, "ERROR! map->file_region does not begin with From\n");
4621 (unsigned char *)map->file_region,
4622 (int) map->file_size, _errorLogging ? 0 : 10);
4624 total_file_size += (off_t) map->file_size;
4625 if ((total_file_size % 4096) == 0) {
4627 "Total file size falls on page boundary, totalsize = %lu\n",
4632 fprintf(df, "\nstat buffer entries: st_ino = %lu, st_dev = %lu, st_nlink = %lu, st_size = %ld\n",
4633 buf.st_ino, buf.st_dev, buf.st_nlink, buf.st_size);
4635 fprintf(df, "\n\n");
4638 fprintf(df, "\nSIGBUS received during output of file mappings.\n");
4639 fprintf(df, "This generally indicates a truncated or deleted file.\n");
4641 sigbus_env_valid = 0;
4642 sigaction(SIGBUS, &old_sig_act, NULL);
4646 // The following routines are required to bind this format specific driver
4647 // into the format neutral layer.
4649 // The first entry point is the capability query interface. This is used
4650 // by the client to determine what capabilities we support.
4653 RFCQueryImpl(DtMail::Session & session,
4655 const char * capability,
4660 if (strcmp(capability, DtMailCapabilityPropsSupported) == 0) {
4661 DtMailBoolean * resp = va_arg(args, DtMailBoolean *);
4666 if (strcmp(capability, DtMailCapabilityImplVersion) == 0) {
4667 char * version = va_arg(args, char *);
4668 strcpy(version, "1.0");
4672 if (strcmp(capability, DtMailCapabilityInboxName) == 0)
4674 DtMailObjectSpace *space = va_arg(args, DtMailObjectSpace *);
4675 void **inbox = va_arg(args, void **);
4677 *space = DtMailFileObject;
4678 *inbox = (void*) getInboxPath(&session);
4682 if (strcmp(capability, DtMailCapabilityMailspoolName) == 0)
4684 DtMailObjectSpace *space = va_arg(args, DtMailObjectSpace *);
4685 void **mailspool = va_arg(args, void **);
4687 *space = DtMailFileObject;
4688 *mailspool = (void*) getMailspoolPath(&session);
4692 if (strcmp(capability, DtMailCapabilityTransport) == 0) {
4693 DtMailBoolean * resp = va_arg(args, DtMailBoolean *);
4698 error.setError(DTME_NotSupported);
4702 // The QueryOpen entry point is used to determin if we can open the specified
4703 // path. If the name is of the form host:/path, or is a simple path, then we
4704 // will say we can. Additional work should be done for content typing here,
4705 // but we'll skip it for now.
4708 RFCQueryOpen(DtMail::Session &,
4710 DtMailObjectSpace space,
4715 // We can do buffers, so just say yes.
4717 if (space == DtMailBufferObject) {
4721 // If this isn't in the file name space, then give up now.
4723 if (space != DtMailFileObject) {
4727 char * path = (char *) arg;
4729 // First, is this of the form "host:/path". If so, it is probably
4730 // an RFC mail box, so we will say we can open it.
4732 for (const char * c = path; *c; c++) {
4734 // We hit a slash first, so colon's are not useful.
4738 if (*c == ':' && *(c+1) == '/') {
4739 // Looks like we have a host name. We should do a more
4740 // robust check at this point, but for now we will assume
4741 // that we have a valid host.
4746 // Okay, there defintely is not a host name. See if we can stat the
4747 // file. If we can, then assume we can open it, otherwise. If the file
4748 // doesn't exist, then we can create a new one.
4751 if (stat(path, &sbuf) < 0) {
4752 if (errno == ENOENT) {
4763 // The mail box construct entry point creates an instance of the RFC
4764 // mail box object. This object is then accessed through the virtual
4765 // DtMail::MailBox class API.
4768 RFCMailBoxConstruct(DtMail::Session & session,
4770 DtMailObjectSpace space,
4775 return(new RFCMailBox(error, &session, space, arg, cb, client_data, "Internet MIME"));
4779 RFCMessageQuery(DtMail::Session &,
4781 DtMailObjectSpace space,
4786 if (space != DtMailBufferObject) {
4794 RFCMessageConstruct(DtMail::Session & session,
4796 DtMailObjectSpace space,
4801 return(new RFCMessage(error, &session, space, arg, cb, client_data));
4805 RFCMIMETransportConstruct(DtMail::Session & session,
4807 DtMailStatusCallback cb,
4810 return(new RFCTransport(error, &session, cb, cb_data, "Internet MIME"));
4813 // The meta factory is responsible for returning the entry points
4814 // required for locating and creating various mail objects based for
4815 // the RFC implementation. It is essentially a switch table.
4819 RFCMetaFactory(const char * op)
4821 if (strcmp(op, QueryImplEntryOp) == 0) {
4822 return((void *)RFCQueryImpl);
4825 if (strcmp(op, QueryOpenEntryOp) == 0) {
4826 return((void *)RFCQueryOpen);
4829 if (strcmp(op, MailBoxConstructEntryOp) == 0) {
4830 return((void *)RFCMailBoxConstruct);
4833 if (strcmp(op, QueryMessageEntryOp) == 0) {
4834 return((void *)RFCMessageQuery);
4837 if (strcmp(op, MessageConstructEntryOp) == 0) {
4838 return((void *)RFCMessageConstruct);
4841 if (strcmp(op, TransportConstructEntryOp) == 0) {
4842 return((void *)RFCMIMETransportConstruct);
4848 // The mail box construct entry point creates an instance of the RFC
4849 // mail box object. This object is then accessed through the virtual
4850 // DtMail::MailBox class API.
4853 V3MailBoxConstruct(DtMail::Session & session,
4855 DtMailObjectSpace space,
4860 return(new RFCMailBox(error, &session, space, arg, cb, client_data,
4865 RFCV3TransportConstruct(DtMail::Session & session,
4867 DtMailStatusCallback cb,
4870 return(new RFCTransport(error, &session, cb, cb_data, "Sun Mail Tool"));
4873 // The meta factory is responsible for returning the entry points
4874 // required for locating and creating various mail objects based for
4875 // the RFC implementation. It is essentially a switch table.
4878 V3MetaFactory(const char * op)
4880 if (strcmp(op, QueryImplEntryOp) == 0) {
4881 return((void *)RFCQueryImpl);
4884 if (strcmp(op, QueryOpenEntryOp) == 0) {
4885 return((void *)RFCQueryOpen);
4888 if (strcmp(op, MailBoxConstructEntryOp) == 0) {
4889 return((void *)V3MailBoxConstruct);
4892 if (strcmp(op, QueryMessageEntryOp) == 0) {
4893 return((void *)RFCMessageQuery);
4896 if (strcmp(op, MessageConstructEntryOp) == 0) {
4897 return((void *)RFCMessageConstruct);
4900 if (strcmp(op, TransportConstructEntryOp) == 0) {
4901 return((void *)RFCV3TransportConstruct);
4909 RFCMailBox::createMailRetrievalAgent(char *password)
4911 DtMailEnv localError;
4912 char *path = _session->expandPath(localError, (char*) _arg);
4913 DtMailServer *server = NULL;
4916 if (! isInboxMailbox(_session, path))
4922 if (NULL != _mra_command) free(_mra_command);
4923 _mra_command = NULL;
4925 if (NULL != password)
4927 if (NULL != _mra_serverpw) free(_mra_serverpw);
4928 _mra_serverpw = strdup(password);
4931 if (NULL != _mra_server) delete _mra_server;
4934 if (True == DtMailServer::get_mailrc_value(
4935 _session, DTMAS_INBOX,
4936 DTMAS_PROPKEY_GETMAILVIASERVER,
4939 protocol = DtMailServer::get_mailrc_value(
4940 _session, DTMAS_INBOX,
4941 DTMAS_PROPKEY_PROTOCOL,
4944 if (! strcasecmp(protocol, DTMAS_PROTO_IMAP))
4945 _mra_server = (DtMailServer*) new IMAPServer(
4946 DTMAS_INBOX, _session, this,
4947 appendCB, (void*) this);
4948 else if (! strcasecmp(protocol, DTMAS_PROTO_APOP))
4949 _mra_server = (DtMailServer*) new APOPServer(
4950 DTMAS_INBOX, _session, this,
4951 appendCB, (void*) this);
4952 else if (! strcasecmp(protocol, DTMAS_PROTO_POP3))
4953 _mra_server = (DtMailServer*) new POP3Server(
4954 DTMAS_INBOX, _session, this,
4955 appendCB, (void*) this);
4956 else if (! strcasecmp(protocol, DTMAS_PROTO_POP2))
4957 _mra_server = (DtMailServer*) new POP2Server(
4958 DTMAS_INBOX, _session, this,
4959 appendCB, (void*) this);
4961 _mra_server = (DtMailServer*) new AUTOServer(
4962 DTMAS_INBOX, _session, this,
4963 appendCB, (void*) this);
4965 if (NULL != _mra_server) _mra_server->set_password(_mra_serverpw);
4968 else if (True == DtMailServer::get_mailrc_value(
4969 _session, DTMAS_INBOX,
4970 DTMAS_PROPKEY_GETMAILVIACOMMAND,
4973 _mra_command = DtMailServer::get_mailrc_value(
4974 _session, DTMAS_INBOX,
4975 DTMAS_PROPKEY_GETMAILCOMMAND,
4982 RFCMailBox::deleteMailRetrievalAgent()
4984 if (NULL != _mra_command) free(_mra_command);
4985 _mra_command = NULL;
4986 if (NULL != _mra_serverpw) free(_mra_serverpw);
4987 _mra_serverpw = NULL;
4988 if (NULL != _mra_server) delete _mra_server;
4993 RFCMailBox::updateMailRetrievalPassword(char *password)
4995 if (NULL == password || NULL == _mra_server) return;
4997 if (NULL != _mra_serverpw) delete _mra_serverpw;
4998 _mra_serverpw = strdup(password);
4999 _mra_server->set_password(_mra_serverpw);
5004 RFCMailBox::retrieveNewMail(DtMailEnv &error)
5006 if (NULL != _mra_server)
5008 _mra_server->retrieve_messages(error);
5010 if (DTME_MailServerAccess_MissingPassword == (DTMailError_t) error ||
5011 DTME_MailServerAccess_AuthorizationFailed == (DTMailError_t) error)
5012 return DTMC_SERVERPASSWORDNEEDED;
5013 else if (DTME_NoError != (DTMailError_t) error)
5014 return DTMC_SERVERACCESSFAILED;
5016 else if (NULL != _mra_command)
5018 int sysstatus = system(_mra_command);
5019 if (-1 == sysstatus)
5022 DTME_GetmailCommandRetrieval_SystemError,
5024 _mra_command, errno, error.errnoMessage(errno));
5025 if (_errorLogging) error.logError(DTM_FALSE, (const char*) error);
5026 return DTMC_GETMAILCOMMANDFAILED;
5028 if (0 == WIFEXITED(sysstatus))
5031 DTME_GetmailCommandRetrieval_AbnormalExit,
5034 if (_errorLogging) error.logError(DTM_FALSE, (const char*) error);
5035 return DTMC_GETMAILCOMMANDFAILED;
5037 else if (0 != WEXITSTATUS(sysstatus))
5042 "system('%s') returned %d\n",
5043 _mra_command, WEXITSTATUS(sysstatus));
5050 RFCMailBox::mailboxAccessShow(time_t mtime, char *prefix)
5054 new_time.modtime = mtime;
5055 new_time.actime = new_time.modtime+1;
5056 SafeUTime(_real_path, &new_time);
5057 #ifdef DEBUG_MAILBOX_ACCESS
5058 fprintf(stderr, "%s: forcing acctime>modtime\n", prefix);
5063 RFCMailBox::mailboxAccessHide(char *prefix)
5065 SafeUTime(_real_path, NULL);
5066 #ifdef DEBUG_MAILBOX_ACCESS
5067 fprintf(stderr, "%s: forcing modtime==acctime\n", prefix);