Current state of my dtmail work.
[oweals/cde.git] / cde / programs / dtmail / libDtMail / RFC / RFCMailBox.C
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
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)
10  * any later version.
11  *
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
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /*
24  *+SNOTICE
25  *
26  *
27  *      $TOG: RFCMailBox.C /main/53 1998/12/10 17:22:41 mgreess $
28  *
29  *      RESTRICTED CONFIDENTIAL INFORMATION:
30  *      
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
37  *      Sun's request.
38  *
39  *      Copyright 1993, 1994, 1995 Sun Microsystems, Inc.  All rights reserved.
40  *
41  *+ENOTICE
42  */
43
44 #ifdef __ppc
45 #include <DtMail/Buffer.hh>
46 #endif
47
48 #define X_INCLUDE_TIME_H
49 #define XOS_USE_NO_LOCKING
50 #include <X11/Xos_r.h>
51
52 #include <EUSCompat.h>
53 #include <errno.h>
54 #include <stdio.h>
55 #include <unistd.h>
56 #include <stdlib.h>
57 #include <stdarg.h>
58 #include <pwd.h>
59 #include <ctype.h>
60 #include <signal.h>
61 #include <setjmp.h>
62 #include <string.h>
63 #include <sys/stat.h>
64 #include <sys/param.h>
65 #include <sys/uio.h>
66 #if !defined(__aix) && !defined(__hpux) && !defined(linux) && !defined(CSRG_BASED)
67 #include <sys/systeminfo.h>
68 #endif
69 #include <sys/wait.h>
70 #include <Dt/DtPStrings.h>
71
72 #ifdef __osf__
73 #ifndef sysinfo
74 #ifdef __cplusplus
75 extern "C" {
76 int sysinfo(int, char *, long);
77 }
78 #endif // __cplusplus
79 #endif // sysinfo
80 #endif // __osf__
81
82 #include <assert.h>
83
84 #if defined(NEED_MMAP_WRAPPER)
85 extern "C" {
86 #endif
87
88 #include <sys/mman.h>
89
90 #if defined(NEED_MMAP_WRAPPER)
91 }
92 #endif
93
94 #include <DtMail/DtMail.hh>
95 #include <DtMail/DtMailServer.hh>
96 #include <DtMail/ImplDriver.hh>
97 #include <DtMail/Threads.hh>
98 #include <DtMail/IO.hh>
99 #include "RFCImpl.hh"
100 #include "str_utils.h"
101
102 #ifndef MAIL_SPOOL_PATH
103 #define MAIL_SPOOL_PATH "/var/mail/%s"
104 #endif
105
106 #ifdef __uxp__
107 extern "C" int madvise(caddr_t, size_t, int);
108 extern "C" ssize_t  pread(int, void *, size_t, off_t);
109 #endif
110
111 #if defined(sun) || defined(USL)
112 #define LCL_SIG_HANDLER_SIGNATURE       
113 #elif defined(__hpux)
114 #define LCL_SIG_HANDLER_SIGNATURE       __harg
115 #elif defined(__aix) || defined(__alpha) || defined(linux) || defined(CSRG_BASED)
116 #define LCL_SIG_HANDLER_SIGNATURE       int
117 #elif defined(__uxp__)
118 #define LCL_SIG_HANDLER_SIGNATURE
119 #endif
120
121 //
122 // Debugging for RFCMailBox.
123 //
124 #if defined(DEBUG_RFCMailBox)
125   #define DEBUG_PRINTF(a)       printf a
126 #else
127   #define DEBUG_PRINTF(a)       
128 #endif
129
130
131 // These macros define a method for executing statements
132 // with set group id privileges enabled
133 //
134 #if defined(MAILGROUP_REQUIRED)
135
136   #define PRIV_ENABLED_OPTIONAL(STATUSVARIABLE, STATEMENT) \
137       STATUSVARIABLE = STATEMENT;
138
139   #define PRIV_ENABLED(STATUSVARIABLE, STATEMENT)   \
140       { \
141           _session->enableGroupPrivileges(); \
142           STATUSVARIABLE = STATEMENT;       \
143           _session->disableGroupPrivileges(); \
144       }
145
146   #define PRIV_ENABLED_OPEN(FILENAME, STATUSVARIABLE, STATEMENT) \
147       if (isSetMailGidNeeded(FILENAME)) { \
148           PRIV_ENABLED(STATUSVARIABLE, STATEMENT) \
149       } else { \
150           STATUSVARIABLE = STATEMENT; \
151       }
152
153 #else
154
155   #define PRIV_ENABLED_OPTIONAL(STATUSVARIABLE, STATEMENT)   \
156       _session->enableGroupPrivileges(); \
157       STATUSVARIABLE = STATEMENT;       \
158       _session->disableGroupPrivileges(); \
159       if (STATUSVARIABLE == -1) {       \
160           STATUSVARIABLE = STATEMENT; \
161       }
162
163   #define PRIV_ENABLED PRIV_ENABLED_OPTIONAL
164
165   #define PRIV_ENABLED_OPEN(FILENAME, STATUSVARIABLE, STATEMENT) \
166       STATUSVARIABLE = STATEMENT;
167
168 #endif
169
170 #define GET_DUMPFILE_NAME(dfn) \
171     sprintf(dfn, "%s/%s/dtmail.dump", getenv("HOME"), DtPERSONAL_TMP_DIRECTORY)
172
173 /*
174  * Local Data Definitions
175  */
176 static const int RFCSignature = 0x448612e5;
177
178 static const int DEFAULT_FOLDER_SIZE = (32 << 10); // 32 KB
179
180 static int sigbus_env_valid = 0;
181
182 static jmp_buf sigbus_env;
183
184 /*
185  * Local Function Declarations
186  */
187 static void     SigBusHandler(LCL_SIG_HANDLER_SIGNATURE);
188 static int      isMailGroupSystemMailbox(const char *mailboxPath);
189 static int      isInboxMailbox(const char *mailboxPath);
190 static char     *getInboxPath(DtMail::Session *session);
191
192 /*
193  * SigBusHandler
194  *
195  * Notified when an interesting (SIGBUS in this case) signal is raised.
196  * I wanted to throw a C++ exception at this point but that's not
197  * supported everywhere.  So, we'll just have to use the thread-unsafe
198  * setjmp/longjmp combination.
199  */
200 static
201 void SigBusHandler(LCL_SIG_HANDLER_SIGNATURE)
202 {
203   if (sigbus_env_valid) {
204     longjmp(sigbus_env, 1);
205   }
206 }
207
208 static
209 void HexDump(FILE *pfp, char *pmsg, unsigned char *pbufr, int plen, int plimit)
210 {
211   unsigned char save[64];
212   long int      x, y, z, word, cnt;
213   FILE          *pfp_r = pfp;
214
215   if (!plen)
216     return;
217   
218   if (pfp_r == (FILE*) NULL) {
219     char        *dumpfilename = new char [MAXPATHLEN+1];
220     _Xctimeparams ctime_buf;
221
222     GET_DUMPFILE_NAME(dumpfilename);
223     pfp_r = fopen(dumpfilename, "a");
224     const time_t clockTime = (const time_t) time((time_t *)0);
225     memset((void*) &ctime_buf, 0, sizeof(_Xctimeparams));
226     fprintf(pfp_r, "--------------------- pid=%ld %s",
227             (long)getpid(), _XCtime(&clockTime, ctime_buf));
228     delete [] dumpfilename;
229   }
230
231   (void) fprintf(pfp_r, "--> %s (%d bytes at 0x%08lx):\n", pmsg, plen, pbufr);
232   fflush(pfp_r);
233   memset((char *)save, 0, sizeof(save));
234   save[0] = ~pbufr[0];
235   z = 0;
236   cnt = plen;
237   for (x = 0; cnt > 0; x++, z += 16)
238   {
239     (void) fprintf(pfp_r, "0x%08lx(+%6.6ld) ", pbufr + z, z);
240     for (y = 0; y < 16; y++)
241     {
242       save[y] = pbufr[x * 16 + y];
243       word = pbufr[x * 16 + y];
244       word &= 0377;
245       if (y < cnt)
246         (void) fprintf(pfp_r, "%2.2lx%c", word, y == 7 ? '-' : ' ');
247       else
248         (void) fprintf(pfp_r, "   ");
249     }
250     (void) fprintf(pfp_r, "%s", " *");
251     for (y = 0; y < 16; y++)
252       if (y >= cnt)
253         (void) fprintf(pfp_r, " ");
254       else if (pbufr[x * 16 + y] < ' ' || pbufr[x * 16 + y] > '~')
255         (void) fprintf(pfp_r, "%s", ".");
256       else
257         (void) fprintf(pfp_r, "%c", pbufr[x * 16 + y]);
258     (void) fprintf(pfp_r, "%s", "*\n");
259     cnt -= 16;
260
261     if (plimit && (x >= (plimit-1)) && (cnt > (plimit*16))) {
262       while (cnt > (plimit*16)) {
263         x++;
264         z+=16;
265         cnt -= 16;
266       }
267       fprintf(pfp_r, "...\n");
268     }
269   }
270   fflush(pfp_r);
271   if (pfp == (FILE*) NULL) {
272     fprintf(pfp_r, "---------------------\n");
273     fclose(pfp_r);
274   }
275 }
276
277 RFCMailBox::RFCMailBox(DtMailEnv & error,
278                        DtMail::Session * session,
279                        DtMailObjectSpace space,
280                        void * arg,
281                        DtMailCallback cb,
282                        void * client_data,
283                        const char * impl_name)
284 : DtMail::MailBox(error, session, space, arg, cb, client_data),
285 _msg_list(128), _mappings(4)
286 {
287     // We are using a condition to block any threads from
288     // trying to use this object until it is open. We will
289     // not set the object valid until we have a valid thread.
290     // Note that this will not be set to true until a stream
291     // has been successfully opened.
292     //
293     DtMailEnv localError;
294     localError.clear();
295     
296     _object_valid = new Condition;
297
298     _object_valid->setFalse();
299
300     _map_lock = MutexInit();
301
302     _impl_name = impl_name;
303
304     _buffer = NULL;
305     _parsed = DTM_FALSE;
306     _fd = -1;
307     _real_path = NULL;
308
309     _mail_box_writable = DTM_FALSE;
310     _use_dot_lock = DTM_TRUE;
311     _long_lock_active = DTM_FALSE;
312     _dot_lock_active = DTM_FALSE;
313     _lockf_active = DTM_FALSE;
314     _uniqueLockId = generateUniqueLockId();
315     assert(_uniqueLockId != NULL);
316     _uniqueLockIdLength = strlen(_uniqueLockId);
317     _lockFileName = (char *)0;
318     _dirty = 0;
319
320     _mr_allowed = DTM_TRUE;
321     _mra_server = NULL;
322     _mra_serverpw = NULL;
323     _mra_command = NULL;
324     createMailRetrievalAgent(NULL);
325
326     // Create a thread for getting new mail and expunging old mail.
327     // Of course we don't need threads for buffer objects.
328     //
329     if (_space != DtMailBufferObject) {
330         _thread_info = new NewMailData;
331         _thread_info->self = this;
332         _thread_info->object_valid = _object_valid;
333         _mbox_daemon = ThreadCreate(ThreadNewMailEntry, _thread_info);
334     }
335
336     // We will have to add a poll oriented method as well. We'll ignore
337     // that for now.
338     //
339     if (_space != DtMailBufferObject) {
340         _session->addEventRoutine(error, PollEntry, this, 15);
341         _last_check = 0;
342         _last_poll = 0; // Causes first poll to fire right way.
343     }
344
345     // We need to figure out what type of locking to use. We use ToolTalk,
346     // when the user has explicitly turned it on via a property.
347     //
348     DtMail::MailRc * mailrc = _session->mailRc(error);
349     const char * value = NULL;
350     mailrc->getValue(localError, "cdetooltalklock", &value);
351     if (localError.isSet()) {
352         _tt_lock = DTM_FALSE;
353         localError.clear();
354     } else {
355         _tt_lock = DTM_TRUE;
356     }
357     if (NULL != value)
358       free((void*) value);
359
360     // Determine if error logging is enabled
361     //
362     value = NULL;
363     localError.clear();
364     mailrc->getValue(localError, "errorlogging", &value);
365     _errorLogging = (localError.isSet() ?
366                         (localError.clear(), DTM_FALSE) : DTM_TRUE);
367     if (NULL != value)
368       free((void*) value);
369
370     _partialList = NULL;
371     _partialListCount = 0;
372
373     error.clear();
374
375     _object_signature = RFCSignature;
376 }
377
378 RFCMailBox::~RFCMailBox(void)
379 {
380     if (_object_signature != RFCSignature) {
381         return;
382     }
383
384     MutexLock lock_scope(_obj_mutex);
385     if (_object_signature == RFCSignature) {
386         _object_valid->setFalse();
387
388         DtMailEnv error;
389         _session->removeEventRoutine(error, PollEntry, this);
390
391         // We need to copy the file if writable and dirty out.
392         // NOTE: the caller should really call writeMailBox() or expunge()
393         // (as appropriate) first before destroying this mailbox, as there
394         // is no way to pass an error indication back up from here to the
395         // caller. If an error happens let syslog handle it.
396         //
397         if (_mail_box_writable == DTM_TRUE) {
398             if (_dirty != 0) {
399               error.clear();
400               writeMailBox(error, DTM_FALSE);
401               if (error.isSet())
402               {
403                 // THE MAILBOX COULD NOT BE WRITTEN!! SHOULD DO SOMETHING
404                 error.logError(DTM_TRUE,
405                                 "~RFCMailBox(): Failed to write mailbox: %s",
406                                 (const char *)error);
407               }
408             }
409         }
410
411         // Let's keep the map locked.
412         //
413         MutexLock lock_map(_map_lock);
414
415         // Next we tear down the message structures.
416         //
417         while (_msg_list.length()) {
418             MessageCache * mc = _msg_list[0];
419             delete mc->message;
420             delete mc;
421             _msg_list.remove(0); // Won't actually touch the object.
422         }
423
424         // Finally we need to get rid of the mapping. There are
425         // actually 3 conditions we need to deal with here.
426         //
427         // 1) We opened a file and mapped it. In that case, we unmap,
428         //    and close the file.
429         //
430         // 2) We created a buffer. In that case the _mapped_region points,
431         //    at the region owned by _buffer. We simply destroy _buffer,
432         //    but we do not attempt to destroy the mapped region.
433         //
434         // 3) The caller asked us to open an existing buffer. In that
435         //    case, do nothing (we didn't build it so we won't destroy it).
436         //
437         if (_fd >= 0) { // Option 1
438             for (int slot = 0; slot < _mappings.length(); slot++) {
439                 munmap(_mappings[slot]->map_region,
440                         (size_t) _mappings[slot]->map_size);
441             }
442             longUnlock(error);
443             SafeClose(_fd);
444         }
445
446         if (_buffer) { // Option 2
447             delete [] _buffer;
448         }
449
450         //
451         // Allocated using malloc and strdup, so free using free.
452         //
453         free((void*) _real_path);
454         free((void*) _uniqueLockId);
455         free((void*) _lockFileName);
456     }
457
458     _object_signature = 0;
459
460     if (NULL != _mra_command) delete _mra_command;
461     if (NULL != _mra_server) delete _mra_server;
462     if (NULL != _mra_serverpw) delete _mra_serverpw;
463 }
464
465 static int isMailGroupSystemMailbox(const char * mailboxPath)
466 {
467     int retval = 0;
468 #ifdef MAILGROUP_REQUIRED
469     static int          oneTimeFlag = 0;
470     static char         *cached_inbox_path = 0;
471
472     if (NULL == mailboxPath) return retval;
473
474     if (! oneTimeFlag)
475     {
476         char *inbox_path = new char[MAXPATHLEN];
477         passwd pw;
478
479         GetPasswordEntry(pw);
480         sprintf(inbox_path, MAIL_SPOOL_PATH, pw.pw_name);
481         cached_inbox_path = strdup(inbox_path);
482         oneTimeFlag++;
483         delete [] inbox_path;
484     }
485
486     assert(cached_inbox_path);
487     retval = (!strcmp(mailboxPath, cached_inbox_path));
488 #endif
489     return retval;
490 }
491
492 static char *getMailspoolPath(DtMail::Session *session)
493 {
494     DtMailEnv            error;
495     DtMail::MailRc      *mailrc = session->mailRc(error);
496     char                *mailspoolpath = 0;
497     char                *syspath = new char[MAXPATHLEN];
498     passwd               pw;
499     
500     GetPasswordEntry(pw);
501     sprintf(syspath, MAIL_SPOOL_PATH, pw.pw_name);
502     mailspoolpath = strdup(syspath);
503
504     assert(NULL!=mailspoolpath);
505     delete [] syspath;
506     return mailspoolpath;
507 }
508
509 static char *getInboxPath(DtMail::Session *session)
510 {
511     DtMailEnv   error;
512     DtMail::MailRc      *mailrc = session->mailRc(error);
513     char                *inboxpath = 0;
514
515     mailrc->getValue(error, DTMAS_PROPKEY_INBOXPATH, (const char**) &inboxpath);
516     if (error.isSet())
517     {
518         mailrc->getValue(error, "DT_MAIL", (const char**) &inboxpath);
519         if (error.isSet())
520         {
521             mailrc->getValue(error, "MAIL", (const char**) &inboxpath);
522             if (error.isSet())
523             {
524                 inboxpath = getMailspoolPath(session);
525             }
526             else
527               error.clear();
528         }
529         else
530           error.clear();
531     }
532     else
533       error.clear();
534
535     if (inboxpath && 0 == (strcmp(inboxpath, "MAILSPOOL_FILE")))
536     {
537         free(inboxpath);
538         inboxpath = getMailspoolPath(session);
539     }
540
541     assert(NULL!=inboxpath);
542     return inboxpath;
543 }
544
545 static int isInboxMailbox(DtMail::Session *session, const char * mailboxPath)
546 {
547     int         retval = 0;
548     char        *inbox_path = NULL;
549
550     inbox_path = getInboxPath(session);
551     assert(NULL!=inbox_path);
552     retval = (!strcmp(mailboxPath, inbox_path));
553     free(inbox_path);
554     return retval;
555 }
556
557 // Function: alterPageMappingAdvice - change OS mapping advice on selected map
558 // Description:
559 //  Give the operating system new advice on the referencing activity that
560 //  should be expected for a region of file mapped pages.
561 // Method:
562 //  Spin through all map regions recorded in _mappings looking for the
563 //  specified map (or all maps if -1 passed in as the map region), and
564 //  if found issue an madvise call on the specified region of memory.
565 // Arguments:
566 //  map         -- either a specific region that is part of _mappings,
567 //              -- OR (MapRegion *)-1 to change ALL map regions in _mappings
568 //  advice      -- advice as per madvise(3) Operating System call
569 // Outputs:
570 //  <none>
571 // Returns:
572 //  <none>
573 //
574 void
575 RFCMailBox::alterPageMappingAdvice(MapRegion *map, int advice)
576 {
577   int me = _mappings.length();
578   for (int m = 0; m < me; m++) {
579     MapRegion *map_t = _mappings[m];
580
581 #if !defined(USL) && !defined(__uxp__) && !defined(linux)
582     // no madvise on these systems
583     if (map_t == map || map == (MapRegion *)-1)
584       madvise(map_t->map_region, (size_t) map_t->map_size, advice);
585 #endif
586     }
587 }
588
589 // Function: memoryPageSize - return hardware natural memory page size
590 // Description:
591 //  Compute and return the natural memory page size of the current hardware
592 // Method:
593 //  Use sysconf to query the operating system about the current hardware;
594 //  cache the value so that repeated calls to this function do not generate
595 //  repeated calls to the operating system.
596 // Arguments:
597 //  <none>
598 // Outputs:
599 //  <none>
600 // Returns:
601 //  long -- natural page size of the current hardware
602 // 
603 static long
604 memoryPageSize()
605 {
606   static long mach_page_size = -1;              // -1 is "one time value"
607
608   if (mach_page_size == -1) {
609     mach_page_size = sysconf(_SC_PAGESIZE);
610     assert(mach_page_size != -1);
611   }
612
613   return(mach_page_size);
614 }
615
616
617 // Functions: addressIsMapped - check if address is in file mapped memory
618 // Description:
619 //  Check to see if a given memory address is within the bounds of any
620 //  memory that may have been mapped from a mailbox file into memory,
621 //  and return an indication of whether it is or not.
622 // Method:
623 //  Go through the list of file to memory mappings and check to see if
624 //  the given address is within the bounds of one of the mappings.
625 // Arguments:
626 //  addressToCheck -- address in memory to check against mappings
627 // Outputs:
628 //  <none>
629 // Returns:
630 //  DTM_TRUE - memory address is in file mapped memory and can be accessed
631 //  DTM_FALSE - memory address is not in file mapped memory - may not be valid
632 //
633 DtMailBoolean
634 RFCMailBox::addressIsMapped(void *addressToCheck)
635 {
636   assert(addressToCheck != NULL);
637   int me = _mappings.length();
638   for (int m = 0; m < me; m++)
639   {
640     MapRegion *map = _mappings[m];
641     if ( (addressToCheck >= map->map_region)
642          && (addressToCheck < (map->map_region+map->file_size)) )
643       return(DTM_TRUE);
644   }
645   return(DTM_FALSE);
646 }
647
648 void
649 RFCMailBox::appendCB(DtMailEnv &error, char *buf, int len, void *clientData)
650 {
651     RFCMailBox  *obj = (RFCMailBox*) clientData;
652
653     if (NULL == obj) return;
654     obj->append(error, buf, len);
655 }
656
657 void
658 RFCMailBox::append(DtMailEnv &error, char *buf, int len)
659 {
660     int         status;
661     off_t       end = lseek(_fd, 0, SEEK_END);
662
663     // Add a new-line at the end to distinguish separate messages.
664     status = SafeWrite(_fd, buf, len);
665     
666     if (status < 0)
667     {
668         char    *path = _session->expandPath(error, (char *)_arg);
669         switch (errno)
670         {
671             case EFBIG:
672               error.vSetError(
673                         DTME_AppendMailboxFile_FileTooBig, DTM_FALSE, NULL,
674                         path, errno, error.errnoMessage(errno));
675               break;
676
677 #if defined(__osf__) || defined(CSRG_BASED)
678             case ENOTDIR:
679 #else
680             case ENOLINK:
681 #endif
682               error.vSetError(
683                         DTME_AppendMailboxFile_LinkLost, DTM_FALSE, NULL,
684                         path, errno, error.errnoMessage(errno));
685               break;
686
687             case ENOSPC:
688               error.vSetError(
689                         DTME_AppendMailboxFile_NoSpaceLeft, DTM_FALSE, NULL,
690                         path, errno, error.errnoMessage(errno));
691               break;
692
693             default:
694               error.vSetError(
695                         DTME_AppendMailboxFile_SystemError, DTM_FALSE, NULL,
696                         path, errno, error.errnoMessage(errno));
697         }
698         free(path);
699     }
700
701     if (_hide_access_events)
702       mailboxAccessHide("append");
703     else
704     {
705         time_t  now = time((time_t) NULL);
706         mailboxAccessShow(now, "append");
707     }
708
709 }
710
711 void
712 RFCMailBox::create(DtMailEnv & error, mode_t create_mode)
713 {
714     error.clear();
715
716     MutexLock lock_scope(_obj_mutex);
717     MutexLock lock_map(_map_lock);
718
719     switch (_space) {
720       case DtMailBufferObject:
721       {
722           DtMailBuffer * buf = (DtMailBuffer *)_arg;
723
724           _buffer = (char *)malloc(DEFAULT_FOLDER_SIZE);
725           buf->buffer = _buffer;
726           buf->size = DEFAULT_FOLDER_SIZE;
727
728           MapRegion * map = new MapRegion;
729           map->file_region = map->map_region = _buffer;
730           map->file_size = map->map_size = DEFAULT_FOLDER_SIZE;
731           map->offset = 0;
732           _mappings.append(map);
733
734           _mail_box_writable = DTM_TRUE;
735
736           break;
737       }
738
739       case DtMailFileObject:
740       {
741           openRealFile(error, O_RDWR | O_CREAT, create_mode);
742           if (error.isSet()) {
743               break;
744           }
745
746           mapFile(error);
747           break;
748       }
749
750       default:
751         error.setError(DTME_NotSupported);
752     }
753
754     _at_eof.setTrue();
755
756     _object_valid->setTrue();
757
758     return;
759 }
760
761 void
762 RFCMailBox::open(DtMailEnv & error,
763                  DtMailBoolean auto_create,
764                  int open_mode, 
765                  mode_t create_mode,
766                  DtMailBoolean lock_flag,
767                  DtMailBoolean auto_parse
768 )
769 {
770     error.clear();
771
772     if (_tt_lock == DTM_TRUE && lock_flag == DTM_TRUE)
773       _lock_flag = DTM_TRUE;
774     else
775       _lock_flag = DTM_FALSE;
776
777     MutexLock lock_scope(_obj_mutex);
778
779     if (auto_parse) {
780         
781         MutexLock lock_map(_map_lock);
782
783         switch (_space) {
784           case DtMailBufferObject:
785           {
786               DtMailBuffer * buf = (DtMailBuffer *)_arg;
787
788               MapRegion * map = new MapRegion;
789               map->file_region = map->map_region = (char *)buf->buffer;
790               map->file_size = map->map_size = buf->size;
791               map->offset = 0;
792               _mappings.append(map);
793
794               _mail_box_writable = DTM_FALSE;
795
796               break;
797           }
798             
799           case DtMailFileObject:
800           {
801             int mode = O_RDONLY;
802             int return_result;
803             
804             _mail_box_writable = DTM_FALSE;
805             char * path = _session->expandPath(error, (char *)_arg);
806             PRIV_ENABLED_OPTIONAL(return_result, SafeAccess(path, W_OK));
807             if (return_result == 0) {
808                 mode = O_RDWR;
809             }
810             free(path);
811
812             // We need to use the most restrictive mode that is possible
813             // on the file. If the caller has requested the file be open
814             // read-only, then we should do that, even if read-write is
815             // allowed. We don't want to try to open the file read-write
816             // if we don't have adequate permission however.
817             //
818             mode = open_mode == O_RDONLY ? open_mode : mode;
819             
820             openRealFile(error, mode, create_mode);
821             if (error.isSet()) {
822                 if (auto_create == DTM_TRUE) {
823                     error.clear();
824                     lock_scope.unlock();
825                     lock_map.unlock();
826                     create(error);
827                 }
828                 return;
829             }
830             
831             mapFile(error);
832             break;
833         }
834           
835         default:
836           error.setError(DTME_NotSupported);
837       }
838
839       if (error.isSet()) { // Can't parse this.
840           return;
841       }
842
843       _at_eof.setFalse();
844
845       lock_map.unlock();
846
847 #if defined(POSIX_THREADS)
848       ThreadCreate(ThreadParseEntry, this);
849 #else
850       parseFile(error, 0);
851 #endif
852
853       _object_valid->setTrue(); // New mail watcher starts now.
854       _parsed = DTM_TRUE;
855     }
856     else {
857         
858         // Open file.
859         
860         int mode = O_RDONLY;
861         int return_status;
862         
863         _mail_box_writable = DTM_FALSE;
864         char * path = _session->expandPath(error, (char *)_arg);
865         PRIV_ENABLED_OPTIONAL(return_status, SafeAccess(path, W_OK));
866         if (return_status == 0) {
867             mode = O_RDWR;
868         }
869         free(path);
870         
871         // We need to use the most restrictive mode that is possible
872         // on the file. If the caller has requested the file be open
873         // read-only, then we should do that, even if read-write is
874         // allowed. We don't want to try to open the file read-write
875         // if we don't have adequate permission however.
876         //
877         mode = open_mode == O_RDONLY ? open_mode : mode;
878         
879         openRealFile(error, mode, create_mode);
880         if (error.isSet()) {
881             if (auto_create == DTM_TRUE) {
882                 error.clear();
883                 
884                 // Legacy code
885                 // Isn't this wrong?  Shouldn't create() be called
886                 // before unlocking lock_scope?
887                 
888                 lock_scope.unlock();
889                 create(error);
890             }
891             return;
892         }
893
894         // Validate this file if it's not empty
895         if (_file_size) {
896           // When move/copy to a file, we want to make sure the first
897           // five characters are "From "
898           char inbuf[6];
899
900 #if defined(sun) || defined(USL) || defined(__uxp__)
901           pread(_fd, (void *)inbuf, 5, 0);
902 #else
903           lseek(_fd, (off_t) 0L, SEEK_SET);
904           read(_fd, (void *)inbuf, 5);
905           lseek(_fd, (off_t) 0L, SEEK_SET);
906 #endif
907           inbuf[5] = (char)0;
908
909           if (strcmp(inbuf, "From ") != 0) {
910             error.setError(DTME_NotMailBox);
911             return;
912           }
913         }
914         
915         _at_eof.setFalse();
916         _parsed = DTM_FALSE;
917     }
918
919     _object_valid->setTrue(); // New mail watcher starts now.
920     lock_scope.unlock();
921     return;
922 }
923
924 void
925 RFCMailBox::lock()
926 {
927     DtMailEnv   error;
928     longLock(error);
929 }
930
931 void
932 RFCMailBox::unlock()
933 {
934     DtMailEnv   error;
935     longUnlock(error);
936 }
937
938 void
939 RFCMailBox::save()
940 {
941     CheckPointEvent();
942 }
943
944 #ifdef DEAD_WOOD
945 int
946 RFCMailBox::messageCount(DtMailEnv & error)
947 {
948         error.clear();
949
950     _at_eof.waitTrue();
951
952     if (_object_valid->state() <= 0) {
953         error.setError(DTME_ObjectInvalid);
954         return(0);
955     }
956
957     return(_msg_list.length());
958 }
959 #endif /* DEAD_WOOD */
960
961 DtMailMessageHandle
962 RFCMailBox::getFirstMessageSummary(DtMailEnv & error,
963                                     const DtMailHeaderRequest & request,
964                                     DtMailHeaderLine & summary)
965 {
966         error.clear();
967
968     waitForMsgs(0);
969
970     if (_object_valid->state() <= 0) {
971         error.setError(DTME_ObjectInvalid);
972         return(NULL);
973     }
974
975     int slot = nextNotDel(0);
976
977     if (slot >= _msg_list.length()) {
978         return(NULL);
979     }
980
981     makeHeaderLine(error, 0, request, summary);
982
983     return(_msg_list[0]);
984 }
985
986 DtMailMessageHandle
987 RFCMailBox::getNextMessageSummary(DtMailEnv & error,
988                                    DtMailMessageHandle last,
989                                    const DtMailHeaderRequest & request,
990                                    DtMailHeaderLine & summary)
991 {
992     if (_object_valid->state() <= 0) {
993         error.setError(DTME_ObjectInvalid);
994         return(NULL);
995     }
996
997     // Let's treat a null last as the start of the list.
998     //
999     if (last == NULL) {
1000         return(getFirstMessageSummary(error, request, summary));
1001     }
1002
1003         error.clear();
1004
1005     int slot = _msg_list.indexof((MessageCache *)last);
1006     if (slot < 0) {
1007         return(NULL);
1008     }
1009
1010     slot += 1;
1011     waitForMsgs(slot);
1012
1013     slot = nextNotDel(slot);
1014
1015     if (slot >= _msg_list.length()) {
1016         return(NULL);
1017     }
1018
1019     makeHeaderLine(error, slot, request, summary);
1020
1021     return(_msg_list[slot]);
1022 }
1023
1024 void
1025 RFCMailBox::getMessageSummary(DtMailEnv & error,
1026                                DtMailMessageHandle handle,
1027                                const DtMailHeaderRequest & request,
1028                                DtMailHeaderLine & summary)
1029 {
1030     MutexLock lock_map(_map_lock);
1031
1032     if (_object_valid->state() <= 0) {
1033         error.setError(DTME_ObjectInvalid);
1034         return;
1035     }
1036
1037         error.clear();
1038
1039     int slot = _msg_list.indexof((MessageCache *)handle);
1040     if (slot < 0 || slot >= _msg_list.length()) {
1041         error.setError(DTME_ObjectInvalid);
1042         return;
1043     }
1044
1045     makeHeaderLine(error, slot, request, summary);
1046
1047     return;
1048 }
1049
1050 void
1051 RFCMailBox::clearMessageSummary(DtMailHeaderLine & headers)
1052 {
1053     if (NULL != headers.header_values)
1054       delete []headers.header_values;
1055 }
1056
1057 DtMail::Message *
1058 RFCMailBox::getMessage(DtMailEnv & error, DtMailMessageHandle hnd)
1059 {
1060     if (_object_valid->state() <= 0) {
1061         error.setError(DTME_ObjectInvalid);
1062         return(NULL);
1063     }
1064
1065     error.clear();
1066
1067     int slot = _msg_list.indexof((MessageCache *)hnd);
1068     if (slot < 0) {
1069         error.setError(DTME_ObjectInvalid);
1070         return(NULL);
1071     }
1072
1073     MessageCache * mc = _msg_list[slot];
1074     return(mc->message);
1075 }
1076
1077 DtMail::Message *
1078 RFCMailBox::getFirstMessage(DtMailEnv & error)
1079 {
1080     error.clear();
1081
1082     waitForMsgs(0);
1083
1084     if (_object_valid->state() <= 0) {
1085         error.setError(DTME_ObjectInvalid);
1086         return(NULL);
1087     }
1088
1089     int slot = nextNotDel(0);
1090
1091     if (slot >= _msg_list.length()) {
1092         return(NULL);
1093     }
1094
1095     MessageCache * mc = _msg_list[slot];
1096     return(mc->message);
1097 }
1098
1099 DtMail::Message *
1100 RFCMailBox::getNextMessage(DtMailEnv & error,
1101                            DtMail::Message * last)
1102 {
1103     error.clear();
1104
1105     int slot = lookupByMsg((RFCMessage *)last);
1106     if (slot < 0) {
1107         return(NULL);
1108     }
1109
1110     slot += 1;
1111     waitForMsgs(slot);
1112
1113     slot = nextNotDel(slot);
1114
1115     if (slot >= _msg_list.length()) {
1116         return(NULL);
1117     }
1118
1119     MessageCache * mc = _msg_list[slot];
1120     return(mc->message);
1121 }
1122
1123 void
1124 RFCMailBox::copyMessage(DtMailEnv & error,
1125                          DtMail::Message * msg)
1126 {
1127 #if defined(DEBUG_RFCMailBox)
1128     char        *pname = "RFCMailBox::copyMessage";
1129 #endif
1130
1131     if (_object_valid->state() <= 0) {
1132         error.setError(DTME_ObjectInvalid);
1133         return;
1134     }
1135         
1136     error.clear();
1137     
1138     // The following is a hack for PAR0.5. In the future, we will use
1139     // this test for an optimization, but right now, we can only copy
1140     // RFC->RFC.
1141     //
1142     const char * msg_impl = msg->impl(error);
1143     if (strcmp(msg_impl, "Internet MIME") != 0 &&
1144         strcmp(msg_impl, "Sun Mail Tool") != 0) {
1145         error.setError(DTME_NotSupported);
1146         return;
1147     }
1148     
1149     RFCMessage * rfc_msg = (RFCMessage *)msg;
1150     
1151     // We need to protect the file from access. Locking this will also
1152     // block any attempts by the new mail thread.
1153     //
1154     error.clear();
1155       
1156     DEBUG_PRINTF( ("%s:  locking mailbox\n", pname) );
1157     lockFile(error);
1158     if (error.isSet()) {
1159         DtMailEnv       tmp_error;
1160         unlockFile(tmp_error, _fd);
1161         return;
1162     }
1163     
1164     int status;
1165     
1166     off_t end = lseek(_fd, 0, SEEK_END);
1167     status = SafeWrite(_fd, rfc_msg->_msg_start, 
1168         rfc_msg->_msg_end - rfc_msg->_msg_start + 1);
1169     
1170     // We are going to put this at the real end of the file. We don't
1171     // really care what the current thought size is because new mail
1172     // will take care of that problem.
1173     //
1174
1175     // Add a new-line at the end.  
1176     // It serves to distinguish separate messages.
1177
1178     SafeWrite(_fd, "\n", 1);
1179
1180     DEBUG_PRINTF( ("%s:  unlocking mailbox\n", pname) );
1181     unlockFile(error, _fd);
1182     
1183     if (status < 0)
1184       error.setError(DTME_ObjectCreationFailed);
1185 }
1186
1187 void
1188 RFCMailBox::copyMailBox(DtMailEnv & error, DtMail::MailBox * mbox)
1189 {
1190     error.clear();
1191
1192     for (DtMail::Message * msg = mbox->getFirstMessage(error);
1193          msg && error.isNotSet();
1194          msg = mbox->getNextMessage(error, msg)) {
1195         copyMessage(error, msg);
1196         if (error.isSet()) {
1197             return;
1198         }
1199     }
1200 }
1201
1202 void
1203 RFCMailBox::checkForMail(
1204     DtMailEnv & error,
1205     const DtMailBoolean already_locked
1206 )
1207 {
1208     error.clear();
1209
1210     if (_space != DtMailFileObject) {
1211         error.setError(DTME_NotSupported);
1212         return;
1213     }
1214
1215     NewMailEvent(already_locked);
1216 }
1217
1218 void
1219 RFCMailBox::expunge(DtMailEnv & error)
1220 {
1221     error.clear();
1222
1223     for (int msg = 0; msg < _msg_list.length(); msg++) {
1224         MessageCache * mc = _msg_list[msg];
1225         if (mc->delete_pending == DTM_FALSE) {
1226             DtMail::Envelope * env = mc->message->getEnvelope(error);
1227             
1228             DtMailValueSeq value;
1229             env->getHeader(error, RFCDeleteHeader, DTM_FALSE, value);
1230             if (!error.isSet()) {
1231                 mc->delete_pending = DTM_TRUE;
1232             }
1233             else {
1234                 error.clear();
1235             }
1236         }
1237     }
1238
1239     error.clear();
1240     writeMailBox(error, DTM_FALSE);
1241     if ((DTMailError_t)error ==
1242          DTME_CannotWriteToTemporaryMailboxFile_NoFreeSpaceLeft)
1243     {
1244            // Need to do some thing here don't know now.
1245     }
1246     if (error.isSet())
1247     {
1248         // THE MAILBOX COULD NOT BE WRITTEN!! CALLER MUST DO SOMETHING
1249         error.logError(
1250                 DTM_TRUE,
1251                 "RFCMailBox::expunge(): Failed to write mailbox: %s",
1252                 (const char *) error
1253                 );
1254     }
1255 }
1256
1257 const char *
1258 RFCMailBox::impl(DtMailEnv & error)
1259 {
1260     error.clear();
1261     return(_impl_name);
1262 }
1263
1264 void
1265 RFCMailBox::markDirty(const int delta)
1266 {
1267     _dirty += delta;
1268     if (!_dirty) {
1269         // Make sure we really are dirty.
1270         _dirty += 1;
1271     }
1272 }
1273
1274 void
1275 RFCMailBox::callCallback(DtMailCallbackOp op, void * arg)
1276 {
1277     if (_object_signature != RFCSignature || !_object_valid->state()) {
1278         return;
1279     }
1280
1281     const char * path;
1282     if (_space == DtMailFileObject) {
1283         path = (char *)_arg;
1284     }
1285     else {
1286         path = NULL;
1287     }
1288
1289     if (NULL != _callback)
1290       _callback(op, path, NULL, _cb_data, arg);
1291
1292     return;
1293 }
1294
1295 DtMail::Message *
1296 RFCMailBox::newMessage(DtMailEnv & error)
1297 {
1298     // RFC does not support a straightforward concept of adding
1299     // a message to a mailbox. We implement move/copy with a sort
1300     // of kludge, but we can only extend the kludge so far.
1301     //
1302     error.setError(DTME_NotSupported);
1303     return(NULL);
1304 }
1305
1306 void
1307 RFCMailBox::openRealFile(DtMailEnv & error, int open_mode, mode_t create_mode)
1308 {
1309     // We first want to check the access modes we have on the
1310     // file. If we can write the file, then we will need to
1311     // root out the real path just to make sure we don't munge
1312     // a link someplace.
1313     //
1314     struct stat buf;
1315     char * path = _session->expandPath(error, (char *)_arg);
1316     if (error.isSet()) {
1317       return;           // could not expand path
1318     }
1319
1320     if ((open_mode & O_RDWR) == O_RDONLY) {
1321         _real_path = strdup(path);
1322         SafeStat(_real_path, &buf);
1323     }
1324     else {
1325         _real_path = (char *)malloc(MAXPATHLEN);
1326         char *link_path = new char[MAXPATHLEN];
1327         strcpy(link_path, path);
1328         strcpy(_real_path, path);
1329
1330         if (SafeLStat(link_path, &buf) == 0 && (open_mode & O_CREAT) == 0) {
1331             while(S_ISLNK(buf.st_mode)) {
1332                 int size = readlink(link_path, _real_path, sizeof(link_path));
1333                 if (size < 0) {
1334                     error.setError(DTME_NoSuchFile);
1335                     free(_real_path);
1336                     _real_path = (char *)0;
1337                     free(path);
1338                     path = (char *)0;
1339                     delete [] link_path;
1340                     return;
1341                 }
1342
1343                 _real_path[size] = 0;
1344                 if (_real_path[0] == '/') {
1345                     strcpy(link_path, _real_path);
1346                 }
1347                 else {
1348                     char * last_slash = strrchr(link_path, '/');
1349                     if (last_slash) {
1350                         strcpy(last_slash + 1, _real_path);
1351                     }
1352                     else {
1353                         strcpy(link_path, "./");
1354                         strcat(link_path, _real_path);
1355                     }
1356                 }
1357
1358                 strcpy(_real_path, link_path);
1359
1360                 if (SafeLStat(link_path, &buf)) {
1361                     error.setError(DTME_NoSuchFile);
1362                     free(_real_path);
1363                     _real_path = (char *)0;
1364                     free(path);
1365                     path = (char *)0;
1366                     delete [] link_path;
1367                     return;
1368                 }
1369             }
1370         }
1371         else {
1372             if ((open_mode & O_CREAT) == 0) {
1373                 error.setError(DTME_NoSuchFile);
1374                 free(_real_path);
1375                 _real_path = (char *)0;
1376                 free(path);
1377                 path = (char *)0;
1378                 delete [] link_path;
1379                 return;
1380             }
1381         }
1382         delete [] link_path;
1383     }
1384
1385     free(path);
1386
1387     // We should now have a path we can open or create.
1388     // We must now make sure that if the file is being created that
1389     // it is created with the correct permissions and group owner.
1390     //
1391     // If the mailbox to be created is NOT the system mailbox for this
1392     // user (e.g. the one that official delivery agents use), then the
1393     // permissions for the file should only let the current user have access.
1394     //
1395     // If the mailbox to be created IS the system mailbox for this user,
1396     // and MAILGROUP_REQUIRED is defined, we must allow group read/write
1397     // and make sure the mailbox is owned by the correct group
1398     //
1399     int requiresMailGroupCreation = 0;
1400     mode_t oldUmask;
1401     int saveErrno = 0;
1402
1403     // delivery agent runs as specific non-root/wheel group
1404     requiresMailGroupCreation = isMailGroupSystemMailbox(_real_path);
1405     if ( (open_mode & O_CREAT) && requiresMailGroupCreation) {
1406       oldUmask = umask(DTMAIL_DEFAULT_CREATE_UMASK_MAILGROUP);
1407       create_mode = DTMAIL_DEFAULT_CREATE_MODE_MAILGROUP;
1408     }
1409     else {
1410       oldUmask = umask(DTMAIL_DEFAULT_CREATE_UMASK);
1411     }
1412     
1413     // We have 2 choices for locking an RFC file. The first is
1414     // to use the ToolTalk file scoping paradigm, and the second
1415     // is the normal lockf protocol. If we are using ToolTalk,
1416     // we need to initialize the ToolTalk locking object.
1417     //
1418     // Unfortunately lockf() on an NFS mounted file system will
1419     // upset mmap(). A quick hack is to not use lockf() on any
1420     // remotely mounted file. Since mailx doesn't do tooltalk
1421     // locking now, it is possible to corrupt the spool file when
1422     // using mailx while mailtool has a NFS mounted mail file loaded.
1423     //
1424     // We should find a solution where mailtool and mailx work
1425     // well together.
1426     //
1427     // open the file before we obtain the lock can cause sync problem
1428     // if another owns the lock and has modified the mailbox
1429
1430     DTMBX_LONGLOCK answer = DTMBX_LONGLOCK_SUCCESSFUL;
1431
1432     if (_lock_flag == DTM_TRUE) {
1433       answer = longLock(error);
1434       if (answer == DTMBX_LONGLOCK_FAILED_CANCEL) {
1435         free(_real_path);
1436         _real_path = (char *)0;
1437         return;
1438       }
1439
1440       // we have obtained the lock, go ahead and open the mailbox
1441       // Do not open the mailbox with privileges enabled, even if
1442       // creating the file
1443       //
1444       PRIV_ENABLED_OPEN(_real_path, _fd,
1445                         SafeOpen(_real_path, open_mode, create_mode));
1446       saveErrno = errno;
1447     }
1448     else
1449     {
1450       PRIV_ENABLED_OPEN(_real_path, _fd,
1451                         SafeOpen(_real_path, open_mode, create_mode));
1452       saveErrno = errno;
1453
1454 #if !defined(SENDMAIL_LOCKS)
1455       // On some systems, sendmail uses lockf while delivering mail.
1456       // We can not hold a lockf style lock on those systems. Of course
1457       // if the user has turned off ToolTalk locking, they are playing
1458       // without a net...
1459       //
1460       if ( (_fd >= 0) && ( (open_mode & O_RDWR) == O_RDWR) )
1461       {
1462         enum DtmFileLocality    location;
1463
1464         //
1465         // Only attempt lockf() if file was opened for reading and writing
1466         //
1467         location = DtMail::DetermineFileLocality(_real_path);
1468 #if defined(DEBUG_RFCMailBox)
1469         char *locstr = (location==Dtm_FL_UNKNOWN) ?
1470                        "Unknown" :
1471                        ((location==Dtm_FL_LOCAL) ? "Local" : "Remote");
1472         DEBUG_PRINTF(("openRealFile: location is %s\n", locstr));
1473 #endif
1474         int return_status;
1475     
1476         switch (location)
1477         {
1478           case Dtm_FL_UNKNOWN:
1479             //
1480             // locality unknown -- assume local and lock
1481             //
1482           case Dtm_FL_LOCAL:
1483             //
1484             // locality local - apply lock
1485             //
1486             assert(_lockf_active == DTM_FALSE);
1487             lseek(_fd, 0, SEEK_SET);
1488             PRIV_ENABLED_OPTIONAL(return_status,SafeLockf(_fd, F_TLOCK, 0));
1489             if (return_status == -1) {
1490               error.setError(DTME_OtherOwnsWrite);
1491               SafeClose(_fd);
1492               return;
1493             } else {
1494               _lockf_active = DTM_TRUE;
1495             }
1496             break;
1497           default:
1498             //
1499             // locality otherwise -- assume remote dont lock
1500             //
1501             break;      
1502         }
1503       }
1504 #endif
1505
1506     }
1507
1508     (void) umask(oldUmask);
1509     
1510     if (_fd < 0) {
1511         longUnlock(error);
1512
1513         switch (saveErrno) {
1514           case EACCES:
1515             error.setError(DTME_NoPermission);
1516             break;
1517
1518           case EISDIR:
1519             error.setError(DTME_IsDirectory);
1520             break;
1521
1522           case ENOENT:
1523             error.setError(DTME_NoSuchFile);
1524             break;
1525
1526           default:
1527             error.setError(DTME_ObjectAccessFailed);
1528         }
1529
1530         free(_real_path);
1531         _real_path = (char *)0;
1532
1533         return;
1534     }
1535
1536     if ((open_mode & O_CREAT) && requiresMailGroupCreation) {
1537       //
1538       // Make sure a newly created file has the correct group owner i.d.
1539       //
1540       gid_t groupId = GetIdForGroupName(DTMAIL_DEFAULT_CREATE_MAILGROUP);
1541       if (groupId >= 0)
1542         (void) SafeFchown(_fd, (unsigned int) -1, groupId);
1543     }
1544
1545     if ((open_mode & 0x3) == O_RDWR) {
1546       if (answer == DTMBX_LONGLOCK_SUCCESSFUL ||
1547           answer == DTMBX_LONGLOCK_FAILED_READWRITE)
1548         _mail_box_writable = DTM_TRUE;
1549       else
1550         _mail_box_writable = DTM_FALSE;
1551     }
1552     else {
1553       _mail_box_writable = DTM_FALSE;
1554     }
1555     error.clear();
1556
1557     _file_size = lseek(_fd, 0, SEEK_END);
1558     lseek(_fd, 0, SEEK_SET);
1559     _links = buf.st_nlink;
1560
1561     _lockFileName = generateLockFileName();
1562     assert(_lockFileName != NULL);
1563     return;
1564 }
1565
1566 off_t
1567 RFCMailBox::realFileSize(DtMailEnv & error, struct stat * stat_buf)
1568 {
1569     error.clear();
1570
1571     // We have to deal with the problem of editors that will unlink
1572     // the file we are watching and rename it to a new file. This
1573     // will cause us to miss new mail, and eventually write data that
1574     // is out of sync with the real state of the file.
1575     //
1576     // We do this by checking the link count first. If it has changed,
1577     // we will close, reopen, and then stat. Note that we could say if
1578     // the link count has gone down we will do this, but since we can't
1579     // be sure exactly what has happened to this file, we will consider
1580     // any change in the link count to require us to reopen the file.
1581     //
1582     struct stat buf;
1583     int error_code;
1584
1585     error_code = SafeFStat(_fd, &buf);
1586     if (error_code >= 0) {
1587         if (buf.st_nlink != _links) {
1588             mode_t      old_mode;
1589
1590             DEBUG_PRINTF( ("realFileSize:  (buf.st_nlink!=_links\n") );
1591             old_mode = fcntl(_fd, F_GETFL) & O_ACCMODE;
1592
1593             int fd;
1594             PRIV_ENABLED_OPEN(_real_path,
1595                               fd,
1596                               SafeOpen(_real_path, old_mode, 0666));
1597             if (fd < 0) {
1598                 error.setError(DTME_ObjectAccessFailed);
1599                 return(0);
1600             }
1601
1602             transferLock(_fd, fd);
1603             SafeClose(_fd);
1604             _fd = fd;
1605
1606             if (SafeFStat(_fd, &buf) >= 0) {
1607                 _links = buf.st_nlink;
1608             } else {
1609                 if (_errorLogging)
1610                   error.logError(DTM_FALSE,
1611                         "realFileSize(): fstat(%d/%s) failed errno=%d\n",
1612                         _fd, _real_path, errno);
1613                 error.setError(DTME_ObjectAccessFailed);
1614                 return(0);
1615             }
1616         }
1617
1618         if (stat_buf) {
1619             *stat_buf = buf;
1620         }
1621     } else {
1622         if (_errorLogging)
1623           error.logError(DTM_FALSE,
1624                        "realFileSize(): fstat(%d/%s) failed errno=%d\n",
1625                        _fd, _real_path, errno);
1626         error.setError(DTME_ObjectAccessFailed);
1627         return(0);
1628     }
1629
1630     return(buf.st_size);
1631 }
1632
1633 int
1634 RFCMailBox::mapFile(DtMailEnv & error,
1635                     const DtMailBoolean already_locked,
1636                     mode_t create_mode)
1637 {
1638   char *pname = "mapFile";
1639   int err_phase = 0;
1640
1641   if (already_locked == DTM_FALSE) {
1642       DEBUG_PRINTF( ("%s:  locking mailbox\n", pname) );
1643       lockFile(error);
1644       if (error.isSet()) {
1645         DtMailEnv       tmp_error;
1646         unlockFile(tmp_error, _fd);
1647         return(-1);
1648       }
1649   }
1650   
1651   // We must map in whole pages. This is done by rounding the
1652   // file size up to the next larger size.
1653   //
1654   
1655   // Some notes on the singing and dancing that follows are in order.
1656   // As of 6/21/95 it has been determined that there is a bug in S494-17
1657   // and S495-25 whereby a mmap() of the mailbox can return sections that
1658   // have "nulls" where valid data should be found. To guard against this,
1659   // we check for nulls at the beginning and the end of a new mmap()ed region
1660   // and if they are found we retry the operation.
1661   // 
1662   // If NFS attribute caching is enabled (which is the default), a
1663   // stat/fstat of a NFS file may not return the correct true size of the
1664   // mailbox if it has been appended to since the last time it was
1665   // mmap()ed. To get around this problem, once it is noticed via
1666   // stat()/fstat() that the mailbox has changed, we must open the mailbox
1667   // on a separate file descriptor, read() in a byte, and then do a fstat()
1668   // to determine the true correct size of the mailbox.
1669   //
1670
1671   // fstat() currently open mailbox
1672   //
1673   struct stat tempStatbuf;
1674
1675   err_phase++;
1676   if (SafeFStat(_fd, &tempStatbuf) < 0) {
1677     if (_errorLogging)
1678       error.logError(
1679           DTM_FALSE,
1680           "%s(%d): fstat(%d/%s) failed errno=%d\n",
1681           pname, err_phase, _fd, _real_path, errno);
1682     if (already_locked == DTM_FALSE) {
1683       DEBUG_PRINTF( ("%s:  unlocking mailbox\n", pname) );
1684       unlockFile(error, _fd);
1685     }
1686     error.setError(DTME_ObjectAccessFailed);
1687     return(-1);
1688   }
1689
1690   // Obtain guaranteed current stat buf for mailbox object,
1691   // regarless of whether it is local or remote
1692   //
1693   struct stat statbuf;
1694
1695   err_phase++;
1696   if (SafeGuaranteedStat(_real_path, &statbuf) == -1) {
1697     if (_errorLogging)
1698       error.logError(
1699           DTM_FALSE,
1700           "%s(%d): SafeGuaranteedStat(%s) failed errno=%d\n",
1701           pname, err_phase, _real_path, errno);
1702     if (already_locked == DTM_FALSE) {
1703       DEBUG_PRINTF( ("%s:  unlocking mailbox\n", pname) );
1704       unlockFile(error, _fd);
1705     }
1706     error.setError(DTME_ObjectAccessFailed);
1707     return(-1);
1708   }
1709
1710   // At this point, 
1711   // statbuf -- contains the guaranteed stat struct for the mailbox
1712   // tempStatbuf -- contains fstat stat struct for original mailbox
1713   //
1714
1715   // See if the inode has changed - if so the file has been
1716   // modified out from under is
1717   //
1718   err_phase++;
1719   if (statbuf.st_ino != tempStatbuf.st_ino) {
1720     if (_errorLogging)
1721       error.logError(DTM_FALSE,
1722           "%s(%d):  inode fstat(%d/%s) != stat(%s)\nfstat buffer entries: ino = %d, dev = %d, nlink = %d, size = %ld\n stat buffer entries: ino = %d, dev = %d, nlink = %d, size = %ld\n",
1723           pname, err_phase, _fd, _real_path, _real_path,
1724           statbuf.st_ino, statbuf.st_dev,
1725           statbuf.st_nlink, statbuf.st_size,
1726           tempStatbuf.st_ino, tempStatbuf.st_dev,
1727           tempStatbuf.st_nlink, tempStatbuf.st_size);
1728     if (already_locked == DTM_FALSE) {
1729       DEBUG_PRINTF( ("%s:  unlocking mailbox\n", pname) );
1730       unlockFile(error, _fd);
1731     }
1732     error.setError(DTME_ObjectInvalid);
1733     return(-1);
1734   }
1735   
1736   long pagesize = memoryPageSize();
1737   
1738   // We will only map any file space we haven't mapped so far.
1739   // We always map entire pages to make this easier. We must
1740   // remap the partial pages so we will get the real bits, not
1741   // the zero fill bits provided by mmap.
1742   //
1743   // The arithmetic for partial mappings is a little odd, and
1744   // is worthy of explanation. The main issue is that we must
1745   // always start on a page boundary, and we must map page
1746   // size chunks. So, for any offset, we need to back up to
1747   // the start of a page in the file. This gives us the following
1748   // entries in MapRegion:
1749   //
1750   // map_region - Address where the mapping starts.
1751   // map_size - Size of the mapping (will always be a multiple of pagesize).
1752   // file_region - Address where requested file offset begins within
1753   //      map_region.
1754   // file_size - Size of file within this mapping.
1755   // offset - Where the mapping begins (which is almost always different
1756   //      than the offset where file_region begins.
1757   //
1758   // So, the new offset begins where the last real file size ended.
1759   // We get this by adding the previous offset, to the file size mapped,
1760   // and then add any the difference between the mapped region and the
1761   // real file offset. This gets us an offset back to the old end of
1762   // file. Now if you are not confused, we need to adjust this new offset
1763   // back to the closest page boundary and begin the mapping from there!
1764   //
1765   // File by messages:
1766   //   v--------v------------v----v---------------------------v----------------v
1767   //   |  Msg1  |    Msg2    |Msg3|  Msg4                     |  Msg5          |
1768   //   ^--------^------------^----^---------------------------^----------------^
1769   //   File by pages:
1770   //   +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
1771   //   | Pg1 | Pg2 | Pg3 | Pg4 | Pg5 | Pg6 | Pg7 | Pg8 | Pg9 | Pg10| Pg11| Pg12|
1772   //   +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
1773   //   MapRegions:
1774   //   +-----+--v--+
1775   //   \\\\\\\\\\  |
1776   //   +-----+--^--+
1777   //         +--v--+-----+---v-+
1778   //         |  \\\\\\\\\\\\\\ |
1779   //         +--^--+-----+---^-+
1780   //         ^  ^
1781   //         ^  ^
1782   //         ^  ^
1783   //.map_region ^
1784   //         ^  ^
1785   //         ^  ^
1786   //         ^ .file_region
1787   //         ^  ^
1788   //   |---->| .offset
1789   //         ^  ^
1790   //         |->| offset_from_map
1791   //  
1792   //                     +---v-+--v--+
1793   //                     |   \\\\\\  |
1794   //                     +---^-+--^--+
1795   //                     ^   ^
1796   //                     ^   ^
1797   //                     ^   ^
1798   //           .map_region   ^
1799   //                     ^   ^
1800   //                     ^   ^
1801   //                     ^   .file_region
1802   //                     ^   ^
1803   //   |---------------->| .offset
1804   //                     ^   ^
1805   //                     |-->| offset_from_map
1806   //  
1807   //  
1808
1809   MapRegion * map = new MapRegion;
1810   long offset_from_map;
1811   
1812   if (_mappings.length()) {
1813     MapRegion * prev_map = _mappings[_mappings.length() - 1];
1814     map->offset = prev_map->offset + prev_map->file_size + 
1815                   (prev_map->file_region - prev_map->map_region);
1816     offset_from_map = map->offset % pagesize;
1817     map->offset -= offset_from_map;
1818   }
1819   else {
1820     map->offset = 0;
1821     offset_from_map = 0;
1822   }
1823   
1824   map->file_size = statbuf.st_size - map->offset - offset_from_map;
1825   map->map_size = statbuf.st_size - map->offset;
1826   map->map_size += (pagesize - (map->map_size % pagesize));
1827   
1828   int flags = MAP_PRIVATE;
1829   
1830 #if defined(MAP_NORESERVE)
1831   // We are not supposed to be writing to these pages. If
1832   // we don't specify MAP_NORESERVE however, the system will
1833   // reserve swap space equal to the file size to deal with
1834   // potential writes. This is wasteful to say the least.
1835   //
1836   flags |= MAP_NORESERVE;
1837 #endif
1838
1839   // Need to obtain an mmap()ed region and one way or another
1840   // have the data from the mail file placed into that
1841   // region. There are two ways of accomplishing this:
1842   // 
1843   //   Method 1: mmap() the data directly from the file
1844   //   Method 2: mmap() /dev/zero and then read from the file to the region
1845   // 
1846   // We want to use method #1 if at all possible as it allows the
1847   // VM system to page the data in as it is accessed.
1848   // 
1849   // There is a potential problem with method #1 in that since
1850   // the region is a "view" into the real file data, if the file
1851   // itself is reduced in size behind our back by another
1852   // process, we have the potential for generating SIGBUS memory
1853   // reference errors if we try and access a byte that is no
1854   // longer within the file. This is true even is MAP_PRIVATE is used:
1855   //
1856   // ** The behavior of PROT_WRITE can be influenced by setting
1857   // ** MAP_PRIVATE in the flags parameter. MAP_PRIVATE means
1858   // ** "Changes are private".
1859   // ** 
1860   // ** MAP_SHARED and MAP_PRIVATE describe the disposition of write
1861   // ** references to the memory object.  If MAP_SHARED is
1862   // ** specified, write references will change the memory object.
1863   // ** If MAP_PRIVATE is specified, the initial write reference
1864   // ** will create a private copy of the memory object page and
1865   // ** redirect the mapping to the copy. Either MAP_SHARED or
1866   // ** MAP_PRIVATE must be specified, but not both.  The mapping
1867   // ** type is retained across a fork(2).
1868   // ** 
1869   // ** Note that the private copy is not created until the first
1870   // ** write; until then, other users who have the object mapped
1871   // ** MAP_SHARED can change the object.
1872   // 
1873   // While this is always the case for a process that does not
1874   // abide by the advisory only locking protocols used to protect
1875   // against this, to guard against this method #1 is only used
1876   // if the mailbox is writable and we can obtain a short term
1877   // lock on the mailbox. This prevents the mailbox from changing
1878   // size at the moment we map it. Nonetheless, a SIGBUS
1879   // interrupt handler must be armed to handle the case where the
1880   // mailbox file is changed out from under us by devious means.
1881   // 
1882   // If this condition is not met, or the mmap() call for some
1883   // reason fails, then fall back to method #2.
1884   //
1885
1886   char *mmap_format_string = "%s(%d): mmap(0, map_size=%ld, prot=0x%04x, flags=0x%04x, fd=%d(%s), %x) == map_region=%x, errno == %d\n";
1887
1888   map->map_region = (char *)-1;
1889   
1890   if (   (_use_dot_lock == DTM_TRUE)
1891       && (_mail_box_writable == DTM_TRUE)
1892 #if defined(__osf__)
1893       && (_long_lock_active == DTM_TRUE)
1894 #endif
1895      ) {
1896
1897 #if defined(__osf__) && OSMAJORVERSION < 4
1898         // This version of mmap does NOT allow requested length to be
1899         // greater than the file size ... in contradiction to the
1900         // documentation (don't round up).
1901     map->map_region = (char *) mmap(
1902                                 0, (statbuf.st_size - map->offset),
1903                                 PROT_READ, flags, _fd, (off_t) map->offset);
1904 #else
1905     map->map_region = (char *)mmap(
1906                                 0, (unsigned int) map->map_size,
1907                                 PROT_READ, flags, _fd, (off_t) map->offset);
1908 #endif
1909
1910     // ERROR/CONSISTENCY CHECKING: if the region was not mapped,
1911     // or the first or last byte of the region is a null, then
1912     // the mmap() is considered to have failed: write an entry
1913     // to the dump file, undo the damage, and bail to the caller
1914
1915     err_phase++;
1916     if ( (map->map_region == (char *) -1) ||
1917          (map->file_size && (map->map_region[offset_from_map] == '\0')) ||
1918          (map->file_size &&
1919             (map->map_region[offset_from_map+map->file_size-1] == '\0'))
1920        ) {
1921
1922       DEBUG_PRINTF(
1923         ("mapFile: Error mmap(1) == %p, errno = %d\n", map->map_region, errno));
1924
1925       if (_errorLogging)
1926         writeToDumpFile(
1927             mmap_format_string,
1928             pname, err_phase,
1929             map->map_size, PROT_READ, flags, _fd, _real_path, map->offset,
1930             map->map_region, errno);
1931         writeToDumpFile(
1932             "%s(%d): statbuf: ino=%d, dev=%d, nlink=%d, size=%ld\n",
1933             pname, err_phase,
1934             statbuf.st_ino, statbuf.st_dev, statbuf.st_nlink, statbuf.st_size);
1935
1936       if (map->map_region == (char *) -1)
1937       {
1938           if (_errorLogging)
1939             writeToDumpFile(
1940                 "%s(%d): mmap failed: errno = %d:  %s\n",
1941                 pname, err_phase, errno, strerror(errno));
1942       }
1943       else
1944       {
1945         int i, cnt;
1946         if (_errorLogging)
1947         {
1948           for (i=(int)offset_from_map, cnt=0;
1949                i<map->file_size+offset_from_map;
1950                i++)
1951             if (map->map_region[i] == '\0') cnt++;
1952
1953           writeToDumpFile(
1954               "%s(%d):  mmap failed: %d NULLs in map from byte %d to %d:\n",
1955               pname, err_phase,
1956               cnt, offset_from_map, map->file_size+offset_from_map);
1957         }
1958
1959         //
1960         // Pass throught to attempt mapping through /dev/zero.
1961         //
1962         munmap(map->map_region, (size_t) map->map_size);
1963         map->map_region = (char*) -1;
1964 #if 0
1965         if (_errorLogging) {
1966           HexDump(
1967                 (FILE*) NULL,
1968                 "Region mapped in",
1969                 (unsigned char *)map->map_region,
1970                 map->map_size,
1971                 _errorLogging ? 0 : 100);
1972           HexDump(
1973                 (FILE*) NULL,
1974                 "Offset Region mapped in",
1975                 (unsigned char *)map->map_region+offset_from_map,
1976                 map->file_size,
1977                 _errorLogging ? 0 : 100);
1978         }
1979         delete map;
1980         if (already_locked == DTM_FALSE) {
1981           DEBUG_PRINTF( ("%s:  unlocking mailbox\n", pname) );
1982           unlockFile(error, _fd);
1983         }
1984         error.setError(
1985                 map->map_region != (char *)-1 ?
1986                 DTME_ObjectAccessFailed :
1987                 DTME_ObjectCreationFailed);
1988
1989         return(-1);
1990 #endif
1991       }
1992     }
1993     else
1994       DEBUG_PRINTF(
1995         ("mapFile: mmap(1) okay = %p, errno = %d\n", map->map_region, errno) );
1996   }
1997   
1998   // If a region was mapped, cause OS to use sequential access paging rules
1999   //
2000   if (map->map_region != (char *) -1)
2001     alterPageMappingAdvice(map);
2002     
2003   if (map->map_region == (char *) -1) {
2004     // Either the direct mmap failed, or we decided not to do a direct mmap
2005     // of the new data in the mailbox file. Now must create a mmap()ed
2006     // region against /dev/zero and then read the new data into that region
2007     //
2008     char *devzero = "/dev/zero";
2009
2010 #if defined(DO_ANONYMOUS_MAP)
2011     int fd = -1;
2012     mode_t mode = create_mode;
2013     flags |= MAP_ANONYMOUS;
2014 #else
2015     int fd = SafeOpen(devzero, O_RDWR, create_mode);
2016 #endif
2017     
2018     map->map_region = (char *)mmap(
2019                                 0, (unsigned int) map->map_size,
2020                                 PROT_READ|PROT_WRITE, flags, fd, 0);
2021
2022     err_phase++;
2023     if (map->map_region == (char *)-1) {
2024       if (_errorLogging)
2025       {
2026         error.logError(
2027           DTM_TRUE,
2028           mmap_format_string,
2029           pname, err_phase,
2030           map->map_size, PROT_READ|PROT_WRITE, flags, fd, devzero, errno);
2031         writeToDumpFile(
2032           mmap_format_string,
2033           pname, err_phase,
2034           map->map_size, PROT_READ|PROT_WRITE, flags, fd, devzero, errno);
2035       }
2036       if (already_locked == DTM_FALSE) {
2037         DEBUG_PRINTF( ("%s:  unlocking mailbox\n", pname) );
2038         unlockFile(error, _fd);
2039       }
2040       error.setError(DTME_NoMemory);
2041       delete map;
2042       return(-1);
2043     }
2044     
2045     if (fd != -1 && (SafeClose(fd) < 0)) {
2046       // should deal with the error here (on /dev/zero??)
2047     }
2048
2049     // Have created a sufficiently large region mapped to /dev/zero
2050     // Read the "new bytes" into this region
2051     // Note that there is a suspicion that a window exists where even
2052     // though the file size appears to be increased by "n", that all
2053     // of the data may not be written in the file yet, and a read may
2054     // fall "short" in this case, especially if running over nfs. We
2055     // must be prepared to handle any condition whereby a read from
2056     // the file either yields less data than expected, or even though
2057     // a good return is received, the data may not be there, and thus
2058     // the check for "nulls" at the beginning and end of the buffer.
2059     //
2060     // CDExc20318 -
2061     // Copying messages between mailboxes often leaves a single
2062     // NULL character at the end of the file.
2063     // Check the last 2 bytes for NULL characters.
2064     //
2065     err_phase++;
2066     lseek(_fd, (off_t) map->offset, SEEK_SET);
2067     size_t bytesToRead = (size_t)(statbuf.st_size - map->offset);
2068     ssize_t readResults = SafeRead(_fd, map->map_region, bytesToRead);
2069     if ( (readResults != bytesToRead) ||
2070          (readResults && (map->map_region[0] == '\0')) ||
2071          (readResults && (map->map_region[readResults-1] == '\0')) ||
2072          (readResults && (map->map_region[offset_from_map] == '\0')) ||
2073          (readResults &&
2074             (map->map_region[offset_from_map+map->file_size-1] == '\0'))
2075        ) {
2076
2077       if (_errorLogging)
2078         writeToDumpFile(
2079             "%s(%d):  SafeRead(%d(%s), 0x%08lx, %d) == %d, errno == %d\n",
2080             pname, err_phase, _fd, _real_path, map->map_region, bytesToRead,
2081             readResults, errno);
2082         writeToDumpFile(
2083             "%s(%d):  stat buf: ino=%d, dev=%d, nlink=%d, size=%ld\n",
2084             pname, err_phase, statbuf.st_ino, statbuf.st_dev,
2085             statbuf.st_nlink, statbuf.st_size);
2086
2087       if (readResults > 0) {
2088         if (_errorLogging)
2089           HexDump(
2090                 (FILE*) NULL,
2091                 "Buffer read in",
2092                 (unsigned char *)map->map_region, readResults,
2093                 _errorLogging ? 0 : 100);
2094       }
2095
2096       munmap(map->map_region, (size_t) map->map_size);
2097       delete map;
2098       if (already_locked == DTM_FALSE) {
2099           DEBUG_PRINTF( ("%s:  unlocking mailbox\n", pname) );
2100           unlockFile(error, _fd);
2101       }
2102
2103       error.setError(
2104                 readResults >= 0 ?
2105                 DTME_ObjectAccessFailed :
2106                 DTME_ObjectCreationFailed);
2107       return(-1);
2108     }
2109     
2110     mprotect(map->map_region, (size_t) map->map_size, PROT_READ);
2111     alterPageMappingAdvice(map);
2112   }
2113   
2114   map->file_region = map->map_region + offset_from_map;
2115
2116   // Ok, we think we have got all of the new data that has been
2117   // appended to the mailbox - just to make absolutely sure, stat
2118   // the file again and make sure that the file size has remained
2119   // consistent throughout this operation. If it has changed, it
2120   // means some process ignored our lock on the file and appended
2121   // data anyway. In this case, throw away our current effort and
2122   // recursively call this function to try the attempt again.
2123   //
2124
2125   err_phase++;
2126   if (SafeGuaranteedStat(_real_path, &tempStatbuf) < 0) {
2127     if (_errorLogging)
2128       error.logError(
2129           DTM_FALSE,
2130           "%s(%d): SafeGuaranteedStat(%s) failed errno=%d:  %s\n",
2131           pname, err_phase, _real_path, errno, strerror(errno));
2132     munmap(map->map_region, (size_t) map->map_size);
2133     delete map;
2134     if (already_locked == DTM_FALSE) {
2135       DEBUG_PRINTF( ("%s:  unlocking mailbox\n", pname) );
2136       unlockFile(error, _fd);
2137     }
2138     error.setError(DTME_ObjectAccessFailed);
2139     return(-1);
2140   }
2141
2142   err_phase++;
2143   if (tempStatbuf.st_size != statbuf.st_size) {
2144     if (_errorLogging)
2145       error.logError(
2146           DTM_FALSE,
2147           "%s(%d): fstat(%d/%s) size changed %d/%d\n",
2148           pname, err_phase,
2149           _fd, _real_path, statbuf.st_size, tempStatbuf.st_size);
2150     munmap(map->map_region, (size_t) map->map_size);
2151     delete map;
2152     int mapResults = mapFile(error, DTM_TRUE);
2153     if (error.isSet()) {
2154       if (already_locked == DTM_FALSE) {
2155         DEBUG_PRINTF( ("%s:  unlocking mailbox\n", pname) );
2156         unlockFile(error, _fd);
2157       }
2158       return(-1);
2159     }
2160     return(mapResults);
2161   }
2162
2163   if (already_locked == DTM_FALSE) {
2164     DEBUG_PRINTF( ("%s:  unlocking mailbox\n", pname) );
2165     unlockFile(error, _fd);
2166   }
2167   
2168   // We need to set _file_size here, because if we get the file size
2169   // when the mailbox is not locked, it is possible that we could be
2170   // stating the file while it is being written to.  Since we have the
2171   // lock in this routine, we know the file size isn't changing, and it
2172   // is consistent with the amount being mapped in.
2173   //
2174   _file_size = statbuf.st_size;
2175
2176   if (_hide_access_events) mailboxAccessHide("mapFile");
2177
2178   return(_mappings.append(map));
2179 }
2180
2181 int
2182 RFCMailBox::nextNotDel(const int cur)
2183 {
2184     int del;
2185     for (del = cur; del < _msg_list.length(); del++) {
2186         MessageCache * mc = _msg_list[del];
2187         if (mc->delete_pending == DTM_FALSE) {
2188             break;
2189         }
2190     }
2191
2192     return(del);
2193 }
2194
2195 int
2196 RFCMailBox::prevNotDel(const int cur)
2197 {
2198     int del;
2199     for (del = cur; del >= 0; del--) {
2200         MessageCache * mc = _msg_list[del];
2201         if (mc->delete_pending == DTM_FALSE) {
2202             break;
2203         }
2204     }
2205
2206     return(del);
2207 }
2208
2209 int
2210 RFCMailBox::lookupByMsg(RFCMessage * msg)
2211 {
2212     for (int slot = 0; slot < _msg_list.length(); slot++) {
2213         MessageCache * mc = _msg_list[slot];
2214         if (mc->message == msg) {
2215             return(slot);
2216         }
2217     }
2218
2219     return(-1);
2220 }
2221
2222 void *
2223 RFCMailBox::ThreadParseEntry(void * client_data)
2224 {
2225     RFCMailBox * self = (RFCMailBox *)client_data;
2226
2227     DtMailEnv error;
2228     MutexLock lock_map(self->_map_lock);
2229
2230     self->parseFile(error, 0);
2231
2232     lock_map.unlock();
2233
2234     ThreadExit(0);
2235     return(NULL);
2236 }
2237
2238 void
2239 RFCMailBox::parseFile(DtMailEnv & error, int map_slot)
2240 {
2241
2242     error.clear();
2243
2244     // We are not at the eof.
2245     //
2246     _at_eof.setFalse();
2247
2248     const char * begin = _mappings[map_slot]->file_region;
2249     const char * end = begin + _mappings[map_slot]->file_size - 1;
2250
2251     if (end <= begin) {
2252         // Empty file. DO NOTHING OR IT WILL CRASH!
2253         //
2254         _at_eof.setTrue();
2255         return;
2256     }
2257
2258     // Parsing will always be a sequential access to the pages.
2259     // We will give the kernel a clue what we are up to and perhaps
2260     // help our parsing time in the process.
2261     //
2262     unsigned long pagelimit = _mappings[map_slot]->map_size;
2263
2264 #if !defined(USL) && !defined(__uxp__) && !defined(linux)
2265     // no madvise; dont use optimization
2266     madvise(
2267         (char *)_mappings[map_slot]->map_region,
2268         (size_t) pagelimit, MADV_SEQUENTIAL);
2269 #endif
2270     
2271     // We should always begin with a "From " if this is an RFC file,
2272     // with a valid offset. If we have something else, then look
2273     // forward until we find one.
2274     //
2275     const char * parse_loc = begin;
2276     if (strncmp(parse_loc, "From ", 5)) {
2277       if (*parse_loc == ' ' || *parse_loc == '\n' || *parse_loc == '\t') {
2278         // We allow any number of white spaces before "From"
2279         // But "From" still has to be the first word in the line
2280         while ( (*parse_loc == ' ' || *parse_loc == '\n'
2281                  || *parse_loc == '\t') && parse_loc <= end) {
2282           parse_loc++;
2283         }
2284
2285         if (parse_loc >= end) {
2286             error.setError(DTME_NotMailBox);
2287             _at_eof.setTrue(); // We are, but so what.
2288             return;
2289         }
2290
2291         parse_loc--;
2292
2293         if (strncmp(parse_loc, "\nFrom ", 6)) {
2294           error.setError(DTME_NotMailBox);
2295           return;
2296         }
2297       } else {
2298         // This file does not start with either From or white space
2299         error.setError(DTME_NotMailBox);
2300         return;
2301       }
2302     }
2303
2304     if (*parse_loc == '\n') {
2305         parse_loc += 1;
2306     }
2307
2308     // We are sitting at the start of a message. We will build message
2309     // objects for each message in the list.
2310     //
2311     do {
2312         MessageCache * cache = new MessageCache;
2313         cache->delete_pending = DTM_FALSE;
2314
2315         cache->message = new RFCMessage(error, this, &parse_loc,
2316                                         end);
2317         if (error.isNotSet()) {
2318
2319 //#ifdef MESSAGE_PARTIAL
2320         // message/partial processing is currently not working, so we are 
2321         // taking out message/parital processing for now until we figure 
2322         // out how to get it to work again.  Only the following block of 
2323         // code needs to be taken out in order to disable message/partial.
2324         // Also, when it was turned on, it was trying to combine partial 
2325         // messages that were non-MIME (no MIME-VERSION header).  This 
2326         // caused even more problems.  We should only check for partial 
2327         // messages if it is MIME.
2328
2329           if (_isPartial(error, cache->message)) {
2330
2331             if (error.isNotSet()) {
2332               cache->message->setFlag(error, DtMailMessagePartial);
2333
2334               if (error.isNotSet()) {
2335                 cache->message = _assemblePartial(error, cache->message);
2336
2337                 if (error.isNotSet()) {
2338                   _msg_list.append(cache);
2339                   continue;
2340                 }
2341               }
2342             }
2343           }
2344 //#endif // MESSAGE_PARTIAL
2345           _msg_list.append(cache);
2346         }
2347         else {
2348             error.clear();
2349         }
2350     } while (parse_loc <= end);
2351
2352     // At this point we most likely will see random behavior. We will
2353     // tell the kernel to pull in the minimum number of extra pages.
2354     //
2355 #if !defined(USL) && !defined(__uxp__) && !defined(linux)
2356     // no madvise; dont use optimization
2357     madvise(
2358         (char *)_mappings[map_slot]->map_region,
2359         (size_t) pagelimit, MADV_RANDOM);
2360 #endif
2361     // IBM code for message/partial vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
2362     // we need delete those messages if they are satisfied the
2363     // following two conditions
2364     // (1) marked for delete  and
2365     // (2) it is a message/partial message
2366     //
2367     // Otherwise, we will get segmentation error when the method
2368     //  writeMailBox is invoked because a new message were
2369     //  generated by assembling partial messages
2370     //
2371     for (int msg = 0; msg < _msg_list.length(); msg++) {
2372         MessageCache * mc = _msg_list[msg];
2373         if (mc->delete_pending == DTM_FALSE) {
2374             DtMail::Envelope * env = mc->message->getEnvelope(error);
2375
2376             DtMailValueSeq value;
2377             char           *type=NULL;
2378             static const char       * partial = "message/partial";
2379             static const char       * contentType = "content-type";
2380         // get content-type
2381           env->getHeader(error, contentType , DTM_FALSE, value);
2382           if (error.isNotSet()) {
2383               type = strdup(*(value[0]));
2384              }
2385              else{
2386               error.clear();
2387            }
2388           if(type != NULL) {
2389             if (error.isNotSet()) {
2390                 env->getHeader(error, RFCDeleteHeader, DTM_FALSE, value);
2391
2392                if (!error.isSet() && (strncasecmp(type, partial, 15) == 0)) {
2393                  delete mc->message;         // remove message storage
2394                  delete mc;                  // remove message cache storage
2395                  _msg_list.remove(msg);      // remove message from message list
2396                  msg -= 1;                   // next message is where we are at now
2397                  continue;
2398                   }
2399               else {
2400                 error.clear();
2401                  }
2402             }
2403             free(type);
2404           }
2405         }
2406     }
2407    //IBM code for message/partial ^^^^^^^^^^^^^^^^^^^^^^^^^^^
2408
2409     _at_eof.setTrue();
2410     error.clear();
2411 }
2412
2413 void *
2414 RFCMailBox::ThreadNewMailEntry(void * client_data)
2415 {
2416     NewMailData * info = (NewMailData *)client_data;
2417
2418     // We need to wait for the object to become valid. We have
2419     // nothing to do until the mail box is open.
2420     info->object_valid->waitTrue();
2421
2422     // Get the various configuration parameters from .mailrc. The
2423     // RFC_PING_INTERVAL controls how often we look for new mail.
2424     // RFC_CHECK is the number of pings between checks and
2425     // RFC_EXPUNGE is the number of pings between expunges.
2426     //
2427     DtMailEnv error;
2428     DtMail::MailRc * mailrc = info->self->session()->mailRc(error);
2429
2430     // Defaults.
2431     time_t ping = 15;
2432     time_t check_per_ping = 120;
2433     time_t expunge_per_ping = 240;
2434
2435     const char * value = NULL;
2436     mailrc->getValue(error, "RFC_PING_INTERVAL", &value);
2437     if (error.isNotSet()) {
2438         ping = (time_t) strtol(value, NULL, 10);
2439     }
2440     else {
2441         error.clear();
2442     }
2443     if (NULL != value)
2444       free((void*) value);
2445
2446     value = NULL;
2447     mailrc->getValue(error, "RFC_CHECK", &value);
2448     if (error.isNotSet()) {
2449         check_per_ping = (time_t) strtol(value, NULL, 10);
2450     }
2451     else {
2452         error.clear();
2453     }
2454     if (NULL != value)
2455       free((void*) value);
2456
2457     value = NULL;
2458     mailrc->getValue(error, "RFC_EXPUNGE", &value);
2459     if (error.isNotSet()) {
2460         expunge_per_ping = (time_t) strtol(value, NULL, 10);
2461     }
2462     else {
2463         error.clear();
2464     }
2465     if (NULL != value)
2466       free((void*) value);
2467
2468     int pinged = 0;
2469
2470     unsigned int unslept = 0;
2471
2472     // Wait until the mail file is parsed.
2473     //
2474     info->self->_at_eof.waitTrue();
2475
2476     // We loop until the object tries to close, then we exit.
2477     //
2478     while(info->object_valid->state()) {
2479
2480         // The following sequence is a little wierd, but here is why.
2481         // We need to sleep for the ping interval. We can be awaken
2482         // early however if the thread catches a signal. The main
2483         // thread will send a SIGTERM from the mail box destructor
2484         // to wake us up. If the object state is no longer valid, then
2485         // this is the cause, so we need to check it first and exit
2486         // if this is the case.
2487         //
2488         // We can also be awaken by other signals that we don't care
2489         // about. In that case we need to go back through the loop
2490         // again and sleep any unslept time. We can't be sure however
2491         // that this additional sleep won't be awaken before it is
2492         // done so we have to keep looping and checking the object
2493         // state.
2494         //
2495         if (unslept) {
2496             unslept = (unsigned int) ThreadSleep(unslept);
2497             continue; // We got rudely awaken!
2498         }
2499
2500         unslept = (unsigned int) ThreadSleep(ping);
2501         if (!info->object_valid->state()) {
2502             break;
2503         }
2504
2505         if (unslept) {
2506             continue;
2507         }
2508
2509         // We made here, we slept and are well rested, and
2510         // we should have a valid object. Run the events.
2511         //
2512
2513         pinged += 1;
2514
2515         if (check_per_ping && (pinged % check_per_ping) == 0) {
2516 //          error.clear();
2517 //          info->self->CheckPointEvent(error);
2518             info->self->CheckPointEvent();
2519 //          if (error.isSet()) {
2520 //            // MAILBOX COULD NOT BE CHECKPOINTED!!! MUST DO SOMETHING!!!
2521 //          }
2522             continue;
2523         }
2524
2525         if (expunge_per_ping && (pinged % expunge_per_ping) == 0) {
2526             info->self->ExpungeEvent();
2527             continue;
2528         }
2529
2530         info->self->NewMailEvent();
2531     }
2532
2533     // We are responsible for cleaning up the condition variable.
2534     //
2535     delete info->object_valid;
2536     delete info;
2537
2538     ThreadExit(0);
2539
2540     return(NULL);
2541 }
2542
2543
2544 DtMailBoolean
2545 RFCMailBox::PollEntry(void * client_data)
2546 {
2547     RFCMailBox * self = (RFCMailBox *)client_data;
2548
2549     // Get the various configuration parameters from .mailrc. The
2550     // RFC_PING_INTERVAL controls how often we look for new mail.
2551     // RFC_CHECK is the number of pings between checks and
2552     // RFC_EXPUNGE is the number of pings between expunges.
2553     //
2554     DtMailEnv error;
2555     error.clear();
2556     
2557     DtMail::MailRc * mailrc = self->session()->mailRc(error);
2558     error.clear();              // IGNORING ERRORS FROM MAILRC CALL!!
2559
2560     // Defaults.
2561     time_t ping = 60;           // check for new mail every 60 seconds
2562     time_t save_interval = (30 * 60); // autosave every 30 minutes
2563     long minimumIdleTime = 30;  // must have 30 seconds idle time to poll
2564
2565     // Retrieve current setting for how often we are supposed to
2566     // look for new mail (retrieveinterval)
2567     //
2568     const char * value;
2569
2570     value = NULL;
2571     mailrc->getValue(error, "retrieveinterval", &value);
2572     if (error.isNotSet() && value != NULL && *value != '\0')
2573     {
2574         ping = (time_t) strtol(value, NULL, 10);
2575         if (ping <= 0)
2576           ping = 0;
2577         else if (ping < 15)
2578           ping = 15;
2579     }
2580     error.clear();
2581     if (NULL != value) free((void*) value);
2582
2583     // Retrieve current setting for how often we are supposed to
2584     // perform an auto save of the mailbox
2585     //
2586     value = NULL;
2587     mailrc->getValue(error, "dontautosave", &value);
2588     if (error.isSet()) {
2589         error.clear();
2590         if (NULL != value)
2591           free((void*) value);
2592
2593         value = NULL;
2594         mailrc->getValue(error, "saveinterval", &value);
2595         if (error.isNotSet() && value != NULL && *value != '\0') {
2596             save_interval = (time_t) strtol(value, NULL, 10) * 60;
2597         }
2598         error.clear();
2599         if (save_interval < 60)
2600           //save_interval = 60; // The minimum time is every minute
2601           save_interval = 15;   // The minimum time is every minute
2602     }
2603     else save_interval = -1;
2604     if (NULL != value)
2605       free((void*) value);
2606
2607     // Get the current time in seconds, and compute how long it has
2608     // been since the last interactive input (keystroke, buttonpress)
2609     // was done by the user
2610     //
2611     time_t now = time(NULL);
2612     time_t lastInteractive =
2613                 (time_t) self->session()->lastInteractiveEventTime();
2614     time_t interactiveIdleTime = (now - lastInteractive);
2615
2616     // If there has not been at least <<inactivityinterval> seconds of
2617     // interactive inactivity, skip processing until later so as not to
2618     // place the user in the horrible position of being "locked out" for
2619     // the duration of an auto save/and/or/new mail incorporation
2620     //
2621
2622     value = NULL;
2623     mailrc->getValue(error, "inactivityinterval", &value);
2624     if (error.isNotSet() && value != NULL && *value != '\0') {
2625       minimumIdleTime = strtol(value, NULL, 10);
2626       if (minimumIdleTime < 15)
2627           minimumIdleTime = 15;         // at least 15 seconds must go by
2628       else if (minimumIdleTime > 600)
2629           minimumIdleTime = 600;        // but not more than 10 minutes
2630     }
2631     error.clear();
2632     if (NULL != value)
2633       free((void*) value);
2634     
2635     if (interactiveIdleTime < minimumIdleTime)
2636       return(DTM_FALSE);
2637
2638     // See if time's up for doing a new mail incorporate
2639     //
2640     if (ping > 0 && (now - self->_last_poll > ping))
2641     {
2642         self->_last_poll = now;
2643         self->NewMailEvent();
2644     }
2645
2646     // See if time's up for doing an auto-save.
2647     // If time's up, check to see if the flag is clear for doing one.
2648     // Flag is not clear if say, the user is in the middle of composing
2649     // a message...
2650
2651     if (save_interval >= 0 &&
2652         ((now - self->_last_check) > save_interval) &&
2653         (self->session()->getAutoSaveFlag())) {
2654
2655         self->_last_check = (int) now;
2656
2657 //      error.clear();
2658 //      self->CheckPointEvent(error);
2659         self->CheckPointEvent();
2660 //      if (error.isSet()) {
2661 //        // MAILBOX COULD NOT BE CHECKPOINTED!!! MUST DO SOMETHING!!!
2662 //      }
2663     }
2664     return(DTM_TRUE);
2665 }
2666
2667 void
2668 RFCMailBox::NewMailEvent(
2669     const DtMailBoolean already_locked
2670 )
2671 {
2672     DtMailEnv error, error1;
2673     struct stat info;
2674     struct stat tempStatbuf;
2675     struct stat statbuf;
2676     DtMailEventPacket event;
2677     DtMailCallbackOp op;
2678
2679     if (!_object_valid->state()) return;
2680     if (!_mr_allowed) return;
2681
2682     _session->setBusyState(error1, DtMailBusyState_NewMail);
2683
2684     op = retrieveNewMail(error);
2685     if (error.isSet())
2686     {
2687         const char *errmsg = NULL;
2688         errmsg = (const char *) error;
2689
2690         event.key = _key;
2691         event.target = DTM_TARGET_MAILBOX;
2692         event.target_object = this;
2693         event.operation = (void*) op;
2694         if (NULL != errmsg)
2695           event.argument = strdup(errmsg);
2696         else
2697           event.argument = NULL;
2698         event.event_time = time(NULL);
2699
2700         // longUnlock(error);
2701         _session->writeEventData(error, &event, sizeof(event));
2702         _session->setBusyState(error1, DtMailBusyState_NotBusy);
2703         return;
2704     }
2705
2706     if ((SafeFStat(_fd, &tempStatbuf) < 0) ||
2707         (SafeGuaranteedStat(_real_path, &statbuf) == -1))
2708     {
2709         longUnlock(error);
2710         _mail_box_writable = DTM_FALSE;
2711
2712         event.key = _key;
2713         event.target = DTM_TARGET_MAILBOX;
2714         event.target_object = this;
2715         event.operation = (void *)DTMC_ACCESSFAILED;
2716         event.argument = NULL;
2717         event.event_time = time(NULL);
2718
2719         error.setError(DTME_ObjectAccessFailed);
2720         _session->writeEventData(error, &event, sizeof(event));
2721         _session->setBusyState(error1, DtMailBusyState_NotBusy);
2722         return;
2723     }
2724
2725     // See if the inode has changed - if so the file has been
2726     // modified out from under is
2727     //
2728     if (statbuf.st_ino != tempStatbuf.st_ino)
2729     {
2730         longUnlock(error);
2731         _mail_box_writable = DTM_FALSE;
2732
2733         event.key = _key;
2734         event.target = DTM_TARGET_MAILBOX;
2735         event.target_object = this;
2736         event.operation = (void *)DTMC_INODECHANGED;
2737         event.argument = NULL;
2738         event.event_time = time(NULL);
2739
2740         error.setError(DTME_MailboxInodeChanged);
2741         _session->writeEventData(error, &event, sizeof(event));
2742         _session->setBusyState(error1, DtMailBusyState_NotBusy);
2743         return;
2744     }
2745   
2746     // We need to compare the current file size to the last size we
2747     // knew about. If it has changed, we need to do something. There
2748     // are a few possibilities here that we must deal with:
2749     //
2750     // 1. The file size has not changed. Boring. Simply return.
2751     //
2752     // 2. The file size is larger, and the first byte begins with
2753     //    either "From ", "\nFrom ", or "\r\nFrom ". In this case,
2754     //    we need to parse the rest of the file, and notify the client
2755     //    that new mail has arrived.
2756     //
2757     // 3. The file size is either smaller, or it is larger and the first
2758     //    few bytes dont match #2. This is the worse possible case. This
2759     //    means that somebody has modified the object from beneath us.
2760     //    We will turn on our "lost state" mode and refuse to do any
2761     //    further processing. This thread exits immediately.
2762     //
2763     // Even though we get the file size here, we can't use it to set the
2764     // new _file_size, because the file isn't currently locked, so another
2765     // process could be writing to it at this time.  We rely on mapFile()
2766     // to get and set _file_size while it has the file locked.
2767
2768     off_t size = realFileSize(error, &info);
2769     if (error.isSet()) {
2770         longUnlock(error);
2771         _mail_box_writable = DTM_FALSE;
2772
2773         event.key = _key;
2774         event.target = DTM_TARGET_MAILBOX;
2775         event.target_object = this;
2776         event.operation = (void *)DTMC_ACCESSFAILED;
2777         event.argument = NULL;
2778         event.event_time = time(NULL);
2779
2780         _session->writeEventData(error, &event, sizeof(event));
2781         _session->setBusyState(error1, DtMailBusyState_NotBusy);
2782         return;
2783     }
2784
2785     MutexLock lock_object(_obj_mutex);
2786
2787     if (size == _file_size)
2788     {
2789         _session->setBusyState(error1, DtMailBusyState_NotBusy);
2790         if (_hide_access_events!=DTM_TRUE && info.st_atime<=info.st_mtime)
2791           mailboxAccessShow(info.st_mtime, "NewMailEvent: file_size unchanged");
2792
2793         return;
2794     }
2795     else if (size > _file_size)
2796     {
2797         incorporate(error, already_locked);
2798         if (_hide_access_events!=DTM_TRUE && info.st_atime<=info.st_mtime)
2799           mailboxAccessShow(info.st_mtime, "NewMailEvent: file_size grew");
2800     }
2801     else
2802     {
2803         longUnlock(error);
2804         _mail_box_writable = DTM_FALSE;
2805         *_object_valid = -1;
2806
2807         event.key = _key;
2808         event.target = DTM_TARGET_MAILBOX;
2809         event.target_object = this;
2810         event.operation = (void *)DTMC_BADSTATE;
2811         event.argument = NULL;
2812         event.event_time = time(NULL);
2813
2814         _session->writeEventData(error, &event, sizeof(event));
2815
2816         // Debug
2817         char *strbuf = new char[128];
2818         sprintf(strbuf,
2819                 "NewMailEvent: File shrank %ld<%ld: already_locked is %s\n",
2820                 (unsigned long)size, (unsigned long)_file_size,
2821                 already_locked ? "TRUE" : "FALSE");
2822         dumpMaps(strbuf);
2823         delete [] strbuf;
2824     }
2825
2826     // We need to bail right now if we have lost a valid mapping.
2827     //
2828     if (_object_valid->state() < 0) {
2829         lock_object.unlock();
2830         ThreadExit(1);
2831     }
2832
2833     _session->setBusyState(error1, DtMailBusyState_NotBusy);
2834     return;
2835 }
2836
2837 void
2838 //RFCMailBox::CheckPointEvent(DtMailEnv & error)
2839 RFCMailBox::CheckPointEvent()
2840 {
2841   if (!_object_valid->state() || _dirty == 0) {
2842     return;
2843   }
2844   
2845   // Write the mail box out.
2846   //
2847   DtMailEnv error;
2848   error.clear();
2849
2850   // This is INCORRECT usage of the DtMailEnv class.  It is against
2851   // standard policy to not clear the error token before passing
2852   // it to a function.  We are basically breaking the error handling
2853   // model in order to percolate an error in the BE up to the FE.
2854
2855   // setBusyState() does not modify the error token, it simply 
2856   // uses it to report errors back to the user.
2857   _session->setBusyState(error, DtMailBusyState_AutoSave);
2858
2859   writeMailBox(error, _hide_access_events);
2860
2861   if ((DTMailError_t) error ==
2862        DTME_CannotWriteToTemporaryMailboxFile_NoFreeSpaceLeft)
2863   {
2864       DtMailEnv error1;
2865
2866       startAutoSave(error1,DTM_FALSE);
2867       showError((char *) error.getClient());
2868       error.setClient(NULL);
2869       startAutoSave(error1,DTM_TRUE);
2870   }
2871
2872   if (error.isSet()) {
2873     // MAILBOX COULD NOT BE CHECKPOINTED!!! setBusyState must handle error.
2874     error.logError(DTM_TRUE, "RFCMailBox::CheckPointEvent(): Failed to write mailbox: %s", (const char *)error);
2875   }
2876   _session->setBusyState(error, DtMailBusyState_NotBusy);
2877 }
2878
2879 void
2880 RFCMailBox::ExpungeEvent(DtMailBoolean closing)
2881 {
2882     if (!_object_valid->state() && closing == DTM_FALSE) {
2883         return;
2884     }
2885
2886     // We need to get the expiration time for a message.
2887     //
2888     DtMailEnv error;
2889
2890     DtMail::MailRc * mailrc = _session->mailRc(error);
2891
2892     long expire_days;
2893     const char * expire_time = NULL;
2894     mailrc->getValue(error, "DTMAIL_EXPIRE_TIME", &expire_time);
2895     if (error.isNotSet()) {
2896         expire_days = strtol(expire_time, NULL, 10);
2897     }
2898     else {
2899         error.clear();
2900         expire_days = 0;
2901     }
2902     if (NULL != expire_time)
2903       free((void*) expire_time);
2904
2905     if (expire_days == 0 && closing == DTM_FALSE) {
2906         // Only scan on close. No timed delete in effect.
2907         //
2908         return;
2909     }
2910
2911     if (expire_days > 0 && closing == DTM_TRUE) {
2912         // Timed delete is in effect so we don't destroy messages on
2913         // close.
2914         return;
2915     }
2916
2917     time_t expire_secs = (time_t) expire_days * (24 * 3600);
2918     time_t now = time(NULL);
2919
2920     for (int msg = 0; msg < _msg_list.length(); msg++) {
2921         MessageCache * mc = _msg_list[msg];
2922         if (mc->delete_pending == DTM_TRUE) {
2923             // Can only delete it so many times!
2924             //
2925             continue;
2926         }
2927
2928         DtMail::Envelope * env = mc->message->getEnvelope(error);
2929
2930         DtMailValueSeq value;
2931         env->getHeader(error, RFCDeleteHeader, DTM_FALSE, value);
2932         if (!error.isSet()) {
2933             time_t deleted;
2934             deleted = (time_t) strtol(*(value[0]), NULL, 16);
2935             if ((deleted + expire_secs) < now) {
2936                 mc->delete_pending = DTM_TRUE;
2937                 _dirty += 1;
2938
2939                 // We need to tell our client this message is really
2940                 // gone. Of course, if they are closing, why bother
2941                 // them.
2942                 //
2943                 if (closing == DTM_FALSE) {
2944                     DtMailEventPacket event;
2945                     event.key = _key;
2946                     event.target = DTM_TARGET_MAILBOX;
2947                     event.target_object = this;
2948                     event.operation = (void *)DTMC_DELETEMSG;
2949                     event.argument = mc;
2950                     event.event_time = time(NULL);
2951                     _session->writeEventData(error, &event, sizeof(event));
2952                 }
2953             }
2954         }
2955         else {
2956             error.clear();
2957         }
2958     }
2959 }
2960
2961 // Function: RFCMailBox::createTemporaryMailboxFile
2962 // Description:
2963 //  Given the name for a temporary mailbox file, create a proper
2964 //  temporary mailbox file and return a file descriptor opened on
2965 //  the newly created file
2966 // Method:
2967 //   . obtain information on current mailbox file
2968 //   . create the new temporary file
2969 //   . set the permissions, owner and group of the newly created file
2970 //     to match those of the current mailbox
2971 // Arguments:
2972 //  DtMailEnv & error - standard error structure used by caller
2973 //  tmp_name    -- -> name for temporary file
2974 // Outputs:
2975 //  error.isset() will indicate if there were any errors encountered, in which
2976 //  case the temporary file has not been created.
2977 // Returns:
2978 //  int file descriptor opened on the newly created temporary mailbox file
2979 //  if no error encountered.
2980 // 
2981 int
2982 RFCMailBox::createTemporaryMailboxFile(DtMailEnv & error, char *tmp_name)
2983 {
2984   int fd;
2985   int return_status;
2986   struct stat info;
2987   if (SafeFStat(_fd, &info) < 0) {
2988     int errno2 = errno;
2989     if (_errorLogging)
2990       error.logError(DTM_FALSE,
2991                    "createTemporaryMailboxFile(): fstat(%d) failed errno=%d\n",
2992                    _fd, errno);
2993     error.vSetError(DTME_CannotObtainInformationOnOpenMailboxFile,
2994                     DTM_FALSE, NULL, _real_path, error.errnoMessage(errno2));
2995     return(-1);
2996   }
2997
2998   PRIV_ENABLED(fd,SafeOpen(tmp_name, O_RDWR | O_CREAT | O_TRUNC, info.st_mode));
2999   if (fd < 0) {
3000     int errno2 = errno;
3001     switch (errno2) {
3002     case EACCES:
3003       error.setError(DTME_CannotCreateTemporaryMailboxFile_NoPermission);
3004       break;
3005       
3006     case EISDIR:
3007       error.setError(DTME_CannotCreateTemporaryMailboxFile_IsDirectory);
3008       break;
3009       
3010     case ENOENT:
3011       error.setError(DTME_CannotCreateTemporaryMailboxFile_NoSuchFile);
3012       break;
3013
3014 #if defined(__osf__) || defined(CSRG_BASED)
3015     case ENOTDIR:
3016 #else
3017     case ENOLINK:
3018 #endif
3019       error.setError(DTME_CannotCreateTemporaryMailboxFile_RemoteAccessLost);
3020       break;
3021       
3022     default:
3023       error.vSetError(DTME_CannotCreateTemporaryMailboxFile,
3024                       DTM_FALSE, NULL, tmp_name, error.errnoMessage(errno2));
3025     }
3026     return(-1);
3027   }
3028
3029   PRIV_ENABLED_OPTIONAL(return_status,SafeFChmod(fd, info.st_mode & 07777));
3030   if (return_status == -1) {
3031     int errno2 = errno;
3032     (void) SafeClose(fd);
3033     PRIV_ENABLED(return_status,SafeUnlink(tmp_name));
3034     error.vSetError(DTME_CannotSetPermissionsOfTemporaryMailboxFile,
3035                     DTM_FALSE, NULL, tmp_name, info.st_mode & 07777,
3036                     error.errnoMessage(errno2));
3037     return(-1);
3038   }
3039
3040   PRIV_ENABLED(return_status, SafeFchown(fd, info.st_uid, (unsigned int) -1));
3041 #if 0
3042   // bug 1216914 - dtmail should be able to auto-save a mailbox which is not
3043   // opened [Read Only] if the user has write access to the original mailbox,
3044   // then the user should be able to overwrite the mailbox even if the owner
3045   // gets changes. A mailbox is only opened read-write if the "access()"
3046   // system call indicates the user has write permissions.
3047   //
3048   if (return_status == -1) {
3049     int errno2 = errno;
3050     (void) SafeClose(fd);
3051     PRIV_ENABLED(return_status,SafeUnlink(tmp_name));
3052     error.vSetError(DTME_CannotSetOwnerOfTemporaryMailboxFile,
3053                     DTM_FALSE, NULL, tmp_name, info.st_uid,
3054                     error.errnoMessage(errno2));
3055     return(-1);
3056   }
3057 #endif
3058
3059   PRIV_ENABLED(return_status, SafeFchown(fd, (unsigned int) -1, info.st_gid));
3060   return(fd);
3061 }
3062
3063 // Function: RFCMailBox::writeMailBox - create new copy of complete mailbox
3064 // Description:
3065 //   writeMailBox causes a complete copy of the current mailbox to be written
3066 //   out to permanent storage. This is done by creating a temporary file, in
3067 //   the same location the permanent file resides, and writing the new mailbox
3068 //   contents to it. If this is successful, the temporary file is then renamed
3069 //   over the old permanent file, taking its place. In this way, if there is
3070 //   an error creating the new file, the old file still remains intact.
3071 // Method:
3072 //   . make sure the current mailbox is writeable
3073 //   . lock current mailbox
3074 //   . check for new mail and incorporate if necessary
3075 //   . cause the OS to begin mapping as much of the current mailbox into memory
3076 //      as it can so it is available as soon as possible to be written
3077 //   . create temporary file to hold new mailbox contents
3078 //   . create message list fixing the location of all non-deleted messages
3079 //      (dirty messages will be allocated temporary storage and created)
3080 //   . build a vectored write array based upon the message list
3081 //   . cause the new mailbox contents to be written via one call to SafeWritev
3082 //   . delete all "deleted" messages from the message list
3083 //   . map the new mailbox file into memory
3084 //   . revise all message pointers based upon new mailbox contents
3085 //   . remove the mappings and unmap all previous regions used
3086 //   . add the one single new mailbox region to the mappings list
3087 //   . transfer any lock on the old mailbox file to the new mailbox file
3088 //   . rename the new mailbox file to the correct permanent path name
3089 //   . set the modification and access times of the mailbox file properly
3090 // Arguments:
3091 //  DtMailEnv & error - standard error structure used by caller
3092 // Outputs:
3093 //  error.isset() will indicate if there were any errors encountered, in which
3094 //  case the current contents of the mailbox have not been written.
3095 //
3096 //  At some point in the call chain, someone must check to see if this is
3097 //  a FATAL ERROR (e.g. error.isFatal) and if so, display the returned
3098 //  message and then immediately exit, as this means the mailbox could not
3099 //  be written and the internal mailbox state is hopelessly munged..
3100 // Returns:
3101 //  <none>
3102 // 
3103 void
3104 RFCMailBox::writeMailBox(DtMailEnv &error, DtMailBoolean hide_access)
3105 {
3106   static char *pname = "writeMailBox";
3107   char *fsname=NULL;
3108   DtMailEnv error2;     // When we need to preserve error during error cleanup
3109   int return_status;    // for handling priv/unpriv operations
3110 #if defined(DEBUG_RFCMailBox)
3111   char *pname = "writeMailBox";
3112 #endif
3113
3114   MutexLock lock_map(_map_lock);
3115
3116   error.clear();
3117   if (_mail_box_writable == DTM_FALSE) {
3118     return;
3119   }
3120
3121   // Need to lock the file while we do this.
3122   //
3123   DEBUG_PRINTF( ("%s:  locking mailbox\n", pname) );
3124   lockFile(error);
3125   if (error.isSet()) {
3126     DtMailEnv   tmp_error;
3127     unlockFile(tmp_error, _fd);
3128     return;
3129   }
3130
3131   // Need to deal with any potentially new mail that
3132   // has arrived and we don't know about.
3133   //
3134   if (_mra_server == NULL) checkForMail(error, DTM_TRUE);
3135   if (error.isSet()) {
3136     DEBUG_PRINTF( ("%s:  locking mailbox\n", pname) );
3137     unlockFile(error2, _fd);
3138     return;
3139   }
3140
3141   // Create the new temporary file to hold the mailbox contents
3142   //
3143   static char tmp_name[MAXPATHLEN+1];
3144   sprintf(tmp_name, "%s.tmp.%08lx", _real_path, (long)time(NULL));
3145   assert(strlen(tmp_name)<sizeof(tmp_name));
3146
3147   int fd = createTemporaryMailboxFile(error, tmp_name);
3148   if (error.isSet()) {
3149     DEBUG_PRINTF( ("%s:  locking mailbox\n", pname) );
3150     unlockFile(error2, _fd);
3151     return;
3152   }
3153   assert(fd != -1);
3154   
3155   // Got the new mailbox file all set up to be our friend.
3156   // zip through current message structure fixing the location of all
3157   // non-deleted messages in preparation of writing them all out to
3158   // the temporary file just opened. The act of "fixing" the location of
3159   // a message will cause dirty messages to be constructed in temporary
3160   // allocated areas which must later be deallocated. This deallocation
3161   // is done either by calling adjustMessageLocation() to make the location
3162   // of the message permanent, or by calling unfixMessageLocation) to
3163   // abandon the new message (e.g. if new mailbox cannot be created).
3164   //
3165   int deletesPending = 0;       // count # of delete_pending messages scanned
3166   int msg = 0;
3167
3168   struct tempMsgList {
3169     MessageCache *tmlMc;        // -> mc of this message
3170     long tmlRealOffset;         // byte offset from 0 where message is written
3171     long tmlBodyOffset;         // byte offset from 0 of body parts of message
3172     char *tmlHeaderStart;       // -> start of headers for this message
3173     long tmlHeaderLen;          // length of headers
3174     char *tmlBodyStart;         // -> start of bodies for this message
3175     long tmlBodyLen;            // length of bodies
3176     int tmlTemporary;           // ==0:permanent, !=0:temporary(must move)
3177   };
3178   
3179   long tmlTotalSize = _msg_list.length()*4;
3180   tempMsgList *const tmlFirst = (tempMsgList*)
3181                         malloc((size_t) (sizeof(tempMsgList)*tmlTotalSize));
3182   tempMsgList *tmlLast = tmlFirst;
3183
3184   for (msg = 0; msg < _msg_list.length(); msg++) {
3185     MessageCache *mc = _msg_list[msg];
3186     if (mc->delete_pending == DTM_TRUE) {       // ignore deleted messages
3187       deletesPending++;                         // but remember if any seen
3188       continue;
3189     }
3190     tmlLast->tmlMc = mc;
3191     mc->message->fixMessageLocation(&tmlLast->tmlHeaderStart, tmlLast->tmlHeaderLen,
3192                                     &tmlLast->tmlBodyStart, tmlLast->tmlBodyLen,
3193                                     tmlLast->tmlTemporary, tmlLast->tmlBodyOffset);
3194 #if 0
3195     fprintf(stdout, "msg %03d @ %08lx %08lx/%06d %08lx/%06d %04d %s\n",
3196            msg, mc, tmlLast->tmlHeaderStart, tmlLast->tmlHeaderLen,
3197            tmlLast->tmlBodyStart, tmlLast->tmlBodyLen,
3198            tmlLast->tmlBodyOffset, tmlLast->tmlTemporary ? "T" : "P");
3199     HexDump(stdout, "header", (unsigned char *)tmlLast->tmlHeaderStart, tmlLast->tmlHeaderLen, 10);
3200     HexDump(stdout, "body", (unsigned char *)tmlLast->tmlBodyStart, tmlLast->tmlBodyLen, 10);
3201     fflush(stdout);
3202 #endif
3203     assert(tmlLast->tmlHeaderStart != NULL);
3204     assert(tmlLast->tmlHeaderLen);
3205     tmlLast->tmlRealOffset = -1;
3206     tmlLast++;
3207   }
3208   assert((tmlLast-tmlFirst) < tmlTotalSize);
3209
3210   // we can now allocate the vectored write array and fill it according
3211   // to the data stored in the message list we just created
3212   //
3213   long iovSize = ((tmlLast-tmlFirst)+2)*3;
3214   iovec *const iovFirst = (iovec *) malloc((size_t) (sizeof(iovec)*iovSize));
3215   iovec *iovLast = iovFirst;
3216   iovec *iovPrev = (iovec *)0;
3217   long iovCurrentOffset = 0;
3218
3219   for (tempMsgList *tmlNdx = tmlFirst; tmlNdx < tmlLast; tmlNdx++) {
3220     // if this message happens to start on the first byte following the
3221     // last byte of the previous message, combine into a single vector,
3222     // else add this as another vector in the vector list
3223     //
3224     tmlNdx->tmlRealOffset = iovCurrentOffset;
3225     if ( (iovPrev != (iovec *)0) && (!tmlNdx->tmlTemporary) &&
3226          ((char *)((size_t) iovPrev->iov_base+iovPrev->iov_len) ==
3227           tmlNdx->tmlHeaderStart)
3228        ) {
3229       iovPrev->iov_len += (int)(tmlNdx->tmlHeaderLen+tmlNdx->tmlBodyLen);
3230       iovCurrentOffset += (int)(tmlNdx->tmlHeaderLen+tmlNdx->tmlBodyLen);
3231     }
3232     else if (!tmlNdx->tmlTemporary) {
3233       iovLast->iov_base = (caddr_t)tmlNdx->tmlHeaderStart;
3234       iovLast->iov_len = (int)(tmlNdx->tmlHeaderLen+tmlNdx->tmlBodyLen);
3235       iovCurrentOffset += iovLast->iov_len;
3236       iovPrev = iovLast++;
3237     }
3238     else {
3239       // Message is temporary - headers and bodies are in different areas,
3240       // and the headers are in a "temporary" area
3241       //
3242       iovLast->iov_base = (caddr_t)tmlNdx->tmlHeaderStart;
3243       iovLast->iov_len = (int)tmlNdx->tmlHeaderLen;
3244       iovCurrentOffset += iovLast->iov_len;
3245       iovPrev = iovLast++;
3246       
3247       // Write out bodies only if the length is non-zero, otherwise,
3248       // optimize out the inclusion of a zero length write from the vector
3249       //
3250       if (tmlNdx->tmlBodyLen > 0) {
3251         iovLast->iov_base = (caddr_t)tmlNdx->tmlBodyStart;
3252         iovLast->iov_len = (int)tmlNdx->tmlBodyLen;
3253         iovCurrentOffset += iovLast->iov_len;
3254         iovPrev = iovLast++;
3255       }
3256     }
3257     // The last two bytes of the message must be \n\n
3258     // If not then this must be forced
3259     // Obtain pointer tp to the last byte of the current message
3260     // Given this pointer we now have:
3261     //   tp[-1] -- next to last byte in current message
3262     //   tp[ 0] -- last byte in current message
3263     //   tp[+1] -- first byte of next message (if any)
3264     // There must always be two \n characters between each message. If not,
3265     // we must insert sufficient \n characters into the message stream to
3266     // accomplish this. We want to avoid plopping these in, however, as each
3267     // one will add an extra 1- or 2-byte vector into the vectored write array,
3268     // which will affect the throughput of the overall write.
3269     // Irregardless of whether the current message is temporary, we check to see if the next
3270     // byte or two (as necessary) is in a mapped region; if so, we can then
3271     // peek ahead to see if there are the additional \ns we need.
3272     // If all this fails, we punt and put in a small 1- or 2-byte write
3273     // into the vector.
3274     //
3275     // -> last byte
3276     char *tp = (char *) ((size_t) iovPrev->iov_base + (iovPrev->iov_len - 1));
3277
3278     if (*tp == '\n') {
3279       if ( ((addressIsMapped(tp+1) == DTM_TRUE) )
3280            && (*(tp+1) == '\n') ) {
3281         iovPrev->iov_len++;
3282         iovCurrentOffset++;
3283         continue;
3284       }
3285       else {
3286         iovLast->iov_base = (caddr_t)"\n";              // add \n
3287         iovLast->iov_len = 1;
3288         iovCurrentOffset++;
3289         iovPrev = iovLast++;
3290       }
3291     }
3292     else {
3293       if ( (( (addressIsMapped(tp+1) == DTM_TRUE)
3294                                       && (addressIsMapped(tp+2) == DTM_TRUE)))
3295            && (*(tp+1) == '\n') && (*(tp+2) == '\n')) {
3296         iovPrev->iov_len+= 2;
3297         iovCurrentOffset+= 2;
3298         continue;
3299       }
3300       else {
3301         iovLast->iov_base = (caddr_t)"\n\n";            // add \n\n
3302         iovLast->iov_len = 2;
3303         iovCurrentOffset += 2;
3304         iovPrev = iovLast++;
3305       }
3306     }
3307   }
3308
3309   assert((iovLast-iovFirst) < iovSize);
3310
3311   // All of the messages are properly accounted for in the write vector;
3312   // cause the damage to be done by calling SafeWritev. After it returns,
3313   // Make absolutely sure that all of the mailbox data has made it to
3314   // the final destination, especially if the mailbox is not local -
3315   // this way if we run out of disk space or some other such problem,
3316   // it is caught here and now.
3317   //
3318   unsigned long bytesWritten = SafeWritev(fd, iovFirst, iovLast-iovFirst);
3319   if (bytesWritten == (unsigned long)-1 || fsync(fd) == -1) {
3320     int errno2 = errno;
3321     for (tempMsgList *tml = tmlFirst; tml < tmlLast; tml++) {
3322       MessageCache *mc = tml->tmlMc;
3323       assert(mc != NULL);
3324       mc->message->unfixMessageLocation(tml->tmlHeaderStart, tml->tmlTemporary);
3325     }
3326     FileSystemSpace(tmp_name, 0,&fsname);
3327     (void) SafeClose(fd);
3328     DEBUG_PRINTF( ("%s:  locking mailbox\n", pname) );
3329     unlockFile(error2, _fd);
3330     PRIV_ENABLED(return_status,SafeUnlink(tmp_name));
3331     free (tmlFirst);
3332     free (iovFirst);
3333     switch (errno2) {
3334     case EFBIG:
3335       error.setError(
3336         DTME_CannotWriteToTemporaryMailboxFile_ProcessLimitsExceeded);
3337       break;
3338
3339 #if defined(__osf__) || defined(CSRG_BASED)
3340     case ENOTDIR:
3341 #else
3342     case ENOLINK:
3343 #endif
3344       error.setError(DTME_CannotWriteToTemporaryMailboxFile_RemoteAccessLost);
3345       break;
3346
3347     case ENOSPC:
3348       error.setClient(fsname);
3349       error.setError(DTME_CannotWriteToTemporaryMailboxFile_NoFreeSpaceLeft);
3350       break;
3351
3352     default:
3353       error.vSetError(DTME_CannotWriteToTemporaryMailboxFile,
3354                       DTM_FALSE, NULL, tmp_name, error.errnoMessage(errno2));
3355     }
3356     return;
3357   }
3358
3359   // The current contents of the mailbox have successfully been written
3360   // to the temporary file. Cause the new mailbox file to be mapped
3361   // into memory.
3362   //
3363   MapRegion * map = mapNewRegion(error, fd, bytesWritten);
3364   if (error.isSet()) {
3365     for (tempMsgList *tml = tmlFirst; tml < tmlLast; tml++) {
3366       MessageCache *mc = tml->tmlMc;
3367       assert(mc != NULL);
3368       mc->message->unfixMessageLocation(tml->tmlHeaderStart, tml->tmlTemporary);
3369     }
3370     (void) SafeClose(fd);
3371     DEBUG_PRINTF( ("%s:  locking mailbox\n", pname) );
3372     unlockFile(error2, _fd);
3373     PRIV_ENABLED(return_status,SafeUnlink(tmp_name));
3374     free (tmlFirst);
3375     free (iovFirst);
3376     return;
3377   }
3378
3379   // POINT OF NO RETURN -- NEW MAILBOX MUST BE SUCCESSFULLY LINKED UP WITH
3380   // BECAUSE THE MACHINATIONS AND POINTER MUNGING DONE BELOW CANNOT BE UNDONE
3381   //
3382
3383   // Flush all deleted messages (if any were previously detected)
3384   //
3385   if (deletesPending)
3386     for (msg = 0; msg < _msg_list.length(); msg++) {
3387       MessageCache * mc = _msg_list[msg];
3388       if (mc->delete_pending == DTM_TRUE) {
3389         delete mc->message;             // remove message storage
3390         delete mc;                      // remove message cache storage
3391         _msg_list.remove(msg);          // remove message from message list
3392         msg -= 1;                       // next message is where we are at now
3393       }
3394     }
3395
3396   // spin through all "written messages" and fixup their pointers so they
3397   // point into the new region
3398   //
3399
3400   // For this occasion advise the OS that we will be doing sequential access
3401   //
3402   alterPageMappingAdvice(map);
3403
3404   for (tempMsgList *tml = tmlFirst; tml < tmlLast; tml++) {
3405     MessageCache *mc = tml->tmlMc;
3406     assert(mc != NULL);
3407     assert(mc->delete_pending == DTM_FALSE);
3408     mc->message->adjustMessageLocation(tml->tmlHeaderStart, map->file_region+tml->tmlRealOffset, tml->tmlHeaderLen+tml->tmlBodyLen, tml->tmlTemporary, tml->tmlBodyOffset);
3409   }
3410
3411   // Loop through the current mappings, and unmap each.
3412   // Then make the new single large map the only mapping.
3413   //
3414   while(_mappings.length()) {
3415       MapRegion * c_map = _mappings[0];
3416       munmap(c_map->map_region, (size_t) c_map->map_size);
3417       delete c_map;
3418       _mappings.remove(0);
3419   }
3420   _mappings.append(map);
3421
3422   // fix for cmvc defect 7912 - Queued mail lost upon closing dtmail
3423   // If we are using the .lock protocol, we are locking on a file name
3424   // basis, and therefore can rename the new mailbox over the old mailbox
3425   // without a worry about locks; however, if we are using another type
3426   // of locking which locks on a *file* basis, then as soon as the rename
3427   // is done if there is not a lock on the file a process like sendmail
3428   // could come in and complicate matters. After being properly locked,
3429   // rename the new mailbox file over the old mailbox file, and then
3430   // remove the old lock if applicable.
3431   //
3432   lockNewMailboxFile(fd);
3433   PRIV_ENABLED(return_status,SafeRename(tmp_name, _real_path));
3434   if (return_status == -1) {
3435     // the rename failed -- we are in a world of hurt now.
3436     // We have successfully written the new mailbox out, unmapped the
3437     // old file, mapped in the new file, and bashed all of the various
3438     // pointers to point to the new mailbox; however, we cannot rename
3439     // the new mailbox over the old mailbox. We cannot continue, so return
3440     // this as a fatal error so that the caller can exit properly.
3441     //
3442     error.vSetError(DTME_CannotRenameNewMailboxFileOverOld,
3443                     DTM_TRUE, NULL, _real_path, tmp_name,
3444                     error.errnoMessage());
3445     (void) SafeClose(fd);
3446     (void) SafeClose(_fd);
3447     return;     // no complete cleanup necessary as we should exit real fast...
3448   }
3449
3450   assert(map->file_size == bytesWritten);
3451
3452   DtMailBoolean file_grew;
3453   if (map->file_size > _file_size)
3454     file_grew = DTM_TRUE;
3455   else 
3456     file_grew = DTM_FALSE;
3457
3458   _file_size = map->file_size;
3459
3460   struct stat info;
3461   if (SafeFStat(fd, &info) < 0)
3462   {
3463       if (_errorLogging)
3464           error.logError(DTM_TRUE,
3465                 "%s: fstat(%d/%s) failed errno=%d\n",
3466                 pname, _fd, tmp_name, errno);
3467       error.setError(DTME_ObjectCreationFailed, DTM_TRUE, (Tt_message) 0);
3468   }
3469   else
3470   {
3471       if (info.st_size != _file_size)
3472       {
3473           error.logError(DTM_TRUE,
3474                      "%s: new mailbox size not consistent with expected size = %d\nfstat: st_ino = %d, st_dev = %d, st_nlink = %d, st_size = %ld\n",
3475                      pname, bytesWritten,
3476                      info.st_ino, info.st_dev, info.st_nlink, info.st_size);
3477           error.setError(DTME_ObjectCreationFailed, DTM_TRUE, (Tt_message) 0);
3478       }
3479
3480       if (hide_access == DTM_FALSE && info.st_atime <= info.st_mtime)
3481         mailboxAccessShow(info.st_mtime, "writeMailBox");
3482   }
3483
3484   // order of unlocks is important here:
3485   // unlockOldMailboxFile checks the state of _long_lock_active
3486   // but does not alter it, whereas unlockFile does.
3487   //
3488   unlockOldMailboxFile(_fd);            // unlock old mailbox file first
3489   DEBUG_PRINTF( ("%s:  locking mailbox\n", pname) );
3490   unlockFile(error2,fd);                // then unlock new mailbox file 
3491   if (SafeClose(_fd) < 0) {
3492       // should do something with the error here.
3493   }
3494
3495   _fd = fd;                             // new mailbox file now current one
3496   _dirty = 0;                           // mark mailbox as no longer dirty.
3497
3498   free (tmlFirst);
3499   free (iovFirst);
3500
3501   return;
3502 }
3503
3504 void
3505 RFCMailBox::incorporate(DtMailEnv & error, const DtMailBoolean already_locked)
3506 {
3507     DtMailEventPacket event;
3508
3509     if (already_locked == DTM_FALSE) {
3510         MutexLock lock_map(_map_lock);
3511     }
3512
3513     int slot = mapFile(error, already_locked);
3514     if (error.isSet()) {
3515         if (DTME_ObjectInvalid == (DTMailError_t) error)
3516         {
3517             longUnlock(error);
3518             _mail_box_writable = DTM_FALSE;
3519
3520             event.key = _key;
3521             event.target = DTM_TARGET_MAILBOX;
3522             event.target_object = this;
3523             event.operation = (void *)DTMC_INODECHANGED;
3524             event.argument = NULL;
3525             event.event_time = time(NULL);
3526             _session->writeEventData(error, &event, sizeof(event));
3527         }
3528         else if (DTME_ObjectAccessFailed == (DTMailError_t) error)
3529         {
3530             longUnlock(error);
3531             _mail_box_writable = DTM_FALSE;
3532
3533             event.key = _key;
3534             event.target = DTM_TARGET_MAILBOX;
3535             event.target_object = this;
3536             event.operation = (void *)DTMC_ACCESSFAILED;
3537             event.argument = NULL;
3538             event.event_time = time(NULL);
3539             _session->writeEventData(error, &event, sizeof(event));
3540         }
3541         return;
3542     }
3543
3544     error.clear();
3545
3546     MapRegion * map = _mappings[slot];
3547     const char * buf = map->file_region;
3548
3549     // Let's accept any white space as okay in front of the
3550     // "From ".
3551     //
3552     for (; buf < (map->map_region + map->map_size) && 
3553          isspace(*buf); buf++) {
3554         continue;
3555     }
3556     
3557     int num_tries = 0;
3558     DtMailBoolean done = DTM_FALSE;
3559
3560     while (num_tries < 5 && !done) {
3561         if (strncmp(buf, "From ", 5) == 0) {
3562             DtMailMessageHandle last = NULL;
3563
3564             // We can be here via either of two scenarios:
3565             // 1- Aside from incorporating new mail, there is no other 
3566             // activity on the mail box.  This is the "normal" case.
3567             // already_locked is its default value (FALSE).
3568             //
3569             // 2- We are here because we were doing a Destroy Deleted Messages
3570             // when we noticed new mail that had to be first incorporated.
3571             // already_locked is TRUE (i.e., the Expunge method has lock...)
3572             // We cannot place the handle of the last entity in the array in
3573             // the stream because the entity may be marked for delete, and
3574             // the Destroy Deleted Messages operation will expunge the entity
3575             // shortly after it has been placed on the stream for the FE.
3576
3577             // In both cases, we want to get the last UNDELETED message and
3578             // place it on the stream for the FE.  Undeleted entities will
3579             // be valid even after a DDM while deleted entities will be invalid
3580             // after a DMM. 
3581
3582             // Place the handle of the last entity in the array in the
3583             // callback stream.  The FE will get it and can invoke 
3584             // getNextMessageSummary() on the mailbox using the handle to 
3585             // retrieve the new messages.
3586
3587             if (_msg_list.length() > 0) {
3588                 
3589                 if (already_locked) {
3590                     // already_locked is TRUE only in one case:
3591                     // We were trying to destroy deleted messages when we 
3592                     // noticed new mail has come in and we need to incorporate
3593                     // it.
3594                     // This will return the index of the last undeleted 
3595                     // message. That entity, we are assured, will remain 
3596                     // valid after a DMM that may appear just before the event
3597                     // is received by the FE's callback method.
3598                     
3599                     int index = prevNotDel(_msg_list.length() - 1);
3600                     last = _msg_list[index];
3601                 } 
3602                 else {
3603                     // already_locked is FALSE
3604                     // Normal case.
3605                     // No possiblity of entities in _msg_list being expunged.
3606                     // Give the current last one to the FE and let it retrieve
3607                     // new messages via the getNext() method on that entity.
3608                     last = _msg_list[_msg_list.length() - 1];
3609                 }
3610             }
3611             
3612             // Does locking...
3613
3614             parseFile(error, slot);
3615             
3616             event.key = _key;
3617             event.target = DTM_TARGET_MAILBOX;
3618             event.target_object = this;
3619             event.operation = (void *)DTMC_NEWMAIL;
3620             event.argument = last;
3621             event.event_time = time(NULL);
3622             _session->writeEventData(error, &event, sizeof(event));
3623             done = DTM_TRUE;
3624         }
3625         else {
3626             longUnlock(error);
3627             _mail_box_writable = DTM_FALSE;
3628             *_object_valid = -1;
3629
3630             event.key = _key;
3631             event.target = DTM_TARGET_MAILBOX;
3632             event.target_object = this;
3633             event.operation = (void *)DTMC_BADSTATE;
3634             event.argument = NULL;
3635             event.event_time = time(NULL);
3636
3637             error.setError(DTME_ObjectInvalid);
3638             _session->writeEventData(error, &event, sizeof(event));
3639
3640             // Debug
3641             char *strbuf = new char[256];
3642             if (already_locked) {
3643             
3644                 sprintf(strbuf, 
3645                     "Attempt #%d: RFCMailBox::incorporate(): No From: buf=<%.10s>, already_locked is TRUE\n", num_tries + 1, buf);
3646             }
3647             else {
3648                 sprintf(strbuf, 
3649                     "Attempt #%d: RFCMailBox::incorporate(): No From: buf=<%.10s>, already_locked is FALSE\n", num_tries + 1, buf);
3650             }
3651             dumpMaps(strbuf);
3652             delete [] strbuf;
3653
3654             if (num_tries > 0)
3655                 sleep(num_tries);
3656             num_tries++;
3657         }
3658     }
3659 }
3660
3661 char *
3662 RFCMailBox::generateLockFileName(void)
3663 {
3664   char *s;
3665   char *lock_path = new char[MAXPATHLEN+20];
3666
3667   assert(_real_path != NULL);
3668   (void) sprintf(lock_path, "%s.lock", _real_path);
3669   s = strdup(lock_path);
3670   delete [] lock_path;
3671   return s;
3672 }
3673
3674 // Function: RFCMailBox::generateUniqueLockId - create unique ID for this mailbox lock files
3675 // Description:
3676 //   generateUniqueLockId creates a unique ID which is written into .lock files and
3677 //   can then be checked to make sure that the lock file has not been compromised by
3678 //   another process.
3679 // Method:
3680 //   The ID generated consists of three parts:
3681 //     <process id/%08d><current time in seconds/%08d><hardware serial number/%d>
3682 //   Thus, a "typical" id would look like this:
3683 //      000018577971028681915751068
3684 //   Which breaks down as:
3685 //      00001857 797102868 1915751068
3686 // Arguments:
3687 //  <none>
3688 // Outputs:
3689 //  <none>
3690 // Returns:
3691 //  char * -> allocated memory in which unique id has been created
3692 // 
3693 char *
3694 RFCMailBox::generateUniqueLockId(void)
3695 {
3696   char theId[128];
3697   char hwserialbuf[64];
3698
3699 #if !defined(__aix) && !defined(__hpux) && !defined(__osf__) && !defined(linux) && !defined(CSRG_BASED)
3700   if (sysinfo(SI_HW_SERIAL, (char *)hwserialbuf, sizeof(hwserialbuf)-1) == -1)
3701 #endif
3702     strcpy(hwserialbuf, "dtmail");
3703   (void) sprintf(theId, "%08ld%08ld%s\0", (long)getpid(), (long)time((time_t *)0), hwserialbuf);
3704   assert(strlen(theId)<sizeof(theId));
3705   return(strdup(theId));
3706 }
3707
3708 void
3709 RFCMailBox::checkLockFileOwnership(DtMailEnv & error)
3710 {
3711   char *pname = "checkLockFileOwnership";
3712
3713   assert(_lockFileName != NULL);
3714   struct stat info;
3715   if (SafeStat(_lockFileName, &info) < 0) {
3716     if (_errorLogging)
3717       error.logError(DTM_FALSE,
3718          "%s: lock cannot be stat()ed: %s, errno = %d\n",
3719          pname, _lockFileName, errno);
3720     error.setError(DTME_ObjectInvalid);
3721     return;
3722   }
3723
3724   int lock_fd;
3725   int flags;
3726
3727 #ifndef O_RSYNC
3728   flags =  O_RDONLY | O_SYNC;
3729 #else
3730   flags =  O_RDONLY | O_RSYNC | O_SYNC;
3731 #endif /* O_RSYNC */
3732
3733 #ifdef MAILGROUP_REQUIRED
3734   PRIV_ENABLED_OPEN(_lockFileName, lock_fd, SafeOpen(_lockFileName, flags, 0));
3735 #else
3736   PRIV_ENABLED_OPTIONAL(lock_fd, SafeOpen(_lockFileName, flags, 0));
3737 #endif
3738
3739   if (lock_fd == -1) {
3740     if (_errorLogging)
3741       error.logError(
3742           DTM_FALSE,
3743          "%s: lock cannot be open()ed: %s, errno = %d\n",
3744          pname, _lockFileName, errno);
3745     error.setError(DTME_ObjectInvalid);
3746     return;
3747   }
3748
3749   char lockBuf[MAXPATHLEN];
3750   int status = SafeRead(lock_fd, lockBuf, sizeof(lockBuf)-1);
3751   if (status <= 0) {
3752     if (_errorLogging)
3753       error.logError(
3754           DTM_FALSE,
3755           "%s: lock cannot be read: %s, errno = %d\n",
3756           pname, _lockFileName, errno);
3757     (void) SafeClose(lock_fd);
3758     error.setError(DTME_ObjectInvalid);
3759     return;
3760   }
3761
3762   if ( (status < _uniqueLockIdLength+2)
3763        || (strncmp(lockBuf+2, _uniqueLockId, _uniqueLockIdLength) != 0) ) {
3764     error.logError(
3765         DTM_FALSE,
3766         "%s: dtmail lock file stolen by another process\n", pname);
3767     if (_errorLogging)
3768       HexDump(
3769                 (FILE*) NULL,
3770                 "lock file stolen - lock file contents",
3771                 (unsigned char *)lockBuf, status, 0);
3772     (void) SafeClose(lock_fd);
3773     error.setError(DTME_ObjectInvalid);
3774     return;
3775   }
3776   (void) SafeClose(lock_fd);
3777   return;  
3778 }
3779
3780 // Function: RFCMailBox::linkLockFile - create and link temporary lock file to real lock file
3781 // Description:
3782 //  Create a lock file for the current mailbox. Return success of failure.
3783 // Method:
3784 //  . create a temporary lock file with a unique signature id of this process
3785 //  . link the temporary lock file to the real lock file
3786 //  . if the link is not successful, remove the temporary lock file and return the
3787 //    time() in seconds on the remote system of when the temporary lock file was created
3788 //  . if the link is successful, remove the temporary lock file (link) and return 0.
3789 // Arguments:
3790 //   error      -- standard error structure used by caller
3791 // Outputs:
3792 //   If error.isSet() it is a fatal error from which the caller should return to its caller,
3793 //              return value will always be time(0)
3794 //   If !error.isSet() then check results of return value
3795 // Returns:
3796 //   time_t == 0 : indicates that the real lock file has been created and we own it
3797 //          != 0 : could not create real lock file, return value is the time *on the remote system*
3798 //                 that the temporary lock file was created with (from comparison with existing
3799 //                 lock file to see how old it is)
3800 //
3801 time_t
3802 RFCMailBox::linkLockFile(DtMailEnv & error, char *tempLockFileName)
3803 {
3804   int lock_fd;
3805   int return_status;
3806   struct stat sbuf;
3807
3808   // Create the temporary lock file. Failure to do so indicates lack of write permission
3809   // in the directory or some other fatal error
3810   //
3811
3812 #ifndef O_RSYNC
3813   int flags = O_RDWR | O_CREAT | O_EXCL | O_SYNC;
3814 #else
3815   int flags = O_RDWR | O_CREAT | O_EXCL | O_SYNC | O_RSYNC;
3816 #endif
3817   PRIV_ENABLED(lock_fd,SafeOpen(tempLockFileName, flags, 0666));
3818   if (lock_fd < 0) {
3819     // We are not able to create the temporary lock file.
3820     // We will have to punt on trying to lock here from now on.
3821     //
3822     switch (errno) {
3823     case EACCES:
3824       error.setError(DTME_CannotCreateMailboxLockFile_NoPermission);
3825       break;
3826       
3827     case EISDIR:
3828       error.setError(DTME_CannotCreateMailboxLockFile_IsDirectory);
3829       break;
3830       
3831     case ENOENT:
3832       error.setError(DTME_CannotCreateMailboxLockFile_NoSuchFile);
3833       break;
3834       
3835 #if defined(__osf__) || defined(CSRG_BASED)
3836     case ENOTDIR:
3837 #else
3838     case ENOLINK:
3839 #endif
3840       error.setError(DTME_CannotCreateMailboxLockFile_RemoteAccessLost);
3841       break;
3842       
3843     default:
3844       error.vSetError(DTME_CannotCreateMailboxLockFile,
3845                       DTM_FALSE, NULL, tempLockFileName, error.errnoMessage());
3846       break;
3847     }
3848     return(time(0));
3849   }
3850
3851   // Get creation time of temporary file *on remote system*
3852   //
3853   if (SafeFStat(lock_fd, &sbuf) == -1) {
3854     if (_errorLogging)
3855       error.logError(DTM_FALSE,
3856                    "linkLockFile(): temporary lock file cannot be stat()ed: %s, errno = %d\n",
3857                    tempLockFileName, errno);
3858     error.vSetError(DTME_CannotCreateMailboxLockFile,
3859                     DTM_FALSE, NULL, tempLockFileName, error.errnoMessage());
3860     (void) SafeClose(lock_fd);
3861     return(time(0));
3862   }
3863
3864   // Write proper contents to lock file:
3865   // Write the string "0" into the lock file to give us some
3866   // interoperability with SVR4 mailers.  SVR4 mailers expect
3867   // a process ID to be written into the lock file and then
3868   // use kill() to see if the process is alive or not.  We write
3869   // 0 into it so that SVR4 mailers will always think our lock file
3870   // is valid. In addition we include a unique ID so we can verify
3871   // if the lock file is stolen out from under us.
3872   //
3873   ssize_t writeResults;
3874   writeResults = SafeWrite(lock_fd, "0\0", 2);
3875   if (writeResults == 2)
3876     writeResults += SafeWrite(lock_fd, _uniqueLockId, _uniqueLockIdLength);
3877   if ( (writeResults != _uniqueLockIdLength+2) ){
3878     if (_errorLogging)
3879       error.logError(DTM_FALSE,
3880                    "linkLockFile(): write to temporary lock file failed: %s, errno = %d\n",
3881                    tempLockFileName, errno);
3882     error.vSetError(DTME_CannotCreateMailboxLockFile,
3883                     DTM_FALSE, NULL, tempLockFileName, error.errnoMessage());
3884     (void) SafeClose(lock_fd);
3885     return(time(0));
3886   }
3887
3888   // sync up the lock file with the ultimate storage device
3889   //
3890   if (fsync(lock_fd) == -1) {
3891     if (_errorLogging)
3892       error.logError(DTM_FALSE,
3893                    "linkLockFile(): fsync to temporary lock file failed: %s, errno = %d\n",
3894                    tempLockFileName, errno);
3895     error.vSetError(DTME_CannotCreateMailboxLockFile,
3896                     DTM_FALSE, NULL, tempLockFileName, error.errnoMessage());
3897     (void) SafeClose(lock_fd);
3898     return(time(0));
3899   }
3900
3901   // close the file
3902   //
3903   if (SafeClose(lock_fd) == -1) {
3904     if (_errorLogging)
3905       error.logError(
3906         DTM_FALSE,
3907         "linkLockFile(): close of temporary lock file failed: %s, errno = %d\n",
3908         tempLockFileName, errno);
3909     error.vSetError(DTME_CannotCreateMailboxLockFile,
3910                     DTM_FALSE, NULL, tempLockFileName, error.errnoMessage());
3911     return(time(0));
3912   }
3913          
3914   // The temporary lock file has been created - now try and link it to the
3915   // real lock file.  Failure here is not fatal as we will retry and possible
3916   // try and remove the real lock file later on.
3917   //
3918   PRIV_ENABLED(return_status,SafeLink(tempLockFileName, _lockFileName));
3919   if (return_status == -1) {
3920       PRIV_ENABLED(return_status,SafeRemove(tempLockFileName));
3921       return(sbuf.st_ctime);
3922   }
3923
3924   // We successfully linked the temp lock file to the real lock file name
3925   // This means we have the dot lock for our process - remove the temporary lock
3926   // file name (link) and return
3927   //
3928   PRIV_ENABLED(return_status,SafeRemove(tempLockFileName));
3929   return(0);
3930 }
3931
3932 void
3933 RFCMailBox::lockFile(DtMailEnv & error)
3934 {
3935 #if defined(DEBUG_RFCMailBox)
3936   char *pname = "RFCMailBox::lockFile";
3937 #endif
3938   int return_status = 0;
3939     
3940   // We will create a simple lock file to keep the file from
3941   // changing while we are doing critical work.
3942   //
3943   
3944   // On some platforms, sendmail will place a lockf lock on the
3945   // file during mail delivery. If this is the case, then we
3946   // need to make sure we have the lock here.
3947   //
3948 #if defined(SENDMAIL_LOCKS)
3949   assert(_lockf_active == DTM_FALSE);
3950   lseek(_fd, 0, SEEK_SET);
3951   PRIV_ENABLED_OPTIONAL(return_status,SafeLockf(_fd, F_LOCK, 0));
3952   if (return_status != -1)
3953   {
3954       _lockf_active = DTM_TRUE;
3955       DEBUG_PRINTF( ("%s:  lockf succeeded\n", pname) );
3956   }
3957 #endif
3958   
3959   if (_use_dot_lock == DTM_FALSE) {
3960       DEBUG_PRINTF( ("%s:  not using dot lock\n", pname) );
3961       return;
3962   }
3963
3964   // Implement the .lock short term lock protocol
3965   // This code was "adapted" from Solaris 2.5 (SunOS 5.5)
3966   // usr/src/cmd/mail/maillock.c.
3967   //
3968   assert(_dot_lock_active == DTM_FALSE);
3969   
3970   // Create the temporary mail lock file name
3971   // It has the form <_lockfilename><XXXXXX> or mailbox.lockXXXXXX
3972   // mktemp then creates a unique temporary file for the template
3973   //
3974   assert(_lockFileName != NULL);
3975   char *tempLockFileName = new char[MAXPATHLEN];
3976   sprintf(tempLockFileName, "%sXXXXXX", _real_path);
3977   mktemp(tempLockFileName);
3978   PRIV_ENABLED(return_status,SafeRemove(tempLockFileName));
3979
3980   // loop through attempting to create the temporary lock file,
3981   // and if successful attempt to link the temporary lock file
3982   // to the real lock file. If not successful, retry for up to 5
3983   // minutes, and remove the current lock file if it is more than
3984   // 5 minutes old.
3985   //
3986   int statFailed = 0;
3987   struct stat sbuf;
3988   
3989   for (;;) {
3990     // Attempt to create a temporary file and link it to the intended lock file
3991     // If it is successful, we have the lock and can return.
3992     // If it is not successful and error.isSet then it is a non-recoverable
3993     // error,
3994     // in which case the mailbox is deemed not-writable any more.
3995     // If it is not successful and !error.isSet then it is a recoverable error,
3996     // in which case we spin and try again according to the retry rules.
3997     //
3998     time_t t = linkLockFile(error, tempLockFileName);
3999     if (error.isSet()) {
4000       // hard error? -- something is wrong, assume read/only
4001       _use_dot_lock = DTM_FALSE;
4002       _mail_box_writable = DTM_FALSE;
4003       (void) SafeRemove(tempLockFileName);
4004       DEBUG_PRINTF(
4005         ("%s:  failed to link dot_lock file %s\n", pname, tempLockFileName) );
4006       delete [] tempLockFileName;
4007       return;
4008     }
4009     if (t == 0) {
4010       checkLockFileOwnership(error);
4011       if (!error.isSet()) 
4012       {
4013           _dot_lock_active = DTM_TRUE;
4014           DEBUG_PRINTF( ("%s:  succeeded acquiring dot_lock file\n", pname) );
4015       }
4016       delete [] tempLockFileName;
4017       return;
4018     }
4019
4020     // Could not link the temporary lock file to the intended real lock file
4021     // See if the lock file exists and if so if we can remove it because it
4022     // is > 5 mins old.  If the stat fails it means the lock file disappeared
4023     // between our attempt to link to it and now - only allow this to go on
4024     // so many times before punting
4025     //
4026     if (SafeStat(_lockFileName, &sbuf) == -1) {
4027       if (statFailed++ > 5) {
4028         error.vSetError(DTME_CannotCreateMailboxLockFile,
4029                         DTM_FALSE, NULL, _lockFileName, error.errnoMessage());
4030         delete [] tempLockFileName;
4031         return;
4032       }
4033       sleep(5);
4034       continue;
4035     }
4036
4037     // The lock file already exists - compare the time of the temp
4038     // file with the time of the lock file, rather than with the
4039     // current time of day, since the files may reside on another
4040     // machine whose time of day differs from the one this program
4041     // is running on. If the lock file is less than 5 minutes old,
4042     // keep trying, otherwise, remove the lock file and try again.
4043     //
4044     statFailed = 0;
4045     if (t < (sbuf.st_ctime + 300)) {
4046       sleep(5);
4047       continue;
4048     }
4049
4050     error.logError(DTM_FALSE,
4051         "lockFile(): removing stale lock file %s ctime %08ld temp lock %s ctime %08ld diff %08ld\n",
4052         _lockFileName, sbuf.st_ctime, tempLockFileName, t, t-sbuf.st_ctime);
4053     DEBUG_PRINTF(
4054         ("%s:  giving up; removing dot_lock file %s\n", pname, _lockFileName));
4055
4056     PRIV_ENABLED(return_status,SafeRemove(_lockFileName));
4057     if (return_status == -1) {
4058       // We were not able to unlink the file. This means that
4059       // we do not have write access to the directory. We will
4060       // have to pass on taking long locks.
4061       //
4062       _use_dot_lock = DTM_FALSE;
4063       _mail_box_writable = DTM_FALSE;
4064       error.vSetError(DTME_CannotRemoveStaleMailboxLockFile,
4065                      DTM_FALSE, NULL, _lockFileName, error.errnoMessage());
4066       delete [] tempLockFileName;
4067       return;
4068     }
4069   }
4070   delete [] tempLockFileName;
4071 }
4072
4073 // lockNewMailboxFile -- before renaming a new mailbox file over an old
4074 // mailbox file, we need to establish a lock on the new mailbox file is
4075 // a lock was established on the old mailbox file. Since the .lock protocol
4076 // works on a file name basis we dont have to worry here, but if this
4077 // system uses lockf() to lock the files we need to establish that lock
4078 // on the new file first before renaming it over the old mailbox file.
4079 //
4080 void
4081 RFCMailBox::lockNewMailboxFile(int new_fd)
4082 {
4083   int return_status = 0;
4084   if (_lockf_active == DTM_TRUE) {
4085     lseek(new_fd, 0, SEEK_SET);
4086     PRIV_ENABLED_OPTIONAL(return_status,SafeLockf(new_fd, F_LOCK, 0));
4087   }
4088 }
4089
4090 // unlockOldMailboxFile -- after renaming a new mailbox file over an old
4091 // mailbox file, if a lockf() style lock was established on the old mailbox
4092 // file, it needs to be removed
4093 //
4094 void
4095 RFCMailBox::unlockOldMailboxFile(int old_fd)
4096 {
4097   int return_status = 0;
4098   if (_lockf_active == DTM_TRUE) {
4099     lseek(old_fd, 0, SEEK_SET);
4100     PRIV_ENABLED_OPTIONAL(return_status,SafeLockf(old_fd, F_ULOCK, 0));
4101   }
4102 }
4103
4104 void
4105 RFCMailBox::transferLock(int old_fd, int new_fd)
4106 {
4107   int return_status = 0;
4108   if (_lockf_active == DTM_TRUE) {
4109     lseek(new_fd, 0, SEEK_SET);
4110     PRIV_ENABLED_OPTIONAL(return_status,SafeLockf(new_fd, F_LOCK, 0));
4111
4112     lseek(old_fd, F_ULOCK, 0);
4113     PRIV_ENABLED_OPTIONAL(return_status,SafeLockf(old_fd, F_ULOCK, 0));
4114   }
4115 }
4116
4117 void
4118 RFCMailBox::unlockFile(DtMailEnv & error, int fd)
4119 {
4120 #if defined(DEBUG_RFCMailBox)
4121   char *pname = "RFCMailBox::unlockFile";
4122 #endif
4123   int return_status;
4124     
4125   // We will create a simple lock file to keep the file from
4126   // changing while we are doing critical work.
4127   //
4128   
4129   if (_use_dot_lock == DTM_TRUE) {
4130     assert(_dot_lock_active == DTM_TRUE);
4131     assert(_lockFileName != NULL);
4132     _dot_lock_active = DTM_FALSE;
4133     checkLockFileOwnership(error);
4134     if (!error.isSet()) {
4135         DEBUG_PRINTF(("%s:  unlinking dot_lock file\n", pname, _lockFileName));
4136
4137         PRIV_ENABLED(return_status,SafeUnlink(_lockFileName));
4138         if (return_status == -1) {
4139           error.vSetError(DTME_CannotRemoveMailboxLockFile,
4140                      DTM_FALSE, NULL, _lockFileName, error.errnoMessage());
4141         }
4142     }
4143   }
4144   
4145 #if defined(SENDMAIL_LOCKS)
4146     if (_lockf_active == DTM_TRUE) {
4147       DEBUG_PRINTF(("%s:  removing lockf\n", pname));
4148
4149       lseek(fd, 0, SEEK_SET);
4150       PRIV_ENABLED_OPTIONAL(return_status,SafeLockf(fd, F_ULOCK, 0));
4151       _lockf_active = DTM_FALSE;
4152     }
4153 #endif
4154 }
4155
4156 #define DOT_DTMAIL_SUFFIX "dtmail"
4157 void
4158 RFCMailBox::dotDtmailLockFile(char *filename, int)
4159 {
4160
4161   // Attempt to link the temporary lock file to the real lock file.
4162   assert(_real_path != NULL);
4163   (void) sprintf(filename, "%s.%s", _real_path, DOT_DTMAIL_SUFFIX);
4164
4165 }
4166
4167 void
4168 RFCMailBox::dotDtmailLock(DtMailEnv & error)
4169 {
4170   char lockBuf[MAXPATHLEN];
4171   char dotDtmailPath[MAXPATHLEN+1];
4172   char *tempLockFileName = new char[MAXPATHLEN];
4173   int len;
4174   int return_status = 0;
4175   int lock_fd = 0;
4176 #ifndef O_RSYNC
4177   int flags = O_RDWR | O_CREAT | O_EXCL | O_SYNC;
4178 #else
4179   int flags = O_RDWR | O_CREAT | O_EXCL | O_SYNC | O_RSYNC;
4180 #endif
4181     
4182   // We will create a .dtmail file to prevent conflicts between dtmail's
4183   // operating on the same mailbox.
4184   
4185   // Create the temporary mail lock file name.
4186   sprintf(tempLockFileName, "%sXXXXXX", _real_path);
4187   mktemp(tempLockFileName);
4188   PRIV_ENABLED(return_status,SafeRemove(tempLockFileName));
4189
4190   // Attempt to create the temporary file.
4191   PRIV_ENABLED(lock_fd,SafeOpen(tempLockFileName, flags, 0666));
4192   if (lock_fd < 0) {
4193     switch (errno) {
4194     case EACCES:
4195       error.setError(DTME_CannotCreateMailboxLockFile_NoPermission);
4196       break;
4197     case EISDIR:
4198       error.setError(DTME_CannotCreateMailboxLockFile_IsDirectory);
4199       break;
4200     case ENOENT:
4201       error.setError(DTME_CannotCreateMailboxLockFile_NoSuchFile);
4202       break;
4203 #if defined(__osf__) || defined(CSRG_BASED)
4204     case ENOTDIR:
4205 #else
4206     case ENOLINK:
4207 #endif
4208       error.setError(DTME_CannotCreateMailboxLockFile_RemoteAccessLost);
4209       break;
4210     default:
4211       error.vSetError(DTME_CannotCreateMailboxLockFile,
4212                       DTM_FALSE, NULL, tempLockFileName,
4213                       error.errnoMessage());
4214       break;
4215     }
4216     delete [] tempLockFileName;
4217     return;
4218   }
4219
4220   sprintf(lockBuf, "%d\n", getpid());
4221   len = strlen(lockBuf);
4222   len = SafeWrite(lock_fd, lockBuf, len);
4223   SafeClose(lock_fd);
4224
4225   dotDtmailLockFile(dotDtmailPath, MAXPATHLEN);
4226
4227   PRIV_ENABLED(return_status,SafeLink(tempLockFileName, dotDtmailPath));
4228   if (return_status == -1)
4229     error.vSetError(DTME_CannotCreateMailboxDotDtmailLockFile,
4230                       DTM_FALSE, NULL, dotDtmailPath,
4231                       error.errnoMessage());
4232
4233   PRIV_ENABLED(return_status,SafeRemove(tempLockFileName));
4234   delete [] tempLockFileName;
4235 }
4236
4237 void
4238 RFCMailBox::dotDtmailUnlock(DtMailEnv &)
4239 {
4240   char dotDtmailPath[MAXPATHLEN+1];
4241   char lockBuf[MAXPATHLEN];
4242   pid_t pid;
4243   int return_status = 0;
4244   int lock_fd = 0;
4245 #ifndef O_RSYNC
4246   int flags = O_RDONLY | O_EXCL | O_SYNC;
4247 #else
4248   int flags = O_RDONLY | O_EXCL | O_SYNC | O_RSYNC;
4249 #endif
4250
4251   dotDtmailLockFile(dotDtmailPath, MAXPATHLEN);
4252   PRIV_ENABLED(lock_fd,SafeOpen(dotDtmailPath, flags, 0666));
4253   if (lock_fd < 0)
4254     return;
4255
4256   return_status = SafeRead(lock_fd, lockBuf, sizeof(lockBuf)-1);
4257   if (return_status <= 0) 
4258   {
4259       (void) SafeClose(lock_fd);
4260       return;
4261   }
4262
4263   sscanf(lockBuf, "%d", &pid);
4264   if (pid != getpid())
4265   {
4266       (void) SafeClose(lock_fd);
4267       return;
4268   }
4269
4270   (void) SafeClose(lock_fd);
4271   PRIV_ENABLED(return_status,SafeRemove(dotDtmailPath));
4272 }
4273
4274 //
4275 // Possible return values,
4276 // DTMBX_LONGLOCK_FAILED_CANCEL - failed, cancel operation
4277 // DTMBX_LONGLOCK_FAILED_READONLY       - failed, open read only
4278 // DTMBX_LONGLOCK_SUCCESSFUL    - succeeded
4279 //
4280 // This function will also manipulate "_mail_box_writable" and "_lock_flag"
4281 //
4282 DTMBX_LONGLOCK
4283 RFCMailBox::longLock(DtMailEnv & error)
4284 {
4285     DTMBX_LONGLOCK      rtn = DTMBX_LONGLOCK_FAILED_CANCEL;
4286
4287     _long_lock_active = DTM_TRUE;
4288     _lock_obj = new FileShare(error,
4289                             _session,
4290                             _real_path,
4291                             _callback,
4292                             _cb_data);
4293
4294     if (error.isSet())
4295     {
4296       //
4297       // TT is not available.  Attempt to lock using the .dtmail lock.
4298       //
4299       delete _lock_obj;
4300       _lock_obj = NULL;
4301
4302       error.clear();
4303       dotDtmailLock(error);
4304       if (error.isSet())
4305       {
4306           _long_lock_active = DTM_FALSE;
4307           if (NULL != _callback)
4308           {
4309               //
4310               // .dtmail lock failed.  The best we can do is read only.
4311               //
4312               DtMailBoolean     ans;
4313               char              dotDtmailPath[MAXPATHLEN+1];
4314
4315               dotDtmailLockFile(dotDtmailPath, MAXPATHLEN);
4316               ans = _callback(
4317                         DTMC_DOTDTMAILLOCKFAILED, _real_path, NULL, _cb_data,
4318                         (char*) dotDtmailPath, (const char*) error);
4319               if (DTM_TRUE == ans)
4320               {
4321                   rtn = DTMBX_LONGLOCK_FAILED_READONLY;
4322                   error.clear();
4323               }
4324               else
4325               {
4326                   rtn = DTMBX_LONGLOCK_FAILED_CANCEL;
4327                   error.setError(DTME_UserInterrupted);
4328               }
4329           }
4330       }
4331       else
4332         rtn = DTMBX_LONGLOCK_SUCCESSFUL;
4333     }
4334     else
4335     {
4336       //
4337       // Ask TT to lock the file.
4338       //
4339       _lock_obj->lockFile(error);
4340       if (error.isSet())
4341       {
4342           //
4343           // TT rejected our lock request.  The best we can do is read only.
4344           //
4345
4346           if (_lock_obj->readOnly(error) == DTM_TRUE)
4347           {
4348               rtn = DTMBX_LONGLOCK_FAILED_READONLY;
4349               error.clear();
4350           }
4351           else
4352           {
4353               rtn = DTMBX_LONGLOCK_FAILED_CANCEL;
4354               error.setError(DTME_UserInterrupted);
4355           }
4356
4357           delete _lock_obj;
4358           _lock_obj = NULL;
4359           _long_lock_active = DTM_FALSE;
4360       }
4361       else
4362         rtn = DTMBX_LONGLOCK_SUCCESSFUL;
4363     }
4364
4365     return rtn;
4366 }
4367
4368 void
4369 RFCMailBox::longUnlock(DtMailEnv & error)
4370 {
4371   error.clear();
4372   
4373   if (DTM_TRUE == _lock_flag)
4374   {
4375     if (NULL != _lock_obj)
4376     {
4377       delete _lock_obj;
4378       _lock_obj = NULL;
4379     }
4380     else if (_long_lock_active == DTM_TRUE)
4381       dotDtmailUnlock(error);
4382   }
4383   
4384 #if 0
4385   if (_mail_box_writable == DTM_FALSE)
4386     return;
4387 #endif
4388   
4389 #if !defined(SENDMAIL_LOCKS)
4390   if (_lockf_active == DTM_TRUE)
4391   {
4392     int return_status = 0;
4393     lseek(_fd, 0, SEEK_SET);
4394     PRIV_ENABLED_OPTIONAL(return_status,SafeLockf(_fd, F_ULOCK, 0));
4395     _lockf_active = DTM_FALSE;
4396   }
4397 #endif
4398
4399   _long_lock_active = DTM_FALSE;
4400 }
4401
4402 RFCMailBox::MapRegion *
4403 RFCMailBox::mapNewRegion(DtMailEnv & error, int fd, unsigned long size)
4404 {
4405   assert(fd);
4406   
4407   // Create the mapped region.
4408   //
4409   MapRegion * map = new MapRegion;
4410   map->offset = 0;
4411   map->file_size = size;
4412   long page_size = memoryPageSize();
4413
4414 #if defined(__osf__) && OSMAJORVERSION < 4
4415   // Problem with mapping. You can not round up to the nearest 
4416   // memory block size when mapping a file. You need the exact
4417   // file size of less. - ff
4418   map->map_size = map->file_size;
4419 #else
4420   map->map_size = map->file_size + (page_size - (map->file_size % page_size)) + page_size;
4421 #endif
4422   
4423   int flags = MAP_PRIVATE;
4424   
4425 #if defined(MMAP_NORESERVE)
4426   // We are not supposed to be writing to these pages. If
4427   // we don't specify MAP_NORESERVE however, the system will
4428   // reserve swap space equal to the file size to deal with
4429   // potential writes. This is wasteful to say the least.
4430   //
4431   flags |= MAP_NORESERVE;
4432 #endif
4433   
4434   map->map_region = (char *)mmap(0, (size_t) map->map_size,
4435                                  PROT_READ, flags, fd, 0);
4436   if (map->map_region == (char *)-1) {
4437     switch(errno) {
4438     case ENOMEM:
4439       error.setError(DTME_CannotReadNewMailboxFile_OutOfMemory);
4440       break;
4441       
4442     default:
4443       error.vSetError(DTME_CannotReadNewMailboxFile,
4444                       DTM_FALSE, NULL, error.errnoMessage());
4445       break;
4446     }
4447     
4448     delete map;
4449     return((MapRegion *)NULL);
4450   }
4451   
4452   map->file_region = map->map_region;
4453   
4454   return(map);
4455 }
4456
4457 void
4458 RFCMailBox::makeHeaderLine(DtMailEnv & error, 
4459                             int slot,
4460                             const DtMailHeaderRequest & request,
4461                             DtMailHeaderLine & headers)
4462 {
4463     MessageCache * mc = _msg_list[slot];
4464     DtMail::Envelope * env = mc->message->getEnvelope(error);
4465
4466     // For each request, we need to retrieve the header values.
4467     //
4468     headers.number_of_names = request.number_of_names;
4469     headers.header_values = new DtMailValueSeq[headers.number_of_names];
4470
4471     for (int req = 0; req < request.number_of_names; req++) {
4472         // RFC Message::getHeader will pass abstract names through
4473         // as transport names if they can not be found in the abstract
4474         // table. Because of this we say all names are abstract and
4475         // rely on the specific implementation of RFCMessage. This
4476         // is potentially dangerous, but we are allowed to define
4477         // and require these semantics for RFCMessage (and this same
4478         // information appears in the appropriate place in getHeader.
4479         //
4480         env->getHeader(error,
4481                        request.header_name[req],
4482                        DTM_TRUE,
4483                        headers.header_values[req]);
4484         if (error.isSet()) {
4485             headers.header_values[req].clear();
4486             error.clear();
4487         }
4488     }
4489
4490     error.clear();
4491 }
4492
4493 void
4494 RFCMailBox::waitForMsgs(int needed)
4495 {
4496     while(_at_eof == 0 && needed >= _msg_list.length()) {
4497         ThreadSleep(1);
4498     }
4499     return;
4500 }
4501
4502 void
4503 RFCMailBox::writeToDumpFile(const char *format, ...)
4504 {
4505   DtMailEnv error;
4506   char  *dumpfilename = new char[MAXPATHLEN+1];
4507   _Xctimeparams ctime_buf;
4508
4509   GET_DUMPFILE_NAME(dumpfilename);
4510   FILE *df = fopen(dumpfilename, "a");
4511   
4512   const time_t clockTime = (const time_t) time((time_t *)0);
4513   memset((void*) &ctime_buf, 0, sizeof(_Xctimeparams));
4514   fprintf(df, "--------------------- pid=%ld %s", 
4515           (long)getpid(), _XCtime(&clockTime, ctime_buf));
4516   
4517   va_list       var_args;
4518   va_start(var_args, format);
4519   vfprintf(df, format, var_args);
4520   va_end(var_args);
4521   
4522   fprintf(df, "---------------------\n");
4523   fprintf(df, "\n\n");
4524   
4525   delete [] dumpfilename;
4526   fclose(df);
4527 }
4528
4529 void
4530 RFCMailBox::startAutoSave(DtMailEnv & error,
4531                           DtMailBoolean start
4532                          )
4533 {
4534
4535    if(start)
4536    {
4537         _session->addEventRoutine(error, PollEntry, this, 60);
4538         _last_check = 0;
4539         _last_poll = 0; // Causes first poll to fire right way.
4540    }
4541    else
4542         _session->removeEventRoutine(error, PollEntry, this);
4543 }
4544
4545 void
4546 RFCMailBox::dumpMaps(const char *str)
4547 {
4548   struct stat buf;
4549   DtMailEnv error;
4550   off_t total_file_size = 0;
4551   struct sigaction sig_act, old_sig_act;
4552   char  *dumpfilename = new char[MAXPATHLEN+1];
4553
4554   GET_DUMPFILE_NAME(dumpfilename);
4555   FILE *df = fopen(dumpfilename, "a");
4556   
4557   if (df==NULL && _errorLogging)
4558     error.logError(
4559                 DTM_FALSE,
4560                 "dumpMaps():  Cant open dump file %s\n", dumpfilename);
4561   delete [] dumpfilename;
4562
4563   if (SafeFStat(_fd, &buf) < 0) {
4564     fprintf(df, "dumpMaps(): fstat(%d) failed errno=%d\n", _fd, errno);
4565   }
4566
4567   if (str == NULL) {
4568     str = "\n";
4569   }
4570
4571   /*
4572    * Prepare signal handler for exception handling.
4573    */
4574   (void) sigemptyset(&sig_act.sa_mask);
4575   sig_act.sa_flags = 0;
4576 #if defined(USL)
4577   sig_act.sa_handler = (void(*)())SigBusHandler;
4578 #else
4579   sig_act.sa_handler = SigBusHandler;
4580 #endif /* USL */
4581   sigaction(SIGBUS, &sig_act, &old_sig_act);
4582   sigbus_env_valid = 1;
4583   if (setjmp(sigbus_env) == 0) {
4584     const time_t clockTime = (const time_t) time((time_t *)0);
4585     _Xctimeparams ctime_buf;
4586     memset((void*) &ctime_buf, 0, sizeof(_Xctimeparams));
4587     fprintf(df, "--------------------- pid=%ld %s", 
4588             (long)getpid(), _XCtime(&clockTime, ctime_buf));
4589     fprintf(df, str);
4590     fprintf(df, "---------------------\n");
4591     fprintf(df, "Mappings = %d\n", _mappings.length());
4592     fprintf(df, "Map Entries:\n");
4593     for (int m = 0; m < _mappings.length(); m++) {
4594       MapRegion *map = _mappings[m];
4595       fprintf(df, "map[%d]: map_region = 0x%08lx, map_size = 0x%08lx(%08ld)\n",
4596               m, map->map_region, map->map_size, map->map_size);
4597       if (map->map_size % memoryPageSize()) {
4598         fprintf(df, "ERROR! map->map_size not mod %d\n", memoryPageSize());
4599       }
4600       HexDump(df, "map_region", (unsigned char *)map->map_region,
4601               (int) map->file_size, _errorLogging ? 0 : 10);
4602       fprintf(df,
4603               "map[%d]: file_region = 0x%08lx, file_size = 0x%08lx(%08ld)\n",
4604               m, map->file_region, map->file_size, map->file_size);
4605       fprintf(df, "map[%d]: offset = 0x%08lx(%08ld)\n",
4606               m, map->offset, map->offset);
4607       if (map->file_size == 0) {
4608           fprintf(df, "No data in file_region\n");
4609       }
4610       else {
4611         if (strncasecmp(map->file_region, "From", 4)) {
4612           fprintf(df, "ERROR! map->file_region does not begin with From\n");
4613         }
4614         HexDump(df,
4615                 "file_region",
4616                 (unsigned char *)map->file_region,
4617                 (int) map->file_size, _errorLogging ? 0 : 10);
4618       }
4619       total_file_size += (off_t) map->file_size;
4620       if ((total_file_size % 4096) == 0) {
4621         fprintf(df,
4622                 "Total file size falls on page boundary, totalsize = %d\n",
4623                 total_file_size);
4624       }
4625     }
4626   
4627     fprintf(df, "\nstat buffer entries: st_ino = %d, st_dev = %d, st_nlink = %d, st_size = %ld\n",
4628             buf.st_ino, buf.st_dev, buf.st_nlink, buf.st_size);
4629   
4630     fprintf(df, "\n\n");
4631   }
4632   else {
4633     fprintf(df, "\nSIGBUS received during output of file mappings.\n");
4634     fprintf(df, "This generally indicates a truncated or deleted file.\n");
4635   }
4636   sigbus_env_valid = 0;
4637   sigaction(SIGBUS, &old_sig_act, NULL);
4638   fclose(df);
4639 }
4640
4641 // The following routines are required to bind this format specific driver
4642 // into the format neutral layer.
4643 //
4644 // The first entry point is the capability query interface. This is used
4645 // by the client to determine what capabilities we support.
4646 //
4647 void
4648 RFCQueryImpl(DtMail::Session & session,
4649               DtMailEnv & error,
4650               const char * capability,
4651               va_list args)
4652 {
4653     error.clear();
4654
4655     if (strcmp(capability, DtMailCapabilityPropsSupported) == 0) {
4656         DtMailBoolean * resp = va_arg(args, DtMailBoolean *);
4657         *resp = DTM_FALSE;
4658         return;
4659     }
4660
4661     if (strcmp(capability, DtMailCapabilityImplVersion) == 0) {
4662         char * version = va_arg(args, char *);
4663         strcpy(version, "1.0");
4664         return;
4665     }
4666
4667     if (strcmp(capability, DtMailCapabilityInboxName) == 0)
4668     {
4669         DtMailObjectSpace       *space = va_arg(args, DtMailObjectSpace *);
4670         void                    **inbox = va_arg(args, void **);
4671
4672         *space = DtMailFileObject;
4673         *inbox = (void*) getInboxPath(&session);
4674         return;
4675     }
4676
4677     if (strcmp(capability, DtMailCapabilityMailspoolName) == 0)
4678     {
4679         DtMailObjectSpace       *space = va_arg(args, DtMailObjectSpace *);
4680         void                    **mailspool = va_arg(args, void **);
4681
4682         *space = DtMailFileObject;
4683         *mailspool = (void*) getMailspoolPath(&session);
4684         return;
4685     }
4686
4687     if (strcmp(capability, DtMailCapabilityTransport) == 0) {
4688         DtMailBoolean * resp = va_arg(args, DtMailBoolean *);
4689         *resp = DTM_TRUE;
4690         return;
4691     }
4692
4693     error.setError(DTME_NotSupported);
4694     return;
4695 }
4696
4697 // The QueryOpen entry point is used to determin if we can open the specified
4698 // path. If the name is of the form host:/path, or is a simple path, then we
4699 // will say we can. Additional work should be done for content typing here,
4700 // but we'll skip it for now.
4701 //
4702 DtMailBoolean
4703 RFCQueryOpen(DtMail::Session &,
4704               DtMailEnv & error,
4705               DtMailObjectSpace space,
4706               void * arg)
4707 {
4708     error.clear();
4709
4710     // We can do buffers, so just say yes.
4711     //
4712     if (space == DtMailBufferObject) {
4713         return(DTM_TRUE);
4714     }
4715
4716     // If this isn't in the file name space, then give up now.
4717     //
4718     if (space != DtMailFileObject) {
4719         return(DTM_FALSE);
4720     }
4721
4722     char * path = (char *) arg;
4723
4724     // First, is this of the form "host:/path". If so, it is probably
4725     // an RFC mail box, so we will say we can open it.
4726     //
4727     for (const char * c = path; *c; c++) {
4728         if (*c == '/') {
4729             // We hit a slash first, so colon's are not useful.
4730             break;
4731         }
4732
4733         if (*c == ':' && *(c+1) == '/') {
4734             // Looks like we have a host name. We should do a more
4735             // robust check at this point, but for now we will assume
4736             // that we have a valid host.
4737             return(DTM_TRUE);
4738         }
4739     }
4740
4741     // Okay, there defintely is not a host name. See if we can stat the
4742     // file. If we can, then assume we can open it, otherwise. If the file
4743     // doesn't exist, then we can create a new one.
4744     //
4745     struct stat sbuf;
4746     if (stat(path, &sbuf) < 0) {
4747         if (errno == ENOENT) {
4748             return(DTM_TRUE);
4749         }
4750         else {
4751             return(DTM_FALSE);
4752         }
4753     }
4754
4755     return(DTM_TRUE);
4756 }
4757
4758 // The mail box construct entry point creates an instance of the RFC
4759 // mail box object. This object is then accessed through the virtual
4760 // DtMail::MailBox class API.
4761 //
4762 DtMail::MailBox *
4763 RFCMailBoxConstruct(DtMail::Session & session,
4764                      DtMailEnv & error,
4765                      DtMailObjectSpace space,
4766                      void * arg,
4767                      DtMailCallback cb,
4768                      void * client_data)
4769 {
4770     return(new RFCMailBox(error, &session, space, arg, cb, client_data, "Internet MIME"));
4771 }
4772
4773 DtMailBoolean
4774 RFCMessageQuery(DtMail::Session &,
4775                 DtMailEnv & error,
4776                 DtMailObjectSpace space,
4777                 void *)
4778 {
4779     error.clear();
4780
4781     if (space != DtMailBufferObject) {
4782         return(DTM_FALSE);
4783     }
4784
4785     return(DTM_TRUE);
4786 }
4787
4788 DtMail::Message *
4789 RFCMessageConstruct(DtMail::Session & session,
4790                     DtMailEnv & error,
4791                     DtMailObjectSpace space,
4792                     void * arg,
4793                     DtMailCallback cb,
4794                     void * client_data)
4795 {
4796     return(new RFCMessage(error, &session, space, arg, cb, client_data));
4797 }
4798
4799 DtMail::Transport *
4800 RFCMIMETransportConstruct(DtMail::Session & session,
4801                           DtMailEnv & error,
4802                           DtMailStatusCallback cb,
4803                           void * cb_data)
4804 {
4805     return(new RFCTransport(error, &session, cb, cb_data, "Internet MIME"));
4806 }
4807
4808 // The meta factory is responsible for returning the entry points
4809 // required for locating and creating various mail objects based for
4810 // the RFC implementation. It is essentially a switch table.
4811 //
4812
4813 extern "C" void *
4814 RFCMetaFactory(const char * op)
4815 {
4816     if (strcmp(op, QueryImplEntryOp) == 0) {
4817         return((void *)RFCQueryImpl);
4818     }
4819
4820     if (strcmp(op, QueryOpenEntryOp) == 0) {
4821         return((void *)RFCQueryOpen);
4822     }
4823
4824     if (strcmp(op, MailBoxConstructEntryOp) == 0) {
4825         return((void *)RFCMailBoxConstruct);
4826     }
4827
4828     if (strcmp(op, QueryMessageEntryOp) == 0) {
4829         return((void *)RFCMessageQuery);
4830     }
4831
4832     if (strcmp(op, MessageConstructEntryOp) == 0) {
4833         return((void *)RFCMessageConstruct);
4834     }
4835
4836     if (strcmp(op, TransportConstructEntryOp) == 0) {
4837         return((void *)RFCMIMETransportConstruct);
4838     }
4839
4840     return(NULL);
4841 }
4842
4843 // The mail box construct entry point creates an instance of the RFC
4844 // mail box object. This object is then accessed through the virtual
4845 // DtMail::MailBox class API.
4846 //
4847 DtMail::MailBox *
4848 V3MailBoxConstruct(DtMail::Session & session,
4849                      DtMailEnv & error,
4850                      DtMailObjectSpace space,
4851                      void * arg,
4852                      DtMailCallback cb,
4853                      void * client_data)
4854 {
4855     return(new RFCMailBox(error, &session, space, arg, cb, client_data, 
4856                                 "Sun Mail Tool"));
4857 }
4858
4859 DtMail::Transport *
4860 RFCV3TransportConstruct(DtMail::Session & session,
4861                         DtMailEnv & error,
4862                         DtMailStatusCallback cb,
4863                         void * cb_data)
4864 {
4865     return(new RFCTransport(error, &session, cb, cb_data, "Sun Mail Tool"));
4866 }
4867
4868 // The meta factory is responsible for returning the entry points
4869 // required for locating and creating various mail objects based for
4870 // the RFC implementation. It is essentially a switch table.
4871 //
4872 extern "C" void *
4873 V3MetaFactory(const char * op)
4874 {
4875     if (strcmp(op, QueryImplEntryOp) == 0) {
4876         return((void *)RFCQueryImpl);
4877     }
4878
4879     if (strcmp(op, QueryOpenEntryOp) == 0) {
4880         return((void *)RFCQueryOpen);
4881     }
4882
4883     if (strcmp(op, MailBoxConstructEntryOp) == 0) {
4884         return((void *)V3MailBoxConstruct);
4885     }
4886
4887     if (strcmp(op, QueryMessageEntryOp) == 0) {
4888         return((void *)RFCMessageQuery);
4889     }
4890
4891     if (strcmp(op, MessageConstructEntryOp) == 0) {
4892         return((void *)RFCMessageConstruct);
4893     }
4894
4895     if (strcmp(op, TransportConstructEntryOp) == 0) {
4896         return((void *)RFCV3TransportConstruct);
4897     }
4898
4899     return(NULL);
4900 }
4901
4902
4903 void
4904 RFCMailBox::createMailRetrievalAgent(char *password)
4905 {
4906     DtMailEnv           localError;
4907     char                *path = _session->expandPath(localError, (char*) _arg);
4908     DtMailServer        *server = NULL;
4909     char                *protocol;
4910
4911     if (! isInboxMailbox(_session, path))
4912     {
4913         free(path);
4914         return;
4915     }
4916
4917     if (NULL != _mra_command) free(_mra_command);
4918     _mra_command = NULL;
4919
4920     if (NULL != password)
4921     {
4922         if (NULL != _mra_serverpw) free(_mra_serverpw);
4923         _mra_serverpw = strdup(password);
4924     }
4925     
4926     if (NULL != _mra_server) delete _mra_server;
4927     _mra_server = NULL;
4928
4929     if (True == DtMailServer::get_mailrc_value(
4930                                         _session, DTMAS_INBOX,
4931                                         DTMAS_PROPKEY_GETMAILVIASERVER,
4932                                         (Boolean) False))
4933     {
4934         protocol = DtMailServer::get_mailrc_value(
4935                                         _session, DTMAS_INBOX,
4936                                         DTMAS_PROPKEY_PROTOCOL,
4937                                         DTMAS_PROTO_POP3);
4938
4939         if (! strcasecmp(protocol, DTMAS_PROTO_IMAP))
4940           _mra_server = (DtMailServer*) new IMAPServer(
4941                                         DTMAS_INBOX, _session, this,
4942                                         appendCB, (void*) this);
4943         else if (! strcasecmp(protocol, DTMAS_PROTO_APOP))
4944           _mra_server = (DtMailServer*) new APOPServer(
4945                                         DTMAS_INBOX, _session, this,
4946                                         appendCB, (void*) this);
4947         else if (! strcasecmp(protocol, DTMAS_PROTO_POP3))
4948           _mra_server = (DtMailServer*) new POP3Server(
4949                                         DTMAS_INBOX, _session, this,
4950                                         appendCB, (void*) this);
4951         else if (! strcasecmp(protocol, DTMAS_PROTO_POP2))
4952           _mra_server = (DtMailServer*) new POP2Server(
4953                                         DTMAS_INBOX, _session, this,
4954                                         appendCB, (void*) this);
4955         else
4956           _mra_server = (DtMailServer*) new AUTOServer(
4957                                         DTMAS_INBOX, _session, this,
4958                                         appendCB, (void*) this);
4959
4960         if (NULL != _mra_server) _mra_server->set_password(_mra_serverpw);
4961         if (NULL != protocol) free(protocol);
4962     }
4963     else if (True == DtMailServer::get_mailrc_value(
4964                                               _session, DTMAS_INBOX,
4965                                               DTMAS_PROPKEY_GETMAILVIACOMMAND,
4966                                               (Boolean) False))
4967     {
4968         _mra_command = DtMailServer::get_mailrc_value(
4969                                                 _session, DTMAS_INBOX,
4970                                                 DTMAS_PROPKEY_GETMAILCOMMAND,
4971                                                 (char*) NULL);
4972     }
4973 }
4974
4975
4976 void
4977 RFCMailBox::deleteMailRetrievalAgent()
4978 {
4979     if (NULL != _mra_command) free(_mra_command);
4980     _mra_command = NULL;
4981     if (NULL != _mra_serverpw) free(_mra_serverpw);
4982     _mra_serverpw = NULL;
4983     if (NULL != _mra_server) delete _mra_server;
4984     _mra_server = NULL;
4985 }
4986
4987 void
4988 RFCMailBox::updateMailRetrievalPassword(char *password)
4989 {
4990     if (NULL == password || NULL == _mra_server) return;
4991
4992     if (NULL != _mra_serverpw) delete _mra_serverpw;
4993     _mra_serverpw = strdup(password);
4994     _mra_server->set_password(_mra_serverpw);
4995 }
4996
4997
4998 DtMailCallbackOp
4999 RFCMailBox::retrieveNewMail(DtMailEnv &error)
5000 {
5001     if (NULL != _mra_server)
5002     {
5003         _mra_server->retrieve_messages(error);
5004
5005         if (DTME_MailServerAccess_MissingPassword == (DTMailError_t) error ||
5006             DTME_MailServerAccess_AuthorizationFailed == (DTMailError_t) error)
5007           return DTMC_SERVERPASSWORDNEEDED;
5008         else if (DTME_NoError != (DTMailError_t) error)
5009           return DTMC_SERVERACCESSFAILED;
5010     }
5011     else if (NULL != _mra_command)
5012     {
5013         int     sysstatus = system(_mra_command);
5014         if (-1 == sysstatus)
5015         {
5016             error.vSetError(
5017                         DTME_GetmailCommandRetrieval_SystemError,
5018                         DTM_FALSE, NULL,
5019                         _mra_command, errno, error.errnoMessage(errno));
5020             if (_errorLogging) error.logError(DTM_FALSE, (const char*) error);
5021             return DTMC_GETMAILCOMMANDFAILED;
5022         }
5023         if (0 == WIFEXITED(sysstatus))
5024         {
5025             error.vSetError(
5026                         DTME_GetmailCommandRetrieval_AbnormalExit,
5027                         DTM_FALSE, NULL,
5028                         _mra_command);
5029             if (_errorLogging) error.logError(DTM_FALSE, (const char*) error);
5030             return DTMC_GETMAILCOMMANDFAILED;
5031         }
5032         else if (0 != WEXITSTATUS(sysstatus))
5033         {
5034             if (_errorLogging)
5035               error.logError(
5036                         DTM_FALSE,
5037                         "system('%s') returned %d\n",
5038                         _mra_command, WEXITSTATUS(sysstatus));
5039         }
5040     }
5041     return DTMC_NOOP;
5042 }
5043
5044 void
5045 RFCMailBox::mailboxAccessShow(time_t mtime, char *prefix)
5046 {
5047     utimbuf new_time;
5048
5049     new_time.modtime = mtime;
5050     new_time.actime = new_time.modtime+1;
5051     SafeUTime(_real_path, &new_time);
5052 #ifdef DEBUG_MAILBOX_ACCESS
5053     fprintf(stderr, "%s:  forcing acctime>modtime\n", prefix);
5054 #endif
5055 }
5056
5057 void
5058 RFCMailBox::mailboxAccessHide(char *prefix)
5059 {
5060     SafeUTime(_real_path, NULL);
5061 #ifdef DEBUG_MAILBOX_ACCESS
5062     fprintf(stderr, "%s:  forcing modtime==acctime\n", prefix);
5063 #endif
5064 }