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