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>
74 #if defined(NEED_MMAP_WRAPPER)
80 #if defined(NEED_MMAP_WRAPPER)
84 #include <DtMail/DtMail.hh>
85 #include <DtMail/DtMailServer.hh>
86 #include <DtMail/ImplDriver.hh>
87 #include <DtMail/Threads.hh>
88 #include <DtMail/IO.hh>
90 #include "str_utils.h"
92 #ifndef MAIL_SPOOL_PATH
93 #define MAIL_SPOOL_PATH "/var/mail/%s"
97 #define LCL_SIG_HANDLER_SIGNATURE
99 #define LCL_SIG_HANDLER_SIGNATURE __harg
100 #elif defined(__aix) || defined(__alpha) || defined(linux) || defined(CSRG_BASED)
101 #define LCL_SIG_HANDLER_SIGNATURE int
105 // Debugging for RFCMailBox.
107 #if defined(DEBUG_RFCMailBox)
108 #define DEBUG_PRINTF(a) printf a
110 #define DEBUG_PRINTF(a)
114 // These macros define a method for executing statements
115 // with set group id privileges enabled
117 #if defined(MAILGROUP_REQUIRED)
119 #define PRIV_ENABLED_OPTIONAL(STATUSVARIABLE, STATEMENT) \
120 STATUSVARIABLE = STATEMENT;
122 #define PRIV_ENABLED(STATUSVARIABLE, STATEMENT) \
124 _session->enableGroupPrivileges(); \
125 STATUSVARIABLE = STATEMENT; \
126 _session->disableGroupPrivileges(); \
129 #define PRIV_ENABLED_OPEN(FILENAME, STATUSVARIABLE, STATEMENT) \
130 if (isSetMailGidNeeded(FILENAME)) { \
131 PRIV_ENABLED(STATUSVARIABLE, STATEMENT) \
133 STATUSVARIABLE = STATEMENT; \
138 #define PRIV_ENABLED_OPTIONAL(STATUSVARIABLE, STATEMENT) \
139 _session->enableGroupPrivileges(); \
140 STATUSVARIABLE = STATEMENT; \
141 _session->disableGroupPrivileges(); \
142 if (STATUSVARIABLE == -1) { \
143 STATUSVARIABLE = STATEMENT; \
146 #define PRIV_ENABLED PRIV_ENABLED_OPTIONAL
148 #define PRIV_ENABLED_OPEN(FILENAME, STATUSVARIABLE, STATEMENT) \
149 STATUSVARIABLE = STATEMENT;
153 #define GET_DUMPFILE_NAME(dfn) \
154 snprintf(dfn, sizeof(dfn), "%s/%s/dtmail.dump", getenv("HOME"), DtPERSONAL_TMP_DIRECTORY)
157 * Local Data Definitions
159 static const int RFCSignature = 0x448612e5;
161 static const int DEFAULT_FOLDER_SIZE = (32 << 10); // 32 KB
163 static int sigbus_env_valid = 0;
165 static jmp_buf sigbus_env;
168 * Local Function Declarations
170 static void SigBusHandler(LCL_SIG_HANDLER_SIGNATURE);
171 static int isMailGroupSystemMailbox(const char *mailboxPath);
172 static int isInboxMailbox(const char *mailboxPath);
173 static char *getInboxPath(DtMail::Session *session);
178 * Notified when an interesting (SIGBUS in this case) signal is raised.
179 * I wanted to throw a C++ exception at this point but that's not
180 * supported everywhere. So, we'll just have to use the thread-unsafe
181 * setjmp/longjmp combination.
184 void SigBusHandler(LCL_SIG_HANDLER_SIGNATURE)
186 if (sigbus_env_valid) {
187 longjmp(sigbus_env, 1);
192 void HexDump(FILE *pfp, char *pmsg, unsigned char *pbufr, int plen, int plimit)
194 unsigned char save[64];
195 long int x, y, z, word, cnt;
201 if (pfp_r == (FILE*) NULL) {
202 char dumpfilename[MAXPATHLEN+1];
203 _Xctimeparams ctime_buf;
205 GET_DUMPFILE_NAME(dumpfilename);
206 pfp_r = fopen(dumpfilename, "a");
207 const time_t clockTime = (const time_t) time((time_t *)0);
208 memset((void*) &ctime_buf, 0, sizeof(_Xctimeparams));
209 fprintf(pfp_r, "--------------------- pid=%ld %s",
210 (long)getpid(), _XCtime(&clockTime, ctime_buf));
213 (void) fprintf(pfp_r, "--> %s (%d bytes at %p):\n", pmsg, plen, pbufr);
215 memset((char *)save, 0, sizeof(save));
219 for (x = 0; cnt > 0; x++, z += 16)
221 (void) fprintf(pfp_r, "%p(+%6.6ld) ", pbufr + z, z);
222 for (y = 0; y < 16; y++)
224 save[y] = pbufr[x * 16 + y];
225 word = pbufr[x * 16 + y];
228 (void) fprintf(pfp_r, "%2.2lx%c", word, y == 7 ? '-' : ' ');
230 (void) fprintf(pfp_r, " ");
232 (void) fprintf(pfp_r, "%s", " *");
233 for (y = 0; y < 16; y++)
235 (void) fprintf(pfp_r, " ");
236 else if (pbufr[x * 16 + y] < ' ' || pbufr[x * 16 + y] > '~')
237 (void) fprintf(pfp_r, "%s", ".");
239 (void) fprintf(pfp_r, "%c", pbufr[x * 16 + y]);
240 (void) fprintf(pfp_r, "%s", "*\n");
243 if (plimit && (x >= (plimit-1)) && (cnt > (plimit*16))) {
244 while (cnt > (plimit*16)) {
249 fprintf(pfp_r, "...\n");
253 if (pfp == (FILE*) NULL) {
254 fprintf(pfp_r, "---------------------\n");
259 RFCMailBox::RFCMailBox(DtMailEnv & error,
260 DtMail::Session * session,
261 DtMailObjectSpace space,
265 const char * impl_name)
266 : DtMail::MailBox(error, session, space, arg, cb, client_data),
267 _msg_list(128), _mappings(4)
269 // We are using a condition to block any threads from
270 // trying to use this object until it is open. We will
271 // not set the object valid until we have a valid thread.
272 // Note that this will not be set to true until a stream
273 // has been successfully opened.
275 DtMailEnv localError;
278 _object_valid = new Condition;
280 _object_valid->setFalse();
282 _map_lock = MutexInit();
284 _impl_name = impl_name;
291 _mail_box_writable = DTM_FALSE;
292 _use_dot_lock = DTM_TRUE;
293 _long_lock_active = DTM_FALSE;
294 _dot_lock_active = DTM_FALSE;
295 _lockf_active = DTM_FALSE;
296 _uniqueLockId = generateUniqueLockId();
297 assert(_uniqueLockId != NULL);
298 _uniqueLockIdLength = strlen(_uniqueLockId);
299 _lockFileName = (char *)0;
302 _mr_allowed = DTM_TRUE;
304 _mra_serverpw = NULL;
306 createMailRetrievalAgent(NULL);
308 // Create a thread for getting new mail and expunging old mail.
309 // Of course we don't need threads for buffer objects.
311 if (_space != DtMailBufferObject) {
312 _thread_info = new NewMailData;
313 _thread_info->self = this;
314 _thread_info->object_valid = _object_valid;
315 _mbox_daemon = ThreadCreate(ThreadNewMailEntry, _thread_info);
318 // We will have to add a poll oriented method as well. We'll ignore
321 if (_space != DtMailBufferObject) {
322 _session->addEventRoutine(error, PollEntry, this, 15);
324 _last_poll = 0; // Causes first poll to fire right way.
327 // We need to figure out what type of locking to use. We use ToolTalk,
328 // when the user has explicitly turned it on via a property.
330 DtMail::MailRc * mailrc = _session->mailRc(error);
331 const char * value = NULL;
332 mailrc->getValue(localError, "cdetooltalklock", &value);
333 if (localError.isSet()) {
334 _tt_lock = DTM_FALSE;
342 // Determine if error logging is enabled
346 mailrc->getValue(localError, "errorlogging", &value);
347 _errorLogging = (localError.isSet() ?
348 (localError.clear(), DTM_FALSE) : DTM_TRUE);
353 _partialListCount = 0;
357 _object_signature = RFCSignature;
360 RFCMailBox::~RFCMailBox(void)
362 if (_object_signature != RFCSignature) {
366 MutexLock lock_scope(_obj_mutex);
367 if (_object_signature == RFCSignature) {
368 _object_valid->setFalse();
371 _session->removeEventRoutine(error, PollEntry, this);
373 // We need to copy the file if writable and dirty out.
374 // NOTE: the caller should really call writeMailBox() or expunge()
375 // (as appropriate) first before destroying this mailbox, as there
376 // is no way to pass an error indication back up from here to the
377 // caller. If an error happens let syslog handle it.
379 if (_mail_box_writable == DTM_TRUE) {
382 writeMailBox(error, DTM_FALSE);
385 // THE MAILBOX COULD NOT BE WRITTEN!! SHOULD DO SOMETHING
386 error.logError(DTM_TRUE,
387 "~RFCMailBox(): Failed to write mailbox: %s",
388 (const char *)error);
393 // Let's keep the map locked.
395 MutexLock lock_map(_map_lock);
397 // Next we tear down the message structures.
399 while (_msg_list.length()) {
400 MessageCache * mc = _msg_list[0];
403 _msg_list.remove(0); // Won't actually touch the object.
406 // Finally we need to get rid of the mapping. There are
407 // actually 3 conditions we need to deal with here.
409 // 1) We opened a file and mapped it. In that case, we unmap,
410 // and close the file.
412 // 2) We created a buffer. In that case the _mapped_region points,
413 // at the region owned by _buffer. We simply destroy _buffer,
414 // but we do not attempt to destroy the mapped region.
416 // 3) The caller asked us to open an existing buffer. In that
417 // case, do nothing (we didn't build it so we won't destroy it).
419 if (_fd >= 0) { // Option 1
420 for (int slot = 0; slot < _mappings.length(); slot++) {
421 munmap(_mappings[slot]->map_region,
422 (size_t) _mappings[slot]->map_size);
428 if (_buffer) { // Option 2
433 // Allocated using malloc and strdup, so free using free.
435 free((void*) _real_path);
436 free((void*) _uniqueLockId);
437 free((void*) _lockFileName);
440 _object_signature = 0;
442 if (NULL != _mra_command) delete _mra_command;
443 if (NULL != _mra_server) delete _mra_server;
444 if (NULL != _mra_serverpw) delete _mra_serverpw;
447 static int isMailGroupSystemMailbox(const char * mailboxPath)
450 #ifdef MAILGROUP_REQUIRED
451 static int oneTimeFlag = 0;
452 static char *cached_inbox_path = 0;
454 if (NULL == mailboxPath) return retval;
458 char *inbox_path = new char[MAXPATHLEN];
461 GetPasswordEntry(pw);
462 sprintf(inbox_path, MAIL_SPOOL_PATH, pw.pw_name);
463 cached_inbox_path = strdup(inbox_path);
465 delete [] inbox_path;
468 assert(cached_inbox_path);
469 retval = (!strcmp(mailboxPath, cached_inbox_path));
474 static char *getMailspoolPath(DtMail::Session *session)
477 DtMail::MailRc *mailrc = session->mailRc(error);
478 char *mailspoolpath = 0;
479 char *syspath = new char[MAXPATHLEN];
482 GetPasswordEntry(pw);
483 sprintf(syspath, MAIL_SPOOL_PATH, pw.pw_name);
484 mailspoolpath = strdup(syspath);
486 assert(NULL!=mailspoolpath);
488 return mailspoolpath;
491 static char *getInboxPath(DtMail::Session *session)
494 DtMail::MailRc *mailrc = session->mailRc(error);
497 mailrc->getValue(error, DTMAS_PROPKEY_INBOXPATH, (const char**) &inboxpath);
500 mailrc->getValue(error, "DT_MAIL", (const char**) &inboxpath);
503 mailrc->getValue(error, "MAIL", (const char**) &inboxpath);
506 inboxpath = getMailspoolPath(session);
517 if (inboxpath && 0 == (strcmp(inboxpath, "MAILSPOOL_FILE")))
520 inboxpath = getMailspoolPath(session);
523 assert(NULL!=inboxpath);
527 static int isInboxMailbox(DtMail::Session *session, const char * mailboxPath)
530 char *inbox_path = NULL;
532 inbox_path = getInboxPath(session);
533 assert(NULL!=inbox_path);
534 retval = (!strcmp(mailboxPath, inbox_path));
539 // Function: alterPageMappingAdvice - change OS mapping advice on selected map
541 // Give the operating system new advice on the referencing activity that
542 // should be expected for a region of file mapped pages.
544 // Spin through all map regions recorded in _mappings looking for the
545 // specified map (or all maps if -1 passed in as the map region), and
546 // if found issue an madvise call on the specified region of memory.
548 // map -- either a specific region that is part of _mappings,
549 // -- OR (MapRegion *)-1 to change ALL map regions in _mappings
550 // advice -- advice as per madvise(3) Operating System call
557 RFCMailBox::alterPageMappingAdvice(MapRegion *map, int advice)
559 int me = _mappings.length();
560 for (int m = 0; m < me; m++) {
561 MapRegion *map_t = _mappings[m];
563 #if !defined(linux) && !defined(sun)
564 // no madvise on these systems
565 if (map_t == map || map == (MapRegion *)-1)
566 madvise(map_t->map_region, (size_t) map_t->map_size, advice);
571 // Function: memoryPageSize - return hardware natural memory page size
573 // Compute and return the natural memory page size of the current hardware
575 // Use sysconf to query the operating system about the current hardware;
576 // cache the value so that repeated calls to this function do not generate
577 // repeated calls to the operating system.
583 // long -- natural page size of the current hardware
588 static long mach_page_size = -1; // -1 is "one time value"
590 if (mach_page_size == -1) {
591 mach_page_size = sysconf(_SC_PAGESIZE);
592 assert(mach_page_size != -1);
595 return(mach_page_size);
599 // Functions: addressIsMapped - check if address is in file mapped memory
601 // Check to see if a given memory address is within the bounds of any
602 // memory that may have been mapped from a mailbox file into memory,
603 // and return an indication of whether it is or not.
605 // Go through the list of file to memory mappings and check to see if
606 // the given address is within the bounds of one of the mappings.
608 // addressToCheck -- address in memory to check against mappings
612 // DTM_TRUE - memory address is in file mapped memory and can be accessed
613 // DTM_FALSE - memory address is not in file mapped memory - may not be valid
616 RFCMailBox::addressIsMapped(void *addressToCheck)
618 assert(addressToCheck != NULL);
619 int me = _mappings.length();
620 for (int m = 0; m < me; m++)
622 MapRegion *map = _mappings[m];
623 if ( (addressToCheck >= map->map_region)
624 && (addressToCheck < (map->map_region+map->file_size)) )
631 RFCMailBox::appendCB(DtMailEnv &error, char *buf, int len, void *clientData)
633 RFCMailBox *obj = (RFCMailBox*) clientData;
635 if (NULL == obj) return;
636 obj->append(error, buf, len);
640 RFCMailBox::append(DtMailEnv &error, char *buf, int len)
643 off_t end = lseek(_fd, 0, SEEK_END);
645 // Add a new-line at the end to distinguish separate messages.
646 status = SafeWrite(_fd, buf, len);
650 char *path = _session->expandPath(error, (char *)_arg);
655 DTME_AppendMailboxFile_FileTooBig, DTM_FALSE, NULL,
656 path, errno, error.errnoMessage(errno));
659 #if defined(CSRG_BASED)
665 DTME_AppendMailboxFile_LinkLost, DTM_FALSE, NULL,
666 path, errno, error.errnoMessage(errno));
671 DTME_AppendMailboxFile_NoSpaceLeft, DTM_FALSE, NULL,
672 path, errno, error.errnoMessage(errno));
677 DTME_AppendMailboxFile_SystemError, DTM_FALSE, NULL,
678 path, errno, error.errnoMessage(errno));
683 if (_hide_access_events)
684 mailboxAccessHide("append");
687 time_t now = time((time_t) NULL);
688 mailboxAccessShow(now, "append");
694 RFCMailBox::create(DtMailEnv & error, mode_t create_mode)
698 MutexLock lock_scope(_obj_mutex);
699 MutexLock lock_map(_map_lock);
702 case DtMailBufferObject:
704 DtMailBuffer * buf = (DtMailBuffer *)_arg;
706 _buffer = (char *)malloc(DEFAULT_FOLDER_SIZE);
707 buf->buffer = _buffer;
708 buf->size = DEFAULT_FOLDER_SIZE;
710 MapRegion * map = new MapRegion;
711 map->file_region = map->map_region = _buffer;
712 map->file_size = map->map_size = DEFAULT_FOLDER_SIZE;
714 _mappings.append(map);
716 _mail_box_writable = DTM_TRUE;
721 case DtMailFileObject:
723 openRealFile(error, O_RDWR | O_CREAT, create_mode);
733 error.setError(DTME_NotSupported);
738 _object_valid->setTrue();
744 RFCMailBox::open(DtMailEnv & error,
745 DtMailBoolean auto_create,
748 DtMailBoolean lock_flag,
749 DtMailBoolean auto_parse
754 if (_tt_lock == DTM_TRUE && lock_flag == DTM_TRUE)
755 _lock_flag = DTM_TRUE;
757 _lock_flag = DTM_FALSE;
759 MutexLock lock_scope(_obj_mutex);
763 MutexLock lock_map(_map_lock);
766 case DtMailBufferObject:
768 DtMailBuffer * buf = (DtMailBuffer *)_arg;
770 MapRegion * map = new MapRegion;
771 map->file_region = map->map_region = (char *)buf->buffer;
772 map->file_size = map->map_size = buf->size;
774 _mappings.append(map);
776 _mail_box_writable = DTM_FALSE;
781 case DtMailFileObject:
786 _mail_box_writable = DTM_FALSE;
787 char * path = _session->expandPath(error, (char *)_arg);
788 PRIV_ENABLED_OPTIONAL(return_result, SafeAccess(path, W_OK));
789 if (return_result == 0) {
794 // We need to use the most restrictive mode that is possible
795 // on the file. If the caller has requested the file be open
796 // read-only, then we should do that, even if read-write is
797 // allowed. We don't want to try to open the file read-write
798 // if we don't have adequate permission however.
800 mode = open_mode == O_RDONLY ? open_mode : mode;
802 openRealFile(error, mode, create_mode);
804 if (auto_create == DTM_TRUE) {
818 error.setError(DTME_NotSupported);
821 if (error.isSet()) { // Can't parse this.
829 #if defined(POSIX_THREADS)
830 ThreadCreate(ThreadParseEntry, this);
835 _object_valid->setTrue(); // New mail watcher starts now.
845 _mail_box_writable = DTM_FALSE;
846 char * path = _session->expandPath(error, (char *)_arg);
847 PRIV_ENABLED_OPTIONAL(return_status, SafeAccess(path, W_OK));
848 if (return_status == 0) {
853 // We need to use the most restrictive mode that is possible
854 // on the file. If the caller has requested the file be open
855 // read-only, then we should do that, even if read-write is
856 // allowed. We don't want to try to open the file read-write
857 // if we don't have adequate permission however.
859 mode = open_mode == O_RDONLY ? open_mode : mode;
861 openRealFile(error, mode, create_mode);
863 if (auto_create == DTM_TRUE) {
867 // Isn't this wrong? Shouldn't create() be called
868 // before unlocking lock_scope?
876 // Validate this file if it's not empty
878 // When move/copy to a file, we want to make sure the first
879 // five characters are "From "
883 pread(_fd, (void *)inbuf, 5, 0);
885 lseek(_fd, (off_t) 0L, SEEK_SET);
886 if(-1 == read(_fd, (void *)inbuf, 5)) {
887 error.setError(DTME_NotMailBox);
890 lseek(_fd, (off_t) 0L, SEEK_SET);
894 if (strcmp(inbuf, "From ") != 0) {
895 error.setError(DTME_NotMailBox);
904 _object_valid->setTrue(); // New mail watcher starts now.
931 RFCMailBox::messageCount(DtMailEnv & error)
937 if (_object_valid->state() <= 0) {
938 error.setError(DTME_ObjectInvalid);
942 return(_msg_list.length());
944 #endif /* DEAD_WOOD */
947 RFCMailBox::getFirstMessageSummary(DtMailEnv & error,
948 const DtMailHeaderRequest & request,
949 DtMailHeaderLine & summary)
955 if (_object_valid->state() <= 0) {
956 error.setError(DTME_ObjectInvalid);
960 int slot = nextNotDel(0);
962 if (slot >= _msg_list.length()) {
966 makeHeaderLine(error, 0, request, summary);
968 return(_msg_list[0]);
972 RFCMailBox::getNextMessageSummary(DtMailEnv & error,
973 DtMailMessageHandle last,
974 const DtMailHeaderRequest & request,
975 DtMailHeaderLine & summary)
977 if (_object_valid->state() <= 0) {
978 error.setError(DTME_ObjectInvalid);
982 // Let's treat a null last as the start of the list.
985 return(getFirstMessageSummary(error, request, summary));
990 int slot = _msg_list.indexof((MessageCache *)last);
998 slot = nextNotDel(slot);
1000 if (slot >= _msg_list.length()) {
1004 makeHeaderLine(error, slot, request, summary);
1006 return(_msg_list[slot]);
1010 RFCMailBox::getMessageSummary(DtMailEnv & error,
1011 DtMailMessageHandle handle,
1012 const DtMailHeaderRequest & request,
1013 DtMailHeaderLine & summary)
1015 MutexLock lock_map(_map_lock);
1017 if (_object_valid->state() <= 0) {
1018 error.setError(DTME_ObjectInvalid);
1024 int slot = _msg_list.indexof((MessageCache *)handle);
1025 if (slot < 0 || slot >= _msg_list.length()) {
1026 error.setError(DTME_ObjectInvalid);
1030 makeHeaderLine(error, slot, request, summary);
1036 RFCMailBox::clearMessageSummary(DtMailHeaderLine & headers)
1038 if (NULL != headers.header_values)
1039 delete []headers.header_values;
1043 RFCMailBox::getMessage(DtMailEnv & error, DtMailMessageHandle hnd)
1045 if (_object_valid->state() <= 0) {
1046 error.setError(DTME_ObjectInvalid);
1052 int slot = _msg_list.indexof((MessageCache *)hnd);
1054 error.setError(DTME_ObjectInvalid);
1058 MessageCache * mc = _msg_list[slot];
1059 return(mc->message);
1063 RFCMailBox::getFirstMessage(DtMailEnv & error)
1069 if (_object_valid->state() <= 0) {
1070 error.setError(DTME_ObjectInvalid);
1074 int slot = nextNotDel(0);
1076 if (slot >= _msg_list.length()) {
1080 MessageCache * mc = _msg_list[slot];
1081 return(mc->message);
1085 RFCMailBox::getNextMessage(DtMailEnv & error,
1086 DtMail::Message * last)
1090 int slot = lookupByMsg((RFCMessage *)last);
1098 slot = nextNotDel(slot);
1100 if (slot >= _msg_list.length()) {
1104 MessageCache * mc = _msg_list[slot];
1105 return(mc->message);
1109 RFCMailBox::copyMessage(DtMailEnv & error,
1110 DtMail::Message * msg)
1112 #if defined(DEBUG_RFCMailBox)
1113 char *pname = "RFCMailBox::copyMessage";
1116 if (_object_valid->state() <= 0) {
1117 error.setError(DTME_ObjectInvalid);
1123 // The following is a hack for PAR0.5. In the future, we will use
1124 // this test for an optimization, but right now, we can only copy
1127 const char * msg_impl = msg->impl(error);
1128 if (strcmp(msg_impl, "Internet MIME") != 0 &&
1129 strcmp(msg_impl, "Sun Mail Tool") != 0) {
1130 error.setError(DTME_NotSupported);
1134 RFCMessage * rfc_msg = (RFCMessage *)msg;
1136 // We need to protect the file from access. Locking this will also
1137 // block any attempts by the new mail thread.
1141 DEBUG_PRINTF( ("%s: locking mailbox\n", pname) );
1143 if (error.isSet()) {
1144 DtMailEnv tmp_error;
1145 unlockFile(tmp_error, _fd);
1151 off_t end = lseek(_fd, 0, SEEK_END);
1152 status = SafeWrite(_fd, rfc_msg->_msg_start,
1153 rfc_msg->_msg_end - rfc_msg->_msg_start + 1);
1155 // We are going to put this at the real end of the file. We don't
1156 // really care what the current thought size is because new mail
1157 // will take care of that problem.
1160 // Add a new-line at the end.
1161 // It serves to distinguish separate messages.
1163 SafeWrite(_fd, "\n", 1);
1165 DEBUG_PRINTF( ("%s: unlocking mailbox\n", pname) );
1166 unlockFile(error, _fd);
1169 error.setError(DTME_ObjectCreationFailed);
1173 RFCMailBox::copyMailBox(DtMailEnv & error, DtMail::MailBox * mbox)
1177 for (DtMail::Message * msg = mbox->getFirstMessage(error);
1178 msg && error.isNotSet();
1179 msg = mbox->getNextMessage(error, msg)) {
1180 copyMessage(error, msg);
1181 if (error.isSet()) {
1188 RFCMailBox::checkForMail(
1190 const DtMailBoolean already_locked
1195 if (_space != DtMailFileObject) {
1196 error.setError(DTME_NotSupported);
1200 NewMailEvent(already_locked);
1204 RFCMailBox::expunge(DtMailEnv & error)
1208 for (int msg = 0; msg < _msg_list.length(); msg++) {
1209 MessageCache * mc = _msg_list[msg];
1210 if (mc->delete_pending == DTM_FALSE) {
1211 DtMail::Envelope * env = mc->message->getEnvelope(error);
1213 DtMailValueSeq value;
1214 env->getHeader(error, RFCDeleteHeader, DTM_FALSE, value);
1215 if (!error.isSet()) {
1216 mc->delete_pending = DTM_TRUE;
1225 writeMailBox(error, DTM_FALSE);
1226 if ((DTMailError_t)error ==
1227 DTME_CannotWriteToTemporaryMailboxFile_NoFreeSpaceLeft)
1229 // Need to do some thing here don't know now.
1233 // THE MAILBOX COULD NOT BE WRITTEN!! CALLER MUST DO SOMETHING
1236 "RFCMailBox::expunge(): Failed to write mailbox: %s",
1237 (const char *) error
1243 RFCMailBox::impl(DtMailEnv & error)
1250 RFCMailBox::markDirty(const int delta)
1254 // Make sure we really are dirty.
1260 RFCMailBox::callCallback(DtMailCallbackOp op, void * arg)
1262 if (_object_signature != RFCSignature || !_object_valid->state()) {
1267 if (_space == DtMailFileObject) {
1268 path = (char *)_arg;
1274 if (NULL != _callback)
1275 _callback(op, path, NULL, _cb_data, arg);
1281 RFCMailBox::newMessage(DtMailEnv & error)
1283 // RFC does not support a straightforward concept of adding
1284 // a message to a mailbox. We implement move/copy with a sort
1285 // of kludge, but we can only extend the kludge so far.
1287 error.setError(DTME_NotSupported);
1292 RFCMailBox::openRealFile(DtMailEnv & error, int open_mode, mode_t create_mode)
1294 // We first want to check the access modes we have on the
1295 // file. If we can write the file, then we will need to
1296 // root out the real path just to make sure we don't munge
1297 // a link someplace.
1300 char * path = _session->expandPath(error, (char *)_arg);
1301 if (error.isSet()) {
1302 return; // could not expand path
1305 if ((open_mode & O_RDWR) == O_RDONLY) {
1306 _real_path = strdup(path);
1307 SafeStat(_real_path, &buf);
1310 _real_path = (char *)malloc(MAXPATHLEN);
1311 char *link_path = new char[MAXPATHLEN];
1312 strcpy(link_path, path);
1313 strcpy(_real_path, path);
1315 if (SafeLStat(link_path, &buf) == 0 && (open_mode & O_CREAT) == 0) {
1316 while(S_ISLNK(buf.st_mode)) {
1317 int size = readlink(link_path, _real_path, sizeof(link_path));
1319 error.setError(DTME_NoSuchFile);
1321 _real_path = (char *)0;
1324 delete [] link_path;
1328 _real_path[size] = 0;
1329 if (_real_path[0] == '/') {
1330 strcpy(link_path, _real_path);
1333 char * last_slash = strrchr(link_path, '/');
1335 strcpy(last_slash + 1, _real_path);
1338 strcpy(link_path, "./");
1339 strcat(link_path, _real_path);
1343 strcpy(_real_path, link_path);
1345 if (SafeLStat(link_path, &buf)) {
1346 error.setError(DTME_NoSuchFile);
1348 _real_path = (char *)0;
1351 delete [] link_path;
1357 if ((open_mode & O_CREAT) == 0) {
1358 error.setError(DTME_NoSuchFile);
1360 _real_path = (char *)0;
1363 delete [] link_path;
1367 delete [] link_path;
1372 // We should now have a path we can open or create.
1373 // We must now make sure that if the file is being created that
1374 // it is created with the correct permissions and group owner.
1376 // If the mailbox to be created is NOT the system mailbox for this
1377 // user (e.g. the one that official delivery agents use), then the
1378 // permissions for the file should only let the current user have access.
1380 // If the mailbox to be created IS the system mailbox for this user,
1381 // and MAILGROUP_REQUIRED is defined, we must allow group read/write
1382 // and make sure the mailbox is owned by the correct group
1384 int requiresMailGroupCreation = 0;
1388 // delivery agent runs as specific non-root/wheel group
1389 requiresMailGroupCreation = isMailGroupSystemMailbox(_real_path);
1390 if ( (open_mode & O_CREAT) && requiresMailGroupCreation) {
1391 oldUmask = umask(DTMAIL_DEFAULT_CREATE_UMASK_MAILGROUP);
1392 create_mode = DTMAIL_DEFAULT_CREATE_MODE_MAILGROUP;
1395 oldUmask = umask(DTMAIL_DEFAULT_CREATE_UMASK);
1398 // We have 2 choices for locking an RFC file. The first is
1399 // to use the ToolTalk file scoping paradigm, and the second
1400 // is the normal lockf protocol. If we are using ToolTalk,
1401 // we need to initialize the ToolTalk locking object.
1403 // Unfortunately lockf() on an NFS mounted file system will
1404 // upset mmap(). A quick hack is to not use lockf() on any
1405 // remotely mounted file. Since mailx doesn't do tooltalk
1406 // locking now, it is possible to corrupt the spool file when
1407 // using mailx while mailtool has a NFS mounted mail file loaded.
1409 // We should find a solution where mailtool and mailx work
1412 // open the file before we obtain the lock can cause sync problem
1413 // if another owns the lock and has modified the mailbox
1415 DTMBX_LONGLOCK answer = DTMBX_LONGLOCK_SUCCESSFUL;
1417 if (_lock_flag == DTM_TRUE) {
1418 answer = longLock(error);
1419 if (answer == DTMBX_LONGLOCK_FAILED_CANCEL) {
1421 _real_path = (char *)0;
1425 // we have obtained the lock, go ahead and open the mailbox
1426 // Do not open the mailbox with privileges enabled, even if
1427 // creating the file
1429 PRIV_ENABLED_OPEN(_real_path, _fd,
1430 SafeOpen(_real_path, open_mode, create_mode));
1435 PRIV_ENABLED_OPEN(_real_path, _fd,
1436 SafeOpen(_real_path, open_mode, create_mode));
1439 #if !defined(SENDMAIL_LOCKS)
1440 // On some systems, sendmail uses lockf while delivering mail.
1441 // We can not hold a lockf style lock on those systems. Of course
1442 // if the user has turned off ToolTalk locking, they are playing
1445 if ( (_fd >= 0) && ( (open_mode & O_RDWR) == O_RDWR) )
1447 enum DtmFileLocality location;
1450 // Only attempt lockf() if file was opened for reading and writing
1452 location = DtMail::DetermineFileLocality(_real_path);
1453 #if defined(DEBUG_RFCMailBox)
1454 char *locstr = (location==Dtm_FL_UNKNOWN) ?
1456 ((location==Dtm_FL_LOCAL) ? "Local" : "Remote");
1457 DEBUG_PRINTF(("openRealFile: location is %s\n", locstr));
1463 case Dtm_FL_UNKNOWN:
1465 // locality unknown -- assume local and lock
1469 // locality local - apply lock
1471 assert(_lockf_active == DTM_FALSE);
1472 lseek(_fd, 0, SEEK_SET);
1473 PRIV_ENABLED_OPTIONAL(return_status,SafeLockf(_fd, F_TLOCK, 0));
1474 if (return_status == -1) {
1475 error.setError(DTME_OtherOwnsWrite);
1479 _lockf_active = DTM_TRUE;
1484 // locality otherwise -- assume remote dont lock
1493 (void) umask(oldUmask);
1498 switch (saveErrno) {
1500 error.setError(DTME_NoPermission);
1504 error.setError(DTME_IsDirectory);
1508 error.setError(DTME_NoSuchFile);
1512 error.setError(DTME_ObjectAccessFailed);
1516 _real_path = (char *)0;
1521 if ((open_mode & O_CREAT) && requiresMailGroupCreation) {
1523 // Make sure a newly created file has the correct group owner i.d.
1525 gid_t groupId = GetIdForGroupName(DTMAIL_DEFAULT_CREATE_MAILGROUP);
1527 (void) SafeFchown(_fd, (unsigned int) -1, groupId);
1530 if ((open_mode & 0x3) == O_RDWR) {
1531 if (answer == DTMBX_LONGLOCK_SUCCESSFUL ||
1532 answer == DTMBX_LONGLOCK_FAILED_READWRITE)
1533 _mail_box_writable = DTM_TRUE;
1535 _mail_box_writable = DTM_FALSE;
1538 _mail_box_writable = DTM_FALSE;
1542 _file_size = lseek(_fd, 0, SEEK_END);
1543 lseek(_fd, 0, SEEK_SET);
1544 _links = buf.st_nlink;
1546 _lockFileName = generateLockFileName();
1547 assert(_lockFileName != NULL);
1552 RFCMailBox::realFileSize(DtMailEnv & error, struct stat * stat_buf)
1556 // We have to deal with the problem of editors that will unlink
1557 // the file we are watching and rename it to a new file. This
1558 // will cause us to miss new mail, and eventually write data that
1559 // is out of sync with the real state of the file.
1561 // We do this by checking the link count first. If it has changed,
1562 // we will close, reopen, and then stat. Note that we could say if
1563 // the link count has gone down we will do this, but since we can't
1564 // be sure exactly what has happened to this file, we will consider
1565 // any change in the link count to require us to reopen the file.
1570 error_code = SafeFStat(_fd, &buf);
1571 if (error_code >= 0) {
1572 if (buf.st_nlink != _links) {
1575 DEBUG_PRINTF( ("realFileSize: (buf.st_nlink!=_links\n") );
1576 old_mode = fcntl(_fd, F_GETFL) & O_ACCMODE;
1579 PRIV_ENABLED_OPEN(_real_path,
1581 SafeOpen(_real_path, old_mode, 0666));
1583 error.setError(DTME_ObjectAccessFailed);
1587 transferLock(_fd, fd);
1591 if (SafeFStat(_fd, &buf) >= 0) {
1592 _links = buf.st_nlink;
1595 error.logError(DTM_FALSE,
1596 "realFileSize(): fstat(%d/%s) failed errno=%d\n",
1597 _fd, _real_path, errno);
1598 error.setError(DTME_ObjectAccessFailed);
1608 error.logError(DTM_FALSE,
1609 "realFileSize(): fstat(%d/%s) failed errno=%d\n",
1610 _fd, _real_path, errno);
1611 error.setError(DTME_ObjectAccessFailed);
1615 return(buf.st_size);
1619 RFCMailBox::mapFile(DtMailEnv & error,
1620 const DtMailBoolean already_locked,
1623 char *pname = "mapFile";
1626 if (already_locked == DTM_FALSE) {
1627 DEBUG_PRINTF( ("%s: locking mailbox\n", pname) );
1629 if (error.isSet()) {
1630 DtMailEnv tmp_error;
1631 unlockFile(tmp_error, _fd);
1636 // We must map in whole pages. This is done by rounding the
1637 // file size up to the next larger size.
1640 // Some notes on the singing and dancing that follows are in order.
1641 // As of 6/21/95 it has been determined that there is a bug in S494-17
1642 // and S495-25 whereby a mmap() of the mailbox can return sections that
1643 // have "nulls" where valid data should be found. To guard against this,
1644 // we check for nulls at the beginning and the end of a new mmap()ed region
1645 // and if they are found we retry the operation.
1647 // If NFS attribute caching is enabled (which is the default), a
1648 // stat/fstat of a NFS file may not return the correct true size of the
1649 // mailbox if it has been appended to since the last time it was
1650 // mmap()ed. To get around this problem, once it is noticed via
1651 // stat()/fstat() that the mailbox has changed, we must open the mailbox
1652 // on a separate file descriptor, read() in a byte, and then do a fstat()
1653 // to determine the true correct size of the mailbox.
1656 // fstat() currently open mailbox
1658 struct stat tempStatbuf;
1661 if (SafeFStat(_fd, &tempStatbuf) < 0) {
1665 "%s(%d): fstat(%d/%s) failed errno=%d\n",
1666 pname, err_phase, _fd, _real_path, errno);
1667 if (already_locked == DTM_FALSE) {
1668 DEBUG_PRINTF( ("%s: unlocking mailbox\n", pname) );
1669 unlockFile(error, _fd);
1671 error.setError(DTME_ObjectAccessFailed);
1675 // Obtain guaranteed current stat buf for mailbox object,
1676 // regarless of whether it is local or remote
1678 struct stat statbuf;
1681 if (SafeGuaranteedStat(_real_path, &statbuf) == -1) {
1685 "%s(%d): SafeGuaranteedStat(%s) failed errno=%d\n",
1686 pname, err_phase, _real_path, errno);
1687 if (already_locked == DTM_FALSE) {
1688 DEBUG_PRINTF( ("%s: unlocking mailbox\n", pname) );
1689 unlockFile(error, _fd);
1691 error.setError(DTME_ObjectAccessFailed);
1696 // statbuf -- contains the guaranteed stat struct for the mailbox
1697 // tempStatbuf -- contains fstat stat struct for original mailbox
1700 // See if the inode has changed - if so the file has been
1701 // modified out from under is
1704 if (statbuf.st_ino != tempStatbuf.st_ino) {
1706 error.logError(DTM_FALSE,
1707 "%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",
1708 pname, err_phase, _fd, _real_path, _real_path,
1709 statbuf.st_ino, statbuf.st_dev,
1710 statbuf.st_nlink, statbuf.st_size,
1711 tempStatbuf.st_ino, tempStatbuf.st_dev,
1712 tempStatbuf.st_nlink, tempStatbuf.st_size);
1713 if (already_locked == DTM_FALSE) {
1714 DEBUG_PRINTF( ("%s: unlocking mailbox\n", pname) );
1715 unlockFile(error, _fd);
1717 error.setError(DTME_ObjectInvalid);
1721 long pagesize = memoryPageSize();
1723 // We will only map any file space we haven't mapped so far.
1724 // We always map entire pages to make this easier. We must
1725 // remap the partial pages so we will get the real bits, not
1726 // the zero fill bits provided by mmap.
1728 // The arithmetic for partial mappings is a little odd, and
1729 // is worthy of explanation. The main issue is that we must
1730 // always start on a page boundary, and we must map page
1731 // size chunks. So, for any offset, we need to back up to
1732 // the start of a page in the file. This gives us the following
1733 // entries in MapRegion:
1735 // map_region - Address where the mapping starts.
1736 // map_size - Size of the mapping (will always be a multiple of pagesize).
1737 // file_region - Address where requested file offset begins within
1739 // file_size - Size of file within this mapping.
1740 // offset - Where the mapping begins (which is almost always different
1741 // than the offset where file_region begins.
1743 // So, the new offset begins where the last real file size ended.
1744 // We get this by adding the previous offset, to the file size mapped,
1745 // and then add any the difference between the mapped region and the
1746 // real file offset. This gets us an offset back to the old end of
1747 // file. Now if you are not confused, we need to adjust this new offset
1748 // back to the closest page boundary and begin the mapping from there!
1750 // File by messages:
1751 // v--------v------------v----v---------------------------v----------------v
1752 // | Msg1 | Msg2 |Msg3| Msg4 | Msg5 |
1753 // ^--------^------------^----^---------------------------^----------------^
1755 // +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
1756 // | Pg1 | Pg2 | Pg3 | Pg4 | Pg5 | Pg6 | Pg7 | Pg8 | Pg9 | Pg10| Pg11| Pg12|
1757 // +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
1762 // +--v--+-----+---v-+
1763 // | \\\\\\\\\\\\\\ |
1764 // +--^--+-----+---^-+
1775 // |->| offset_from_map
1788 // |---------------->| .offset
1790 // |-->| offset_from_map
1794 MapRegion * map = new MapRegion;
1795 long offset_from_map;
1797 if (_mappings.length()) {
1798 MapRegion * prev_map = _mappings[_mappings.length() - 1];
1799 map->offset = prev_map->offset + prev_map->file_size +
1800 (prev_map->file_region - prev_map->map_region);
1801 offset_from_map = map->offset % pagesize;
1802 map->offset -= offset_from_map;
1806 offset_from_map = 0;
1809 map->file_size = statbuf.st_size - map->offset - offset_from_map;
1810 map->map_size = statbuf.st_size - map->offset;
1811 map->map_size += (pagesize - (map->map_size % pagesize));
1813 int flags = MAP_PRIVATE;
1815 #if defined(MAP_NORESERVE)
1816 // We are not supposed to be writing to these pages. If
1817 // we don't specify MAP_NORESERVE however, the system will
1818 // reserve swap space equal to the file size to deal with
1819 // potential writes. This is wasteful to say the least.
1821 flags |= MAP_NORESERVE;
1824 // Need to obtain an mmap()ed region and one way or another
1825 // have the data from the mail file placed into that
1826 // region. There are two ways of accomplishing this:
1828 // Method 1: mmap() the data directly from the file
1829 // Method 2: mmap() /dev/zero and then read from the file to the region
1831 // We want to use method #1 if at all possible as it allows the
1832 // VM system to page the data in as it is accessed.
1834 // There is a potential problem with method #1 in that since
1835 // the region is a "view" into the real file data, if the file
1836 // itself is reduced in size behind our back by another
1837 // process, we have the potential for generating SIGBUS memory
1838 // reference errors if we try and access a byte that is no
1839 // longer within the file. This is true even is MAP_PRIVATE is used:
1841 // ** The behavior of PROT_WRITE can be influenced by setting
1842 // ** MAP_PRIVATE in the flags parameter. MAP_PRIVATE means
1843 // ** "Changes are private".
1845 // ** MAP_SHARED and MAP_PRIVATE describe the disposition of write
1846 // ** references to the memory object. If MAP_SHARED is
1847 // ** specified, write references will change the memory object.
1848 // ** If MAP_PRIVATE is specified, the initial write reference
1849 // ** will create a private copy of the memory object page and
1850 // ** redirect the mapping to the copy. Either MAP_SHARED or
1851 // ** MAP_PRIVATE must be specified, but not both. The mapping
1852 // ** type is retained across a fork(2).
1854 // ** Note that the private copy is not created until the first
1855 // ** write; until then, other users who have the object mapped
1856 // ** MAP_SHARED can change the object.
1858 // While this is always the case for a process that does not
1859 // abide by the advisory only locking protocols used to protect
1860 // against this, to guard against this method #1 is only used
1861 // if the mailbox is writable and we can obtain a short term
1862 // lock on the mailbox. This prevents the mailbox from changing
1863 // size at the moment we map it. Nonetheless, a SIGBUS
1864 // interrupt handler must be armed to handle the case where the
1865 // mailbox file is changed out from under us by devious means.
1867 // If this condition is not met, or the mmap() call for some
1868 // reason fails, then fall back to method #2.
1871 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";
1873 map->map_region = (char *)-1;
1875 if ( (_use_dot_lock == DTM_TRUE)
1876 && (_mail_box_writable == DTM_TRUE)
1878 map->map_region = (char *)mmap(
1879 0, (unsigned int) map->map_size,
1880 PROT_READ, flags, _fd, (off_t) map->offset);
1882 // ERROR/CONSISTENCY CHECKING: if the region was not mapped,
1883 // or the first or last byte of the region is a null, then
1884 // the mmap() is considered to have failed: write an entry
1885 // to the dump file, undo the damage, and bail to the caller
1888 if ( (map->map_region == (char *) -1) ||
1889 (map->file_size && (map->map_region[offset_from_map] == '\0')) ||
1891 (map->map_region[offset_from_map+map->file_size-1] == '\0'))
1895 ("mapFile: Error mmap(1) == %p, errno = %d\n", map->map_region, errno));
1901 map->map_size, PROT_READ, flags, _fd, _real_path, map->offset,
1902 map->map_region, errno);
1904 "%s(%d): statbuf: ino=%d, dev=%d, nlink=%d, size=%ld\n",
1906 statbuf.st_ino, statbuf.st_dev, statbuf.st_nlink, statbuf.st_size);
1908 if (map->map_region == (char *) -1)
1912 "%s(%d): mmap failed: errno = %d: %s\n",
1913 pname, err_phase, errno, strerror(errno));
1920 for (i=(int)offset_from_map, cnt=0;
1921 i<map->file_size+offset_from_map;
1923 if (map->map_region[i] == '\0') cnt++;
1926 "%s(%d): mmap failed: %d NULLs in map from byte %d to %d:\n",
1928 cnt, offset_from_map, map->file_size+offset_from_map);
1932 // Pass throught to attempt mapping through /dev/zero.
1934 munmap(map->map_region, (size_t) map->map_size);
1935 map->map_region = (char*) -1;
1937 if (_errorLogging) {
1941 (unsigned char *)map->map_region,
1943 _errorLogging ? 0 : 100);
1946 "Offset Region mapped in",
1947 (unsigned char *)map->map_region+offset_from_map,
1949 _errorLogging ? 0 : 100);
1952 if (already_locked == DTM_FALSE) {
1953 DEBUG_PRINTF( ("%s: unlocking mailbox\n", pname) );
1954 unlockFile(error, _fd);
1957 map->map_region != (char *)-1 ?
1958 DTME_ObjectAccessFailed :
1959 DTME_ObjectCreationFailed);
1967 ("mapFile: mmap(1) okay = %p, errno = %d\n", map->map_region, errno) );
1970 // If a region was mapped, cause OS to use sequential access paging rules
1972 if (map->map_region != (char *) -1)
1973 alterPageMappingAdvice(map);
1975 if (map->map_region == (char *) -1) {
1976 // Either the direct mmap failed, or we decided not to do a direct mmap
1977 // of the new data in the mailbox file. Now must create a mmap()ed
1978 // region against /dev/zero and then read the new data into that region
1980 char *devzero = "/dev/zero";
1982 #if defined(DO_ANONYMOUS_MAP)
1984 mode_t mode = create_mode;
1985 flags |= MAP_ANONYMOUS;
1987 int fd = SafeOpen(devzero, O_RDWR, create_mode);
1990 map->map_region = (char *)mmap(
1991 0, (unsigned int) map->map_size,
1992 PROT_READ|PROT_WRITE, flags, fd, 0);
1995 if (map->map_region == (char *)-1) {
2002 map->map_size, PROT_READ|PROT_WRITE, flags, fd, devzero, errno);
2006 map->map_size, PROT_READ|PROT_WRITE, flags, fd, devzero, errno);
2008 if (already_locked == DTM_FALSE) {
2009 DEBUG_PRINTF( ("%s: unlocking mailbox\n", pname) );
2010 unlockFile(error, _fd);
2012 error.setError(DTME_NoMemory);
2014 #if !defined(DO_ANONYMOUS_MAP)
2020 if (fd != -1 && (SafeClose(fd) < 0)) {
2021 // should deal with the error here (on /dev/zero??)
2024 // Have created a sufficiently large region mapped to /dev/zero
2025 // Read the "new bytes" into this region
2026 // Note that there is a suspicion that a window exists where even
2027 // though the file size appears to be increased by "n", that all
2028 // of the data may not be written in the file yet, and a read may
2029 // fall "short" in this case, especially if running over nfs. We
2030 // must be prepared to handle any condition whereby a read from
2031 // the file either yields less data than expected, or even though
2032 // a good return is received, the data may not be there, and thus
2033 // the check for "nulls" at the beginning and end of the buffer.
2036 // Copying messages between mailboxes often leaves a single
2037 // NULL character at the end of the file.
2038 // Check the last 2 bytes for NULL characters.
2041 lseek(_fd, (off_t) map->offset, SEEK_SET);
2042 size_t bytesToRead = (size_t)(statbuf.st_size - map->offset);
2043 ssize_t readResults = SafeRead(_fd, map->map_region, bytesToRead);
2044 if ( (readResults != bytesToRead) ||
2045 (readResults && (map->map_region[0] == '\0')) ||
2046 (readResults && (map->map_region[readResults-1] == '\0')) ||
2047 (readResults && (map->map_region[offset_from_map] == '\0')) ||
2049 (map->map_region[offset_from_map+map->file_size-1] == '\0'))
2054 "%s(%d): SafeRead(%d(%s), 0x%08lx, %d) == %d, errno == %d\n",
2055 pname, err_phase, _fd, _real_path, map->map_region, bytesToRead,
2056 readResults, errno);
2058 "%s(%d): stat buf: ino=%d, dev=%d, nlink=%d, size=%ld\n",
2059 pname, err_phase, statbuf.st_ino, statbuf.st_dev,
2060 statbuf.st_nlink, statbuf.st_size);
2062 if (readResults > 0) {
2067 (unsigned char *)map->map_region, readResults,
2068 _errorLogging ? 0 : 100);
2071 munmap(map->map_region, (size_t) map->map_size);
2073 if (already_locked == DTM_FALSE) {
2074 DEBUG_PRINTF( ("%s: unlocking mailbox\n", pname) );
2075 unlockFile(error, _fd);
2080 DTME_ObjectAccessFailed :
2081 DTME_ObjectCreationFailed);
2085 mprotect(map->map_region, (size_t) map->map_size, PROT_READ);
2086 alterPageMappingAdvice(map);
2089 map->file_region = map->map_region + offset_from_map;
2091 // Ok, we think we have got all of the new data that has been
2092 // appended to the mailbox - just to make absolutely sure, stat
2093 // the file again and make sure that the file size has remained
2094 // consistent throughout this operation. If it has changed, it
2095 // means some process ignored our lock on the file and appended
2096 // data anyway. In this case, throw away our current effort and
2097 // recursively call this function to try the attempt again.
2101 if (SafeGuaranteedStat(_real_path, &tempStatbuf) < 0) {
2105 "%s(%d): SafeGuaranteedStat(%s) failed errno=%d: %s\n",
2106 pname, err_phase, _real_path, errno, strerror(errno));
2107 munmap(map->map_region, (size_t) map->map_size);
2109 if (already_locked == DTM_FALSE) {
2110 DEBUG_PRINTF( ("%s: unlocking mailbox\n", pname) );
2111 unlockFile(error, _fd);
2113 error.setError(DTME_ObjectAccessFailed);
2118 if (tempStatbuf.st_size != statbuf.st_size) {
2122 "%s(%d): fstat(%d/%s) size changed %d/%d\n",
2124 _fd, _real_path, statbuf.st_size, tempStatbuf.st_size);
2125 munmap(map->map_region, (size_t) map->map_size);
2127 int mapResults = mapFile(error, DTM_TRUE);
2128 if (error.isSet()) {
2129 if (already_locked == DTM_FALSE) {
2130 DEBUG_PRINTF( ("%s: unlocking mailbox\n", pname) );
2131 unlockFile(error, _fd);
2138 if (already_locked == DTM_FALSE) {
2139 DEBUG_PRINTF( ("%s: unlocking mailbox\n", pname) );
2140 unlockFile(error, _fd);
2143 // We need to set _file_size here, because if we get the file size
2144 // when the mailbox is not locked, it is possible that we could be
2145 // stating the file while it is being written to. Since we have the
2146 // lock in this routine, we know the file size isn't changing, and it
2147 // is consistent with the amount being mapped in.
2149 _file_size = statbuf.st_size;
2151 if (_hide_access_events) mailboxAccessHide("mapFile");
2153 return(_mappings.append(map));
2157 RFCMailBox::nextNotDel(const int cur)
2160 for (del = cur; del < _msg_list.length(); del++) {
2161 MessageCache * mc = _msg_list[del];
2162 if (mc->delete_pending == DTM_FALSE) {
2171 RFCMailBox::prevNotDel(const int cur)
2174 for (del = cur; del >= 0; del--) {
2175 MessageCache * mc = _msg_list[del];
2176 if (mc->delete_pending == DTM_FALSE) {
2185 RFCMailBox::lookupByMsg(RFCMessage * msg)
2187 for (int slot = 0; slot < _msg_list.length(); slot++) {
2188 MessageCache * mc = _msg_list[slot];
2189 if (mc->message == msg) {
2198 RFCMailBox::ThreadParseEntry(void * client_data)
2200 RFCMailBox * self = (RFCMailBox *)client_data;
2203 MutexLock lock_map(self->_map_lock);
2205 self->parseFile(error, 0);
2214 RFCMailBox::parseFile(DtMailEnv & error, int map_slot)
2219 // We are not at the eof.
2223 const char * begin = _mappings[map_slot]->file_region;
2224 const char * end = begin + _mappings[map_slot]->file_size - 1;
2227 // Empty file. DO NOTHING OR IT WILL CRASH!
2233 // Parsing will always be a sequential access to the pages.
2234 // We will give the kernel a clue what we are up to and perhaps
2235 // help our parsing time in the process.
2237 unsigned long pagelimit = _mappings[map_slot]->map_size;
2239 #if !defined(linux) && !defined(sun)
2240 // no madvise; dont use optimization
2242 (char *)_mappings[map_slot]->map_region,
2243 (size_t) pagelimit, MADV_SEQUENTIAL);
2246 // We should always begin with a "From " if this is an RFC file,
2247 // with a valid offset. If we have something else, then look
2248 // forward until we find one.
2250 const char * parse_loc = begin;
2251 if (strncmp(parse_loc, "From ", 5)) {
2252 if (*parse_loc == ' ' || *parse_loc == '\n' || *parse_loc == '\t') {
2253 // We allow any number of white spaces before "From"
2254 // But "From" still has to be the first word in the line
2255 while ( (*parse_loc == ' ' || *parse_loc == '\n'
2256 || *parse_loc == '\t') && parse_loc <= end) {
2260 if (parse_loc >= end) {
2261 error.setError(DTME_NotMailBox);
2262 _at_eof.setTrue(); // We are, but so what.
2268 if (strncmp(parse_loc, "\nFrom ", 6)) {
2269 error.setError(DTME_NotMailBox);
2273 // This file does not start with either From or white space
2274 error.setError(DTME_NotMailBox);
2279 if (*parse_loc == '\n') {
2283 // We are sitting at the start of a message. We will build message
2284 // objects for each message in the list.
2287 MessageCache * cache = new MessageCache;
2288 cache->delete_pending = DTM_FALSE;
2290 cache->message = new RFCMessage(error, this, &parse_loc,
2292 if (error.isNotSet()) {
2294 //#ifdef MESSAGE_PARTIAL
2295 // message/partial processing is currently not working, so we are
2296 // taking out message/parital processing for now until we figure
2297 // out how to get it to work again. Only the following block of
2298 // code needs to be taken out in order to disable message/partial.
2299 // Also, when it was turned on, it was trying to combine partial
2300 // messages that were non-MIME (no MIME-VERSION header). This
2301 // caused even more problems. We should only check for partial
2302 // messages if it is MIME.
2304 if (_isPartial(error, cache->message)) {
2306 if (error.isNotSet()) {
2307 cache->message->setFlag(error, DtMailMessagePartial);
2309 if (error.isNotSet()) {
2310 cache->message = _assemblePartial(error, cache->message);
2312 if (error.isNotSet()) {
2313 _msg_list.append(cache);
2319 //#endif // MESSAGE_PARTIAL
2320 _msg_list.append(cache);
2325 } while (parse_loc <= end);
2327 // At this point we most likely will see random behavior. We will
2328 // tell the kernel to pull in the minimum number of extra pages.
2330 #if !defined(linux) && !defined(sun)
2331 // no madvise; dont use optimization
2333 (char *)_mappings[map_slot]->map_region,
2334 (size_t) pagelimit, MADV_RANDOM);
2336 // IBM code for message/partial vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
2337 // we need delete those messages if they are satisfied the
2338 // following two conditions
2339 // (1) marked for delete and
2340 // (2) it is a message/partial message
2342 // Otherwise, we will get segmentation error when the method
2343 // writeMailBox is invoked because a new message were
2344 // generated by assembling partial messages
2346 for (int msg = 0; msg < _msg_list.length(); msg++) {
2347 MessageCache * mc = _msg_list[msg];
2348 if (mc->delete_pending == DTM_FALSE) {
2349 DtMail::Envelope * env = mc->message->getEnvelope(error);
2351 DtMailValueSeq value;
2353 static const char * partial = "message/partial";
2354 static const char * contentType = "content-type";
2356 env->getHeader(error, contentType , DTM_FALSE, value);
2357 if (error.isNotSet()) {
2358 type = strdup(*(value[0]));
2364 if (error.isNotSet()) {
2365 env->getHeader(error, RFCDeleteHeader, DTM_FALSE, value);
2367 if (!error.isSet() && (strncasecmp(type, partial, 15) == 0)) {
2368 delete mc->message; // remove message storage
2369 delete mc; // remove message cache storage
2370 _msg_list.remove(msg); // remove message from message list
2371 msg -= 1; // next message is where we are at now
2382 //IBM code for message/partial ^^^^^^^^^^^^^^^^^^^^^^^^^^^
2389 RFCMailBox::ThreadNewMailEntry(void * client_data)
2391 NewMailData * info = (NewMailData *)client_data;
2393 // We need to wait for the object to become valid. We have
2394 // nothing to do until the mail box is open.
2395 info->object_valid->waitTrue();
2397 // Get the various configuration parameters from .mailrc. The
2398 // RFC_PING_INTERVAL controls how often we look for new mail.
2399 // RFC_CHECK is the number of pings between checks and
2400 // RFC_EXPUNGE is the number of pings between expunges.
2403 DtMail::MailRc * mailrc = info->self->session()->mailRc(error);
2407 time_t check_per_ping = 120;
2408 time_t expunge_per_ping = 240;
2410 const char * value = NULL;
2411 mailrc->getValue(error, "RFC_PING_INTERVAL", &value);
2412 if (error.isNotSet()) {
2413 ping = (time_t) strtol(value, NULL, 10);
2419 free((void*) value);
2422 mailrc->getValue(error, "RFC_CHECK", &value);
2423 if (error.isNotSet()) {
2424 check_per_ping = (time_t) strtol(value, NULL, 10);
2430 free((void*) value);
2433 mailrc->getValue(error, "RFC_EXPUNGE", &value);
2434 if (error.isNotSet()) {
2435 expunge_per_ping = (time_t) strtol(value, NULL, 10);
2441 free((void*) value);
2445 unsigned int unslept = 0;
2447 // Wait until the mail file is parsed.
2449 info->self->_at_eof.waitTrue();
2451 // We loop until the object tries to close, then we exit.
2453 while(info->object_valid->state()) {
2455 // The following sequence is a little weird, but here is why.
2456 // We need to sleep for the ping interval. We can be awaken
2457 // early however if the thread catches a signal. The main
2458 // thread will send a SIGTERM from the mail box destructor
2459 // to wake us up. If the object state is no longer valid, then
2460 // this is the cause, so we need to check it first and exit
2461 // if this is the case.
2463 // We can also be awaken by other signals that we don't care
2464 // about. In that case we need to go back through the loop
2465 // again and sleep any unslept time. We can't be sure however
2466 // that this additional sleep won't be awaken before it is
2467 // done so we have to keep looping and checking the object
2471 unslept = (unsigned int) ThreadSleep(unslept);
2472 continue; // We got rudely awaken!
2475 unslept = (unsigned int) ThreadSleep(ping);
2476 if (!info->object_valid->state()) {
2484 // We made here, we slept and are well rested, and
2485 // we should have a valid object. Run the events.
2490 if (check_per_ping && (pinged % check_per_ping) == 0) {
2492 // info->self->CheckPointEvent(error);
2493 info->self->CheckPointEvent();
2494 // if (error.isSet()) {
2495 // // MAILBOX COULD NOT BE CHECKPOINTED!!! MUST DO SOMETHING!!!
2500 if (expunge_per_ping && (pinged % expunge_per_ping) == 0) {
2501 info->self->ExpungeEvent();
2505 info->self->NewMailEvent();
2508 // We are responsible for cleaning up the condition variable.
2510 delete info->object_valid;
2520 RFCMailBox::PollEntry(void * client_data)
2522 RFCMailBox * self = (RFCMailBox *)client_data;
2524 // Get the various configuration parameters from .mailrc. The
2525 // RFC_PING_INTERVAL controls how often we look for new mail.
2526 // RFC_CHECK is the number of pings between checks and
2527 // RFC_EXPUNGE is the number of pings between expunges.
2532 DtMail::MailRc * mailrc = self->session()->mailRc(error);
2533 error.clear(); // IGNORING ERRORS FROM MAILRC CALL!!
2536 time_t ping = 60; // check for new mail every 60 seconds
2537 time_t save_interval = (30 * 60); // autosave every 30 minutes
2538 long minimumIdleTime = 30; // must have 30 seconds idle time to poll
2540 // Retrieve current setting for how often we are supposed to
2541 // look for new mail (retrieveinterval)
2546 mailrc->getValue(error, "retrieveinterval", &value);
2547 if (error.isNotSet() && value != NULL && *value != '\0')
2549 ping = (time_t) strtol(value, NULL, 10);
2556 if (NULL != value) free((void*) value);
2558 // Retrieve current setting for how often we are supposed to
2559 // perform an auto save of the mailbox
2562 mailrc->getValue(error, "dontautosave", &value);
2563 if (error.isSet()) {
2566 free((void*) value);
2569 mailrc->getValue(error, "saveinterval", &value);
2570 if (error.isNotSet() && value != NULL && *value != '\0') {
2571 save_interval = (time_t) strtol(value, NULL, 10) * 60;
2574 if (save_interval < 60)
2575 //save_interval = 60; // The minimum time is every minute
2576 save_interval = 15; // The minimum time is every minute
2578 else save_interval = -1;
2580 free((void*) value);
2582 // Get the current time in seconds, and compute how long it has
2583 // been since the last interactive input (keystroke, buttonpress)
2584 // was done by the user
2586 time_t now = time(NULL);
2587 time_t lastInteractive =
2588 (time_t) self->session()->lastInteractiveEventTime();
2589 time_t interactiveIdleTime = (now - lastInteractive);
2591 // If there has not been at least <<inactivityinterval> seconds of
2592 // interactive inactivity, skip processing until later so as not to
2593 // place the user in the horrible position of being "locked out" for
2594 // the duration of an auto save/and/or/new mail incorporation
2598 mailrc->getValue(error, "inactivityinterval", &value);
2599 if (error.isNotSet() && value != NULL && *value != '\0') {
2600 minimumIdleTime = strtol(value, NULL, 10);
2601 if (minimumIdleTime < 15)
2602 minimumIdleTime = 15; // at least 15 seconds must go by
2603 else if (minimumIdleTime > 600)
2604 minimumIdleTime = 600; // but not more than 10 minutes
2608 free((void*) value);
2610 if (interactiveIdleTime < minimumIdleTime)
2613 // See if time's up for doing a new mail incorporate
2615 if (ping > 0 && (now - self->_last_poll > ping))
2617 self->_last_poll = now;
2618 self->NewMailEvent();
2621 // See if time's up for doing an auto-save.
2622 // If time's up, check to see if the flag is clear for doing one.
2623 // Flag is not clear if say, the user is in the middle of composing
2626 if (save_interval >= 0 &&
2627 ((now - self->_last_check) > save_interval) &&
2628 (self->session()->getAutoSaveFlag())) {
2630 self->_last_check = (int) now;
2633 // self->CheckPointEvent(error);
2634 self->CheckPointEvent();
2635 // if (error.isSet()) {
2636 // // MAILBOX COULD NOT BE CHECKPOINTED!!! MUST DO SOMETHING!!!
2643 RFCMailBox::NewMailEvent(
2644 const DtMailBoolean already_locked
2647 DtMailEnv error, error1;
2649 struct stat tempStatbuf;
2650 struct stat statbuf;
2651 DtMailEventPacket event;
2652 DtMailCallbackOp op;
2654 if (!_object_valid->state()) return;
2655 if (!_mr_allowed) return;
2657 _session->setBusyState(error1, DtMailBusyState_NewMail);
2659 op = retrieveNewMail(error);
2662 const char *errmsg = NULL;
2663 errmsg = (const char *) error;
2666 event.target = DTM_TARGET_MAILBOX;
2667 event.target_object = this;
2668 event.operation = (void*) op;
2670 event.argument = strdup(errmsg);
2672 event.argument = NULL;
2673 event.event_time = time(NULL);
2675 // longUnlock(error);
2676 _session->writeEventData(error, &event, sizeof(event));
2677 _session->setBusyState(error1, DtMailBusyState_NotBusy);
2681 if ((SafeFStat(_fd, &tempStatbuf) < 0) ||
2682 (SafeGuaranteedStat(_real_path, &statbuf) == -1))
2685 _mail_box_writable = DTM_FALSE;
2688 event.target = DTM_TARGET_MAILBOX;
2689 event.target_object = this;
2690 event.operation = (void *)DTMC_ACCESSFAILED;
2691 event.argument = NULL;
2692 event.event_time = time(NULL);
2694 error.setError(DTME_ObjectAccessFailed);
2695 _session->writeEventData(error, &event, sizeof(event));
2696 _session->setBusyState(error1, DtMailBusyState_NotBusy);
2700 // See if the inode has changed - if so the file has been
2701 // modified out from under is
2703 if (statbuf.st_ino != tempStatbuf.st_ino)
2706 _mail_box_writable = DTM_FALSE;
2709 event.target = DTM_TARGET_MAILBOX;
2710 event.target_object = this;
2711 event.operation = (void *)DTMC_INODECHANGED;
2712 event.argument = NULL;
2713 event.event_time = time(NULL);
2715 error.setError(DTME_MailboxInodeChanged);
2716 _session->writeEventData(error, &event, sizeof(event));
2717 _session->setBusyState(error1, DtMailBusyState_NotBusy);
2721 // We need to compare the current file size to the last size we
2722 // knew about. If it has changed, we need to do something. There
2723 // are a few possibilities here that we must deal with:
2725 // 1. The file size has not changed. Boring. Simply return.
2727 // 2. The file size is larger, and the first byte begins with
2728 // either "From ", "\nFrom ", or "\r\nFrom ". In this case,
2729 // we need to parse the rest of the file, and notify the client
2730 // that new mail has arrived.
2732 // 3. The file size is either smaller, or it is larger and the first
2733 // few bytes dont match #2. This is the worse possible case. This
2734 // means that somebody has modified the object from beneath us.
2735 // We will turn on our "lost state" mode and refuse to do any
2736 // further processing. This thread exits immediately.
2738 // Even though we get the file size here, we can't use it to set the
2739 // new _file_size, because the file isn't currently locked, so another
2740 // process could be writing to it at this time. We rely on mapFile()
2741 // to get and set _file_size while it has the file locked.
2743 off_t size = realFileSize(error, &info);
2744 if (error.isSet()) {
2746 _mail_box_writable = DTM_FALSE;
2749 event.target = DTM_TARGET_MAILBOX;
2750 event.target_object = this;
2751 event.operation = (void *)DTMC_ACCESSFAILED;
2752 event.argument = NULL;
2753 event.event_time = time(NULL);
2755 _session->writeEventData(error, &event, sizeof(event));
2756 _session->setBusyState(error1, DtMailBusyState_NotBusy);
2760 MutexLock lock_object(_obj_mutex);
2762 if (size == _file_size)
2764 _session->setBusyState(error1, DtMailBusyState_NotBusy);
2765 if (_hide_access_events!=DTM_TRUE && info.st_atime<=info.st_mtime)
2766 mailboxAccessShow(info.st_mtime, "NewMailEvent: file_size unchanged");
2770 else if (size > _file_size)
2772 incorporate(error, already_locked);
2773 if (_hide_access_events!=DTM_TRUE && info.st_atime<=info.st_mtime)
2774 mailboxAccessShow(info.st_mtime, "NewMailEvent: file_size grew");
2779 _mail_box_writable = DTM_FALSE;
2780 *_object_valid = -1;
2783 event.target = DTM_TARGET_MAILBOX;
2784 event.target_object = this;
2785 event.operation = (void *)DTMC_BADSTATE;
2786 event.argument = NULL;
2787 event.event_time = time(NULL);
2789 _session->writeEventData(error, &event, sizeof(event));
2792 char *strbuf = new char[128];
2794 "NewMailEvent: File shrank %ld<%ld: already_locked is %s\n",
2795 (unsigned long)size, (unsigned long)_file_size,
2796 already_locked ? "TRUE" : "FALSE");
2801 // We need to bail right now if we have lost a valid mapping.
2803 if (_object_valid->state() < 0) {
2804 lock_object.unlock();
2808 _session->setBusyState(error1, DtMailBusyState_NotBusy);
2813 //RFCMailBox::CheckPointEvent(DtMailEnv & error)
2814 RFCMailBox::CheckPointEvent()
2816 if (!_object_valid->state() || _dirty == 0) {
2820 // Write the mail box out.
2825 // This is INCORRECT usage of the DtMailEnv class. It is against
2826 // standard policy to not clear the error token before passing
2827 // it to a function. We are basically breaking the error handling
2828 // model in order to percolate an error in the BE up to the FE.
2830 // setBusyState() does not modify the error token, it simply
2831 // uses it to report errors back to the user.
2832 _session->setBusyState(error, DtMailBusyState_AutoSave);
2834 writeMailBox(error, _hide_access_events);
2836 if ((DTMailError_t) error ==
2837 DTME_CannotWriteToTemporaryMailboxFile_NoFreeSpaceLeft)
2841 startAutoSave(error1,DTM_FALSE);
2842 showError((char *) error.getClient());
2843 error.setClient(NULL);
2844 startAutoSave(error1,DTM_TRUE);
2847 if (error.isSet()) {
2848 // MAILBOX COULD NOT BE CHECKPOINTED!!! setBusyState must handle error.
2849 error.logError(DTM_TRUE, "RFCMailBox::CheckPointEvent(): Failed to write mailbox: %s", (const char *)error);
2851 _session->setBusyState(error, DtMailBusyState_NotBusy);
2855 RFCMailBox::ExpungeEvent(DtMailBoolean closing)
2857 if (!_object_valid->state() && closing == DTM_FALSE) {
2861 // We need to get the expiration time for a message.
2865 DtMail::MailRc * mailrc = _session->mailRc(error);
2868 const char * expire_time = NULL;
2869 mailrc->getValue(error, "DTMAIL_EXPIRE_TIME", &expire_time);
2870 if (error.isNotSet()) {
2871 expire_days = strtol(expire_time, NULL, 10);
2877 if (NULL != expire_time)
2878 free((void*) expire_time);
2880 if (expire_days == 0 && closing == DTM_FALSE) {
2881 // Only scan on close. No timed delete in effect.
2886 if (expire_days > 0 && closing == DTM_TRUE) {
2887 // Timed delete is in effect so we don't destroy messages on
2892 time_t expire_secs = (time_t) expire_days * (24 * 3600);
2893 time_t now = time(NULL);
2895 for (int msg = 0; msg < _msg_list.length(); msg++) {
2896 MessageCache * mc = _msg_list[msg];
2897 if (mc->delete_pending == DTM_TRUE) {
2898 // Can only delete it so many times!
2903 DtMail::Envelope * env = mc->message->getEnvelope(error);
2905 DtMailValueSeq value;
2906 env->getHeader(error, RFCDeleteHeader, DTM_FALSE, value);
2907 if (!error.isSet()) {
2909 deleted = (time_t) strtol(*(value[0]), NULL, 16);
2910 if ((deleted + expire_secs) < now) {
2911 mc->delete_pending = DTM_TRUE;
2914 // We need to tell our client this message is really
2915 // gone. Of course, if they are closing, why bother
2918 if (closing == DTM_FALSE) {
2919 DtMailEventPacket event;
2921 event.target = DTM_TARGET_MAILBOX;
2922 event.target_object = this;
2923 event.operation = (void *)DTMC_DELETEMSG;
2924 event.argument = mc;
2925 event.event_time = time(NULL);
2926 _session->writeEventData(error, &event, sizeof(event));
2936 // Function: RFCMailBox::createTemporaryMailboxFile
2938 // Given the name for a temporary mailbox file, create a proper
2939 // temporary mailbox file and return a file descriptor opened on
2940 // the newly created file
2942 // . obtain information on current mailbox file
2943 // . create the new temporary file
2944 // . set the permissions, owner and group of the newly created file
2945 // to match those of the current mailbox
2947 // DtMailEnv & error - standard error structure used by caller
2948 // tmp_name -- -> name for temporary file
2950 // error.isset() will indicate if there were any errors encountered, in which
2951 // case the temporary file has not been created.
2953 // int file descriptor opened on the newly created temporary mailbox file
2954 // if no error encountered.
2957 RFCMailBox::createTemporaryMailboxFile(DtMailEnv & error, char *tmp_name)
2962 if (SafeFStat(_fd, &info) < 0) {
2965 error.logError(DTM_FALSE,
2966 "createTemporaryMailboxFile(): fstat(%d) failed errno=%d\n",
2968 error.vSetError(DTME_CannotObtainInformationOnOpenMailboxFile,
2969 DTM_FALSE, NULL, _real_path, error.errnoMessage(errno2));
2973 PRIV_ENABLED(fd,SafeOpen(tmp_name, O_RDWR | O_CREAT | O_TRUNC, info.st_mode));
2978 error.setError(DTME_CannotCreateTemporaryMailboxFile_NoPermission);
2982 error.setError(DTME_CannotCreateTemporaryMailboxFile_IsDirectory);
2986 error.setError(DTME_CannotCreateTemporaryMailboxFile_NoSuchFile);
2989 #if defined(CSRG_BASED)
2994 error.setError(DTME_CannotCreateTemporaryMailboxFile_RemoteAccessLost);
2998 error.vSetError(DTME_CannotCreateTemporaryMailboxFile,
2999 DTM_FALSE, NULL, tmp_name, error.errnoMessage(errno2));
3004 PRIV_ENABLED_OPTIONAL(return_status,SafeFChmod(fd, info.st_mode & 07777));
3005 if (return_status == -1) {
3007 (void) SafeClose(fd);
3008 PRIV_ENABLED(return_status,SafeUnlink(tmp_name));
3009 error.vSetError(DTME_CannotSetPermissionsOfTemporaryMailboxFile,
3010 DTM_FALSE, NULL, tmp_name, info.st_mode & 07777,
3011 error.errnoMessage(errno2));
3015 PRIV_ENABLED(return_status, SafeFchown(fd, info.st_uid, (unsigned int) -1));
3017 // bug 1216914 - dtmail should be able to auto-save a mailbox which is not
3018 // opened [Read Only] if the user has write access to the original mailbox,
3019 // then the user should be able to overwrite the mailbox even if the owner
3020 // gets changes. A mailbox is only opened read-write if the "access()"
3021 // system call indicates the user has write permissions.
3023 if (return_status == -1) {
3025 (void) SafeClose(fd);
3026 PRIV_ENABLED(return_status,SafeUnlink(tmp_name));
3027 error.vSetError(DTME_CannotSetOwnerOfTemporaryMailboxFile,
3028 DTM_FALSE, NULL, tmp_name, info.st_uid,
3029 error.errnoMessage(errno2));
3034 PRIV_ENABLED(return_status, SafeFchown(fd, (unsigned int) -1, info.st_gid));
3038 // Function: RFCMailBox::writeMailBox - create new copy of complete mailbox
3040 // writeMailBox causes a complete copy of the current mailbox to be written
3041 // out to permanent storage. This is done by creating a temporary file, in
3042 // the same location the permanent file resides, and writing the new mailbox
3043 // contents to it. If this is successful, the temporary file is then renamed
3044 // over the old permanent file, taking its place. In this way, if there is
3045 // an error creating the new file, the old file still remains intact.
3047 // . make sure the current mailbox is writeable
3048 // . lock current mailbox
3049 // . check for new mail and incorporate if necessary
3050 // . cause the OS to begin mapping as much of the current mailbox into memory
3051 // as it can so it is available as soon as possible to be written
3052 // . create temporary file to hold new mailbox contents
3053 // . create message list fixing the location of all non-deleted messages
3054 // (dirty messages will be allocated temporary storage and created)
3055 // . build a vectored write array based upon the message list
3056 // . cause the new mailbox contents to be written via one call to SafeWritev
3057 // . delete all "deleted" messages from the message list
3058 // . map the new mailbox file into memory
3059 // . revise all message pointers based upon new mailbox contents
3060 // . remove the mappings and unmap all previous regions used
3061 // . add the one single new mailbox region to the mappings list
3062 // . transfer any lock on the old mailbox file to the new mailbox file
3063 // . rename the new mailbox file to the correct permanent path name
3064 // . set the modification and access times of the mailbox file properly
3066 // DtMailEnv & error - standard error structure used by caller
3068 // error.isset() will indicate if there were any errors encountered, in which
3069 // case the current contents of the mailbox have not been written.
3071 // At some point in the call chain, someone must check to see if this is
3072 // a FATAL ERROR (e.g. error.isFatal) and if so, display the returned
3073 // message and then immediately exit, as this means the mailbox could not
3074 // be written and the internal mailbox state is hopelessly munged..
3079 RFCMailBox::writeMailBox(DtMailEnv &error, DtMailBoolean hide_access)
3081 static char *pname = "writeMailBox";
3083 DtMailEnv error2; // When we need to preserve error during error cleanup
3084 int return_status; // for handling priv/unpriv operations
3085 #if defined(DEBUG_RFCMailBox)
3086 char *pname = "writeMailBox";
3089 MutexLock lock_map(_map_lock);
3092 if (_mail_box_writable == DTM_FALSE) {
3096 // Need to lock the file while we do this.
3098 DEBUG_PRINTF( ("%s: locking mailbox\n", pname) );
3100 if (error.isSet()) {
3101 DtMailEnv tmp_error;
3102 unlockFile(tmp_error, _fd);
3106 // Need to deal with any potentially new mail that
3107 // has arrived and we don't know about.
3109 if (_mra_server == NULL) checkForMail(error, DTM_TRUE);
3110 if (error.isSet()) {
3111 DEBUG_PRINTF( ("%s: locking mailbox\n", pname) );
3112 unlockFile(error2, _fd);
3116 // Create the new temporary file to hold the mailbox contents
3118 static char tmp_name[MAXPATHLEN+1];
3119 sprintf(tmp_name, "%s.tmp.%08lx", _real_path, (long)time(NULL));
3120 assert(strlen(tmp_name)<sizeof(tmp_name));
3122 int fd = createTemporaryMailboxFile(error, tmp_name);
3123 if (error.isSet()) {
3124 DEBUG_PRINTF( ("%s: locking mailbox\n", pname) );
3125 unlockFile(error2, _fd);
3130 // Got the new mailbox file all set up to be our friend.
3131 // zip through current message structure fixing the location of all
3132 // non-deleted messages in preparation of writing them all out to
3133 // the temporary file just opened. The act of "fixing" the location of
3134 // a message will cause dirty messages to be constructed in temporary
3135 // allocated areas which must later be deallocated. This deallocation
3136 // is done either by calling adjustMessageLocation() to make the location
3137 // of the message permanent, or by calling unfixMessageLocation) to
3138 // abandon the new message (e.g. if new mailbox cannot be created).
3140 int deletesPending = 0; // count # of delete_pending messages scanned
3143 struct tempMsgList {
3144 MessageCache *tmlMc; // -> mc of this message
3145 long tmlRealOffset; // byte offset from 0 where message is written
3146 long tmlBodyOffset; // byte offset from 0 of body parts of message
3147 char *tmlHeaderStart; // -> start of headers for this message
3148 long tmlHeaderLen; // length of headers
3149 char *tmlBodyStart; // -> start of bodies for this message
3150 long tmlBodyLen; // length of bodies
3151 int tmlTemporary; // ==0:permanent, !=0:temporary(must move)
3154 long tmlTotalSize = _msg_list.length()*4;
3155 tempMsgList *const tmlFirst = (tempMsgList*)
3156 malloc((size_t) (sizeof(tempMsgList)*tmlTotalSize));
3157 tempMsgList *tmlLast = tmlFirst;
3159 for (msg = 0; msg < _msg_list.length(); msg++) {
3160 MessageCache *mc = _msg_list[msg];
3161 if (mc->delete_pending == DTM_TRUE) { // ignore deleted messages
3162 deletesPending++; // but remember if any seen
3165 tmlLast->tmlMc = mc;
3166 mc->message->fixMessageLocation(&tmlLast->tmlHeaderStart, tmlLast->tmlHeaderLen,
3167 &tmlLast->tmlBodyStart, tmlLast->tmlBodyLen,
3168 tmlLast->tmlTemporary, tmlLast->tmlBodyOffset);
3170 fprintf(stdout, "msg %03d @ %08lx %08lx/%06d %08lx/%06d %04d %s\n",
3171 msg, mc, tmlLast->tmlHeaderStart, tmlLast->tmlHeaderLen,
3172 tmlLast->tmlBodyStart, tmlLast->tmlBodyLen,
3173 tmlLast->tmlBodyOffset, tmlLast->tmlTemporary ? "T" : "P");
3174 HexDump(stdout, "header", (unsigned char *)tmlLast->tmlHeaderStart, tmlLast->tmlHeaderLen, 10);
3175 HexDump(stdout, "body", (unsigned char *)tmlLast->tmlBodyStart, tmlLast->tmlBodyLen, 10);
3178 assert(tmlLast->tmlHeaderStart != NULL);
3179 assert(tmlLast->tmlHeaderLen);
3180 tmlLast->tmlRealOffset = -1;
3183 assert((tmlLast-tmlFirst) < tmlTotalSize);
3185 // we can now allocate the vectored write array and fill it according
3186 // to the data stored in the message list we just created
3188 long iovSize = ((tmlLast-tmlFirst)+2)*3;
3189 iovec *const iovFirst = (iovec *) malloc((size_t) (sizeof(iovec)*iovSize));
3190 iovec *iovLast = iovFirst;
3191 iovec *iovPrev = (iovec *)0;
3192 long iovCurrentOffset = 0;
3194 for (tempMsgList *tmlNdx = tmlFirst; tmlNdx < tmlLast; tmlNdx++) {
3195 // if this message happens to start on the first byte following the
3196 // last byte of the previous message, combine into a single vector,
3197 // else add this as another vector in the vector list
3199 tmlNdx->tmlRealOffset = iovCurrentOffset;
3200 if ( (iovPrev != (iovec *)0) && (!tmlNdx->tmlTemporary) &&
3201 ((char *)((size_t) iovPrev->iov_base+iovPrev->iov_len) ==
3202 tmlNdx->tmlHeaderStart)
3204 iovPrev->iov_len += (int)(tmlNdx->tmlHeaderLen+tmlNdx->tmlBodyLen);
3205 iovCurrentOffset += (int)(tmlNdx->tmlHeaderLen+tmlNdx->tmlBodyLen);
3207 else if (!tmlNdx->tmlTemporary) {
3208 iovLast->iov_base = (caddr_t)tmlNdx->tmlHeaderStart;
3209 iovLast->iov_len = (int)(tmlNdx->tmlHeaderLen+tmlNdx->tmlBodyLen);
3210 iovCurrentOffset += iovLast->iov_len;
3211 iovPrev = iovLast++;
3214 // Message is temporary - headers and bodies are in different areas,
3215 // and the headers are in a "temporary" area
3217 iovLast->iov_base = (caddr_t)tmlNdx->tmlHeaderStart;
3218 iovLast->iov_len = (int)tmlNdx->tmlHeaderLen;
3219 iovCurrentOffset += iovLast->iov_len;
3220 iovPrev = iovLast++;
3222 // Write out bodies only if the length is non-zero, otherwise,
3223 // optimize out the inclusion of a zero length write from the vector
3225 if (tmlNdx->tmlBodyLen > 0) {
3226 iovLast->iov_base = (caddr_t)tmlNdx->tmlBodyStart;
3227 iovLast->iov_len = (int)tmlNdx->tmlBodyLen;
3228 iovCurrentOffset += iovLast->iov_len;
3229 iovPrev = iovLast++;
3232 // The last two bytes of the message must be \n\n
3233 // If not then this must be forced
3234 // Obtain pointer tp to the last byte of the current message
3235 // Given this pointer we now have:
3236 // tp[-1] -- next to last byte in current message
3237 // tp[ 0] -- last byte in current message
3238 // tp[+1] -- first byte of next message (if any)
3239 // There must always be two \n characters between each message. If not,
3240 // we must insert sufficient \n characters into the message stream to
3241 // accomplish this. We want to avoid plopping these in, however, as each
3242 // one will add an extra 1- or 2-byte vector into the vectored write array,
3243 // which will affect the throughput of the overall write.
3244 // Irregardless of whether the current message is temporary, we check to see if the next
3245 // byte or two (as necessary) is in a mapped region; if so, we can then
3246 // peek ahead to see if there are the additional \ns we need.
3247 // If all this fails, we punt and put in a small 1- or 2-byte write
3251 char *tp = (char *) ((size_t) iovPrev->iov_base + (iovPrev->iov_len - 1));
3254 if ( ((addressIsMapped(tp+1) == DTM_TRUE) )
3255 && (*(tp+1) == '\n') ) {
3261 iovLast->iov_base = (caddr_t)"\n"; // add \n
3262 iovLast->iov_len = 1;
3264 iovPrev = iovLast++;
3268 if ( (( (addressIsMapped(tp+1) == DTM_TRUE)
3269 && (addressIsMapped(tp+2) == DTM_TRUE)))
3270 && (*(tp+1) == '\n') && (*(tp+2) == '\n')) {
3271 iovPrev->iov_len+= 2;
3272 iovCurrentOffset+= 2;
3276 iovLast->iov_base = (caddr_t)"\n\n"; // add \n\n
3277 iovLast->iov_len = 2;
3278 iovCurrentOffset += 2;
3279 iovPrev = iovLast++;
3284 assert((iovLast-iovFirst) < iovSize);
3286 // All of the messages are properly accounted for in the write vector;
3287 // cause the damage to be done by calling SafeWritev. After it returns,
3288 // Make absolutely sure that all of the mailbox data has made it to
3289 // the final destination, especially if the mailbox is not local -
3290 // this way if we run out of disk space or some other such problem,
3291 // it is caught here and now.
3293 unsigned long bytesWritten = SafeWritev(fd, iovFirst, iovLast-iovFirst);
3294 if (bytesWritten == (unsigned long)-1 || fsync(fd) == -1) {
3296 for (tempMsgList *tml = tmlFirst; tml < tmlLast; tml++) {
3297 MessageCache *mc = tml->tmlMc;
3299 mc->message->unfixMessageLocation(tml->tmlHeaderStart, tml->tmlTemporary);
3301 FileSystemSpace(tmp_name, 0,&fsname);
3302 (void) SafeClose(fd);
3303 DEBUG_PRINTF( ("%s: locking mailbox\n", pname) );
3304 unlockFile(error2, _fd);
3305 PRIV_ENABLED(return_status,SafeUnlink(tmp_name));
3311 DTME_CannotWriteToTemporaryMailboxFile_ProcessLimitsExceeded);
3314 #if defined(CSRG_BASED)
3319 error.setError(DTME_CannotWriteToTemporaryMailboxFile_RemoteAccessLost);
3323 error.setClient(fsname);
3324 error.setError(DTME_CannotWriteToTemporaryMailboxFile_NoFreeSpaceLeft);
3328 error.vSetError(DTME_CannotWriteToTemporaryMailboxFile,
3329 DTM_FALSE, NULL, tmp_name, error.errnoMessage(errno2));
3334 // The current contents of the mailbox have successfully been written
3335 // to the temporary file. Cause the new mailbox file to be mapped
3338 MapRegion * map = mapNewRegion(error, fd, bytesWritten);
3339 if (error.isSet()) {
3340 for (tempMsgList *tml = tmlFirst; tml < tmlLast; tml++) {
3341 MessageCache *mc = tml->tmlMc;
3343 mc->message->unfixMessageLocation(tml->tmlHeaderStart, tml->tmlTemporary);
3345 (void) SafeClose(fd);
3346 DEBUG_PRINTF( ("%s: locking mailbox\n", pname) );
3347 unlockFile(error2, _fd);
3348 PRIV_ENABLED(return_status,SafeUnlink(tmp_name));
3354 // POINT OF NO RETURN -- NEW MAILBOX MUST BE SUCCESSFULLY LINKED UP WITH
3355 // BECAUSE THE MACHINATIONS AND POINTER MUNGING DONE BELOW CANNOT BE UNDONE
3358 // Flush all deleted messages (if any were previously detected)
3361 for (msg = 0; msg < _msg_list.length(); msg++) {
3362 MessageCache * mc = _msg_list[msg];
3363 if (mc->delete_pending == DTM_TRUE) {
3364 delete mc->message; // remove message storage
3365 delete mc; // remove message cache storage
3366 _msg_list.remove(msg); // remove message from message list
3367 msg -= 1; // next message is where we are at now
3371 // spin through all "written messages" and fixup their pointers so they
3372 // point into the new region
3375 // For this occasion advise the OS that we will be doing sequential access
3377 alterPageMappingAdvice(map);
3379 for (tempMsgList *tml = tmlFirst; tml < tmlLast; tml++) {
3380 MessageCache *mc = tml->tmlMc;
3382 assert(mc->delete_pending == DTM_FALSE);
3383 mc->message->adjustMessageLocation(tml->tmlHeaderStart, map->file_region+tml->tmlRealOffset, tml->tmlHeaderLen+tml->tmlBodyLen, tml->tmlTemporary, tml->tmlBodyOffset);
3386 // Loop through the current mappings, and unmap each.
3387 // Then make the new single large map the only mapping.
3389 while(_mappings.length()) {
3390 MapRegion * c_map = _mappings[0];
3391 munmap(c_map->map_region, (size_t) c_map->map_size);
3393 _mappings.remove(0);
3395 _mappings.append(map);
3397 // fix for cmvc defect 7912 - Queued mail lost upon closing dtmail
3398 // If we are using the .lock protocol, we are locking on a file name
3399 // basis, and therefore can rename the new mailbox over the old mailbox
3400 // without a worry about locks; however, if we are using another type
3401 // of locking which locks on a *file* basis, then as soon as the rename
3402 // is done if there is not a lock on the file a process like sendmail
3403 // could come in and complicate matters. After being properly locked,
3404 // rename the new mailbox file over the old mailbox file, and then
3405 // remove the old lock if applicable.
3407 lockNewMailboxFile(fd);
3408 PRIV_ENABLED(return_status,SafeRename(tmp_name, _real_path));
3409 if (return_status == -1) {
3410 // the rename failed -- we are in a world of hurt now.
3411 // We have successfully written the new mailbox out, unmapped the
3412 // old file, mapped in the new file, and bashed all of the various
3413 // pointers to point to the new mailbox; however, we cannot rename
3414 // the new mailbox over the old mailbox. We cannot continue, so return
3415 // this as a fatal error so that the caller can exit properly.
3417 error.vSetError(DTME_CannotRenameNewMailboxFileOverOld,
3418 DTM_TRUE, NULL, _real_path, tmp_name,
3419 error.errnoMessage());
3420 (void) SafeClose(fd);
3421 (void) SafeClose(_fd);
3422 return; // no complete cleanup necessary as we should exit real fast...
3425 assert(map->file_size == bytesWritten);
3427 DtMailBoolean file_grew;
3428 if (map->file_size > _file_size)
3429 file_grew = DTM_TRUE;
3431 file_grew = DTM_FALSE;
3433 _file_size = map->file_size;
3436 if (SafeFStat(fd, &info) < 0)
3439 error.logError(DTM_TRUE,
3440 "%s: fstat(%d/%s) failed errno=%d\n",
3441 pname, _fd, tmp_name, errno);
3442 error.setError(DTME_ObjectCreationFailed, DTM_TRUE, (Tt_message) 0);
3446 if (info.st_size != _file_size)
3448 error.logError(DTM_TRUE,
3449 "%s: new mailbox size not consistent with expected size = %d\nfstat: st_ino = %d, st_dev = %d, st_nlink = %d, st_size = %ld\n",
3450 pname, bytesWritten,
3451 info.st_ino, info.st_dev, info.st_nlink, info.st_size);
3452 error.setError(DTME_ObjectCreationFailed, DTM_TRUE, (Tt_message) 0);
3455 if (hide_access == DTM_FALSE && info.st_atime <= info.st_mtime)
3456 mailboxAccessShow(info.st_mtime, "writeMailBox");
3459 // order of unlocks is important here:
3460 // unlockOldMailboxFile checks the state of _long_lock_active
3461 // but does not alter it, whereas unlockFile does.
3463 unlockOldMailboxFile(_fd); // unlock old mailbox file first
3464 DEBUG_PRINTF( ("%s: locking mailbox\n", pname) );
3465 unlockFile(error2,fd); // then unlock new mailbox file
3466 if (SafeClose(_fd) < 0) {
3467 // should do something with the error here.
3470 _fd = fd; // new mailbox file now current one
3471 _dirty = 0; // mark mailbox as no longer dirty.
3480 RFCMailBox::incorporate(DtMailEnv & error, const DtMailBoolean already_locked)
3482 DtMailEventPacket event;
3484 if (already_locked == DTM_FALSE) {
3485 MutexLock lock_map(_map_lock);
3488 int slot = mapFile(error, already_locked);
3489 if (error.isSet()) {
3490 if (DTME_ObjectInvalid == (DTMailError_t) error)
3493 _mail_box_writable = DTM_FALSE;
3496 event.target = DTM_TARGET_MAILBOX;
3497 event.target_object = this;
3498 event.operation = (void *)DTMC_INODECHANGED;
3499 event.argument = NULL;
3500 event.event_time = time(NULL);
3501 _session->writeEventData(error, &event, sizeof(event));
3503 else if (DTME_ObjectAccessFailed == (DTMailError_t) error)
3506 _mail_box_writable = DTM_FALSE;
3509 event.target = DTM_TARGET_MAILBOX;
3510 event.target_object = this;
3511 event.operation = (void *)DTMC_ACCESSFAILED;
3512 event.argument = NULL;
3513 event.event_time = time(NULL);
3514 _session->writeEventData(error, &event, sizeof(event));
3521 MapRegion * map = _mappings[slot];
3522 const char * buf = map->file_region;
3524 // Let's accept any white space as okay in front of the
3527 for (; buf < (map->map_region + map->map_size) &&
3528 isspace(*buf); buf++) {
3533 DtMailBoolean done = DTM_FALSE;
3535 while (num_tries < 5 && !done) {
3536 if (strncmp(buf, "From ", 5) == 0) {
3537 DtMailMessageHandle last = NULL;
3539 // We can be here via either of two scenarios:
3540 // 1- Aside from incorporating new mail, there is no other
3541 // activity on the mail box. This is the "normal" case.
3542 // already_locked is its default value (FALSE).
3544 // 2- We are here because we were doing a Destroy Deleted Messages
3545 // when we noticed new mail that had to be first incorporated.
3546 // already_locked is TRUE (i.e., the Expunge method has lock...)
3547 // We cannot place the handle of the last entity in the array in
3548 // the stream because the entity may be marked for delete, and
3549 // the Destroy Deleted Messages operation will expunge the entity
3550 // shortly after it has been placed on the stream for the FE.
3552 // In both cases, we want to get the last UNDELETED message and
3553 // place it on the stream for the FE. Undeleted entities will
3554 // be valid even after a DDM while deleted entities will be invalid
3557 // Place the handle of the last entity in the array in the
3558 // callback stream. The FE will get it and can invoke
3559 // getNextMessageSummary() on the mailbox using the handle to
3560 // retrieve the new messages.
3562 if (_msg_list.length() > 0) {
3564 if (already_locked) {
3565 // already_locked is TRUE only in one case:
3566 // We were trying to destroy deleted messages when we
3567 // noticed new mail has come in and we need to incorporate
3569 // This will return the index of the last undeleted
3570 // message. That entity, we are assured, will remain
3571 // valid after a DMM that may appear just before the event
3572 // is received by the FE's callback method.
3574 int index = prevNotDel(_msg_list.length() - 1);
3575 last = _msg_list[index];
3578 // already_locked is FALSE
3580 // No possiblity of entities in _msg_list being expunged.
3581 // Give the current last one to the FE and let it retrieve
3582 // new messages via the getNext() method on that entity.
3583 last = _msg_list[_msg_list.length() - 1];
3589 parseFile(error, slot);
3592 event.target = DTM_TARGET_MAILBOX;
3593 event.target_object = this;
3594 event.operation = (void *)DTMC_NEWMAIL;
3595 event.argument = last;
3596 event.event_time = time(NULL);
3597 _session->writeEventData(error, &event, sizeof(event));
3602 _mail_box_writable = DTM_FALSE;
3603 *_object_valid = -1;
3606 event.target = DTM_TARGET_MAILBOX;
3607 event.target_object = this;
3608 event.operation = (void *)DTMC_BADSTATE;
3609 event.argument = NULL;
3610 event.event_time = time(NULL);
3612 error.setError(DTME_ObjectInvalid);
3613 _session->writeEventData(error, &event, sizeof(event));
3616 char *strbuf = new char[256];
3617 if (already_locked) {
3620 "Attempt #%d: RFCMailBox::incorporate(): No From: buf=<%.10s>, already_locked is TRUE\n", num_tries + 1, buf);
3624 "Attempt #%d: RFCMailBox::incorporate(): No From: buf=<%.10s>, already_locked is FALSE\n", num_tries + 1, buf);
3637 RFCMailBox::generateLockFileName(void)
3640 char *lock_path = new char[MAXPATHLEN+20];
3642 assert(_real_path != NULL);
3643 (void) sprintf(lock_path, "%s.lock", _real_path);
3644 s = strdup(lock_path);
3645 delete [] lock_path;
3649 // Function: RFCMailBox::generateUniqueLockId - create unique ID for this mailbox lock files
3651 // generateUniqueLockId creates a unique ID which is written into .lock files and
3652 // can then be checked to make sure that the lock file has not been compromised by
3655 // The ID generated consists of three parts:
3656 // <process id/%08d><current time in seconds/%08d><hardware serial number/%d>
3657 // Thus, a "typical" id would look like this:
3658 // 000018577971028681915751068
3659 // Which breaks down as:
3660 // 00001857 797102868 1915751068
3666 // char * -> allocated memory in which unique id has been created
3669 RFCMailBox::generateUniqueLockId(void)
3672 char hwserialbuf[64];
3674 #if !defined(__aix) && !defined(__hpux) && !defined(linux) && !defined(CSRG_BASED)
3675 if (sysinfo(SI_HW_SERIAL, (char *)hwserialbuf, sizeof(hwserialbuf)-1) == -1)
3677 strcpy(hwserialbuf, "dtmail");
3678 (void) sprintf(theId, "%08ld%08ld%s\0", (long)getpid(), (long)time((time_t *)0), hwserialbuf);
3679 assert(strlen(theId)<sizeof(theId));
3680 return(strdup(theId));
3684 RFCMailBox::checkLockFileOwnership(DtMailEnv & error)
3686 char *pname = "checkLockFileOwnership";
3688 assert(_lockFileName != NULL);
3690 if (SafeStat(_lockFileName, &info) < 0) {
3692 error.logError(DTM_FALSE,
3693 "%s: lock cannot be stat()ed: %s, errno = %d\n",
3694 pname, _lockFileName, errno);
3695 error.setError(DTME_ObjectInvalid);
3703 flags = O_RDONLY | O_SYNC;
3705 flags = O_RDONLY | O_RSYNC | O_SYNC;
3706 #endif /* O_RSYNC */
3708 #ifdef MAILGROUP_REQUIRED
3709 PRIV_ENABLED_OPEN(_lockFileName, lock_fd, SafeOpen(_lockFileName, flags, 0));
3711 PRIV_ENABLED_OPTIONAL(lock_fd, SafeOpen(_lockFileName, flags, 0));
3714 if (lock_fd == -1) {
3718 "%s: lock cannot be open()ed: %s, errno = %d\n",
3719 pname, _lockFileName, errno);
3720 error.setError(DTME_ObjectInvalid);
3724 char lockBuf[MAXPATHLEN];
3725 int status = SafeRead(lock_fd, lockBuf, sizeof(lockBuf)-1);
3730 "%s: lock cannot be read: %s, errno = %d\n",
3731 pname, _lockFileName, errno);
3732 (void) SafeClose(lock_fd);
3733 error.setError(DTME_ObjectInvalid);
3737 if ( (status < _uniqueLockIdLength+2)
3738 || (strncmp(lockBuf+2, _uniqueLockId, _uniqueLockIdLength) != 0) ) {
3741 "%s: dtmail lock file stolen by another process\n", pname);
3745 "lock file stolen - lock file contents",
3746 (unsigned char *)lockBuf, status, 0);
3747 (void) SafeClose(lock_fd);
3748 error.setError(DTME_ObjectInvalid);
3751 (void) SafeClose(lock_fd);
3755 // Function: RFCMailBox::linkLockFile - create and link temporary lock file to real lock file
3757 // Create a lock file for the current mailbox. Return success of failure.
3759 // . create a temporary lock file with a unique signature id of this process
3760 // . link the temporary lock file to the real lock file
3761 // . if the link is not successful, remove the temporary lock file and return the
3762 // time() in seconds on the remote system of when the temporary lock file was created
3763 // . if the link is successful, remove the temporary lock file (link) and return 0.
3765 // error -- standard error structure used by caller
3767 // If error.isSet() it is a fatal error from which the caller should return to its caller,
3768 // return value will always be time(0)
3769 // If !error.isSet() then check results of return value
3771 // time_t == 0 : indicates that the real lock file has been created and we own it
3772 // != 0 : could not create real lock file, return value is the time *on the remote system*
3773 // that the temporary lock file was created with (from comparison with existing
3774 // lock file to see how old it is)
3777 RFCMailBox::linkLockFile(DtMailEnv & error, char *tempLockFileName)
3783 // Create the temporary lock file. Failure to do so indicates lack of write permission
3784 // in the directory or some other fatal error
3788 int flags = O_RDWR | O_CREAT | O_EXCL | O_SYNC;
3790 int flags = O_RDWR | O_CREAT | O_EXCL | O_SYNC | O_RSYNC;
3792 PRIV_ENABLED(lock_fd,SafeOpen(tempLockFileName, flags, 0666));
3794 // We are not able to create the temporary lock file.
3795 // We will have to punt on trying to lock here from now on.
3799 error.setError(DTME_CannotCreateMailboxLockFile_NoPermission);
3803 error.setError(DTME_CannotCreateMailboxLockFile_IsDirectory);
3807 error.setError(DTME_CannotCreateMailboxLockFile_NoSuchFile);
3810 #if defined(CSRG_BASED)
3815 error.setError(DTME_CannotCreateMailboxLockFile_RemoteAccessLost);
3819 error.vSetError(DTME_CannotCreateMailboxLockFile,
3820 DTM_FALSE, NULL, tempLockFileName, error.errnoMessage());
3826 // Get creation time of temporary file *on remote system*
3828 if (SafeFStat(lock_fd, &sbuf) == -1) {
3830 error.logError(DTM_FALSE,
3831 "linkLockFile(): temporary lock file cannot be stat()ed: %s, errno = %d\n",
3832 tempLockFileName, errno);
3833 error.vSetError(DTME_CannotCreateMailboxLockFile,
3834 DTM_FALSE, NULL, tempLockFileName, error.errnoMessage());
3835 (void) SafeClose(lock_fd);
3839 // Write proper contents to lock file:
3840 // Write the string "0" into the lock file to give us some
3841 // interoperability with SVR4 mailers. SVR4 mailers expect
3842 // a process ID to be written into the lock file and then
3843 // use kill() to see if the process is alive or not. We write
3844 // 0 into it so that SVR4 mailers will always think our lock file
3845 // is valid. In addition we include a unique ID so we can verify
3846 // if the lock file is stolen out from under us.
3848 ssize_t writeResults;
3849 writeResults = SafeWrite(lock_fd, "0\0", 2);
3850 if (writeResults == 2)
3851 writeResults += SafeWrite(lock_fd, _uniqueLockId, _uniqueLockIdLength);
3852 if ( (writeResults != _uniqueLockIdLength+2) ){
3854 error.logError(DTM_FALSE,
3855 "linkLockFile(): write to temporary lock file failed: %s, errno = %d\n",
3856 tempLockFileName, errno);
3857 error.vSetError(DTME_CannotCreateMailboxLockFile,
3858 DTM_FALSE, NULL, tempLockFileName, error.errnoMessage());
3859 (void) SafeClose(lock_fd);
3863 // sync up the lock file with the ultimate storage device
3865 if (fsync(lock_fd) == -1) {
3867 error.logError(DTM_FALSE,
3868 "linkLockFile(): fsync to temporary lock file failed: %s, errno = %d\n",
3869 tempLockFileName, errno);
3870 error.vSetError(DTME_CannotCreateMailboxLockFile,
3871 DTM_FALSE, NULL, tempLockFileName, error.errnoMessage());
3872 (void) SafeClose(lock_fd);
3878 if (SafeClose(lock_fd) == -1) {
3882 "linkLockFile(): close of temporary lock file failed: %s, errno = %d\n",
3883 tempLockFileName, errno);
3884 error.vSetError(DTME_CannotCreateMailboxLockFile,
3885 DTM_FALSE, NULL, tempLockFileName, error.errnoMessage());
3889 // The temporary lock file has been created - now try and link it to the
3890 // real lock file. Failure here is not fatal as we will retry and possible
3891 // try and remove the real lock file later on.
3893 PRIV_ENABLED(return_status,SafeLink(tempLockFileName, _lockFileName));
3894 if (return_status == -1) {
3895 PRIV_ENABLED(return_status,SafeRemove(tempLockFileName));
3896 return(sbuf.st_ctime);
3899 // We successfully linked the temp lock file to the real lock file name
3900 // This means we have the dot lock for our process - remove the temporary lock
3901 // file name (link) and return
3903 PRIV_ENABLED(return_status,SafeRemove(tempLockFileName));
3908 RFCMailBox::lockFile(DtMailEnv & error)
3910 #if defined(DEBUG_RFCMailBox)
3911 char *pname = "RFCMailBox::lockFile";
3913 int return_status = 0;
3915 // We will create a simple lock file to keep the file from
3916 // changing while we are doing critical work.
3919 // On some platforms, sendmail will place a lockf lock on the
3920 // file during mail delivery. If this is the case, then we
3921 // need to make sure we have the lock here.
3923 #if defined(SENDMAIL_LOCKS)
3924 assert(_lockf_active == DTM_FALSE);
3925 lseek(_fd, 0, SEEK_SET);
3926 PRIV_ENABLED_OPTIONAL(return_status,SafeLockf(_fd, F_LOCK, 0));
3927 if (return_status != -1)
3929 _lockf_active = DTM_TRUE;
3930 DEBUG_PRINTF( ("%s: lockf succeeded\n", pname) );
3934 if (_use_dot_lock == DTM_FALSE) {
3935 DEBUG_PRINTF( ("%s: not using dot lock\n", pname) );
3939 // Implement the .lock short term lock protocol
3940 // This code was "adapted" from Solaris 2.5 (SunOS 5.5)
3941 // usr/src/cmd/mail/maillock.c.
3943 assert(_dot_lock_active == DTM_FALSE);
3945 // Create the temporary mail lock file name
3946 // It has the form <_lockfilename><XXXXXX> or mailbox.lockXXXXXX
3947 // mktemp then creates a unique temporary file for the template
3949 assert(_lockFileName != NULL);
3950 char *tempLockFileName = new char[MAXPATHLEN];
3951 sprintf(tempLockFileName, "%sXXXXXX", _real_path);
3952 mktemp(tempLockFileName);
3953 PRIV_ENABLED(return_status,SafeRemove(tempLockFileName));
3955 // loop through attempting to create the temporary lock file,
3956 // and if successful attempt to link the temporary lock file
3957 // to the real lock file. If not successful, retry for up to 5
3958 // minutes, and remove the current lock file if it is more than
3965 // Attempt to create a temporary file and link it to the intended lock file
3966 // If it is successful, we have the lock and can return.
3967 // If it is not successful and error.isSet then it is a non-recoverable
3969 // in which case the mailbox is deemed not-writable any more.
3970 // If it is not successful and !error.isSet then it is a recoverable error,
3971 // in which case we spin and try again according to the retry rules.
3973 time_t t = linkLockFile(error, tempLockFileName);
3974 if (error.isSet()) {
3975 // hard error? -- something is wrong, assume read/only
3976 _use_dot_lock = DTM_FALSE;
3977 _mail_box_writable = DTM_FALSE;
3978 (void) SafeRemove(tempLockFileName);
3980 ("%s: failed to link dot_lock file %s\n", pname, tempLockFileName) );
3981 delete [] tempLockFileName;
3985 checkLockFileOwnership(error);
3988 _dot_lock_active = DTM_TRUE;
3989 DEBUG_PRINTF( ("%s: succeeded acquiring dot_lock file\n", pname) );
3991 delete [] tempLockFileName;
3995 // Could not link the temporary lock file to the intended real lock file
3996 // See if the lock file exists and if so if we can remove it because it
3997 // is > 5 mins old. If the stat fails it means the lock file disappeared
3998 // between our attempt to link to it and now - only allow this to go on
3999 // so many times before punting
4001 if (SafeStat(_lockFileName, &sbuf) == -1) {
4002 if (statFailed++ > 5) {
4003 error.vSetError(DTME_CannotCreateMailboxLockFile,
4004 DTM_FALSE, NULL, _lockFileName, error.errnoMessage());
4005 delete [] tempLockFileName;
4012 // The lock file already exists - compare the time of the temp
4013 // file with the time of the lock file, rather than with the
4014 // current time of day, since the files may reside on another
4015 // machine whose time of day differs from the one this program
4016 // is running on. If the lock file is less than 5 minutes old,
4017 // keep trying, otherwise, remove the lock file and try again.
4020 if (t < (sbuf.st_ctime + 300)) {
4025 error.logError(DTM_FALSE,
4026 "lockFile(): removing stale lock file %s ctime %08ld temp lock %s ctime %08ld diff %08ld\n",
4027 _lockFileName, sbuf.st_ctime, tempLockFileName, t, t-sbuf.st_ctime);
4029 ("%s: giving up; removing dot_lock file %s\n", pname, _lockFileName));
4031 PRIV_ENABLED(return_status,SafeRemove(_lockFileName));
4032 if (return_status == -1) {
4033 // We were not able to unlink the file. This means that
4034 // we do not have write access to the directory. We will
4035 // have to pass on taking long locks.
4037 _use_dot_lock = DTM_FALSE;
4038 _mail_box_writable = DTM_FALSE;
4039 error.vSetError(DTME_CannotRemoveStaleMailboxLockFile,
4040 DTM_FALSE, NULL, _lockFileName, error.errnoMessage());
4041 delete [] tempLockFileName;
4045 delete [] tempLockFileName;
4048 // lockNewMailboxFile -- before renaming a new mailbox file over an old
4049 // mailbox file, we need to establish a lock on the new mailbox file is
4050 // a lock was established on the old mailbox file. Since the .lock protocol
4051 // works on a file name basis we dont have to worry here, but if this
4052 // system uses lockf() to lock the files we need to establish that lock
4053 // on the new file first before renaming it over the old mailbox file.
4056 RFCMailBox::lockNewMailboxFile(int new_fd)
4058 int return_status = 0;
4059 if (_lockf_active == DTM_TRUE) {
4060 lseek(new_fd, 0, SEEK_SET);
4061 PRIV_ENABLED_OPTIONAL(return_status,SafeLockf(new_fd, F_LOCK, 0));
4065 // unlockOldMailboxFile -- after renaming a new mailbox file over an old
4066 // mailbox file, if a lockf() style lock was established on the old mailbox
4067 // file, it needs to be removed
4070 RFCMailBox::unlockOldMailboxFile(int old_fd)
4072 int return_status = 0;
4073 if (_lockf_active == DTM_TRUE) {
4074 lseek(old_fd, 0, SEEK_SET);
4075 PRIV_ENABLED_OPTIONAL(return_status,SafeLockf(old_fd, F_ULOCK, 0));
4080 RFCMailBox::transferLock(int old_fd, int new_fd)
4082 int return_status = 0;
4083 if (_lockf_active == DTM_TRUE) {
4084 lseek(new_fd, 0, SEEK_SET);
4085 PRIV_ENABLED_OPTIONAL(return_status,SafeLockf(new_fd, F_LOCK, 0));
4087 lseek(old_fd, F_ULOCK, 0);
4088 PRIV_ENABLED_OPTIONAL(return_status,SafeLockf(old_fd, F_ULOCK, 0));
4093 RFCMailBox::unlockFile(DtMailEnv & error, int fd)
4095 #if defined(DEBUG_RFCMailBox)
4096 char *pname = "RFCMailBox::unlockFile";
4100 // We will create a simple lock file to keep the file from
4101 // changing while we are doing critical work.
4104 if (_use_dot_lock == DTM_TRUE) {
4105 assert(_dot_lock_active == DTM_TRUE);
4106 assert(_lockFileName != NULL);
4107 _dot_lock_active = DTM_FALSE;
4108 checkLockFileOwnership(error);
4109 if (!error.isSet()) {
4110 DEBUG_PRINTF(("%s: unlinking dot_lock file\n", pname, _lockFileName));
4112 PRIV_ENABLED(return_status,SafeUnlink(_lockFileName));
4113 if (return_status == -1) {
4114 error.vSetError(DTME_CannotRemoveMailboxLockFile,
4115 DTM_FALSE, NULL, _lockFileName, error.errnoMessage());
4120 #if defined(SENDMAIL_LOCKS)
4121 if (_lockf_active == DTM_TRUE) {
4122 DEBUG_PRINTF(("%s: removing lockf\n", pname));
4124 lseek(fd, 0, SEEK_SET);
4125 PRIV_ENABLED_OPTIONAL(return_status,SafeLockf(fd, F_ULOCK, 0));
4126 _lockf_active = DTM_FALSE;
4131 #define DOT_DTMAIL_SUFFIX "dtmail"
4133 RFCMailBox::dotDtmailLockFile(char *filename, int)
4136 // Attempt to link the temporary lock file to the real lock file.
4137 assert(_real_path != NULL);
4138 (void) sprintf(filename, "%s.%s", _real_path, DOT_DTMAIL_SUFFIX);
4143 RFCMailBox::dotDtmailLock(DtMailEnv & error)
4145 char lockBuf[MAXPATHLEN];
4146 char dotDtmailPath[MAXPATHLEN+1];
4147 char *tempLockFileName = new char[MAXPATHLEN];
4149 int return_status = 0;
4152 int flags = O_RDWR | O_CREAT | O_EXCL | O_SYNC;
4154 int flags = O_RDWR | O_CREAT | O_EXCL | O_SYNC | O_RSYNC;
4157 // We will create a .dtmail file to prevent conflicts between dtmail's
4158 // operating on the same mailbox.
4160 // Create the temporary mail lock file name.
4161 sprintf(tempLockFileName, "%sXXXXXX", _real_path);
4162 mktemp(tempLockFileName);
4163 PRIV_ENABLED(return_status,SafeRemove(tempLockFileName));
4165 // Attempt to create the temporary file.
4166 PRIV_ENABLED(lock_fd,SafeOpen(tempLockFileName, flags, 0666));
4170 error.setError(DTME_CannotCreateMailboxLockFile_NoPermission);
4173 error.setError(DTME_CannotCreateMailboxLockFile_IsDirectory);
4176 error.setError(DTME_CannotCreateMailboxLockFile_NoSuchFile);
4178 #if defined(CSRG_BASED)
4183 error.setError(DTME_CannotCreateMailboxLockFile_RemoteAccessLost);
4186 error.vSetError(DTME_CannotCreateMailboxLockFile,
4187 DTM_FALSE, NULL, tempLockFileName,
4188 error.errnoMessage());
4191 delete [] tempLockFileName;
4195 sprintf(lockBuf, "%d\n", getpid());
4196 len = strlen(lockBuf);
4197 len = SafeWrite(lock_fd, lockBuf, len);
4200 dotDtmailLockFile(dotDtmailPath, MAXPATHLEN);
4202 PRIV_ENABLED(return_status,SafeLink(tempLockFileName, dotDtmailPath));
4203 if (return_status == -1)
4204 error.vSetError(DTME_CannotCreateMailboxDotDtmailLockFile,
4205 DTM_FALSE, NULL, dotDtmailPath,
4206 error.errnoMessage());
4208 PRIV_ENABLED(return_status,SafeRemove(tempLockFileName));
4209 delete [] tempLockFileName;
4213 RFCMailBox::dotDtmailUnlock(DtMailEnv &)
4215 char dotDtmailPath[MAXPATHLEN+1];
4216 char lockBuf[MAXPATHLEN];
4218 int return_status = 0;
4221 int flags = O_RDONLY | O_EXCL | O_SYNC;
4223 int flags = O_RDONLY | O_EXCL | O_SYNC | O_RSYNC;
4226 dotDtmailLockFile(dotDtmailPath, MAXPATHLEN);
4227 PRIV_ENABLED(lock_fd,SafeOpen(dotDtmailPath, flags, 0666));
4231 return_status = SafeRead(lock_fd, lockBuf, sizeof(lockBuf)-1);
4232 if (return_status <= 0)
4234 (void) SafeClose(lock_fd);
4238 sscanf(lockBuf, "%d", &pid);
4239 if (pid != getpid())
4241 (void) SafeClose(lock_fd);
4245 (void) SafeClose(lock_fd);
4246 PRIV_ENABLED(return_status,SafeRemove(dotDtmailPath));
4250 // Possible return values,
4251 // DTMBX_LONGLOCK_FAILED_CANCEL - failed, cancel operation
4252 // DTMBX_LONGLOCK_FAILED_READONLY - failed, open read only
4253 // DTMBX_LONGLOCK_SUCCESSFUL - succeeded
4255 // This function will also manipulate "_mail_box_writable" and "_lock_flag"
4258 RFCMailBox::longLock(DtMailEnv & error)
4260 DTMBX_LONGLOCK rtn = DTMBX_LONGLOCK_FAILED_CANCEL;
4262 _long_lock_active = DTM_TRUE;
4263 _lock_obj = new FileShare(error,
4272 // TT is not available. Attempt to lock using the .dtmail lock.
4278 dotDtmailLock(error);
4281 _long_lock_active = DTM_FALSE;
4282 if (NULL != _callback)
4285 // .dtmail lock failed. The best we can do is read only.
4288 char dotDtmailPath[MAXPATHLEN+1];
4290 dotDtmailLockFile(dotDtmailPath, MAXPATHLEN);
4292 DTMC_DOTDTMAILLOCKFAILED, _real_path, NULL, _cb_data,
4293 (char*) dotDtmailPath, (const char*) error);
4294 if (DTM_TRUE == ans)
4296 rtn = DTMBX_LONGLOCK_FAILED_READONLY;
4301 rtn = DTMBX_LONGLOCK_FAILED_CANCEL;
4302 error.setError(DTME_UserInterrupted);
4307 rtn = DTMBX_LONGLOCK_SUCCESSFUL;
4312 // Ask TT to lock the file.
4314 _lock_obj->lockFile(error);
4318 // TT rejected our lock request. The best we can do is read only.
4321 if (_lock_obj->readOnly(error) == DTM_TRUE)
4323 rtn = DTMBX_LONGLOCK_FAILED_READONLY;
4328 rtn = DTMBX_LONGLOCK_FAILED_CANCEL;
4329 error.setError(DTME_UserInterrupted);
4334 _long_lock_active = DTM_FALSE;
4337 rtn = DTMBX_LONGLOCK_SUCCESSFUL;
4344 RFCMailBox::longUnlock(DtMailEnv & error)
4348 if (DTM_TRUE == _lock_flag)
4350 if (NULL != _lock_obj)
4355 else if (_long_lock_active == DTM_TRUE)
4356 dotDtmailUnlock(error);
4360 if (_mail_box_writable == DTM_FALSE)
4364 #if !defined(SENDMAIL_LOCKS)
4365 if (_lockf_active == DTM_TRUE)
4367 int return_status = 0;
4368 lseek(_fd, 0, SEEK_SET);
4369 PRIV_ENABLED_OPTIONAL(return_status,SafeLockf(_fd, F_ULOCK, 0));
4370 _lockf_active = DTM_FALSE;
4374 _long_lock_active = DTM_FALSE;
4377 RFCMailBox::MapRegion *
4378 RFCMailBox::mapNewRegion(DtMailEnv & error, int fd, unsigned long size)
4382 // Create the mapped region.
4384 MapRegion * map = new MapRegion;
4386 map->file_size = size;
4387 long page_size = memoryPageSize();
4389 map->map_size = map->file_size + (page_size - (map->file_size % page_size)) + page_size;
4391 int flags = MAP_PRIVATE;
4393 #if defined(MMAP_NORESERVE)
4394 // We are not supposed to be writing to these pages. If
4395 // we don't specify MAP_NORESERVE however, the system will
4396 // reserve swap space equal to the file size to deal with
4397 // potential writes. This is wasteful to say the least.
4399 flags |= MAP_NORESERVE;
4402 map->map_region = (char *)mmap(0, (size_t) map->map_size,
4403 PROT_READ, flags, fd, 0);
4404 if (map->map_region == (char *)-1) {
4407 error.setError(DTME_CannotReadNewMailboxFile_OutOfMemory);
4411 error.vSetError(DTME_CannotReadNewMailboxFile,
4412 DTM_FALSE, NULL, error.errnoMessage());
4417 return((MapRegion *)NULL);
4420 map->file_region = map->map_region;
4426 RFCMailBox::makeHeaderLine(DtMailEnv & error,
4428 const DtMailHeaderRequest & request,
4429 DtMailHeaderLine & headers)
4431 MessageCache * mc = _msg_list[slot];
4432 DtMail::Envelope * env = mc->message->getEnvelope(error);
4434 // For each request, we need to retrieve the header values.
4436 headers.number_of_names = request.number_of_names;
4437 headers.header_values = new DtMailValueSeq[headers.number_of_names];
4439 for (int req = 0; req < request.number_of_names; req++) {
4440 // RFC Message::getHeader will pass abstract names through
4441 // as transport names if they can not be found in the abstract
4442 // table. Because of this we say all names are abstract and
4443 // rely on the specific implementation of RFCMessage. This
4444 // is potentially dangerous, but we are allowed to define
4445 // and require these semantics for RFCMessage (and this same
4446 // information appears in the appropriate place in getHeader.
4448 env->getHeader(error,
4449 request.header_name[req],
4451 headers.header_values[req]);
4452 if (error.isSet()) {
4453 headers.header_values[req].clear();
4462 RFCMailBox::waitForMsgs(int needed)
4464 while(_at_eof == 0 && needed >= _msg_list.length()) {
4471 RFCMailBox::writeToDumpFile(const char *format, ...)
4474 char dumpfilename[MAXPATHLEN+1];
4475 _Xctimeparams ctime_buf;
4477 GET_DUMPFILE_NAME(dumpfilename);
4478 FILE *df = fopen(dumpfilename, "a");
4480 const time_t clockTime = (const time_t) time((time_t *)0);
4481 memset((void*) &ctime_buf, 0, sizeof(_Xctimeparams));
4482 fprintf(df, "--------------------- pid=%ld %s",
4483 (long)getpid(), _XCtime(&clockTime, ctime_buf));
4486 va_start(var_args, format);
4487 vfprintf(df, format, var_args);
4490 fprintf(df, "---------------------\n");
4491 fprintf(df, "\n\n");
4497 RFCMailBox::startAutoSave(DtMailEnv & error,
4504 _session->addEventRoutine(error, PollEntry, this, 60);
4506 _last_poll = 0; // Causes first poll to fire right way.
4509 _session->removeEventRoutine(error, PollEntry, this);
4512 #if defined(reallyoldsun)
4513 #define SA_HANDLER_TYPE void (*)(void)
4515 #define SA_HANDLER_TYPE void (*)(int)
4519 RFCMailBox::dumpMaps(const char *str)
4523 off_t total_file_size = 0;
4524 struct sigaction sig_act, old_sig_act;
4525 char dumpfilename[MAXPATHLEN+1];
4527 GET_DUMPFILE_NAME(dumpfilename);
4528 FILE *df = fopen(dumpfilename, "a");
4530 if (df==NULL && _errorLogging)
4533 "dumpMaps(): Cant open dump file %s\n", dumpfilename);
4535 if (SafeFStat(_fd, &buf) < 0) {
4536 fprintf(df, "dumpMaps(): fstat(%d) failed errno=%d\n", _fd, errno);
4544 * Prepare signal handler for exception handling.
4546 (void) sigemptyset(&sig_act.sa_mask);
4547 sig_act.sa_flags = 0;
4548 sig_act.sa_handler = (SA_HANDLER_TYPE) SigBusHandler;
4549 sigaction(SIGBUS, &sig_act, &old_sig_act);
4550 sigbus_env_valid = 1;
4551 if (setjmp(sigbus_env) == 0) {
4552 const time_t clockTime = (const time_t) time((time_t *)0);
4553 _Xctimeparams ctime_buf;
4554 memset((void*) &ctime_buf, 0, sizeof(_Xctimeparams));
4555 fprintf(df, "--------------------- pid=%ld %s",
4556 (long)getpid(), _XCtime(&clockTime, ctime_buf));
4557 fprintf(df, "%s", str);
4558 fprintf(df, "---------------------\n");
4559 fprintf(df, "Mappings = %d\n", _mappings.length());
4560 fprintf(df, "Map Entries:\n");
4561 for (int m = 0; m < _mappings.length(); m++) {
4562 MapRegion *map = _mappings[m];
4563 fprintf(df, "map[%d]: map_region = %p, map_size = 0x%08lx(%08ld)\n",
4564 m, map->map_region, map->map_size, map->map_size);
4565 if (map->map_size % memoryPageSize()) {
4566 fprintf(df, "ERROR! map->map_size not mod %lu\n", memoryPageSize());
4568 HexDump(df, "map_region", (unsigned char *)map->map_region,
4569 (int) map->file_size, _errorLogging ? 0 : 10);
4571 "map[%d]: file_region = %p, file_size = 0x%08lx(%08ld)\n",
4572 m, map->file_region, map->file_size, map->file_size);
4573 fprintf(df, "map[%d]: offset = 0x%08lx(%08ld)\n",
4574 m, map->offset, map->offset);
4575 if (map->file_size == 0) {
4576 fprintf(df, "No data in file_region\n");
4579 if (strncasecmp(map->file_region, "From", 4)) {
4580 fprintf(df, "ERROR! map->file_region does not begin with From\n");
4584 (unsigned char *)map->file_region,
4585 (int) map->file_size, _errorLogging ? 0 : 10);
4587 total_file_size += (off_t) map->file_size;
4588 if ((total_file_size % 4096) == 0) {
4590 "Total file size falls on page boundary, totalsize = %lu\n",
4595 fprintf(df, "\nstat buffer entries: st_ino = %lu, st_dev = %lu, st_nlink = %lu, st_size = %ld\n",
4596 buf.st_ino, buf.st_dev, buf.st_nlink, buf.st_size);
4598 fprintf(df, "\n\n");
4601 fprintf(df, "\nSIGBUS received during output of file mappings.\n");
4602 fprintf(df, "This generally indicates a truncated or deleted file.\n");
4604 sigbus_env_valid = 0;
4605 sigaction(SIGBUS, &old_sig_act, NULL);
4609 // The following routines are required to bind this format specific driver
4610 // into the format neutral layer.
4612 // The first entry point is the capability query interface. This is used
4613 // by the client to determine what capabilities we support.
4616 RFCQueryImpl(DtMail::Session & session,
4618 const char * capability,
4623 if (strcmp(capability, DtMailCapabilityPropsSupported) == 0) {
4624 DtMailBoolean * resp = va_arg(args, DtMailBoolean *);
4629 if (strcmp(capability, DtMailCapabilityImplVersion) == 0) {
4630 char * version = va_arg(args, char *);
4631 strcpy(version, "1.0");
4635 if (strcmp(capability, DtMailCapabilityInboxName) == 0)
4637 DtMailObjectSpace *space = va_arg(args, DtMailObjectSpace *);
4638 void **inbox = va_arg(args, void **);
4640 *space = DtMailFileObject;
4641 *inbox = (void*) getInboxPath(&session);
4645 if (strcmp(capability, DtMailCapabilityMailspoolName) == 0)
4647 DtMailObjectSpace *space = va_arg(args, DtMailObjectSpace *);
4648 void **mailspool = va_arg(args, void **);
4650 *space = DtMailFileObject;
4651 *mailspool = (void*) getMailspoolPath(&session);
4655 if (strcmp(capability, DtMailCapabilityTransport) == 0) {
4656 DtMailBoolean * resp = va_arg(args, DtMailBoolean *);
4661 error.setError(DTME_NotSupported);
4665 // The QueryOpen entry point is used to determin if we can open the specified
4666 // path. If the name is of the form host:/path, or is a simple path, then we
4667 // will say we can. Additional work should be done for content typing here,
4668 // but we'll skip it for now.
4671 RFCQueryOpen(DtMail::Session &,
4673 DtMailObjectSpace space,
4678 // We can do buffers, so just say yes.
4680 if (space == DtMailBufferObject) {
4684 // If this isn't in the file name space, then give up now.
4686 if (space != DtMailFileObject) {
4690 char * path = (char *) arg;
4692 // First, is this of the form "host:/path". If so, it is probably
4693 // an RFC mail box, so we will say we can open it.
4695 for (const char * c = path; *c; c++) {
4697 // We hit a slash first, so colon's are not useful.
4701 if (*c == ':' && *(c+1) == '/') {
4702 // Looks like we have a host name. We should do a more
4703 // robust check at this point, but for now we will assume
4704 // that we have a valid host.
4709 // Okay, there defintely is not a host name. See if we can stat the
4710 // file. If we can, then assume we can open it, otherwise. If the file
4711 // doesn't exist, then we can create a new one.
4714 if (stat(path, &sbuf) < 0) {
4715 if (errno == ENOENT) {
4726 // The mail box construct entry point creates an instance of the RFC
4727 // mail box object. This object is then accessed through the virtual
4728 // DtMail::MailBox class API.
4731 RFCMailBoxConstruct(DtMail::Session & session,
4733 DtMailObjectSpace space,
4738 return(new RFCMailBox(error, &session, space, arg, cb, client_data, "Internet MIME"));
4742 RFCMessageQuery(DtMail::Session &,
4744 DtMailObjectSpace space,
4749 if (space != DtMailBufferObject) {
4757 RFCMessageConstruct(DtMail::Session & session,
4759 DtMailObjectSpace space,
4764 return(new RFCMessage(error, &session, space, arg, cb, client_data));
4768 RFCMIMETransportConstruct(DtMail::Session & session,
4770 DtMailStatusCallback cb,
4773 return(new RFCTransport(error, &session, cb, cb_data, "Internet MIME"));
4776 // The meta factory is responsible for returning the entry points
4777 // required for locating and creating various mail objects based for
4778 // the RFC implementation. It is essentially a switch table.
4782 RFCMetaFactory(const char * op)
4784 if (strcmp(op, QueryImplEntryOp) == 0) {
4785 return((void *)RFCQueryImpl);
4788 if (strcmp(op, QueryOpenEntryOp) == 0) {
4789 return((void *)RFCQueryOpen);
4792 if (strcmp(op, MailBoxConstructEntryOp) == 0) {
4793 return((void *)RFCMailBoxConstruct);
4796 if (strcmp(op, QueryMessageEntryOp) == 0) {
4797 return((void *)RFCMessageQuery);
4800 if (strcmp(op, MessageConstructEntryOp) == 0) {
4801 return((void *)RFCMessageConstruct);
4804 if (strcmp(op, TransportConstructEntryOp) == 0) {
4805 return((void *)RFCMIMETransportConstruct);
4811 // The mail box construct entry point creates an instance of the RFC
4812 // mail box object. This object is then accessed through the virtual
4813 // DtMail::MailBox class API.
4816 V3MailBoxConstruct(DtMail::Session & session,
4818 DtMailObjectSpace space,
4823 return(new RFCMailBox(error, &session, space, arg, cb, client_data,
4828 RFCV3TransportConstruct(DtMail::Session & session,
4830 DtMailStatusCallback cb,
4833 return(new RFCTransport(error, &session, cb, cb_data, "Sun Mail Tool"));
4836 // The meta factory is responsible for returning the entry points
4837 // required for locating and creating various mail objects based for
4838 // the RFC implementation. It is essentially a switch table.
4841 V3MetaFactory(const char * op)
4843 if (strcmp(op, QueryImplEntryOp) == 0) {
4844 return((void *)RFCQueryImpl);
4847 if (strcmp(op, QueryOpenEntryOp) == 0) {
4848 return((void *)RFCQueryOpen);
4851 if (strcmp(op, MailBoxConstructEntryOp) == 0) {
4852 return((void *)V3MailBoxConstruct);
4855 if (strcmp(op, QueryMessageEntryOp) == 0) {
4856 return((void *)RFCMessageQuery);
4859 if (strcmp(op, MessageConstructEntryOp) == 0) {
4860 return((void *)RFCMessageConstruct);
4863 if (strcmp(op, TransportConstructEntryOp) == 0) {
4864 return((void *)RFCV3TransportConstruct);
4872 RFCMailBox::createMailRetrievalAgent(char *password)
4874 DtMailEnv localError;
4875 char *path = _session->expandPath(localError, (char*) _arg);
4876 DtMailServer *server = NULL;
4879 if (! isInboxMailbox(_session, path))
4885 if (NULL != _mra_command) free(_mra_command);
4886 _mra_command = NULL;
4888 if (NULL != password)
4890 if (NULL != _mra_serverpw) free(_mra_serverpw);
4891 _mra_serverpw = strdup(password);
4894 if (NULL != _mra_server) delete _mra_server;
4897 if (True == DtMailServer::get_mailrc_value(
4898 _session, DTMAS_INBOX,
4899 DTMAS_PROPKEY_GETMAILVIASERVER,
4902 protocol = DtMailServer::get_mailrc_value(
4903 _session, DTMAS_INBOX,
4904 DTMAS_PROPKEY_PROTOCOL,
4907 if (! strcasecmp(protocol, DTMAS_PROTO_IMAP))
4908 _mra_server = (DtMailServer*) new IMAPServer(
4909 DTMAS_INBOX, _session, this,
4910 appendCB, (void*) this);
4911 else if (! strcasecmp(protocol, DTMAS_PROTO_APOP))
4912 _mra_server = (DtMailServer*) new APOPServer(
4913 DTMAS_INBOX, _session, this,
4914 appendCB, (void*) this);
4915 else if (! strcasecmp(protocol, DTMAS_PROTO_POP3))
4916 _mra_server = (DtMailServer*) new POP3Server(
4917 DTMAS_INBOX, _session, this,
4918 appendCB, (void*) this);
4919 else if (! strcasecmp(protocol, DTMAS_PROTO_POP2))
4920 _mra_server = (DtMailServer*) new POP2Server(
4921 DTMAS_INBOX, _session, this,
4922 appendCB, (void*) this);
4924 _mra_server = (DtMailServer*) new AUTOServer(
4925 DTMAS_INBOX, _session, this,
4926 appendCB, (void*) this);
4928 if (NULL != _mra_server) _mra_server->set_password(_mra_serverpw);
4931 else if (True == DtMailServer::get_mailrc_value(
4932 _session, DTMAS_INBOX,
4933 DTMAS_PROPKEY_GETMAILVIACOMMAND,
4936 _mra_command = DtMailServer::get_mailrc_value(
4937 _session, DTMAS_INBOX,
4938 DTMAS_PROPKEY_GETMAILCOMMAND,
4945 RFCMailBox::deleteMailRetrievalAgent()
4947 if (NULL != _mra_command) free(_mra_command);
4948 _mra_command = NULL;
4949 if (NULL != _mra_serverpw) free(_mra_serverpw);
4950 _mra_serverpw = NULL;
4951 if (NULL != _mra_server) delete _mra_server;
4956 RFCMailBox::updateMailRetrievalPassword(char *password)
4958 if (NULL == password || NULL == _mra_server) return;
4960 if (NULL != _mra_serverpw) delete _mra_serverpw;
4961 _mra_serverpw = strdup(password);
4962 _mra_server->set_password(_mra_serverpw);
4967 RFCMailBox::retrieveNewMail(DtMailEnv &error)
4969 if (NULL != _mra_server)
4971 _mra_server->retrieve_messages(error);
4973 if (DTME_MailServerAccess_MissingPassword == (DTMailError_t) error ||
4974 DTME_MailServerAccess_AuthorizationFailed == (DTMailError_t) error)
4975 return DTMC_SERVERPASSWORDNEEDED;
4976 else if (DTME_NoError != (DTMailError_t) error)
4977 return DTMC_SERVERACCESSFAILED;
4979 else if (NULL != _mra_command)
4981 int sysstatus = system(_mra_command);
4982 if (-1 == sysstatus)
4985 DTME_GetmailCommandRetrieval_SystemError,
4987 _mra_command, errno, error.errnoMessage(errno));
4988 if (_errorLogging) error.logError(DTM_FALSE, (const char*) error);
4989 return DTMC_GETMAILCOMMANDFAILED;
4991 if (0 == WIFEXITED(sysstatus))
4994 DTME_GetmailCommandRetrieval_AbnormalExit,
4997 if (_errorLogging) error.logError(DTM_FALSE, (const char*) error);
4998 return DTMC_GETMAILCOMMANDFAILED;
5000 else if (0 != WEXITSTATUS(sysstatus))
5005 "system('%s') returned %d\n",
5006 _mra_command, WEXITSTATUS(sysstatus));
5013 RFCMailBox::mailboxAccessShow(time_t mtime, char *prefix)
5017 new_time.modtime = mtime;
5018 new_time.actime = new_time.modtime+1;
5019 SafeUTime(_real_path, &new_time);
5020 #ifdef DEBUG_MAILBOX_ACCESS
5021 fprintf(stderr, "%s: forcing acctime>modtime\n", prefix);
5026 RFCMailBox::mailboxAccessHide(char *prefix)
5028 SafeUTime(_real_path, NULL);
5029 #ifdef DEBUG_MAILBOX_ACCESS
5030 fprintf(stderr, "%s: forcing modtime==acctime\n", prefix);