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