-fpermissive to allow GCC to compile old C++
[oweals/cde.git] / cde / programs / dtmail / dtmail / RoamCmds.C
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /*
24  *+SNOTICE
25  *
26  *      $TOG: RoamCmds.C /main/43 1999/07/13 08:41:44 mgreess $
27  *
28  *      RESTRICTED CONFIDENTIAL INFORMATION:
29  *      
30  *      The information in this document is subject to special
31  *      restrictions in a confidential disclosure agreement between
32  *      HP, IBM, Sun, USL, SCO and Univel.  Do not distribute this
33  *      document outside HP, IBM, Sun, USL, SCO, or Univel without
34  *      Sun's specific written approval.  This document and all copies
35  *      and derivative works thereof must be returned or destroyed at
36  *      Sun's request.
37  *
38  *      Copyright 1993, 1994, 1995 Sun Microsystems, Inc.  All rights reserved.
39  *
40  *+ENOTICE
41  */
42 /*
43  *                   Common Desktop Environment
44  *
45  *   (c) Copyright 1993, 1994, 1995 Hewlett-Packard Company
46  *   (c) Copyright 1993, 1994, 1995 International Business Machines Corp.
47  *   (c) Copyright 1993, 1994, 1995 Sun Microsystems, Inc.
48  *   (c) Copyright 1993, 1994, 1995 Novell, Inc.
49  *   (c) Copyright 1995 Digital Equipment Corp.
50  *   (c) Copyright 1995 Fujitsu Limited
51  *   (c) Copyright 1995 Hitachi, Ltd.
52  *                                                                   
53  *
54  *                     RESTRICTED RIGHTS LEGEND                              
55  *
56  *Use, duplication, or disclosure by the U.S. Government is subject to
57  *restrictions as set forth in subparagraph (c)(1)(ii) of the Rights in
58  *Technical Data and Computer Software clause in DFARS 252.227-7013.  Rights
59  *for non-DOD U.S. Government Departments and Agencies are as set forth in
60  *FAR 52.227-19(c)(1,2).
61
62  *Hewlett-Packard Company, 3000 Hanover Street, Palo Alto, CA 94304 U.S.A.
63  *International Business Machines Corp., Route 100, Somers, NY 10589 U.S.A. 
64  *Sun Microsystems, Inc., 2550 Garcia Avenue, Mountain View, CA 94043 U.S.A.
65  *Novell, Inc., 190 River Road, Summit, NJ 07901 U.S.A.
66  *Digital Equipment Corp., 111 Powdermill Road, Maynard, MA 01754, U.S.A.
67  *Fujitsu Limited, 1015, Kamikodanaka Nakahara-Ku, Kawasaki 211, Japan
68  *Hitachi, Ltd., 6, Kanda Surugadai 4-Chome, Chiyoda-ku, Tokyo 101, Japan
69  */
70
71 #include <errno.h>
72 #include <signal.h>
73 #include <ctype.h>
74 #include <string.h>
75 #include <sys/param.h>
76 #include <sys/stat.h>
77 #include <sys/types.h>
78 #include <unistd.h>
79 #include <pwd.h>
80 #include <X11/Intrinsic.h>
81 #include <Xm/Text.h>
82 #include <Xm/FileSBP.h>
83 #include <Xm/FileSB.h>
84 #include <Xm/PushB.h>
85 #include <Xm/ToggleB.h>
86 #include <Xm/PushBG.h>
87 #include <Xm/PanedW.h>
88 #include <Xm/Form.h>
89 #include <Dt/Dts.h>
90 #include <Dt/Action.h>
91 #include <Dt/Help.h>
92 #include <Dt/DtPStrings.h>
93
94 #include <DtMail/IO.hh>
95 #include <DtMail/DtMailError.hh>
96 #ifndef __ppc
97 #include <DtMail/Buffer.hh>
98 #endif
99 #ifdef __ppc
100 #include <DtMail/Buffer.hh>
101 #endif
102 #include <DtMail/OptCmd.h>
103 #include <EUSCompat.h>
104 #include "EUSDebug.hh"
105 #include "Application.h"
106 #include "AttachArea.h"
107 #include "Attachment.h"
108 #include "Dmx.h"
109 #include "DmxPrintJob.h"
110 #include "DtMailEditor.hh"
111 #include "DtMailGenDialog.hh"
112 #include "DtMailHelp.hh"
113 #include "DtMailWDM.hh"
114 #include "FindDialog.h"
115 #include "Help.hh"
116 #include "MailMsg.h"
117 #include "MemUtils.hh"
118 #include "MsgScrollingList.hh"
119 #include "MsgHndArray.hh"
120 #include "RoamApp.h"
121 #include "RoamCmds.h"
122 #include "RoamMenuWindow.h"
123 #include "SendMsgDialog.h"
124 #include "Undelete.hh"
125
126 #if defined(NEED_MMAP_WRAPPER)
127 extern "C" {
128 #endif
129 #include <sys/mman.h>
130 #if defined(NEED_MMAP_WRAPPER)
131 }
132 #endif
133
134 extern "C" {
135 extern XtPointer _XmStringUngenerate (
136                                 XmString string,
137                                 XmStringTag tag,
138                                 XmTextType tag_type,
139                                 XmTextType output_type);
140 }
141
142
143 #include <sys/file.h>
144
145
146 extern forceUpdate( Widget );
147
148 RoamCmd::RoamCmd
149 (char *name, char *label, int active, RoamMenuWindow *window)
150 : NoUndoCmd (name, label, active)
151 {
152     _menuwindow = window;
153 }
154
155 #ifdef DEAD_WOOD
156 SearchCmd::SearchCmd(
157                      char *name, 
158                      char *label,
159                      int active, 
160                      RoamMenuWindow *window
161                      ) : InterruptibleCmd (name, label, active)
162 {
163     _menuwindow = window;
164     _criteria = NULL;
165 }
166
167 void
168 SearchCmd::execute ( 
169                      TaskDoneCallback callback, 
170                      void *clientData 
171                      )
172 {
173     InterruptibleCmd::execute( callback, clientData );
174 }
175
176 void
177 SearchCmd::execute()
178 {
179     
180     _menuwindow->list()->clearMsgs();
181     _menuwindow->busyCursor();
182     
183     if ( !_criteria ) {
184         _criteria=( char * )realloc(_criteria, strlen( this->name()) + 1);
185         strcpy(_criteria, this->name());
186     }
187     
188     InterruptibleCmd::execute();
189 }
190
191 void
192 SearchCmd::doit()
193 {
194     int         count;
195     DtMailEnv   mail_error;
196     
197     // Initialize the mail_error.
198     mail_error.clear();
199     
200     MsgScrollingList *list=_menuwindow->list();
201     
202     
203     // load_headers will retrieve all of the message headers and
204     // add the handles to the list.
205     //
206     count = list->load_headers(mail_error);
207     
208     _menuwindow->normalCursor();
209     
210     if (count == 0) {
211         _menuwindow->message(GETMSG(DT_catd, 3, 46, "Empty container"));
212         _done = TRUE;
213         return;
214     }
215     
216     list->scroll_to_bottom();
217     _done=TRUE;
218 }      
219
220 void
221 SearchCmd::undoit()
222 {
223     // Just print a message that allows us to trace the execution
224     
225     DebugPrintf(1, "%s: undoit\n", name());
226 }       
227
228 void
229 SearchCmd::updateMessage (char *msg)
230 {
231     InterruptibleCmd::updateMessage(msg);
232 }
233 #endif /* DEAD_WOOD */
234
235 CheckForNewMailCmd::CheckForNewMailCmd( 
236                                         char *name, 
237                                         char *label,
238                                         int active, 
239                                         RoamMenuWindow *window
240                                         ) :  NoUndoCmd ( name, label, active )
241 {
242     _menuwindow = window;
243 }
244
245 void
246 CheckForNewMailCmd::doit()
247 {
248     DtMailEnv error;
249     // Initialize the mail_error.
250     error.clear();
251     _menuwindow->checkForMail(error);
252     
253 }
254
255 OpenInboxCmd::OpenInboxCmd(
256     char *name,
257     char *label,
258     int active,
259     RoamMenuWindow *rmw
260 ) : Cmd (name, label, active)
261 {
262
263     _menuWindow = rmw;
264     
265 }
266
267 void
268 OpenInboxCmd::doit()
269 {
270     RoamMenuWindow *rmw = theRoamApp.inboxWindow();
271
272     if (rmw)
273     {
274         MailSession *ses = theRoamApp.session();
275         ses->activateRMW(rmw);
276         rmw->manage();
277     }
278     else
279     {
280         DtMailEnv       mail_error;
281         char            *mail_file = NULL;
282         DtMailObjectSpace space;
283         DtMail::Session *d_session = theRoamApp.session()->session();
284
285         mail_error.clear();
286         d_session->queryImpl(mail_error,
287                          d_session->getDefaultImpl(mail_error),
288                          DtMailCapabilityInboxName,
289                          &space,
290                          &mail_file);
291
292         // Check for error
293         if (mail_file) _menuWindow->view_mail_file(mail_file, DTM_FALSE);
294     }
295 }
296
297 void
298 OpenInboxCmd::undoit()
299 {
300 }
301
302 // 
303 // OpenContainerCmd methods implementation.
304 // For the most part, we treat container->open() as a benign thing.
305 // The magic, as we see it, deals with converting the container 
306 // if necessary.  
307 // For OpenContainerCmd, if no conversion is necessary bingo! it opens.
308 // If conversion is necessary, it punts the work to ConvertContainerCmd.
309 //
310 //
311
312 OpenContainerCmd::OpenContainerCmd (
313                                     char *name, 
314                                     char *label,
315                                     int active, 
316                                     RoamMenuWindow *window
317                                 ) : RoamInterruptibleCmd (name, label, active)
318 {
319     _menuWindow = window;
320 }
321
322 // Parent's execute() ends up calling derived class's doit()
323 void
324 OpenContainerCmd::execute()
325 {
326     RoamInterruptibleCmd::execute();
327 }
328
329 void
330 OpenContainerCmd::execute( 
331                            RoamTaskDoneCallback rtd_callback, 
332                            void *clientData
333                            )
334 {
335     RoamInterruptibleCmd::execute(rtd_callback, clientData);
336 }
337
338 // Tell the RMW to open.  The RMW may end up calling its convert()
339 // which depends on ConvertContainerCmd's doit...
340 // By the time RMW->open() returns, the conversion would have
341 // been done too.
342 // This is the case of a RinterruptibleCmd derived class
343 // getting its work done in its doit() in one call.
344 //
345
346 void
347 OpenContainerCmd::doit()
348 {
349     DtMailEnv error;
350     
351     assert(_menuWindow != NULL);
352     
353     // Initialize the mail_error.
354     error.clear();
355     
356     _menuWindow->open(error, _open_create_flag, _open_lock_flag);
357     if (error.isSet()) {
358         // Post a dialog indicating error and exit?
359         return;         // for now. Should exit instead?
360     }
361     _done = TRUE;
362 }      
363
364 void
365 OpenContainerCmd::undoit()
366 {
367     // Just print a message that allows us to trace the execution
368     
369     DebugPrintf(1, "%s: undoit\n", name());
370 }       
371
372 void
373 OpenContainerCmd::check_if_done()
374 {
375     // Have nothing fancy to do here.  Since we do not want a dialog
376     // in any case, set it to true...
377     
378     _done = TRUE;
379     
380 }
381
382
383 void
384 OpenContainerCmd::post_dialog()
385 {
386     // Empty.  We don't want a dialog on open...
387 }
388
389 void
390 OpenContainerCmd::unpost_dialog()
391 {
392     // Empty.  We don't post, and we don't unpost.
393 }
394 void
395 OpenContainerCmd::updateMessage (char *msg)
396 {
397     RoamInterruptibleCmd::updateMessage(msg);
398 }
399
400 void
401 OpenContainerCmd::set_create_lock_flags(
402                                         DtMailBoolean create,
403                                         DtMailBoolean lock
404                                         )
405 {
406     _open_create_flag = create;
407     _open_lock_flag   = lock;
408 }
409
410
411 // ConvertContainerCmd
412 // Here be dragons!
413
414 ConvertContainerCmd::ConvertContainerCmd(
415                                          char *name, 
416                                          char *label,
417                                          int active, 
418                                          RoamMenuWindow *window
419                                  ) : RoamInterruptibleCmd (name, label, active)
420 {
421     _menuWindow = window;
422     _num_converted = 0;
423     _num_to_be_converted = 0;
424     _dialog = NULL;
425 }
426
427 void
428 ConvertContainerCmd::execute()
429 {
430     if (!_dialog) {
431         _dialog = new DtMailWDM("Convert");
432     }
433     
434     RoamInterruptibleCmd::execute();
435 }
436
437 void
438 ConvertContainerCmd::execute ( RoamTaskDoneCallback rtd_callback, 
439                                void *clientData)
440 {
441     if (!_dialog) {
442         _dialog = new DtMailWDM("Convert");
443     }
444     
445     RoamInterruptibleCmd::execute(rtd_callback, clientData);
446 }
447
448 // Here be bigger dragons!
449 // The doit() calls the session->convert().
450 // And returns right away!
451 // the ses->convert() however ends up calling the conv_cb for every
452 // message that it has converted.
453 // So, we now have two loops working in parallel:
454 // 1) the parent()'s execute() class which called this doit() and is now
455 //    calling check_if_done() periodically via its workProc;
456 // 2) the session->convert() which is calling the _conv_cb() for every
457 //    message that it converts.  In the _conv_cb(), we do the following:
458 //    a) force update the dialog and see if it was interrupted;
459 //    b) if not interrupted, set_convert_data() where we set _done if 
460 //       we are really done.
461 //  
462
463 void
464 ConvertContainerCmd::doit()
465 {
466     assert(_menuWindow != NULL);
467     
468     MailSession *ses = theRoamApp.session();
469     DtMailEnv mail_error;
470     
471     // Initialize the mail_error.
472     mail_error.clear();
473     
474 //    ses->convert(mail_error, _src, _dest, _conv_cb, _menuWindow);
475     
476     if (mail_error.isSet()) {
477         _menuWindow->postErrorDialog(mail_error);
478     }
479 }      
480
481 void
482 ConvertContainerCmd::set_convert_data(
483                                       int converted,
484                                       int to_be_converted
485                                       )
486 {
487     _num_converted = converted;
488     _num_to_be_converted = to_be_converted;
489     
490     if ((_num_converted == _num_to_be_converted) && !_interrupted) {
491         _done = TRUE;
492     }
493 }
494
495 int
496 ConvertContainerCmd::get_num_converted()
497 {
498     return(_num_converted);
499 }
500
501
502 void
503 ConvertContainerCmd::check_if_done()
504 {
505     if (_interrupted) {
506         _done = FALSE;
507     }
508     else if (_num_converted == _num_to_be_converted) {
509         _done = TRUE;
510     }
511 }
512
513 void
514 ConvertContainerCmd::updateDialog(
515                                   char *msg
516                                   )
517 {
518     forceUpdate(_dialog->baseWidget());
519     _dialog->updateDialog( msg );  
520 }
521
522 void
523 ConvertContainerCmd::updateAnimation()
524 {
525     forceUpdate(_dialog->baseWidget());
526     _dialog->updateAnimation();
527 }
528
529 void
530 ConvertContainerCmd::post_dialog()
531 {
532     Dimension x, y, wid, ht;
533     
534     char * buf = new char[25];
535     
536     sprintf(buf, "Converted: %3d%", 0);
537     
538     _dialog->post ("Mailer",
539                    buf,
540                    _menuWindow->baseWidget(),
541                    (void *) this,
542                    NULL, 
543                    &RoamInterruptibleCmd::interruptCallback );
544     
545     XtVaGetValues(_dialog->baseWidget(),
546                   XmNx, &x,
547                   XmNy, &y,
548                   XmNwidth, &wid,
549                   XmNheight, &ht,
550                   NULL);
551     
552 }
553
554 void
555 ConvertContainerCmd::unpost_dialog()
556 {
557     _dialog->unpost();
558 }
559
560 void
561 ConvertContainerCmd::undoit()
562 {
563     // Just print a message that allows us to trace the execution
564     
565     DebugPrintf(1, "%s: undoit\n", name());
566 }       
567
568 void
569 ConvertContainerCmd::set_data(
570                               char *path1,
571                               char *path2,
572                               ConversionStatusCB cb
573                               )
574 {
575     _src = path1;
576     _dest = path2;
577     _conv_cb = cb;
578 }
579
580 char *
581 ConvertContainerCmd::get_destination_name()
582 {
583     return _dest;
584 }
585
586 // Here be sheep!
587 // FindCmd
588
589 FindCmd::FindCmd( 
590                   char *name, 
591                   char *label,
592                   int active,
593                   RoamMenuWindow *window 
594                   ) : RoamCmd ( name, label, active, window )
595 {
596 }
597
598 void
599 FindCmd::doit()
600 {
601     
602     _menuwindow->get_find_dialog();
603     
604 //  SearchCmd::doit();
605     
606 }
607
608
609 ChooseCmd::ChooseCmd( 
610                       char *name, 
611                       char *label,
612                       int active, 
613                       RoamMenuWindow *window
614                       ) :  NoUndoCmd( name, label, active )
615 {
616     _menuwindow = window;
617 }
618
619 void
620 ChooseCmd::doit()
621 {
622     
623 }
624
625 SelectAllCmd::SelectAllCmd(
626                             char *name,
627                             char *label,
628                             int active,
629                             RoamMenuWindow *window
630                             ) : Cmd ( name, label, active )
631 {
632     _menuwindow = window;
633 }
634
635 void
636 SelectAllCmd::doit()
637 {
638     DtMailEnv mail_error;
639
640     _menuwindow->list()->select_all_and_display_last(mail_error);
641 }
642
643 void
644 SelectAllCmd::undoit()
645 {
646 }
647
648
649 DeleteCmd::DeleteCmd( 
650                       char *name, 
651                       char *label,
652                       int active, 
653                       RoamMenuWindow *window
654                       ) :  Cmd ( name, label, active )
655 {
656     _menuwindow = window;
657 }
658
659 void
660 DeleteCmd::doit()
661 {
662     _menuwindow->list()->deleteSelected(FALSE);
663     
664 }
665
666 void
667 DeleteCmd::undoit()
668 {
669     
670 }
671
672 DestroyCmd::DestroyCmd(
673                         char *name, 
674                         char *label, 
675                         int active, 
676                         RoamMenuWindow *window
677                         ) : Cmd(name, label, active)
678 {
679     _menuwindow = window;
680 }
681
682 void
683 DestroyCmd::doit()
684 {
685     // Call Expunge only if there are deleted messages.
686
687     if (_menuwindow->list()->get_num_deleted_messages()) {
688         _menuwindow->expunge();
689     }
690 }
691
692 void
693 DestroyCmd::undoit()
694 {
695 }
696
697 // Unified Select File Cmd stuff
698 int   UnifiedSelectFileCmd::_is_initialized = 0;
699 char *UnifiedSelectFileCmd::_unified_directory = NULL;
700 char *UnifiedSelectFileCmd::_unified_file = NULL;
701 int   UnifiedSelectFileCmd::_unified_hidden = 0;
702 int   UnifiedSelectFileCmd::_unify_selection = 1;
703
704 UnifiedSelectFileCmd::UnifiedSelectFileCmd ( 
705                                         char   *name, 
706                                         char   *label,
707                                         char   *title,
708                                         char   *ok_label,
709                                         int    active, 
710                                         FileCallback
711                                                select_callback,
712                                         void   *client_data,
713                                         Widget parent)
714 :SelectFileCmd (name,
715                 label,
716                 title,
717                 ok_label,
718                 active,
719                 unifiedFileSelectedCB,
720                 this,
721                 unifiedFileCanceledCB,
722                 this,
723                 parent)
724 {
725    if (! _is_initialized)
726    {
727        FORCE_SEGV_DECL(DtMail::Session, m_session);
728        DtMailEnv        error;
729        const char       *dirname = NULL;
730        const char       *expanded_dirname = NULL;
731        const char       *value = NULL;
732        char             *full_dirname = NULL;
733
734        _unified_directory = NULL;
735        _unified_file = NULL;
736        _unified_hidden = 0;
737        _unify_selection = 1;
738        _is_initialized = 1;
739
740        m_session = theRoamApp.session()->session();
741
742        error.clear();
743        m_session->mailRc(error)->getValue(error, "filefolder", &dirname);
744        if (error.isSet()) {
745            dirname = strdup("~");
746            error.clear();
747        }
748        expanded_dirname = m_session->expandPath(error, dirname);
749        _unified_directory = XtNewString(expanded_dirname);
750
751        error.clear();
752        m_session->mailRc(error)->getValue(
753                                         error,
754                                         "dontunifyfileselection",
755                                         &value);
756        if (! error.isSet())
757          _unify_selection = 0;
758
759        free((void*) dirname);
760        free((void*) expanded_dirname);
761        free((void*) value);
762    }
763
764    _select_file_callback = select_callback;
765    _select_file_client_data = client_data;
766    _genDialog = new DtMailGenDialog("Dialog", parent);
767 }
768
769 UnifiedSelectFileCmd::~UnifiedSelectFileCmd()
770 {
771     if (_genDialog) delete _genDialog;
772 }
773
774 void
775 UnifiedSelectFileCmd::doit()
776 {
777     if (NULL == _fileBrowser)
778     {
779         SelectFileCmd::doit();
780         if (NULL != _unified_directory)
781           setDirectory(_unified_directory);
782         if (NULL != _unified_file)
783           setSelected(_unified_file);
784         setHidden(_unified_hidden);
785     }
786     else
787     {
788         if (_unify_selection)
789         {
790             if (NULL != _unified_directory)
791               setDirectory(_unified_directory);
792             if (NULL != _unified_file)
793               setSelected(_unified_file);
794             setHidden(_unified_hidden);
795         }
796         SelectFileCmd::doit();
797     }
798 }
799
800 void
801 UnifiedSelectFileCmd::unifiedFileSelectedCB(void *client_data, char *selection)
802 {
803     UnifiedSelectFileCmd *self = (UnifiedSelectFileCmd *) client_data;
804
805     if (NULL != self)
806     {
807         self->updateUnifiedData();
808         self->unifiedFileSelected(selection);
809     }
810 }
811
812 void
813 UnifiedSelectFileCmd::unifiedFileSelected(char *selection)
814 {
815     DtMailEnv   error;
816
817     SafePathIsAccessible(error, selection);
818     if (error.isSet())
819     {
820         const char *errmsg = NULL;
821         char *err;
822         int answer;
823
824         errmsg = (const char*) error;
825         err = strdup(errmsg);
826
827         _genDialog->setToErrorDialog(GETMSG(DT_catd, 3, 48, "Mailer"), err);
828         answer = _genDialog->post_and_return(DTMAILHELPERROR);
829         if (1 == answer) doit();
830         if (err) free(err);
831         return;
832     }
833
834     if (_select_file_callback)
835       _select_file_callback(_select_file_client_data, selection);
836 }
837
838 void
839 UnifiedSelectFileCmd::unifiedFileCanceledCB(void *client_data, char *)
840 {
841     UnifiedSelectFileCmd *self = (UnifiedSelectFileCmd *) client_data;
842
843     if (NULL != self)
844       self->updateUnifiedData();
845 }
846
847 void
848 UnifiedSelectFileCmd::updateUnifiedData()
849 {
850     if (! _unify_selection)
851       return;
852
853     if (NULL != _unified_file)
854       XtFree(_unified_file);
855     _unified_file = getSelected();
856
857
858     if (NULL != _unified_directory)
859       XtFree(_unified_directory);
860     _unified_directory = getDirectory();
861
862     _unified_hidden = getHidden();
863 }
864
865 // Unified Select Mailbox Cmd stuff
866 int   UnifiedSelectMailboxCmd::_is_initialized = 0;
867 char *UnifiedSelectMailboxCmd::_unified_directory = NULL;
868 char *UnifiedSelectMailboxCmd::_unified_file = NULL;
869 int   UnifiedSelectMailboxCmd::_unified_hidden = 0;
870 int   UnifiedSelectMailboxCmd::_unify_selection = 1;
871
872 UnifiedSelectMailboxCmd::UnifiedSelectMailboxCmd ( 
873                                         char   *name, 
874                                         char   *label,
875                                         char   *title,
876                                         char   *ok_label,
877                                         int    active, 
878                                         FileCallback
879                                                select_callback,
880                                         void   *client_data,
881                                         Widget parent,
882                                         DtMailBoolean only_show_mailboxes)
883 :SelectFileCmd (name,
884                 label,
885                 title,
886                 ok_label,
887                 active,
888                 unifiedMailboxSelectedCB,
889                 this,
890                 unifiedMailboxCanceledCB,
891                 this,
892                 parent)
893 {
894    if (! _is_initialized)
895    {
896        FORCE_SEGV_DECL(DtMail::Session, m_session);
897        DtMailEnv         error;
898        const char       *dirname = NULL;
899        const char       *expanded_dirname = NULL;
900        const char       *value = NULL;
901        char             *full_dirname = NULL;
902
903        _unified_directory = NULL;
904        _unified_file = NULL;
905        _unified_hidden = 0;
906        _unify_selection = 1;
907        _is_initialized = 1;
908        _only_show_mailboxes = only_show_mailboxes;
909
910        m_session = theRoamApp.session()->session();
911
912        error.clear();
913        m_session->mailRc(error)->getValue(error, "folder", &dirname);
914        if (error.isSet()) {
915            dirname = strdup("~");
916            error.clear();
917        }
918        expanded_dirname = m_session->expandPath(error, dirname);
919        _unified_directory = XtNewString(expanded_dirname);
920
921        error.clear();
922        m_session->mailRc(error)->getValue(
923                                         error,
924                                         "dontunifyfileselection",
925                                         &value);
926        if (! error.isSet())
927          _unify_selection = 0;
928
929        free((void*) dirname);
930        free((void*) expanded_dirname);
931        free((void*) value);
932    }
933
934    _select_file_callback = select_callback;
935    _select_file_client_data = client_data;
936    _genDialog = new DtMailGenDialog("Dialog", parent);
937 }
938
939 UnifiedSelectMailboxCmd::~UnifiedSelectMailboxCmd()
940 {
941     if (_genDialog) delete _genDialog;
942 }
943
944 void
945 UnifiedSelectMailboxCmd::doit()
946 {
947     if (NULL == _fileBrowser)
948     {
949         SelectFileCmd::doit();
950         if (NULL != _unified_directory)
951           setDirectory(_unified_directory);
952         if (NULL != _unified_file)
953           setSelected(_unified_file);
954         setHidden(_unified_hidden);
955
956         if (_fileBrowser)
957           XtVaSetValues(
958                 _fileBrowser,
959                 XmNfileSearchProc,
960                 UnifiedSelectMailboxCmd::unifiedMailboxSearchProc,
961                 NULL);
962     }
963     else
964     {
965         if (_unify_selection)
966         {
967             if (NULL != _unified_directory)
968               setDirectory(_unified_directory);
969             if (NULL != _unified_file)
970               setSelected(_unified_file);
971             setHidden(_unified_hidden);
972         }
973         SelectFileCmd::doit();
974     }
975 }
976
977 extern "C" {
978 extern void     _XmOSBuildFileList(
979                         String dirPath,
980                         String pattern,
981 #if NeedWidePrototypes
982                         unsigned int typeMask,
983 #else
984                         unsigned char typeMask,
985 #endif /* NeedWidePrototypes */
986                         String **pEntries,
987                         unsigned int *pNumEntries,
988                         unsigned int *pNumAlloc);
989
990 extern int      _XmOSFileCompare(const void *sp1, const void *sp2);
991 extern char     *_XmStringGetTextConcat(XmString string);
992 }
993
994 void
995 UnifiedSelectMailboxCmd::unifiedMailboxSearchProc(
996                                         Widget w,
997                                         XtPointer sd)
998 {   
999     XmFileSelectionBoxWidget fs =
1000                                 (XmFileSelectionBoxWidget) w;
1001     XmFileSelectionBoxCallbackStruct * searchData =
1002                                 (XmFileSelectionBoxCallbackStruct *) sd;
1003     String          dir;
1004     String          pattern;
1005     Arg             args[3];
1006     int             Index;
1007     String *        fileList;
1008     unsigned int    numFiles;
1009     unsigned int    numItems = 0;
1010     unsigned int    numAlloc;
1011     XmString *      XmStringFileList;
1012     unsigned        dirLen;
1013     XtEnum          fileFilterStyle, pathMode;
1014     unsigned char   fileTypeMask;
1015
1016     if (!(dir = _XmStringGetTextConcat(searchData->dir)))
1017       return ;
1018     
1019     if (!(pattern = _XmStringGetTextConcat(searchData->pattern)))
1020     {
1021         XtFree(dir);
1022         return;
1023     } 
1024     fileList = NULL;
1025
1026     XtVaGetValues(
1027                 w,
1028                 XmNfileTypeMask, &fileTypeMask,
1029                 XmNfileFilterStyle, &fileFilterStyle,
1030                 XmNpathMode, &pathMode,
1031                 NULL);
1032
1033     _XmOSBuildFileList(
1034                 dir, pattern, fileTypeMask,
1035                 &fileList,  &numFiles, &numAlloc);
1036
1037     if (fileList && numFiles)
1038     {
1039         Boolean showDotFiles = (fileFilterStyle == XmFILTER_NONE);
1040
1041         if (numFiles > 1)
1042           qsort((void*) fileList, numFiles, sizeof(char*), _XmOSFileCompare);
1043         
1044         XmStringFileList = (XmString*) XtMalloc(numFiles*sizeof(XmString));
1045         
1046         Index = 0;
1047         dirLen = strlen(dir);
1048
1049         while (Index < numFiles)
1050         {
1051             Boolean     isMailBox = 0;
1052             char        *dataType = NULL;
1053
1054             dataType = DtDtsFileToDataType(fileList[Index]);
1055             if (dataType)
1056               isMailBox = (0 == strcmp(dataType, "DTMAIL_FILE"));
1057             DtDtsFreeDataType(dataType);
1058
1059             if (isMailBox &&
1060                 (showDotFiles || ((fileList[Index])[dirLen] != '.')) )
1061             {   
1062                 if (pathMode ==  XmPATH_MODE_FULL)
1063                   XmStringFileList[numItems++] = 
1064                         XmStringGenerate(fileList[Index],
1065                                          XmFONTLIST_DEFAULT_TAG,
1066                                          XmCHARSET_TEXT, NULL);
1067                 else 
1068                   XmStringFileList[numItems++] = 
1069                         XmStringGenerate(&(fileList[Index])[dirLen],
1070                                          XmFONTLIST_DEFAULT_TAG,
1071                                          XmCHARSET_TEXT, NULL) ;
1072             } 
1073             ++Index ;
1074         } 
1075
1076         /* Update the list.
1077         */
1078         Index = 0 ;
1079         XtVaSetValues(
1080                 w,
1081                 XmNfileListItemCount, numItems,
1082                 XmNfileListItems, XmStringFileList,
1083                 XmNlistUpdated, TRUE,
1084                 NULL);
1085
1086         Index = numFiles;
1087         while(Index--)
1088           XtFree( fileList[Index]);
1089
1090         while(numItems--)
1091           XmStringFree(XmStringFileList[numItems]);
1092
1093         XtFree((char*) XmStringFileList);
1094     }
1095     else
1096     {
1097         XtVaSetValues(
1098                 w,
1099                 XmNfileListItemCount, 0,
1100                 XmNfileListItems, NULL,
1101                 XmNlistUpdated, TRUE,
1102                 NULL);
1103     } 
1104
1105     XtFree((char *) fileList);
1106     XtFree(pattern);
1107     XtFree(dir);
1108     return;
1109 }
1110
1111 void
1112 UnifiedSelectMailboxCmd::unifiedMailboxSelectedCB(
1113                                         void *client_data,
1114                                         char *selection)
1115 {
1116     UnifiedSelectMailboxCmd *self = (UnifiedSelectMailboxCmd *) client_data;
1117
1118     if (NULL != self)
1119     {
1120         self->updateUnifiedData();
1121         self->unifiedMailboxSelected(
1122                                 self->_select_file_callback,
1123                                 self->_select_file_client_data,
1124                                 selection);
1125     }
1126 }
1127
1128 void
1129 UnifiedSelectMailboxCmd::unifiedMailboxSelected(
1130                                                 FileCallback    cb,
1131                                                 void            *client_data,
1132                                                 char            *selection)
1133 {
1134     DtMailEnv   error;
1135
1136     SafePathIsAccessible(error, selection);
1137     if (error.isSet())
1138     {
1139         const char *errmsg = NULL;
1140         char *err = NULL;
1141         int answer;
1142
1143         errmsg = (const char*) error;
1144         err = strdup(errmsg);
1145
1146         _genDialog->setToErrorDialog(GETMSG(DT_catd, 3, 48, "Mailer"), err);
1147         answer = _genDialog->post_and_return(DTMAILHELPERROR);
1148         if (1 == answer) doit();
1149         if (err) free(err);
1150         return;
1151     }
1152
1153     updateUnifiedData();
1154     if (cb) cb(client_data, selection);
1155 }
1156
1157 void
1158 UnifiedSelectMailboxCmd::unifiedMailboxCanceledCB(void *client_data, char *)
1159 {
1160     UnifiedSelectMailboxCmd *self = (UnifiedSelectMailboxCmd *) client_data;
1161
1162     if (NULL != self)
1163       self->updateUnifiedData();
1164 }
1165
1166 void
1167 UnifiedSelectMailboxCmd::updateUnifiedData()
1168 {
1169     if (! _unify_selection)
1170       return;
1171
1172     if (NULL != _unified_file)
1173       XtFree(_unified_file);
1174     _unified_file = getSelected();
1175
1176
1177     if (NULL != _unified_directory)
1178       XtFree(_unified_directory);
1179     _unified_directory = getDirectory();
1180
1181     _unified_hidden = getHidden();
1182 }
1183
1184 ContainerMenuCmd::ContainerMenuCmd(
1185     char *name,
1186     char *label,
1187     int active,
1188     RoamMenuWindow *window,
1189     ContainerOp op
1190 ) : RoamCmd ( name, label, active, window )
1191 {
1192     _menuwindow = window;
1193     _container_name = name;
1194     _operation = op;
1195 }
1196
1197 void
1198 ContainerMenuCmd::doit()
1199 {
1200     DtMailEnv mail_error;
1201
1202     // Initialize mail_error.
1203     mail_error.clear();
1204
1205     theRoamApp.busyAllWindows(GETMSG(DT_catd, 3, 15, "Saving..."));
1206     _menuwindow->mailbox()->save();
1207     theRoamApp.unbusyAllWindows();
1208
1209     switch (_operation)
1210     {
1211         case DTM_NONE:
1212             break;
1213         case DTM_OPEN:
1214             _menuwindow->view_mail_file(_container_name, DTM_FALSE);
1215             break;
1216         case DTM_COPY:
1217             _menuwindow->list()->copySelected(
1218                                         mail_error,
1219                                         _container_name,
1220                                         FALSE, FALSE);
1221             if (mail_error.isSet())
1222             {
1223                 // We had an error in copying the message to a container!
1224             }
1225             break;
1226         case DTM_MOVE:
1227             _menuwindow->list()->copySelected(
1228                                         mail_error,
1229                                         _container_name,
1230                                         TRUE, FALSE);
1231             if (mail_error.isSet())
1232             {
1233                 // We had an error in moving the message to a container!
1234             }
1235             break;
1236     }
1237 }
1238
1239 ContainerMenuCmd::~ContainerMenuCmd()
1240 {
1241 }
1242
1243     
1244 // Move the messages that are selected in the RoamMenuWindow to the Inbox.
1245 MoveToInboxCmd::MoveToInboxCmd(
1246                                char *name,
1247                                char *label,
1248                                int active,
1249                                RoamMenuWindow *window
1250                                ) : RoamCmd (name, label, active, window)
1251 {
1252     _menuwindow = window;
1253 }
1254
1255 void
1256 MoveToInboxCmd::doit()
1257 {
1258     DtMailEnv mail_error;
1259     
1260     // Initialize mail_error.
1261     mail_error.clear();
1262     
1263     // Get a handle to the Inbox.
1264     char * mail_file = NULL;
1265     DtMailObjectSpace space;
1266     DtMail::Session * d_session = theRoamApp.session()->session();
1267     
1268     d_session->queryImpl(mail_error,
1269                          d_session->getDefaultImpl(mail_error),
1270                          DtMailCapabilityInboxName,
1271                          &space,
1272                          &mail_file);
1273     _menuwindow->list()->copySelected(mail_error, mail_file, TRUE, FALSE);
1274     if (mail_error.isSet()) {
1275         // We had an error in moving the messages to the Inbox!
1276     }
1277 }
1278
1279 MoveToInboxCmd::~MoveToInboxCmd()
1280 {
1281 }
1282
1283 // Copy the selected messages to the Inbox.
1284 CopyToInboxCmd::CopyToInboxCmd(
1285                              char *name,
1286                              char *label,
1287                              int active,
1288                              RoamMenuWindow *window
1289                              ) : RoamCmd (name, label, active, window)
1290 {
1291     _menuwindow = window;
1292 }
1293  
1294 void
1295 CopyToInboxCmd::doit()
1296 {
1297     DtMailEnv mail_error;
1298  
1299     // Initialize mail_error.
1300     mail_error.clear();
1301  
1302     // Get a handle to the Inbox.
1303     char * mail_file = NULL;
1304     DtMailObjectSpace space;
1305     DtMail::Session * d_session = theRoamApp.session()->session();
1306  
1307     d_session->queryImpl(mail_error,
1308                          d_session->getDefaultImpl(mail_error),
1309                          DtMailCapabilityInboxName,
1310                          &space,
1311                          &mail_file);
1312     _menuwindow->list()->copySelected(mail_error, mail_file, FALSE, FALSE);
1313     if (mail_error.isSet()) {
1314         // We ad an error in copying the messages to the Inbox!
1315     }
1316 }
1317
1318 CopyToInboxCmd::~CopyToInboxCmd()
1319 {
1320 }
1321
1322
1323 // This is hooked up the Undelete button in the Deleted
1324 // Messages List dialog box.
1325
1326 DoUndeleteCmd::DoUndeleteCmd( 
1327                               char *name, 
1328                               char *label,
1329                               int active, 
1330                               UndelFromListDialog *undelDialog
1331                               ) :  Cmd ( name, label, active )
1332 {
1333     _undelDialog = undelDialog;
1334 }
1335
1336 void
1337 DoUndeleteCmd::doit()
1338 {
1339     // Undelete the selected messages.
1340     _undelDialog->undelSelected();
1341 }
1342
1343 void
1344 DoUndeleteCmd::undoit()
1345 {
1346     // nothing
1347 }
1348
1349 DoUndeleteCmd::~DoUndeleteCmd()
1350 {
1351 }
1352
1353 // This is hooked up to the Close button in the Deleted Messages
1354 // List dialog box.
1355
1356 CloseUndelCmd::CloseUndelCmd( 
1357                               char *name, 
1358                               char *label,
1359                               int active,
1360                               UndelFromListDialog *undelDialog
1361                               ) :  Cmd ( name, label, active )
1362 {
1363     _undelDialog = undelDialog;
1364 }
1365
1366 void
1367 CloseUndelCmd::doit()
1368 {
1369     // Close the dialog.
1370     _undelDialog->popped_down();
1371 }
1372
1373 void
1374 CloseUndelCmd::undoit()
1375 {
1376     // nothing
1377 }
1378
1379
1380 CloseUndelCmd::~CloseUndelCmd()
1381 {
1382 }
1383
1384 UndeleteCmd::UndeleteCmd ( 
1385                            char *name, 
1386                            char *label,
1387                            int active, 
1388                            RoamMenuWindow *window,
1389                            Boolean viaDeleteList
1390                            ) : ChooseCmd  ( name, label, active, window )
1391 {
1392     _menuwindow = window;
1393     _undelFromList = NULL;
1394     _fromList = viaDeleteList;
1395 }
1396
1397 UndeleteCmd::~UndeleteCmd()
1398 {
1399 }
1400
1401 void
1402 UndeleteCmd::doit()
1403 {
1404     FORCE_SEGV_DECL(MsgStruct, tmpMS);
1405     MsgScrollingList *list = _menuwindow->list();
1406     MsgHndArray *deleted_messages;
1407     DtMailEnv mail_error;
1408     
1409     // Initialize the mail_error.
1410     mail_error.clear();
1411     
1412     
1413     if (_fromList) {
1414         // Create the Deleted Messages Dialog
1415         
1416         if (_undelFromList) {
1417             // Hack for user testing.  If the dialog is up, we destroy it.
1418             XtDestroyWidget(_undelFromList->baseWidget());
1419         }
1420 //      if (!_undelFromList) {
1421         _undelFromList = new UndelFromListDialog(
1422                          GETMSG(DT_catd, 1, 227, "Mailer - Deleted Messages"), 
1423                          _menuwindow);  
1424         _undelFromList->initialize();
1425         
1426         // Check for existing list of deleted messages
1427         _num_deleted = list->get_num_deleted_messages();
1428         
1429         // If there are deleted messages, put them in the Deleted
1430         // Messages List.
1431         
1432         if (_num_deleted > 0) {
1433             deleted_messages = list->get_deleted_messages();
1434             _undelFromList->loadMsgs(
1435                                 mail_error, 
1436                                 deleted_messages, 
1437                                 _num_deleted);
1438             if (mail_error.isSet()) {
1439                 // Post an exception here!
1440                 _menuwindow->postErrorDialog(mail_error);
1441             }
1442                 
1443         }
1444         // Display the dialog
1445         
1446         _undelFromList->popped_up();
1447     } else {
1448         // Although we don't display the Deleted Message Dialog here, we
1449         // need to make sure that it gets updated for the next time
1450         // we bring it up.
1451         list->undelete_last_deleted();
1452     }
1453 }
1454
1455
1456 #ifdef DEAD_WOOD
1457 SaveCmd::SaveCmd ( char *name, 
1458                    char *label, 
1459                    int active, 
1460                    RoamMenuWindow *window 
1461                 ) : RoamCmd ( name, label, active, window )
1462 {
1463     
1464 }
1465
1466 void
1467 SaveCmd::doit()
1468 {
1469     
1470     assert(_menuwindow->mailbox() != NULL);
1471 }
1472 #endif /* DEAD_WOOD */
1473
1474
1475
1476 MoveCopyCmd::MoveCopyCmd( char *name, 
1477                           char *label,
1478                           int active, 
1479                           FileCallback move_callback, 
1480                           FileCallback copy_callback, 
1481                           RoamMenuWindow * menu_window,
1482                           Widget parent,
1483                           DtMailBoolean only_show_mailboxes)
1484 : UnifiedSelectMailboxCmd(name,
1485                           label,
1486                           GETMSG(DT_catd, 1, 89, "Mailer - Other Mailboxes"),
1487                           "Move",
1488                           active, 
1489                           move_callback, 
1490                           menu_window,
1491                           parent,
1492                           only_show_mailboxes)
1493 {
1494     _copy_callback = copy_callback;
1495     _menuwindow = menu_window;
1496     _copy_button = NULL;
1497     _move_button = NULL;
1498 }
1499
1500 MoveCopyCmd::~MoveCopyCmd()
1501 {
1502 }
1503
1504 void
1505 MoveCopyCmd::setDefault(Widget button)
1506 {
1507     Arg args[1];
1508
1509     _default_button = button;
1510     XtSetArg( args[0], XmNdefaultButton, _default_button );
1511     XtSetValues( _fileBrowser, args, 1 );
1512 }
1513
1514 void
1515 MoveCopyCmd::doit()
1516 {
1517     XmString move;
1518     Widget filter_button;
1519     Widget unused_button;
1520     Widget action_area;
1521     DtMailEnv error;
1522     
1523     if (!_fileBrowser) {
1524         UnifiedSelectMailboxCmd::doit();
1525         // Customize buttons for MoveCopy dialog
1526         move = XmStringCreateLocalized(GETMSG(DT_catd, 1, 90, "Move"));
1527         
1528         filter_button = XtNameToWidget(_fileBrowser, "*Apply");
1529         _move_button = XtNameToWidget(_fileBrowser, "*OK");
1530         action_area = XtParent(_move_button);
1531         unused_button = XtVaCreateWidget(
1532                         "Unused Button",
1533                         xmPushButtonWidgetClass, _fileBrowser,
1534                         NULL);
1535         _copy_button = XtVaCreateManagedWidget(
1536                         GETMSG(DT_catd, 1, 237, "Copy"),
1537                         /*xmPushButtonWidgetClass, _fileBrowser,*/
1538                         xmPushButtonGadgetClass, _fileBrowser,
1539                         XmNlabelString,
1540                         XmStringCreateLocalized(GETMSG(DT_catd, 1, 43, "Copy")),
1541                         NULL);
1542         printHelpId("Copy", _copy_button);
1543         //
1544         // add help callback
1545         // XtAddCallback(_copy_button, XmNhelpCallback, HelpCB, helpId);
1546         //
1547         XtAddCallback(
1548                 _copy_button,
1549                 XmNhelpCallback, 
1550                 HelpCB,
1551                 "dtmailViewmainWindowWork-AreapanedWform2RowColumnMoveCopy");
1552         XtAddCallback(
1553                 _copy_button,
1554                 XmNactivateCallback,
1555                 &MoveCopyCmd::fileSelectedCallback2,
1556                 (XtPointer) this );
1557
1558         if (_menuwindow->mailbox()->mailBoxWritable(error) == DTM_FALSE)
1559           XtUnmanageChild(_move_button);
1560         else
1561           XtManageChild(_move_button);
1562
1563 //  XtVaSetValues(_move_button, XmNsensitive, FALSE);
1564
1565         _file_list = XtNameToWidget(_fileBrowser, "*ItemsList");
1566         XtAddCallback(
1567                 _file_list,
1568                 XmNbrowseSelectionCallback, &MoveCopyCmd::setDefaultButtonCB,
1569                 (XtPointer) this);
1570         XtAddCallback(
1571                 _file_list,
1572                 XmNextendedSelectionCallback, &MoveCopyCmd::setDefaultButtonCB,
1573                 (XtPointer) this);
1574         XtAddCallback(
1575                 _file_list,
1576                 XmNmultipleSelectionCallback, &MoveCopyCmd::setDefaultButtonCB,
1577                 (XtPointer) this);
1578         XtAddCallback(
1579                 _file_list,
1580                 XmNsingleSelectionCallback, &MoveCopyCmd::setDefaultButtonCB,
1581                 (XtPointer) this);
1582
1583         _file_text = XtNameToWidget(_fileBrowser, "*Text");
1584         if (NULL != _file_text)
1585           XtAddCallback(
1586                 _file_text,
1587                 XmNfocusCallback, &MoveCopyCmd::setDefaultButtonCB,
1588                 (XtPointer) this);
1589
1590         XmStringFree(move);
1591
1592     } else {
1593         UnifiedSelectMailboxCmd::doit();
1594     }
1595     
1596 }
1597
1598 void
1599 MoveCopyCmd::fileSelectedCallback2 ( 
1600                                      Widget     ,
1601                                      XtPointer  clientData,
1602                                      XtPointer  callData
1603                                      )
1604 {
1605     MoveCopyCmd *obj = (MoveCopyCmd *) clientData;
1606     XmFileSelectionBoxCallbackStruct *cb =
1607                 (XmFileSelectionBoxCallbackStruct *) callData;
1608     char        *name = NULL;
1609     char        *dir_str = NULL;
1610     char        *fname = NULL;
1611     char        *dname = NULL;
1612     int         status = 0;
1613     XmString    xmstr;
1614     
1615     static char selected[MAXPATHLEN+1];
1616
1617     // Bring the file selection dialog down.
1618     XtUnmanageChild ( obj->_fileBrowser );  
1619
1620     //
1621     // Get the file name
1622     //
1623     XtVaGetValues(obj->_fileBrowser, XmNdirectory, &xmstr, NULL);
1624     if (xmstr)
1625       dname = (char*) _XmStringUngenerate(
1626                                         xmstr, NULL,
1627                                         XmMULTIBYTE_TEXT, XmMULTIBYTE_TEXT);
1628     
1629     //
1630     // Get the file name
1631     //
1632     XtVaGetValues(obj->_fileBrowser, XmNdirSpec, &xmstr, NULL);
1633     if (xmstr)
1634     {
1635         // Extract the first character string matching the default
1636         // character set from the compound string
1637         fname = (char *) _XmStringUngenerate(
1638                                         xmstr, NULL,
1639                                         XmMULTIBYTE_TEXT, XmMULTIBYTE_TEXT);
1640           
1641         if (NULL == fname || strlen(fname) == 0)
1642           return;
1643
1644         // If a string was successfully extracted, call
1645         // unifiedMailboxSelected to handle the file.
1646         *selected = '\0';
1647         if (NULL != dname) strcat(selected, dname);
1648         strcat(selected, fname);
1649         obj->updateUnifiedData();
1650         obj->unifiedMailboxSelected(
1651                                 obj->_copy_callback,
1652                                 obj->_menuwindow,
1653                                 selected);
1654     }
1655 }
1656
1657 void
1658 MoveCopyCmd::setDefaultButtonCB( 
1659                              Widget,
1660                              XtPointer  clientData,
1661                              XtPointer)
1662 {
1663     MoveCopyCmd *thisCmd = (MoveCopyCmd *) clientData;
1664     thisCmd->setDefault(thisCmd->_default_button);
1665 }
1666
1667
1668 CopyCmd::CopyCmd( char *name, 
1669                    char *label, 
1670                    int active, 
1671                    RoamMenuWindow *window,
1672                    MoveCopyCmd *move_copy_cmd
1673                 ) : RoamCmd ( name, label, active, window )
1674 {
1675     _move_copy_cmd = move_copy_cmd;
1676 }
1677
1678 CopyCmd::~CopyCmd()
1679 {
1680 }
1681
1682 void
1683 CopyCmd::doit()
1684 {
1685     _move_copy_cmd->doit();
1686     _move_copy_cmd->setDefault(_move_copy_cmd->getCopyButton());
1687 }
1688
1689
1690 MoveCmd::MoveCmd( char *name, 
1691                    char *label, 
1692                    int active, 
1693                    RoamMenuWindow *window,
1694                    MoveCopyCmd *move_copy_cmd
1695                 ) : RoamCmd ( name, label, active, window )
1696 {
1697     _move_copy_cmd = move_copy_cmd;
1698 }
1699
1700 MoveCmd::~MoveCmd()
1701 {
1702 }
1703
1704 void
1705 MoveCmd::doit()
1706 {
1707     _move_copy_cmd->doit();
1708     _move_copy_cmd->setDefault(_move_copy_cmd->getMoveButton());
1709 }
1710
1711
1712 NextCmd::NextCmd( 
1713                   char *name, 
1714                   char *label,
1715                   int active, 
1716                   RoamMenuWindow *window 
1717                   ) : RoamCmd ( name, label, active, window )
1718 {
1719 }
1720
1721 void
1722 NextCmd::doit()
1723 {
1724     _menuwindow->list()->select_next_item();
1725 }
1726
1727
1728 PrevCmd::PrevCmd( 
1729                   char *name, 
1730                   char *label,
1731                   int active, 
1732                   RoamMenuWindow *window 
1733                   ) : RoamCmd ( name, label, active, window )
1734 {
1735 }
1736
1737 void
1738 PrevCmd::doit()
1739 {
1740     _menuwindow->list()->select_prev_item();
1741 }
1742
1743 #ifdef DEAD_WOOD
1744 MessagesCmd::MessagesCmd( 
1745                           char *name, 
1746                           char *label,
1747                           int active, 
1748                           RoamMenuWindow *window 
1749                           ) : RoamCmd ( name, label, active, window )
1750 {
1751 }
1752
1753
1754 void
1755 MessagesCmd::doit()
1756 {
1757     Boolean old=_menuwindow->fullHeader();
1758     MsgScrollingList *list=_menuwindow->list();
1759     
1760     ( !strcmp( this->name(), "Full Header" ) ? _menuwindow->fullHeader( True ) : _menuwindow->fullHeader( False ) );
1761     
1762     if ( old!=_menuwindow->fullHeader() && _menuwindow->msgView() ) {
1763 //    list->chooseCurrent();
1764     }
1765     
1766 }
1767 #endif /* DEAD_WOOD */
1768
1769 PrintCmd::PrintCmd ( 
1770                      char *name, 
1771                      char *label,
1772                      int active, 
1773                      int silent,
1774                      RoamMenuWindow *window 
1775                  ) : ChooseCmd ( name, label, active, window ), _tmp_files(5)
1776 {
1777     _parent = window;
1778     _silent = silent;
1779 }
1780
1781 void
1782 PrintCmd::doit()
1783 {
1784     // The entire implementation of print was broken. It has
1785     // be removed until a proper implementation can be provided.
1786     // dlp 10/04/93
1787     
1788     printit(_silent);
1789     return;
1790 }
1791
1792 void
1793 PrintCmd::actioncb(
1794                    DtActionInvocationID id,
1795                    XtPointer     clientData,
1796                    DtActionArg *,
1797                    int,
1798                    int           status
1799                    )
1800 {
1801     PrintCmd    *data;
1802     
1803     switch (status) {
1804       case DtACTION_INVOKED:
1805         break;
1806       default:
1807         data = (PrintCmd *)clientData;
1808         data->_parent->message("");
1809         data->_unregister_tmp_file(id);
1810         break;
1811     }
1812     
1813     return;
1814 }
1815
1816 void
1817 PrintCmd::printjobcb( Widget w, XtPointer client_data, XtPointer )
1818 {
1819     char *filename = (char *) client_data;
1820
1821     XtRemoveCallback(w, XtNdestroyCallback, &PrintCmd::printjobcb, filename);
1822
1823     unlink(filename);
1824     free(filename);
1825 }
1826
1827 void
1828 PrintCmd::printit( int silent )
1829 {
1830     char *p;
1831     char *silent_str = "DTPRINTSILENT";
1832     char *tmpdir = new char[MAXPATHLEN+1];
1833     DtMailEnv   mail_error;
1834     MsgScrollingList    *list;
1835     
1836     DebugPrintf(1, "%s: printit\n", name());
1837     
1838     // Create tmp file.
1839     sprintf(tmpdir, "%s/%s", getenv("HOME"), DtPERSONAL_TMP_DIRECTORY);
1840     if ((p = tempnam(tmpdir, "dtmail")) == NULL) {
1841         delete [] tmpdir;
1842         return;
1843     }
1844     delete [] tmpdir;
1845     
1846     mail_error.clear();
1847     list = _parent->list();
1848     
1849     // Copy selected messages to a temp file
1850     int status = list->copySelected(mail_error, p, FALSE, TRUE);
1851     if (mail_error.isSet())
1852     {
1853         _parent->postErrorDialog(mail_error);
1854         free(p);
1855         return;
1856     }
1857     if (0 != status) return;
1858     
1859
1860     DmxPrintJob *pjob = new DmxPrintJob(p,
1861                                         (silent ? DTM_TRUE : DTM_FALSE),
1862                                         _parent);
1863
1864     XtAddCallback(pjob->baseWidget(),
1865                 XtNdestroyCallback,
1866                 &PrintCmd::printjobcb,
1867                 (XtPointer) strdup(p));
1868
1869     pjob->execute();
1870     free(p);
1871     return;
1872 }
1873
1874 int
1875 PrintCmd::_register_tmp_file(
1876                              const char         *name,
1877                              DtActionInvocationID       id
1878                              )
1879 {
1880     struct tmp_file     *f;
1881     
1882     // Allocate struct to hold id and temp file
1883     if ((f = (struct tmp_file *)malloc(sizeof(struct tmp_file))) == NULL) {
1884         return -1;
1885     }
1886     
1887     // Save file name and action id
1888     f->file = strdup(name);
1889     f->id = id;
1890     
1891     // Add to list of temp files
1892     _tmp_files.append(f);
1893     
1894     return 0;
1895 }
1896
1897 void
1898 PrintCmd::_unregister_tmp_file(
1899                                DtActionInvocationID     id
1900                                )
1901 {
1902     int n;
1903     struct tmp_file *f;
1904     
1905     // Find the temp file that was used by the Action specified by id
1906     for (n = _tmp_files.length() - 1; n >= 0; n--) {
1907         f = _tmp_files[n];
1908         if (f->id == id) {
1909             // Found the file.  Unlink and free data structs
1910             unlink(f->file);
1911             free(f->file);
1912             free(f);
1913             // Remove entry from list
1914             _tmp_files.remove(n);
1915             break;
1916         }
1917     }
1918     
1919     return;
1920 }
1921
1922 #ifdef DEAD_WOOD
1923 PopupCmd::PopupCmd ( 
1924                      char *name, 
1925                      char *label,
1926                      int active,
1927                      PopupWindow * (RoamMenuWindow::* member) (void), 
1928                      RoamMenuWindow *myparent 
1929                      ) : NoUndoCmd ( name, label, active )
1930 {
1931     parent=myparent;
1932     pmpopup=member;
1933 }
1934
1935 void
1936 PopupCmd::doit()
1937 {
1938     PopupWindow *popup=(parent->*pmpopup)();
1939 //  popup->manage();
1940 }
1941 #endif /* DEAD_WOOD */
1942
1943 // OnItemCmd brings up the Help On Item help.
1944 OnItemCmd::OnItemCmd ( char * name, 
1945                        char *label, 
1946                        int active, 
1947                        UIComponent *window ) 
1948 : NoUndoCmd (name, label, active)
1949 {
1950     _parent = window;
1951 }
1952
1953 void
1954 OnItemCmd::doit()
1955 {
1956     int status = DtHELP_SELECT_ERROR;
1957     Widget widget = _parent->baseWidget();
1958     Widget selWidget = NULL;
1959     
1960     // Display the appropriate help information for the selected item.
1961     
1962     status = DtHelpReturnSelectedWidgetId(widget, 0, &selWidget);
1963     
1964     switch ((int) status) {
1965       case DtHELP_SELECT_ERROR:
1966         printf("Selection Error, cannot continue\n");
1967         break;
1968       case DtHELP_SELECT_VALID:
1969         while (selWidget != NULL) {
1970             if ((XtHasCallbacks(selWidget, XmNhelpCallback)
1971                  == XtCallbackHasSome)) {
1972                 XtCallCallbacks((Widget) selWidget, XmNhelpCallback, NULL);
1973                 break;
1974             } else {
1975                 selWidget = XtParent(selWidget);
1976             }
1977         }
1978         break;
1979       case DtHELP_SELECT_ABORT:
1980         printf("Selection Aborted by user.\n");
1981         break;
1982       case DtHELP_SELECT_INVALID:
1983         printf("You must select a component within your app.\n");
1984         break;
1985       default:
1986         ;
1987         // Empty
1988     }
1989     
1990 }
1991
1992 OnAppCmd::OnAppCmd ( char * name, 
1993                      char *label,
1994                      int active, 
1995                      UIComponent *window ) 
1996 : NoUndoCmd (name, label, active)
1997 {
1998     _parent = window;
1999 }
2000
2001 void
2002 OnAppCmd::doit()
2003 {
2004     DisplayMain (_parent->baseWidget(), "Mailer", DTMAILWINDOWID);
2005 }
2006
2007 TasksCmd::TasksCmd ( char * name, 
2008                      char *label,
2009                      int active, 
2010                      UIComponent *window )
2011 : NoUndoCmd (name, label, active)
2012 {
2013     _parent = window;
2014 }
2015
2016 void
2017 TasksCmd::doit()
2018 {
2019     DisplayMain (_parent->baseWidget(), "Mailer", HELP_MAILER_TASKS);
2020 }
2021
2022 ReferenceCmd::ReferenceCmd ( char * name, 
2023                              char *label,
2024                              int active, 
2025                              UIComponent *window )
2026 : NoUndoCmd (name, label, active)
2027 {
2028     _parent = window;
2029 }
2030
2031 void
2032 ReferenceCmd::doit()
2033 {
2034     DisplayMain (_parent->baseWidget(), "Mailer", HELP_MAILER_REFERENCE);
2035 }
2036
2037 UsingHelpCmd::UsingHelpCmd ( char * name, 
2038                              char *label,
2039                              int active, 
2040                              UIComponent *window )
2041 : NoUndoCmd (name, label, active)
2042 {
2043     _parent = window;
2044 }
2045
2046 void
2047 UsingHelpCmd::doit()
2048 {
2049     DisplayMain (_parent->baseWidget(), "Help4Help", "_HOMETOPIC");
2050 }
2051
2052 RelNoteCmd::RelNoteCmd ( char * name, 
2053                          char *label,
2054                          int active, 
2055                          UIComponent *window 
2056                      ) : NoUndoCmd (name, label, active )
2057 {
2058     _parent = window;
2059     _genDialog = NULL;
2060 }
2061
2062 void
2063 RelNoteCmd::doit()
2064 {
2065     // int answer;
2066
2067     // if (!_genDialog)
2068     //   _genDialog = new DtMailGenDialog("AboutBox", _parent->baseWidget());
2069     
2070     // _genDialog->setToAboutDialog();
2071     // answer = _genDialog->post_and_return(GETMSG(DT_catd, 1, 92, "OK"), NULL);
2072
2073     DisplayMain(_parent->baseWidget(), "Mailer", "_copyright");
2074 }
2075
2076 RelNoteCmd::~RelNoteCmd()
2077 {
2078     delete _genDialog;
2079 }
2080
2081 #ifdef DEAD_WOOD
2082 ClearCmd::ClearCmd ( 
2083                      char * name, 
2084                      char *label,
2085                      int active, 
2086                      RoamMenuWindow *window 
2087                      ) : NoUndoCmd (name, label, active )
2088 {
2089     parent=window;
2090 }
2091
2092 void
2093 ClearCmd::doit()
2094 {
2095 //  ((FindPopup *) parent->find_popup())->clear_text_values();
2096 }
2097
2098 StartCmd::StartCmd( char *name, 
2099                     char *label,
2100                     int active ) : Cmd ( name, label, active )
2101 {
2102 }
2103
2104 void 
2105 StartCmd::doit()
2106 {
2107     char *forward= ".forward";
2108     
2109     struct passwd pwd;
2110     GetPasswordEntry(pwd);
2111     
2112     char *forward_filename=new char[strlen(pwd.pw_dir)+1+strlen(forward)+1];
2113     sprintf( forward_filename, "%s/%s", pwd.pw_dir, forward );
2114 }
2115
2116
2117 void 
2118 StartCmd::undoit()
2119 {
2120 }
2121
2122
2123 ChangeCmd::ChangeCmd( 
2124                       char *name, 
2125                       char *label,
2126                       int active 
2127                       ) : Cmd (name, label, active )
2128 {
2129 }
2130
2131 void 
2132 ChangeCmd::doit()
2133 {
2134     struct passwd pwd;
2135     GetPasswordEntry(pwd);
2136     
2137     char *user_name=new char[strlen(pwd.pw_name)+1];
2138     strcpy(user_name,pwd.pw_name);
2139     
2140 }
2141
2142
2143 void 
2144 ChangeCmd::undoit()
2145 {
2146 }
2147
2148
2149 StopCmd::StopCmd( 
2150                   char *name, 
2151                   char *label,
2152                   int active, 
2153                   RoamMenuWindow *window 
2154                   ) : Cmd (name, label, active )
2155 {
2156     parent=window;
2157 }
2158
2159 void
2160 StopCmd::doit()
2161 {
2162     unlink( parent->forwardFilename() );
2163     parent->title( NULL );
2164 }
2165
2166
2167 void
2168 StopCmd::undoit()
2169 {
2170     
2171 }
2172 #endif /* DEAD_WOOD */
2173
2174
2175
2176 SendCmd::SendCmd(
2177                  char *name, 
2178                  char *label,
2179                  int active, 
2180                  SendMsgDialog *parent,
2181                  int trans_type) 
2182 : NoUndoCmd( name, label, active )
2183 {
2184     _parent=parent;
2185     _default_trans = trans_type;
2186 }
2187
2188 void
2189 SendCmd::doit()
2190 {
2191     if (!_parent->isMsgValid())
2192         return;
2193     else
2194         _parent->send_message( this->name(), _default_trans );
2195 }
2196
2197 // JT - Added methods below
2198
2199 OpenMsgCmd::OpenMsgCmd(
2200                        char *name,
2201                        char *label,
2202                        int active,
2203                        RoamMenuWindow *window) 
2204 : RoamCmd (name, label, active, window)
2205 {
2206 }
2207
2208 void
2209 OpenMsgCmd::doit()
2210 {
2211     DtMailEnv mail_error;
2212
2213     // Initialize the mail_error.
2214     mail_error.clear();
2215
2216     _menuwindow->list()->viewInSeparateWindow(mail_error);
2217     if (mail_error.isSet()) {
2218         _menuwindow->postErrorDialog(mail_error);
2219     }
2220
2221 }
2222
2223 // Attachment Cmds stuff
2224
2225 SaveAttachCmd::SaveAttachCmd ( char *name, 
2226                                char *label,
2227                                char * title,
2228                                int active, 
2229                                FileCallback save_callback,
2230                                RoamMenuWindow *clientData,
2231                                Widget parent)
2232 :UnifiedSelectFileCmd (name,
2233                        label,
2234                        title,
2235                        GETMSG(DT_catd, 1, 93, "Save"),
2236                        active,
2237                        save_callback,
2238                        clientData,
2239                        parent)
2240 {
2241    _parent = clientData;
2242 }
2243
2244 SaveAttachCmd::SaveAttachCmd ( 
2245                                char *name, 
2246                                char *label,
2247                                char * title,
2248                                int active, 
2249                                FileCallback save_callback,
2250                                ViewMsgDialog *clientData,
2251                                Widget parent
2252                                )
2253 :UnifiedSelectFileCmd (name,
2254                        label,
2255                        title,
2256                        GETMSG(DT_catd, 1, 93, "Save"),
2257                        active,
2258                        save_callback,
2259                        clientData,
2260                        parent )
2261 {
2262    _parent = clientData;
2263 }
2264
2265 SaveAttachCmd::SaveAttachCmd ( 
2266                                char *name, 
2267                                char *label,
2268                                char * title,
2269                                int active, 
2270                                FileCallback save_callback,
2271                                SendMsgDialog *clientData,
2272                                Widget parent
2273                                )
2274 :UnifiedSelectFileCmd (name,
2275                        label,
2276                        title,
2277                        GETMSG(DT_catd, 1, 93, "Save"),
2278                        active,
2279                        save_callback,
2280                        clientData,
2281                        parent )
2282 {
2283     _parent = clientData;
2284 }
2285
2286 void
2287 SaveAttachCmd::doit()
2288 {
2289     UnifiedSelectFileCmd::doit();
2290
2291     DtMailEditor *editor = _parent->get_editor();
2292     AttachArea *aa = editor->attachArea();
2293     XmString attachmentName = aa->getSelectedAttachName();
2294     XtVaSetValues(_fileBrowser, XmNtextString, attachmentName, NULL);
2295     XtAddCallback ( _fileBrowser, XmNapplyCallback,
2296                     &SaveAttachCmd::updateCallback,
2297                     (XtPointer) this);
2298
2299     _name = XmStringCopy(attachmentName);
2300     XmStringFree(attachmentName);
2301 }
2302
2303 // Attachment Cmds stuff
2304
2305
2306 void SaveAttachCmd::updateCallback(Widget, XtPointer clientData, XtPointer )
2307 {
2308     SaveAttachCmd *obj = (SaveAttachCmd *)clientData;
2309     
2310     XtVaSetValues(obj->_fileBrowser, XmNtextString, obj->_name, NULL);
2311 }
2312
2313 SaveAsTextCmd::SaveAsTextCmd ( 
2314                                char *name, 
2315                                char *label,
2316                                char *title,
2317                                int active, 
2318                                Editor * editor,
2319                                RoamMenuWindow *parent_roam_menu_window,
2320                                Widget parent
2321                                ) 
2322 :UnifiedSelectFileCmd (name,
2323                        label,
2324                        title,
2325                        GETMSG(DT_catd, 1, 95, "Save"),
2326                        active,
2327                        fileCB,
2328                        this,
2329                        parent )
2330 {
2331     _text_editor = editor;
2332     _roam_menu_window = parent_roam_menu_window;
2333 }
2334
2335 SaveAsTextCmd::SaveAsTextCmd ( 
2336                                char *name, 
2337                                char *label,
2338                                char *title,
2339                                int active, 
2340                                Editor * editor,
2341                                void *,
2342                                Widget parent
2343                                ) 
2344 :UnifiedSelectFileCmd (name,
2345                        label,
2346                        title,
2347                        GETMSG(DT_catd, 1, 95, "Save"),
2348                        active,
2349                        fileCB,
2350                        this,
2351                        parent )
2352 {
2353     _text_editor = editor;
2354     _roam_menu_window = NULL;
2355 }
2356
2357 void
2358 SaveAsTextCmd::fileCB(void * client_data, char * selection)
2359 {
2360     SaveAsTextCmd * self = (SaveAsTextCmd *)client_data;
2361     self->saveText(selection);
2362 }
2363
2364 void
2365 SaveAsTextCmd::saveText(const char * filename)
2366 {
2367     int answer, status;
2368     char *buf = new char[2048];
2369     char * helpId;
2370
2371     // Is it already there?
2372     status = SafeAccess(filename, F_OK);
2373     if (0 == status)
2374     {
2375         sprintf(buf,
2376                 GETMSG(DT_catd, 3, 47, "%s already exists.\nOverwrite?"),
2377                 filename);
2378         
2379         _genDialog->setToQuestionDialog(GETMSG(DT_catd, 3, 48, "Mailer"), buf);
2380         helpId = DTMAILHELPERROR;
2381         answer = _genDialog->post_and_return(helpId);
2382         if (answer==2) {
2383             delete [] buf;
2384             return;
2385         }
2386
2387         if (unlink(filename) < 0)
2388         {
2389             sprintf(buf, 
2390                     GETMSG(DT_catd, 3, 49, "Unable to overwrite %s.\n\
2391 Check file permissions and retry."), 
2392                     filename);
2393             _genDialog->setToErrorDialog(GETMSG(DT_catd, 3, 50, "Mailer"), buf);
2394             helpId = DTMAILHELPNOOVERWRITE;
2395             _genDialog->post_and_return(helpId);
2396             delete [] buf;
2397             return;
2398         }
2399     }
2400
2401     // Create or truncate, and then write the bits.
2402     int fd = SafeOpen(filename, O_RDWR | O_CREAT | O_TRUNC, 0600);
2403     if (fd < 0)
2404     {
2405         sprintf(buf, GETMSG(DT_catd, 3, 51, "Unable to create %s."), filename);
2406         _genDialog->setToErrorDialog(GETMSG(DT_catd, 3, 52, "Mailer"), buf);
2407         helpId = DTMAILHELPNOCREATE;        
2408         _genDialog->post_and_return(helpId);
2409         delete [] buf;
2410         return;
2411     }
2412     
2413     if (SafeWrite(fd, "\n", 1) < 1)
2414     {
2415         sprintf(buf, 
2416                 GETMSG(DT_catd, 3, 53, "Unable to write to %s."), 
2417                 filename);
2418         _genDialog->setToErrorDialog(GETMSG(DT_catd, 3, 54, "Mailer"), buf);
2419         helpId = DTMAILHELPNOWRITE;
2420         _genDialog->post_and_return(helpId);
2421         SafeClose(fd);
2422         unlink(filename);
2423         delete [] buf;
2424         return;
2425     }
2426     
2427     if (NULL == _roam_menu_window)
2428     {
2429         char    *text_buf = _text_editor->get_contents();
2430         writeText((XtPointer) fd, text_buf);
2431         XtFree((char*) text_buf);
2432     }
2433     else
2434       writeTextFromScrolledList(fd);
2435     
2436     SafeClose(fd);
2437     delete [] buf;
2438 }
2439
2440 void
2441 SaveAsTextCmd::writeTextFromScrolledList(int fd)
2442 {
2443     static char buf[2048];
2444     char        *helpId;
2445     char        *tmppath;
2446     DtMailEnv   mail_error;
2447     MsgScrollingList *list;
2448     DmxMailbox  *mailbox;
2449     DmxMsg      *next_msg;
2450
2451     //
2452     // Create temp file.
2453     //
2454     char *tmpdir = new char[MAXPATHLEN+1];
2455     sprintf(tmpdir, "%s/%s", getenv("HOME"), DtPERSONAL_TMP_DIRECTORY);
2456     if ((tmppath = tempnam(tmpdir, "dtmail")) == NULL) {
2457         sprintf(buf, GETMSG(DT_catd, 3, 51, "Unable to create %s."), tmpdir);
2458         _genDialog->setToErrorDialog(GETMSG(DT_catd, 3, 52, "Mailer"), buf);
2459         helpId = DTMAILHELPNOCREATE;        
2460         _genDialog->post_and_return(helpId);
2461         delete [] tmpdir;
2462         return;
2463     }
2464     delete [] tmpdir;
2465
2466     mail_error.clear();
2467     list = _roam_menu_window->list();
2468
2469     //
2470     // Copy the selected messages to a temp file.
2471     //
2472     int status = list->copySelected(mail_error, tmppath, FALSE, TRUE);
2473     if (mail_error.isSet()) {
2474         _roam_menu_window->postErrorDialog(mail_error);
2475         free(tmppath);
2476         return;
2477     }
2478     if (0 != status) return;
2479
2480     mailbox = new DmxMailbox(tmppath);
2481     mailbox->loadMessages();
2482     next_msg = mailbox->firstMessage();
2483     do
2484     {
2485         DmxPrintHeadersEnum     visible_headers;
2486
2487         if (_roam_menu_window->fullHeader())
2488           visible_headers = DMX_PRINT_HEADERS_ALL;
2489         else
2490           visible_headers = DMX_PRINT_HEADERS_ABBREV;
2491
2492         next_msg->display(
2493                         visible_headers,
2494                         &SaveAsTextCmd::writeText,
2495                         (XtPointer) fd);
2496         writeText((XtPointer) fd, "\n\n");
2497     } while ((next_msg = mailbox->nextMessage()) != (DmxMsg *) NULL);
2498     delete mailbox;
2499
2500     //
2501     // Clean up the temporary file.
2502     //
2503     unlink(tmppath);
2504     free(tmppath);
2505 }
2506
2507 void
2508 SaveAsTextCmd::writeText(XtPointer filedes, char *text_buf)
2509 {
2510     long        fdl = (long) filedes;
2511     int         fd = (int) fdl;
2512     int         len = strlen(text_buf);
2513     
2514     if (SafeWrite(fd, text_buf, len) < len) {
2515 #if 0
2516         char    buf[2048];
2517         char    *helpId;
2518
2519         sprintf(
2520                 buf,
2521                 GETMSG(DT_catd, 3, 53, "Unable to write to %s."), 
2522                 filename);
2523         helpId = DTMAILHELPNOWRITE;
2524         _genDialog->setToErrorDialog(GETMSG(DT_catd, 3, 56, "Mailer"), buf);
2525         _genDialog->post_and_return(helpId);
2526 #endif
2527     }
2528 }
2529
2530 void
2531 SaveAsTextCmd::doit()
2532 {
2533     MsgScrollingList    *list;
2534     Widget              listW;
2535     int                 *pos_list = NULL;
2536     int                 pos_count = 0;
2537
2538     if (_roam_menu_window &&
2539         (list = _roam_menu_window->list()) &&
2540         (listW = list->get_scrolling_list()))
2541     {
2542         if (!XmListGetSelectedPos(listW, &pos_list, &pos_count))
2543           return;
2544
2545         if (0 == pos_count)
2546         {
2547             DtMailGenDialog *dialog = _roam_menu_window->genDialog();
2548
2549             dialog->setToErrorDialog(
2550                             GETMSG(DT_catd, 3, 50, "Mailer"),
2551                             GETMSG(DT_catd, 2, 16, "No message selected."));
2552             dialog->post_and_return(NULL);
2553
2554             return;
2555         }
2556     }
2557
2558     UnifiedSelectFileCmd::doit();
2559
2560     if (1 == pos_count)
2561     {
2562         int                     pos_selected, last_space;
2563         DtMailEnv               error;
2564         DtMailHeaderLine        header;
2565         DtMailMessageHandle     msg_handle;
2566         DtMailHeaderRequest     request;
2567         DtMail::MailBox         *mbox;
2568
2569         pos_selected = list->get_selected_item();
2570         msg_handle = list->msgno(pos_selected);
2571
2572         request.number_of_names = 1;
2573         request.header_name = (char**) malloc(sizeof(char*));
2574         request.header_name[0] = strdup(DtMailMessageSubject);
2575
2576         mbox = _roam_menu_window->mailbox();
2577         mbox->getMessageSummary(error, msg_handle, request, header);
2578
2579         if (0 != header.header_values[0].length())
2580         {
2581             const char  *orig_subject = *((header.header_values[0])[0]);
2582             int         i,j;
2583             int         orig_len = strlen(orig_subject);
2584             char        *subject = (char*) malloc(orig_len + 1);
2585             XmString    xms;
2586
2587             for (i=0,j=0,last_space=0; i<orig_len; i++)
2588             {
2589                 if (isspace(orig_subject[i]))
2590                 {
2591                     if (last_space < i-1)
2592                       subject[j++] = ' ';
2593
2594                     last_space = i;
2595                 }
2596                 else if (orig_subject[i] == '/')
2597                   subject[j++] = '\\';
2598                 else
2599                   subject[j++] = orig_subject[i];
2600             }
2601             subject[j] = '\0';
2602
2603             xms = XmStringCreateLocalized(subject);
2604             XtVaSetValues(_fileBrowser, XmNtextString, xms, NULL);
2605
2606             XmStringFree(xms);
2607             free(subject);
2608         }
2609
2610         if (NULL != request.header_name)
2611         {
2612           if (NULL != request.header_name[0])
2613             free(request.header_name[0]);
2614           free(request.header_name);
2615         }
2616
2617         mbox->clearMessageSummary(header);
2618     }
2619 }
2620
2621
2622 DeleteAttachCmd::DeleteAttachCmd( 
2623                                   char *name, 
2624                                   char *label,
2625                                   int active,
2626                                   SendMsgDialog *smd
2627                                   
2628                                   ) : Cmd ( name, label, active )
2629 {
2630     _parent = smd;
2631 }
2632
2633 void
2634 DeleteAttachCmd::doit()
2635 {
2636     _parent->delete_selected_attachments();
2637 }
2638
2639 void
2640 DeleteAttachCmd::undoit()
2641 {
2642 }
2643
2644 UndeleteAttachCmd::UndeleteAttachCmd( 
2645                                       char *name, 
2646                                       char *label,
2647                                       int active,
2648                                       SendMsgDialog *smd
2649                                       
2650                                       ) : Cmd ( name, label, active )
2651 {
2652     _parent = smd;
2653 }
2654
2655 void
2656 UndeleteAttachCmd::doit()
2657 {
2658     _parent->undelete_last_deleted_attachment();
2659     
2660 }
2661
2662 void
2663 UndeleteAttachCmd::undoit()
2664 {
2665 }
2666
2667 RenameAttachCmd::RenameAttachCmd ( 
2668                                    char *name, 
2669                                    char *label,
2670                                    int active,
2671                                    SendMsgDialog *smd
2672                                    ) : Cmd ( name, label, active )
2673 {
2674     Widget renameDialog;
2675     XmString message;
2676     
2677     _parent = smd;
2678     renameDialog = XmCreatePromptDialog(
2679                                         smd->baseWidget(), 
2680                                         "renameDialog", 
2681                                         NULL, 
2682                                         0
2683                                         );
2684     
2685     message = XmStringCreateLocalized(GETMSG(DT_catd, 1, 96, "Empty"));
2686     XtVaSetValues(renameDialog, XmNselectionLabelString, message, NULL);
2687     XmStringFree(message);
2688     XmString ok_str = XmStringCreateLocalized(GETMSG(DT_catd, 1, 97, "Rename"));
2689     XtVaSetValues(XtParent(renameDialog),
2690                   XmNtitle, GETMSG(DT_catd, 1, 98, "Mailer - Rename"),
2691                   NULL);
2692     XtVaSetValues(renameDialog,
2693                   XmNokLabelString, ok_str,
2694                   NULL);
2695
2696     XmStringFree(ok_str);
2697     XtUnmanageChild(XmSelectionBoxGetChild(renameDialog, XmDIALOG_HELP_BUTTON));
2698
2699     _parent->get_editor()->attachArea()->setRenameDialog(renameDialog);
2700     XtAddCallback(renameDialog, XmNcancelCallback, 
2701                   &RenameAttachCmd::cancelCallback,
2702                   (XtPointer) this );
2703     XtAddCallback(renameDialog, XmNokCallback, 
2704                   &RenameAttachCmd::okCallback,
2705                   (XtPointer) this );
2706 }
2707
2708 void RenameAttachCmd::doit()
2709 {
2710     Widget renameDialog;
2711     XmString oldAttachName = NULL;
2712     XmString message;
2713     char        buf[512];
2714     AttachArea *aa;
2715     
2716     if (!_parent->renameAttachmentOK()) {
2717         return;
2718     }
2719     
2720     aa = _parent->get_editor()->attachArea();
2721     
2722     oldAttachName = aa->getSelectedAttachName();
2723     
2724     if (oldAttachName == NULL) return;
2725     
2726     renameDialog = aa->getRenameDialog();
2727     
2728     sprintf(buf, GETMSG(DT_catd, 3, 57, "Rename attachment as"));
2729     
2730     message = XmStringCreateLocalized(buf);
2731     
2732     XtVaSetValues(renameDialog, 
2733                   XmNselectionLabelString, message,
2734                   XmNtextString, oldAttachName,
2735                   NULL);
2736     
2737 //     XtFree(buf);
2738     XmStringFree(message);
2739     XmStringFree(oldAttachName);
2740     
2741     XtManageChild(renameDialog);
2742     XtPopup(XtParent(renameDialog), XtGrabNone);
2743 }      
2744
2745 void RenameAttachCmd::undoit()
2746 {
2747     // Just print a message that allows us to trace the execution
2748     
2749 }       
2750
2751 void RenameAttachCmd::cancelCallback ( 
2752                                        Widget, 
2753                                        XtPointer clientData, 
2754                                        XtPointer callData 
2755                                        )
2756 {
2757     RenameAttachCmd *obj = (RenameAttachCmd *) clientData;
2758     
2759     obj->cancel( callData );
2760 }
2761
2762 void RenameAttachCmd::cancel( XtPointer )
2763 {
2764     AttachArea* aa;
2765     
2766     aa = _parent->get_editor()->attachArea();
2767     
2768     Widget renameDialog = aa->getRenameDialog();
2769     
2770     XtUnmanageChild(renameDialog);
2771 }
2772
2773 void RenameAttachCmd::okCallback ( 
2774                                    Widget, 
2775                                    XtPointer clientData, 
2776                                    XtPointer callData 
2777                                    )
2778 {
2779     RenameAttachCmd *obj = (RenameAttachCmd *) clientData;
2780     obj->ok( callData );
2781 }
2782
2783 void RenameAttachCmd::ok( XtPointer callData )
2784 {
2785     XmSelectionBoxCallbackStruct *cbs = 
2786         (XmSelectionBoxCallbackStruct *)callData;
2787     AttachArea *aa;
2788     
2789     aa = _parent->get_editor()->attachArea();
2790     
2791     Widget renameDialog = aa->getRenameDialog();
2792     
2793     XtUnmanageChild(renameDialog);
2794     
2795     aa->setSelectedAttachName(cbs->value);
2796     
2797 }
2798
2799 AttachmentActionCmd::AttachmentActionCmd(
2800                                          char *name,
2801                                          char *label,
2802                                          RoamMenuWindow *rmw,
2803                                          int indx
2804                                          ) : Cmd (name, label, TRUE)
2805 {
2806     _index = indx;
2807     
2808     _parent = rmw;
2809 }
2810
2811 AttachmentActionCmd::AttachmentActionCmd(
2812                                          char *name,
2813                                          char *label,
2814                                          ViewMsgDialog *vmd,
2815                                          int indx
2816                                          ) : Cmd (name, label, TRUE)
2817 {
2818     _index = indx;
2819     
2820     _parent = vmd;
2821 }
2822
2823 AttachmentActionCmd::AttachmentActionCmd(
2824                                          char *name,
2825                                          char *label,
2826                                          SendMsgDialog *smd,
2827                                          int indx
2828                                          ) : Cmd (name, label, TRUE)
2829 {
2830     _index = indx;
2831     
2832     _parent = smd;
2833 }
2834
2835
2836 void
2837 AttachmentActionCmd::doit()
2838 {
2839     _parent->invokeAttachmentAction(_index);
2840 }
2841
2842 void
2843 AttachmentActionCmd::undoit()
2844 {
2845 }
2846
2847 SelectAllAttachsCmd::SelectAllAttachsCmd(
2848                                          char *name,
2849                                          char *label,
2850                                          RoamMenuWindow *rmw
2851                                          ) : Cmd(name, label, TRUE)
2852 {
2853     _parent = rmw;
2854 }
2855
2856 SelectAllAttachsCmd::SelectAllAttachsCmd(
2857                                          char *name,
2858                                          char *label,
2859                                          ViewMsgDialog *vmd
2860                                          ) : Cmd(name, label, TRUE)
2861 {
2862     _parent = vmd;
2863 }
2864
2865 SelectAllAttachsCmd::SelectAllAttachsCmd(
2866                                          char *name,
2867                                          char *label,
2868                                          SendMsgDialog *smd
2869                                          ) : Cmd(name, label, FALSE)
2870 {
2871     _parent = smd;
2872 }
2873
2874 void
2875 SelectAllAttachsCmd::doit()
2876 {
2877     _parent->selectAllAttachments();
2878 }
2879
2880 void
2881 SelectAllAttachsCmd::undoit()
2882 {
2883     // 
2884 }
2885
2886 ShowAttachPaneCmd::ShowAttachPaneCmd(
2887                                      char *name,
2888                                      char *label,
2889                                      AbstractEditorParent *aep
2890                                      ) : ToggleButtonCmd(name, label, TRUE)
2891 {
2892     _parent = aep;
2893 }
2894
2895 void
2896 ShowAttachPaneCmd::doit()
2897 {
2898     // If button is OFF
2899     if (!this->getButtonState()) {
2900         _parent->hideAttachArea();
2901     }
2902     else {  // button is ON
2903         _parent->showAttachArea();
2904     }
2905 }
2906
2907 void
2908 ShowAttachPaneCmd::undoit()
2909 {
2910     // 
2911 }
2912
2913 AbbrevHeadersCmd::AbbrevHeadersCmd(
2914                                    char *name,
2915                                    char *label,
2916                                    RoamMenuWindow *rmw
2917                                    ) : ToggleButtonCmd(name, label, TRUE)
2918 {
2919     _parent = rmw;
2920 }
2921
2922 void
2923 AbbrevHeadersCmd::doit()
2924 {
2925     // If button is OFF
2926     if (!this->getButtonState()) {
2927         _parent->fullHeader(TRUE);
2928     }
2929     else {  // button is ON
2930         _parent->fullHeader(FALSE);
2931     }
2932 }
2933
2934 void
2935 AbbrevHeadersCmd::undoit()
2936 {
2937     // 
2938 }
2939
2940 CloseCmd::CloseCmd( 
2941     char *name, 
2942     char *label,
2943     int active, 
2944     Widget w, 
2945     SendMsgDialog *s ) 
2946     : NoUndoCmd(name, label, active)
2947 {
2948     _compose_dialog = s;
2949     menubar_w = w;
2950 }
2951
2952 void
2953 CloseCmd::doit()
2954 {
2955     // Call the goAway() method on the SMD.  Argument TRUE requests it
2956     // to check if the SMD is dirty.  Let it handle the 
2957     // case where text may be present in the compose window.
2958     if (!_compose_dialog->isMsgValid())
2959         return;
2960     else
2961         _compose_dialog->goAway(TRUE);
2962
2963 }
2964
2965 EditUndoCmd::EditUndoCmd( 
2966     char *name, 
2967     char *label,
2968     int active, 
2969     AbstractEditorParent *w ) 
2970     : NoUndoCmd( name, label, active )
2971 {
2972     editor = w->get_editor()->textEditor();
2973 }
2974
2975 void
2976 EditUndoCmd::doit()
2977 {
2978         editor->undo_edit();
2979 }
2980
2981 EditCutCmd::EditCutCmd( 
2982     char *name, 
2983     char *label,
2984     int active, 
2985     AbstractEditorParent *w
2986
2987     : NoUndoCmd( name, label, active )
2988 {
2989     editor = w->get_editor()->textEditor();
2990
2991     // className() is a virtual method
2992     if (w->className() == "SendMsgDialog") {
2993         _compose_dialog = (SendMsgDialog *)w;
2994     }
2995     else {
2996         _compose_dialog = NULL;
2997     }
2998 }
2999
3000 void
3001 EditCutCmd::doit()
3002 {
3003     editor->cut_selection();
3004         
3005     if (_compose_dialog) {
3006         // Turn Paste on
3007         _compose_dialog->activate_edit_paste();
3008         _compose_dialog->activate_edit_paste_indented();
3009         _compose_dialog->activate_edit_paste_bracketed();
3010     }
3011 }
3012
3013 EditCopyCmd::EditCopyCmd( 
3014     char *name, 
3015     char *label,
3016     int active, 
3017     AbstractEditorParent *w ) 
3018     : NoUndoCmd( name, label, active )
3019 {
3020     editor = w->get_editor()->textEditor();
3021     
3022     // className() is a virtual method
3023     if (w->className() == "SendMsgDialog") {
3024         _compose_dialog = (SendMsgDialog *)w;
3025     }
3026     else {
3027         _compose_dialog = NULL;
3028     }
3029 }
3030
3031 void
3032 EditCopyCmd::doit()
3033 {
3034     editor->copy_selection();
3035     if (_compose_dialog) {
3036         // Turn Paste on
3037         _compose_dialog->activate_edit_paste();
3038         _compose_dialog->activate_edit_paste_indented();
3039         _compose_dialog->activate_edit_paste_bracketed();
3040     }
3041 }
3042
3043 EditPasteCmd::EditPasteCmd( 
3044     char *name, 
3045     char *label,
3046     int active, 
3047     AbstractEditorParent *w ) 
3048     : NoUndoCmd( name, label, active )
3049 {
3050     editor = w->get_editor()->textEditor();
3051 }
3052
3053 void
3054 EditPasteCmd::doit()
3055 {
3056         editor->paste_from_clipboard();
3057 }
3058
3059 EditPasteSpecialCmd::EditPasteSpecialCmd( 
3060     char *name, 
3061     char *label,
3062     int active, 
3063     AbstractEditorParent *w,
3064     Editor::InsertFormat format) 
3065     : NoUndoCmd( name, label, active )
3066 {
3067     editor = w->get_editor()->textEditor();
3068     insert_format = format;
3069 }
3070
3071 void
3072 EditPasteSpecialCmd::doit()
3073 {
3074         editor->paste_special_from_clipboard(insert_format);
3075 }
3076
3077
3078 EditClearCmd::EditClearCmd( 
3079     char *name, 
3080     char *label,
3081     int active, 
3082     AbstractEditorParent *w ) 
3083     : NoUndoCmd( name, label, active )
3084 {
3085         editor = w->get_editor()->textEditor();
3086     // this->deactivate();
3087 }
3088
3089 void
3090 EditClearCmd::doit()
3091 {
3092     editor->clear_selection();
3093         // Turn Paste on
3094         // _edit_paste->activate();
3095 }
3096
3097 EditDeleteCmd::EditDeleteCmd( 
3098     char *name, 
3099     char *label,
3100     int active, 
3101     AbstractEditorParent *w ) 
3102     : NoUndoCmd( name, label, active )
3103 {
3104     editor = w->get_editor()->textEditor();
3105         // this->deactivate();
3106 }
3107
3108 void
3109 EditDeleteCmd::doit()
3110 {
3111     editor->delete_selection();
3112         // Turn Paste off
3113         // _edit_paste->deactivate();
3114 }
3115
3116 EditSelectAllCmd::EditSelectAllCmd( 
3117     char *name, 
3118     char *label,
3119     int active, 
3120     AbstractEditorParent *w ) 
3121     : NoUndoCmd( name, label, active )
3122 {
3123     editor = w->get_editor()->textEditor();
3124 }
3125
3126 void
3127 EditSelectAllCmd::doit()
3128 {
3129         editor->select_all();
3130 }
3131
3132
3133
3134 WordWrapCmd::WordWrapCmd( 
3135     char *name, 
3136     char *label,
3137     int active, 
3138     AbstractEditorParent *w 
3139 ) : ToggleButtonCmd( name, label, active )
3140 {
3141     editor = w->get_editor()->textEditor();
3142     /*
3143      * allow the app-defaults setting for WordWrap
3144      */
3145
3146     Widget _w = editor->get_text_widget();
3147     Arg args[1];
3148
3149     XtSetArg( args[0], XmNwordWrap, &cur_setting );
3150     XtGetValues( _w, args, 1 );
3151     editor->set_word_wrap(cur_setting);         
3152 }
3153
3154 void
3155 WordWrapCmd::doit()
3156 {
3157     cur_setting = ((ToggleButtonCmd *)this)->getButtonState();
3158     editor->set_word_wrap(cur_setting);         
3159 }
3160
3161 Boolean
3162 WordWrapCmd::wordWrap()
3163 {
3164     return(cur_setting);
3165 }
3166
3167 FindChangeCmd::FindChangeCmd( 
3168     char *name, 
3169     char *label,
3170     int active, 
3171     AbstractEditorParent *w ) 
3172     : NoUndoCmd( name, label, active )
3173 {
3174     editor = w->get_editor()->textEditor();
3175 }
3176
3177 void
3178 FindChangeCmd::doit()
3179 {
3180         editor->find_change();
3181 }
3182
3183 SpellCmd::SpellCmd( 
3184     char *name, 
3185     char *label,
3186     int active, 
3187     AbstractEditorParent *w ) 
3188     : NoUndoCmd( name, label, active )
3189 {
3190     editor = w->get_editor()->textEditor();
3191 }
3192
3193 void
3194 SpellCmd::doit()
3195 {
3196         editor->spell();
3197 }
3198
3199 AliasCmd::AliasCmd(
3200     char *name,
3201     char *label,
3202     int active,
3203     Widget header)
3204     : NoUndoCmd (name, label, active)
3205 {
3206     _header = header;
3207     _alias = strdup(name);
3208 }
3209
3210 void
3211 AliasCmd::doit()
3212 {
3213     char        *value;
3214
3215     XtVaGetValues(_header, XmNvalue, &value, NULL);
3216
3217     if (strlen(value))
3218     {
3219         char    *newvalue = (char *) malloc(strlen(value) + strlen(_alias) + 3);
3220         sprintf(newvalue, "%s, %s", value, _alias);
3221         XtVaSetValues(_header, XmNvalue, newvalue, NULL);
3222         free(newvalue);
3223     }
3224     else
3225       XtVaSetValues(_header, XmNvalue, _alias, NULL);
3226     
3227     XtFree(value);
3228 }
3229
3230 AliasCmd::~AliasCmd()
3231 {
3232     free((void*) _alias);
3233 }
3234
3235
3236 OtherAliasesCmd::OtherAliasesCmd(
3237     char *name,
3238     char *label,
3239     int active)
3240     : NoUndoCmd (name, label, active)
3241 {
3242 }
3243
3244 void
3245 OtherAliasesCmd::doit()
3246 {
3247     OptCmd      *optCmd = (OptCmd *) theRoamApp.mailOptions();
3248     optCmd->displayAliasesOptionsPane();
3249 }
3250
3251 OtherAliasesCmd::~OtherAliasesCmd()
3252 {
3253 }
3254
3255
3256 FormatCmd::FormatCmd( 
3257     char *name, 
3258     char *label,
3259     int active, 
3260     AbstractEditorParent *w ) 
3261     : NoUndoCmd( name, label, active )
3262 {
3263     editor = w->get_editor()->textEditor();
3264 }
3265
3266 void
3267 FormatCmd::doit()
3268 {
3269         editor->format();
3270 }
3271
3272 LogMsgCmd::LogMsgCmd( 
3273     char *name, 
3274     char *label,
3275     int active, 
3276     SendMsgDialog * send 
3277 ) : ToggleButtonCmd( name, label, active )
3278 {
3279   // Go to props and find out the default, ie. to log or not to log.
3280   // But for now, just look in .mailrc to see if "record" is set.
3281
3282   DtMailEnv error;
3283   const char *logfile = NULL;
3284
3285   _send = send;
3286
3287 }
3288
3289 void
3290 LogMsgCmd::doit()
3291 {
3292     if (!((ToggleButtonCmd *)this)->getButtonState()) {
3293         // turn off logging for this message
3294         _send->setLogState(DTM_FALSE);
3295     }
3296     else {
3297         // turn on logging for this message
3298         _send->setLogState(DTM_TRUE);
3299     }
3300
3301 }
3302
3303
3304 VacationCmd::VacationCmd(
3305     char *name,
3306     char *label
3307 ) : Cmd (name, label, TRUE)
3308 {
3309
3310     _forwardFile = ".forward";
3311     _backupSuffix = "..BACKUP";
3312     _subject = NULL;
3313     _body = NULL;
3314     _msg = NULL;
3315     _dialog = NULL;
3316
3317     // Check if a .forward file exists.  
3318
3319     _priorVacationRunning = this->priorVacationRunning();
3320
3321     // parse the .vacation.msg file and retain the subject
3322     // and body.
3323     // They need to be retrieved for display in the dialog.
3324
3325     this->parseVacationMessage();
3326 }
3327
3328
3329 VacationCmd::~VacationCmd()
3330 {
3331     if (NULL != _subject)
3332       free((void*) _subject);
3333     
3334     if (NULL != _msg)
3335       delete _msg;
3336 }
3337
3338 void
3339 VacationCmd::doit()
3340 {
3341 }
3342
3343 static unsigned long
3344 writeToFileDesc(const char * buf, int len, va_list args)
3345 {
3346     int fd = va_arg(args, int);
3347     int cnt = va_arg(args, int);
3348     int status = 0;
3349
3350     do {
3351         status = SafeWrite(fd, buf, len);
3352     } while (status < 0 && errno == EAGAIN);
3353         
3354     return(0);
3355 }
3356
3357 int
3358 VacationCmd::startVacation(
3359     char *subj,
3360     char *text
3361 )
3362 {
3363     int i = this->handleMessageFile(subj, text);
3364     if (i == 0) {
3365         i = this->handleForwardFile();
3366     }
3367
3368     return (i);
3369 }
3370
3371 void
3372 VacationCmd::stopVacation()
3373 {
3374     char *forwardfile = new char[MAXPATHLEN+1];
3375     
3376     sprintf(forwardfile, "%s/%s", getenv("HOME"), _forwardFile);
3377
3378     // Remove the current .forward file (it has vacation in it)
3379     // Recover and replace the original backup forward file, if 
3380     // one exists.
3381
3382     unlink(forwardfile);
3383
3384     this->recoverForwardFile(forwardfile);
3385     delete [] forwardfile;
3386 }
3387
3388 Boolean
3389 VacationCmd::priorVacationRunning()
3390
3391 {
3392     char        buf[256];
3393     int fd;
3394     Boolean     retval = FALSE;
3395     char *forwardfile = new char[MAXPATHLEN+1];
3396
3397     sprintf(forwardfile, "%s/%s", getenv("HOME"), _forwardFile);
3398
3399     if (SafeAccess(forwardfile, F_OK) != 0) {
3400         delete [] forwardfile;
3401         return(FALSE);
3402     }
3403         
3404     fd = SafeOpen(forwardfile, O_RDONLY);
3405         
3406     buf[sizeof buf -1] = '\0';
3407     int len;
3408     while ((len = SafeRead(fd, buf, (sizeof buf) - 1)) > 0) {
3409         buf[len] = 0;
3410         if ((strstr(buf, "/usr/bin/vacation")) ||
3411             (strstr(buf, "/usr/ucb/vacation"))) {
3412             retval = TRUE;
3413             break;
3414         }
3415     }
3416
3417     SafeClose(fd);
3418
3419     delete [] forwardfile;
3420     return(retval);
3421 }
3422
3423 int
3424 VacationCmd::handleForwardFile()
3425 {
3426     int         fwd_fd;
3427     int         bkup_fd;
3428     int         fsize;
3429     int         answer;
3430     int         forwarding;
3431     int         lastchar;
3432     caddr_t     fwdptr;
3433     char        *helpId;
3434     Boolean     forwardExists;
3435     DtMailGenDialog     *dialog;
3436
3437     int error_bufLen = 10000;
3438     char *error_buf = new char[error_bufLen];
3439     char *forwardfile = new char[MAXPATHLEN+1];
3440     char *messagefile = new char[256];
3441     char *buf = new char[2048];
3442
3443     // initialize the error_buf
3444     memset(error_buf, 0, error_bufLen);
3445     memset(buf, 0, 2048);
3446
3447     answer = FALSE;
3448     forwarding = FALSE;
3449     forwardExists = FALSE;
3450
3451     passwd pw;
3452     GetPasswordEntry(pw);
3453
3454     sprintf(forwardfile, "%s/%s", pw.pw_dir, _forwardFile);
3455     
3456     if (SafeAccess(forwardfile, F_OK) == 0 ) {
3457         forwardExists = TRUE;
3458         }
3459     else {
3460         forwardExists = FALSE;
3461     }
3462     
3463     if (forwardExists && !_priorVacationRunning) {
3464
3465         /* STRING_EXTRACTION -
3466          *
3467          * This confirmation window is brought up when the user
3468          * tries to update the vacation status when the user is
3469          * already using a .forward file.
3470          */
3471         if (NULL != _dialog)
3472           dialog = _dialog;
3473         else
3474           dialog = theRoamApp.genDialog();
3475
3476         sprintf(error_buf, GETMSG(DT_catd, 1, 102, "You are already using the forwarding facility for\nsomething other than Vacation.  While Vacation is\nrunning, Vacation will be appended to this other\nforwarding activity. Is it still OK to start Vacation?\0"));
3477
3478         dialog->setToQuestionDialog(GETMSG(DT_catd, 1, 103, "Mailer"), 
3479                                     error_buf);
3480
3481         helpId = DTMAILHELPOKSTARTVACATION;
3482         answer = dialog->post_and_return(helpId);
3483
3484         if (answer == 2) {// Cancel chosen
3485             delete [] buf;
3486             delete [] messagefile;
3487             delete [] error_buf;
3488             delete [] forwardfile;
3489             return 1;
3490         }
3491         
3492         if (this->backupFile(forwardfile) < 0) {
3493             delete [] buf;
3494             delete [] messagefile;
3495             delete [] error_buf;
3496             delete [] forwardfile;
3497             return 1;
3498         }
3499
3500         /* A .forward file is currently in use. Merge vacation
3501          * into this file, rather than overwrite it. To do so,
3502          * set the appropriate variable to indicate mode.
3503          */
3504
3505         _priorVacationRunning = TRUE;
3506         
3507         // Turn on the bit to indicate we are currently using a .forward
3508         forwarding = TRUE;
3509     }
3510     else if (forwardExists && _priorVacationRunning) {
3511
3512         /* STRING_EXTRACTION -
3513          *
3514          * This confirmation window is brought up when the user
3515          * tries to update the vacation status when the user is
3516          * already using a .forward file.
3517          */
3518
3519         if (NULL != _dialog)
3520           dialog = _dialog;
3521         else
3522           dialog = theRoamApp.genDialog();
3523
3524         sprintf(error_buf, GETMSG(DT_catd, 1, 104, "You are already running the vacation program in your .forward file.\nConsult documentation on how to stop it and remove it from your .forward file.\nTry this command after fixing that problem.\0"));
3525         
3526         dialog->setToErrorDialog("Error", error_buf);
3527         helpId = DTMAILHELPREMOVEVACATION;
3528         answer = dialog->post_and_return(helpId);
3529
3530         delete [] buf;
3531         delete [] messagefile;
3532         delete [] error_buf;
3533         delete [] forwardfile;
3534         return 1;
3535
3536     }   
3537
3538     // Re-initialize the error_buf
3539     memset(error_buf, 0, error_bufLen);
3540
3541     sprintf(messagefile, "%s/.vacation.msg", pw.pw_dir);
3542
3543     if (forwardExists) {
3544         fwd_fd = SafeOpen(forwardfile, O_WRONLY | O_APPEND | O_CREAT);
3545     }
3546     else {
3547         fwd_fd = SafeOpen(forwardfile, O_WRONLY | O_CREAT);
3548     }
3549
3550     if (fwd_fd < 0 ) {// If fwdfile is not writable/appendable
3551         
3552         // Put up error dialog indicating fwdfile not writable
3553         /* restore the original .forward file */
3554         
3555         this->recoverForwardFile(forwardfile);
3556         delete [] buf;
3557         delete [] messagefile;
3558         delete [] error_buf;
3559         delete [] forwardfile;
3560         return 1;
3561     }
3562
3563     SafeFChmod(fwd_fd, 0644);
3564
3565     // Make buf be forwardfile._backupSuffix.
3566     // Then create a backup file of name buf
3567     
3568     strcpy(buf, forwardfile);
3569     strcat(buf, _backupSuffix);
3570
3571     // If we are currently using a .forward then we need to append/prepend
3572     // the vacation command
3573
3574     if (forwarding) {
3575
3576         /* CREATE NEW .forward FILE
3577          *
3578          * The original .forward file has been renamed to the
3579          * backup file name. We need to open the backup .forward
3580          * file so we can copy from it.
3581          */
3582
3583         if ((bkup_fd = SafeOpen(buf, O_RDONLY)) < 0) {
3584             /* restore the original .forward file */
3585             if (answer)
3586                 this->recoverForwardFile(forwardfile);
3587             delete [] buf;
3588             delete [] messagefile;
3589             delete [] error_buf;
3590             delete [] forwardfile;
3591             return 1;
3592         }
3593
3594         /* COPY OLD .forward TO NEW .forward
3595          *
3596          * Using mmap is quite fast, so rather than do a while
3597          * loop to copy line by line, we'll use mmap followed by
3598          * a write.
3599          */
3600         
3601         fsize= (int)lseek(bkup_fd, 0, SEEK_END);
3602         if (fsize > 0)
3603         {
3604             fwdptr =
3605               (caddr_t) mmap(0, fsize, PROT_READ, MAP_PRIVATE, bkup_fd, 0);
3606
3607             // If map failed
3608             if (fwdptr == (char *)-1) {
3609                 // error
3610                 delete [] buf;
3611                 delete [] messagefile;
3612                 delete [] error_buf;
3613                 delete [] forwardfile;
3614                 SafeClose(bkup_fd);
3615                 return 1;
3616             }
3617
3618             // If write failed
3619             if (SafeWrite(fwd_fd, fwdptr, fsize) < fsize) {
3620                 // error
3621                 delete [] buf;
3622                 delete [] messagefile;
3623                 delete [] error_buf;
3624                 delete [] forwardfile;
3625                 SafeClose(bkup_fd);
3626                 return 1;
3627             }
3628         
3629             /* RELEASE .forward FILE
3630              *
3631              * Un-mmap the new .forward file
3632              */
3633
3634             lastchar = fwdptr[fsize-1];
3635             munmap(fwdptr, fsize);
3636         }
3637         else
3638           lastchar = '\n';
3639
3640         /* APPEND VACATION LINE
3641          *
3642          * The new .forward file is still open, so append the
3643          * new line below as the last line of the .forward file.
3644          * Check to make sure last character in the file is a
3645          * newline. If it's not, add one so our work goes on
3646          * a separate line.
3647          */
3648
3649         if (lastchar != '\n') {
3650             lseek(fwd_fd, 0, SEEK_END);
3651             char *txt = "\n";
3652             if (SafeWrite(fwd_fd, txt, strlen(txt)) < strlen(txt)) {
3653                 // error
3654                 delete [] buf;
3655                 delete [] messagefile;
3656                 delete [] error_buf;
3657                 delete [] forwardfile;
3658                 SafeClose(bkup_fd);
3659                 return 1;
3660             }
3661         }
3662
3663         /*
3664          * Now, add the vacation line to the next line.
3665          */
3666         char *append_buf1 = new char[1024*2];
3667         sprintf(append_buf1, "|\" /usr/bin/vacation %s\"\n", pw.pw_name);
3668
3669         if (SafeWrite(fwd_fd, append_buf1, strlen(append_buf1)) < 
3670             strlen(append_buf1)) {
3671             // error
3672             delete [] buf;
3673             delete [] messagefile;
3674             delete [] error_buf;
3675             delete [] forwardfile;
3676             delete [] append_buf1;
3677             SafeClose(bkup_fd);
3678             return 1;
3679         }
3680         delete [] append_buf1;
3681
3682         SafeClose(bkup_fd);
3683     }
3684
3685         /* Create known backup file. The known backup
3686          * file allows mailtool to differentiate between
3687          * vacation being started from mailtool, and vacation
3688          * being invoked (the Unix program as opposed to the
3689          * MailTool Vacation menu item) via tty session.
3690          */
3691
3692     if (!forwardExists) {
3693
3694         if ((bkup_fd = SafeOpen(buf, O_WRONLY | O_APPEND | O_CREAT)) < 0) {
3695             /* restore the original .forward file */
3696             if (answer)
3697                 this->recoverForwardFile(forwardfile);
3698             delete [] buf;
3699             delete [] messagefile;
3700             delete [] error_buf;
3701             delete [] forwardfile;
3702             SafeClose(fwd_fd);
3703             return 1;
3704         }
3705
3706         char *end_text = "User not using forward file\n";
3707
3708         if (SafeWrite(bkup_fd, end_text, strlen(end_text)) < 
3709             strlen(end_text)) {
3710             // error
3711             delete [] buf;
3712             delete [] messagefile;
3713             delete [] error_buf;
3714             delete [] forwardfile;
3715             SafeClose(fwd_fd);
3716             return 1;
3717         }
3718     }
3719
3720     if (!forwardExists) {
3721
3722         /* WRITE NEW .forward FILE
3723          *
3724          * There was no .forward file, so no appending
3725          * must be done. Simply write the standard
3726          * vacation line into the new .forward file.
3727          */
3728
3729         char *append_buf2 = new char[1024*2];
3730
3731         sprintf(append_buf2, "\\%s, |\" /usr/bin/vacation %s\"\n", 
3732                 pw.pw_name, pw.pw_name);
3733         if (SafeWrite(fwd_fd, append_buf2, strlen(append_buf2)) <
3734             strlen(append_buf2)) {
3735             // error
3736             SafeClose(bkup_fd);
3737             delete [] buf;
3738             delete [] messagefile;
3739             delete [] error_buf;
3740             delete [] forwardfile;
3741             delete [] append_buf2;
3742             return 1;
3743         }
3744         delete [] append_buf2;
3745     }
3746
3747     SafeClose(bkup_fd);
3748     SafeClose(fwd_fd);
3749
3750     system("/usr/bin/vacation -I");
3751
3752     delete [] buf;
3753     delete [] messagefile;
3754     delete [] error_buf;
3755     delete [] forwardfile;
3756     return 0;
3757 }
3758
3759 int
3760 VacationCmd::backupFile(
3761     char *file
3762 )
3763 {
3764         char *buf = new char[MAXPATHLEN+1];
3765
3766         strcpy(buf, file);
3767         strcat(buf, _backupSuffix);
3768
3769         if (rename(file, buf) < 0) {
3770                 /* STRING_EXTRACTION -
3771                  *
3772                  * We tried to make a backup copy of your .forward file, but
3773                  * it failed.  The first %s is the name of the rename
3774                  * target; the second %s is the system error string.
3775                  */
3776         
3777             // Put up error dialog 
3778             delete [] buf;
3779             return(-1);
3780         }
3781
3782         delete [] buf;
3783         return(0);
3784 }
3785
3786 int
3787 VacationCmd::recoverForwardFile(
3788     char        *file
3789 )
3790 {
3791         char *buf = new char[BUFSIZ+1];
3792         int  fd;
3793
3794         sprintf(buf, file);
3795         strcat(buf, _backupSuffix);
3796
3797         if (rename(buf, file) < 0) {
3798
3799             /* STRING_EXTRACTION -
3800              *
3801              * We tried to restore your original .forward file, but could
3802              * not.  The first %s is the name of the original .forward file,
3803              * the second %s is the the system error string.
3804              */
3805
3806             // Handle dialog indicating error in recovering .forward file.
3807             // Error usually caused by starting /usr/bin/vacation outside
3808             // of dtmail
3809
3810             delete [] buf;
3811             return(-1);
3812         }       
3813
3814         if ((fd = SafeOpen(file, O_RDONLY)) == 0) {
3815             delete [] buf;
3816             return(-1);
3817         }
3818         
3819         buf[sizeof file -1] = '\0';
3820         while (SafeRead(fd, buf, BUFSIZ) != 0) {
3821                 if (strstr(buf, "User not using forward file")) {
3822                         unlink(file);
3823                         break;
3824                 }
3825         }
3826
3827         SafeClose(fd);
3828
3829         delete [] buf;
3830         return(0);
3831 }
3832
3833 char *
3834 VacationCmd::subject()
3835 {
3836     return(_subject);
3837 }
3838
3839
3840 char *
3841 VacationCmd::body()
3842 {
3843     if (_body) {
3844         return((char *)_body);
3845     }
3846     else {
3847         return(NULL);
3848     }
3849 }
3850
3851 void
3852 VacationCmd::parseVacationMessage()
3853 {
3854     passwd pw;
3855     int         fd;
3856
3857     DtMailGenDialog     *dialog;
3858     char * helpId;
3859     int answer;
3860     char dialog_text[1024*4];
3861     DtMailEnv error;
3862     DtMail::Session * d_session = theRoamApp.session()->session();
3863     DtMailBuffer mbuf;
3864
3865     if (NULL != _dialog)
3866       dialog = _dialog;
3867     else
3868       dialog = theRoamApp.genDialog();
3869
3870     GetPasswordEntry(pw);
3871
3872     char *messagefile = new char[MAXPATHLEN+1];
3873     sprintf(messagefile, "%s/.vacation.msg", pw.pw_dir);
3874
3875     // See if the messagefile exists.
3876     // If it doesn't create one and throw in the text found in the
3877     // properties sheet.  If no text found, use default template.
3878
3879     char * fullpath = d_session->expandPath(error, messagefile);
3880     delete [] messagefile;
3881     messagefile = NULL;
3882
3883     // Map the file and try to parse it as a message. If it is a message,
3884     // then load it with headers. Otherwise, throw everything into the
3885     // editor.
3886     //
3887
3888     fd = SafeOpen(fullpath, O_RDONLY);
3889     free(fullpath);
3890
3891     if (fd < 0) {// File doesn't exist
3892         
3893         _subject = NULL;
3894         _body = NULL;
3895         return;
3896     }
3897
3898     struct stat buf;
3899     if (SafeFStat(fd, &buf) < 0) {
3900
3901         sprintf(dialog_text, 
3902                 GETMSG(DT_catd, 1, 105, "Cannot open .vacation.msg file -- No write permission."));
3903         dialog->setToQuestionDialog("Mailer", dialog_text);
3904         helpId = DTMAILHELPNOWRITEVACATION;
3905         answer = dialog->post_and_return(helpId);
3906             
3907         _subject = NULL;
3908         _body = NULL;
3909
3910         SafeClose(fd);
3911         return;
3912     }           
3913
3914     int page_size = (int)sysconf(_SC_PAGESIZE);
3915     size_t map_size = (int) (buf.st_size + 
3916                             (page_size - (buf.st_size % page_size)));
3917
3918     if (buf.st_size == 0)
3919       return;
3920
3921     int free_buf = 0;
3922     mbuf.size = buf.st_size;
3923 #ifdef __osf__
3924     mbuf.buffer = (char *)mmap(0, map_size, PROT_READ, MAP_PRIVATE, fd, 0);
3925 #else
3926     mbuf.buffer = mmap(0, map_size, PROT_READ, MAP_PRIVATE, fd, 0);
3927 #endif
3928     if (mbuf.buffer == (char *)-1) {
3929         free_buf = 1;
3930         mbuf.buffer = new char[mbuf.size];
3931         if (mbuf.buffer == NULL) {
3932             dialog->setToErrorDialog(GETMSG(DT_catd, 3, 59, "No Memory"),
3933                                      GETMSG(DT_catd, 3, 60, "There is not enough memory to load the existing .vacation.msg file."));
3934             helpId = DTMAILHELPNOLOADVACATION;
3935             answer = dialog->post_and_return(helpId);
3936             SafeClose(fd);
3937
3938             _subject = NULL;
3939             _body = NULL;
3940
3941             return;
3942         }
3943
3944         if (SafeRead(fd, mbuf.buffer, (unsigned int)mbuf.size) < mbuf.size) {
3945             dialog->setToErrorDialog(GETMSG(DT_catd, 3, 61, "Mailer"),
3946                                      GETMSG(DT_catd, 3, 62, "The existing .vacation.msg file appears to be corrupt."));
3947             helpId = DTMAILHELPCORRUPTVACATION;
3948             answer = dialog->post_and_return(helpId);
3949             SafeClose(fd);
3950             delete [] mbuf.buffer;
3951             _subject = NULL;
3952             _body = NULL;
3953
3954             return;
3955         }
3956     }
3957
3958     // Now we ask the library to parse it. If this fails for any reason, this
3959     // is not a message, so we give up.
3960     //
3961     DtMail::Message * msg = d_session->messageConstruct(error,
3962                                                         DtMailBufferObject,
3963                                                         &mbuf,
3964                                                         NULL,
3965                                                         NULL,
3966                                                         NULL);
3967     SafeClose(fd);
3968
3969     if (error.isSet()) {
3970         _subject = NULL;
3971         _body = NULL;
3972         _msg = NULL;
3973         return;
3974     }
3975     else {
3976         DtMail::Envelope * env = msg->getEnvelope(error);
3977         DtMailHeaderHandle hnd;
3978
3979         int hcount = 0;
3980         char * name;
3981         DtMailValueSeq value;
3982
3983         for (hnd = env->getFirstHeader(error, &name, value);
3984             error.isNotSet() && hnd;
3985             hnd = env->getNextHeader(error, hnd, &name, value)) {
3986             
3987             if (!strcmp(name, "Subject") == 0) {
3988                 continue;
3989             }
3990             else {
3991
3992                 int max_len = 0;
3993                 for (int slen = 0; slen < value.length(); slen++) {
3994                     max_len += strlen(*(value[slen]));
3995                 }
3996
3997                 char * new_str = new char[max_len + (value.length() * 3)];
3998
3999                 strcpy(new_str, "");
4000                 for (int copy = 0; copy < value.length(); copy++) {
4001                     if (copy != 0) {
4002                         strcat(new_str, " ");
4003                     }
4004
4005                     strcat(new_str, *(value[copy]));
4006                 }
4007
4008                 _subject = strdup(new_str);
4009                 value.clear();
4010                 free(name);
4011                 delete [] new_str;
4012                 break;
4013             }
4014         }
4015
4016         DtMail::BodyPart * bp = msg->getFirstBodyPart(error);
4017         if (error.isNotSet()) {
4018             unsigned long length;
4019
4020             bp->getContents(error,
4021                             &_body,
4022                             &length,
4023                             NULL,
4024                             NULL,
4025                             NULL,
4026                             NULL);
4027         }
4028
4029         //
4030         // Avoid a memory leak.
4031         //
4032         _msg = msg;
4033     }
4034
4035 }
4036
4037 int
4038 VacationCmd::handleMessageFile(
4039     char *subj,
4040     char *text
4041 )
4042 {
4043     int         fd;
4044
4045     DtMailGenDialog     *dialog;
4046     char * helpId;
4047     int answer;
4048     char dialog_text[1024*4];
4049     Boolean text_changed = FALSE;
4050     char *messagefile = new char[256];
4051
4052     BufferMemory buf(4096);
4053
4054     if (NULL != _dialog)
4055       dialog = _dialog;
4056     else
4057       dialog = theRoamApp.genDialog();
4058
4059     // Check if a .forward file exists.  
4060     passwd pw;
4061     GetPasswordEntry(pw);
4062
4063     sprintf(messagefile, "%s/.vacation.msg", pw.pw_dir);
4064
4065     // See if the messagefile exists.
4066     // If it doesn't create one and throw in the text found in the
4067     // properties sheet.  If no text found, use default template.
4068
4069     answer = 0;
4070
4071     int msg_file_exists = SafeAccess(messagefile, F_OK);
4072     if (subj != NULL) {
4073          if (_subject == NULL || strcmp(_subject, subj) != 0)
4074                 text_changed = TRUE;
4075     }
4076     else if (_subject != NULL)
4077         text_changed = TRUE;
4078
4079     if (!text_changed) {
4080         if (text != NULL) {
4081                 if (_body == NULL || strcmp((char*)_body, text) != 0)
4082                         text_changed = TRUE;
4083         }
4084         else if (_body != NULL)
4085                 text_changed = TRUE;
4086     }
4087
4088     if (msg_file_exists >= 0 &&  text_changed) {
4089         sprintf(dialog_text, 
4090                 GETMSG(DT_catd, 1, 106, ".vacation.msg file exists.  Replace with new text?"));
4091         dialog->setToQuestionDialog("Mailer", dialog_text);
4092         helpId = DTMAILHELPEXISTSVACATION;
4093         answer = dialog->post_and_return(helpId);
4094
4095         if (answer == 1) {
4096                 // backup the messageFile
4097                 this->backupFile(messagefile);
4098         }
4099     }
4100     
4101     // If the file doesn't exist or if the user has okayed creation
4102
4103     if ((msg_file_exists < 0) || (answer == 1)) {
4104
4105         fd = SafeOpen(messagefile, O_WRONLY | O_CREAT);
4106         if (fd < 0) {
4107             sprintf(dialog_text, 
4108                 GETMSG(DT_catd, 1, 107, "Cannot open .vacation.msg file -- No write permission."));
4109             dialog->setToQuestionDialog("Mailer", dialog_text);
4110             helpId = DTMAILHELPERROR;
4111             answer = dialog->post_and_return(helpId);
4112             
4113             // Handle dialog indicating file not writable
4114             delete [] messagefile;
4115             return (-1);        
4116         }               
4117         SafeFChmod (fd, 0644);
4118
4119         if (!subj) { 
4120             /* NL_COMMENT
4121              * This is the default value of the subject field in the
4122              * message that gets returned to the sender when vacation
4123              * is turned on.
4124              */
4125             subj = GETMSG(DT_catd, 1, 108, "I am on vacation");
4126         } else {
4127             buf.appendData("Subject: ", 9);
4128             buf.appendData(subj, strlen(subj));
4129             buf.appendData("\nPrecedence: junk\n\n", 19);
4130         }
4131         if (_subject)
4132             free (_subject);
4133         _subject = strdup(subj);
4134         
4135         if (!text) {
4136             text = GETMSG(DT_catd, 1, 109,
4137                             "I'm on vacation.\nYour mail regarding \"$SUBJECT\" will be read when I return.\n");
4138         }
4139         buf.appendData(text, strlen(text));
4140         if (strlen(text) > 0 && text[strlen(text) - 1] != '\n') {
4141             buf.appendData("\n", 1);
4142         }
4143         _body = strdup(text);
4144
4145         buf.iterate(writeToFileDesc, fd);
4146         
4147         SafeClose(fd);
4148     }
4149     delete [] messagefile;
4150     return(0);
4151 }