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