Even more spelling fixed
[oweals/cde.git] / cde / programs / dtmail / dtmail / RoamApp.C
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these libraries and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /*
24  *+SNOTICE
25  *
26  *      $TOG: RoamApp.C /main/62 1999/09/14 16:52:18 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 <unistd.h>
72 #include <wchar.h>
73 #if defined(USL) || defined(__uxp__) || defined(linux) || defined(CSRG_BASED)
74 #define wcswcs wcsstr
75 #include <wctype.h>  // iswspace is defined in this header on USL */
76 #endif
77
78 #ifdef DTMAIL_TOOLTALK
79 #include <Tt/tt_c.h>
80 #include <Tt/tttk.h>
81 #include "SendMsgDialog.h"
82 #endif
83
84 #if defined(POSIX_THREADS)
85 #include <thread.h>
86 #endif
87
88 #include <signal.h>
89 #include <ctype.h>
90 #include <locale.h>
91 #include <sys/param.h>
92 #include <sys/wait.h>
93
94 #include <X11/X.h>
95 #include <Xm/MessageB.h>
96
97 #include <Dt/Action.h>
98 #include <Dt/Dt.h>
99 #include <Dt/DtPStrings.h>
100 #include <Dt/Dts.h>
101 #include <Dt/EnvControlP.h>
102
103 #include <DtMail/Common.h>
104 #include <DtMail/DtMailError.hh>
105 #include <DtMail/DtMailSigChld.h>
106 #include <DtMail/IO.hh>
107 #include <DtMail/OptCmd.h>
108
109 #include "DmxPrintJob.h"
110 #include "DtMailGenDialog.hh"
111 #include "DtMailHelp.hh"
112 #include "EUSDebug.hh"
113 #include "Fonts.h"
114 #include "MemUtils.hh"
115 #include "MailMsg.h"
116 #include "RoamApp.h"
117 #include "RoamMenuWindow.h"
118 #include "RoamCmds.h"
119 #include "SendMsgDialog.h"
120 #include "WorkingDialogManager.h"
121
122
123 int use_XmTextEditor = 0;
124
125
126
127 // Provide interface to the DtSvc function DtSimpleError
128 // When this interface is better defined, this can be removed
129 // and replaced with the appropriate include file
130
131 typedef enum {
132   DtIgnore,
133   DtInformation,
134   DtWarning,
135   DtError,
136   DtFatalError,
137   DtInternalError
138 } DtSeverity;
139
140 extern "C" void _DtSimpleError(
141                         char *progName,
142                         DtSeverity severity,
143                         char *help,
144                         char *format,
145                         ...) ;
146
147
148 extern "C" int _DtPrintDefaultErrorSafe(
149                         Display *dpy,
150                         XErrorEvent *event,
151                         char *msg,
152                         int bytes);
153
154 static int x_error_handler(Display *display, XErrorEvent* error_event)
155 {
156     #define _DTMAIL_BUFSIZE 1024
157     char error_msg[_DTMAIL_BUFSIZE];
158
159     // log error
160     _DtPrintDefaultErrorSafe(display, error_event, error_msg, _DTMAIL_BUFSIZE);
161     _DtSimpleError("dtmail", DtWarning, NULL, error_msg, NULL);
162     
163     // if the error occured on the print display we're going to set 
164     // a variable so that and when the job is done, right before calling
165     // XpEndJob, we call XpCancelJob, and notify the user.
166
167     if (theRoamApp.isActivePrintDisplay(display) &&
168         error_event->error_code == BadAlloc)
169     {
170         theRoamApp.setErrorPrintDisplay(display);
171         return 1;
172     }
173     
174     theRoamApp._default_x_error_handler(display, error_event);
175     return 1;
176 }
177
178
179 void
180 force( Widget w)
181 {
182   Widget        shell;
183   Display       *dpy;
184   XWindowAttributes     xwa;
185   Window        window;
186   XtAppContext cxt=XtWidgetToApplicationContext( w );
187   XEvent                event;
188
189   shell=w;
190   dpy=XtDisplay(shell);
191   window=XtWindow( shell );
192
193   while ( XGetWindowAttributes(dpy,window,&xwa)) {
194     if ( XGetWindowAttributes( dpy, window, &xwa ) &&
195          xwa.map_state != IsViewable )
196       break;
197
198     XtAppNextEvent( cxt, &event );
199     XtDispatchEvent( &event );
200   }
201   XmUpdateDisplay(shell);
202 }
203
204 void
205 forceUpdate( Widget w )
206 {
207   Widget diashell, topshell;
208   Window diawindow, topwindow;
209   XtAppContext cxt = XtWidgetToApplicationContext( w );
210   Display               *dpy;
211   XWindowAttributes     xwa;
212   XEvent                event;
213
214   for (diashell=w;!XtIsShell(diashell);diashell=XtParent(diashell));
215   for ( topshell=diashell;XtIsTopLevelShell( topshell );
216         topshell = XtParent( topshell ) );
217
218   dpy=XtDisplay(diashell);
219   diawindow=XtWindow( diashell );
220   topwindow=XtWindow(topshell);
221   while ( XGetWindowAttributes(dpy,diawindow,&xwa)  
222           && XEventsQueued( dpy, QueuedAlready) ) {
223       
224       XtAppNextEvent( cxt, &event );
225       XtDispatchEvent( &event );
226     }
227   XmUpdateDisplay(topshell);
228 }
229
230 XtResource
231 RoamApp::_resources[] = {
232   {
233     "printscript",
234     "PrintScript",
235     XtRString,
236     sizeof( XtRString ),
237     XtOffset ( RoamApp *, _print_script ),
238     XtRString,
239     ( XtPointer ) "lp",
240   },
241   {
242     "mailfiles",
243     "MailFiles",
244     XtRString,
245     sizeof( XtRString ),
246     XtOffset ( RoamApp *, _mailfiles_folder ),
247     XtRString,
248     ( XtPointer ) ".",
249   },
250   {
251     "defaultmailbox",
252     "DefaultMailBox",
253     XtRString,
254     sizeof( XtRString ),
255     XtOffset ( RoamApp *, _default_mailbox ),
256     XtRString,
257     ( XtPointer ) ".",
258   },
259
260   // Fixed width font
261   {
262     "userFont",
263     "UserFont",
264     XtRString,
265     sizeof( XtRString ),
266     XtOffset ( RoamApp *, _user_font ),
267     XtRString,
268     ( XtPointer )"-b&h-lucidatypewriter-medium-r-*-sans-*-120-*-*-*-*-*-*",
269   },
270
271   // Fixed width fontlist
272   {
273     "userFont",
274     "UserFont",
275     XmRFontList,
276     sizeof( XmFontList ),
277     XtOffset ( RoamApp *, _user_fontlist ),
278     XtRString,
279     ( XtPointer )"-b&h-lucidatypewriter-medium-r-*-sans-*-120-*-*-*-*-*-*",
280   },
281
282   // Variable width font
283   {
284     "systemFont",
285     "SystemFont",
286     XtRString,
287     sizeof( XtRString ),
288     XtOffset ( RoamApp *, _system_font ),
289     XtRString,
290     ( XtPointer )"-b&h-lucida-medium-r-*-sans-*-120-*-*-*-*-*-*"
291   },
292
293   // Variable width fontlist
294   {
295     "systemFont",
296     "SystemFont",
297     XmRFontList,
298     sizeof( XmFontList ),
299     XtOffset ( RoamApp *, _system_fontlist ),
300     XtRString,
301     ( XtPointer )"-b&h-lucida-medium-r-*-sans-*-120-*-*-*-*-*-*"
302   },
303
304   // Font used for attachment glyph
305   {
306     "glyphFont",
307     "GlyphFont",
308     XtRString,
309     sizeof( XtRString ),
310     XtOffset ( RoamApp *, _glyph_font ),
311     XtRString,
312     ( XtPointer )
313     NULL,
314   },
315
316 };
317
318 RoamApp theRoamApp("Dtmail");
319 int just_compose = 0;
320
321 void
322 SigUsr1(int)
323 {
324     theRoamApp.quitSilently();
325     theRoamApp.closeAllWindows();
326 }
327
328 void
329 #if defined(sun)
330 panicQuitSignalHandler()
331 #else
332 panicQuitSignalHandler(int)
333 #endif
334 {
335     theRoamApp.setQuitQuickly();
336     theRoamApp.setQuitSilently();
337     theRoamApp.closeAllWindows();
338     _exit(1);
339 }
340
341 void
342 RoamApp::closeAllWindows(void)
343 {
344     // Remove callbacks to prevent XtPhase2Destroy crashes.
345     XtRemoveAllCallbacks(baseWidget(), XmNdestroyCallback);
346
347     for (int win = 0; win < _numWindows; win++)
348       if (quitQuickly())
349         _windows[win]->panicQuit();
350       else if (quitSilently())
351         _windows[win]->quit();
352       else
353         _windows[win]->quit();
354     
355     //shutdown();
356 }
357
358 void
359 RoamApp::statusCallback(DtMailOperationId, DtMailEnv &error, void *)
360 {
361    if (error.isSet()) {
362          // fprintf(stderr, "DEBUG: statusCallback(): Submission failed error = %d\n", error._major);
363    } else {
364          // fprintf(stderr, "DEBUG: statusCallback(): Submission done.%s\n");
365    }
366 }
367
368 #ifdef DTMAIL_TOOLTALK
369
370 int started_by_tt = 0;
371 // Move this to constructor of RoamMenuWindow???
372 int dtmail_mapped = 0;    // For explanation, look in RoamApp.h.
373 static int roam_tt_fd = 0;
374 char *roam_tt_procid = NULL;
375 Tt_pattern *roam_tt_pattern = NULL;
376
377 //  Report ToolTalk error
378 int dieFromTtError(Tt_status errid, char *procname, char *errmsg, char *helpid)
379 {
380     /* Do not die on warnings or TT_OK */
381     if ( tt_is_err(errid) )
382     {
383         char            *title = GETMSG(DT_catd, 2, 1, "Mailer");
384         char            *errmsg = tt_status_message(errid);
385         DtMailEnv        error;
386
387         ttdt_close(0, 0, 1);
388
389         error.logError(
390                 DTM_TRUE,
391                 GETMSG(DT_catd, 2, 30, "%s returned ToolTalk error: %s\n"), 
392                 procname, tt_status_message(errid));
393
394         DtMailGenDialog *exit_dialog = new DtMailGenDialog(
395                                            "ExitDialog", 
396                                            theApplication->baseWidget());
397         exit_dialog->setToErrorDialog(title, errmsg);
398         if (NULL == helpid) helpid = DTMAILHELPERROR;
399         exit_dialog->post_and_return(GETMSG(DT_catd, 1, 1, "OK"), helpid);
400
401         XtRemoveAllCallbacks(theApplication->baseWidget(), XmNdestroyCallback);
402         exit(1);
403     }
404     return 0;
405 }
406
407 Tt_message attachmt_msg_handler(
408                                 Tt_message msg,
409                                 void *client_data,
410                                 Tttk_op op,
411                                 Tt_status diag,
412                                 unsigned char *contents,
413                                 int len,
414                                 char *file,
415                                 char *docname)
416 {
417    static const char *thisFcn = "attachmt_msg_handler()";
418    Tt_status status = TT_OK;
419    Tt_pattern *pattern;
420    SendMsgDialog *compose;
421
422    if ( diag != TT_OK ) {
423       // Toolkit detected an error
424       // Let toolkit handle error
425       return msg;
426    }
427
428    pattern = ttdt_message_accept(msg, NULL, (Widget)0, client_data, 1, 1);
429    dieFromTtError(tt_ptr_error(pattern),
430                 "attachmt_msg_handler.ttdt_message_accept", NULL, NULL);
431
432    if ( op == TTME_MAIL_EDIT ) {
433        status = tt_message_reply(msg);
434        dieFromTtError(status,
435                 "attachmt_msg_handler.tt_message_reply", NULL, NULL);
436
437        // Put the data that is coming in (via buffer or file) as
438        // the attachment of a message.
439        if ( len > 0 ) {    // Pass by content
440            compose = theCompose.getWin();
441            compose->inclAsAttmt(contents, len, docname);
442            if ( docname ) {
443                compose->setTitle(docname);
444                compose->setIconTitle(docname);
445            }
446        } else if ( file != NULL ) {    // Pass by filename
447            compose = theCompose.getWin();
448            compose->inclAsAttmt(file, docname);
449            if ( docname ) {
450                compose->setTitle(docname);
451                compose->setIconTitle(docname);
452            }
453        } else {   // DO NOTHING.
454            // This is not an entry point to bring up a blank compose window
455            // because ttMediaLoadPatCB() returns with diagnosis =
456            // TT_DESKTOP_ENODATA. For INOUT mode, it expects file or buffer.
457        }
458    } else {
459        status = tttk_message_fail( msg, TT_DESKTOP_ENOTSUP, 0, 1 );
460        dieFromTtError(status,
461                 "tooltalk_msg_handler.tttk_message_fail", NULL, NULL);
462    }
463
464    //
465    // Not sure if these should be freed.
466    //
467    //    tt_free( (caddr_t)contents );
468    //    tt_free( file );
469    //    tt_free( docname );
470
471    return 0;
472 }
473
474 typedef struct _msg_handler_done_cb_struct
475 {
476     Tt_message msg;
477     RoamApp *roamApp;
478     RoamMenuWindow *roamWin;
479
480 } MsgHandlerDoneCbData;
481
482 static void
483 tooltalk_msg_handler_done_cb( Widget w, XtPointer client_data, XtPointer )
484 {
485     MsgHandlerDoneCbData        *cbData = (MsgHandlerDoneCbData *) client_data;
486     Tt_status                   status = TT_OK;
487
488     status = tt_message_reply(cbData->msg);
489     dieFromTtError(status,
490                 "tooltalk_msg_handler_done_cb.tt_message_reply", NULL, NULL);
491
492     XtRemoveCallback(
493                 w,
494                 XtNdestroyCallback, tooltalk_msg_handler_done_cb,
495                 cbData);
496
497     cbData->roamApp->unregisterPendingTask();
498     if (NULL != cbData->roamWin)
499     {
500         cbData->roamWin->quit(TRUE);
501         delete cbData->roamWin;
502     }
503     XtFree((char*) cbData);
504 }
505
506 static char *tooltalk_save_buffer_to_file(
507                                 unsigned char   *contents,
508                                 int              len)
509 {
510     char *p = NULL;
511     char *tmpdir = new char[MAXPATHLEN+1];
512
513     // 1. Get buffer content into file.
514     snprintf(tmpdir, MAXPATHLEN+1, "%s/%s", getenv("HOME"), DtPERSONAL_TMP_DIRECTORY);
515     p = tempnam(tmpdir, "mail");
516     if (p == NULL)
517     {
518         delete [] tmpdir;
519         return NULL;
520     }
521
522     int fd = SafeOpen(p, O_RDWR | O_CREAT);
523     if (fd < 0)
524     {
525         delete [] tmpdir;
526         free(p);
527         return NULL;
528     }
529     if (SafeWrite(fd, contents, len) != len)
530     {
531         free(p);
532         p = NULL;
533     }
534
535     close(fd);
536     delete [] tmpdir;
537     return p;
538 }
539
540 static Tt_message
541 tooltalk_msg_handler( 
542     Tt_message msg,
543     void *client_data,
544     Tttk_op op,
545     Tt_status diag,
546     unsigned char *contents,
547     int len,
548     char *file,
549     char *docname 
550 )
551 {
552     static const char *thisFcn = "RoamApp::tooltalk_msg_handler()";
553     Tt_status status = TT_OK;
554     SendMsgDialog *compose;
555     RoamApp *roamapp = (RoamApp *) client_data;
556     Tt_pattern *pattern;
557
558     if ( diag != TT_OK ) {
559         // toolkit detected an error
560         // Let toolkit handle error
561         return msg;
562     }
563
564     // Need to check the return value of this call.
565     //
566     pattern = ttdt_message_accept(msg, NULL, (Widget)0, client_data, 1, 1);
567     dieFromTtError(tt_ptr_error(pattern),
568                 "tooltalk_msg_handler.ttdt_message_accept", NULL, NULL);
569
570     if ( op == TTME_MAIL ) {
571         // Send without GUI.
572         status = tt_message_reply(msg);
573         dieFromTtError(status,
574                         "tooltalk_msg_handler.tt_message_reply", NULL, NULL);
575
576         // Construct message handle
577         DtMailBuffer mbuf;
578         if ( len > 0 ) {
579             mbuf.buffer = (void *)contents;
580             mbuf.size = (unsigned long)len;
581         } else if ( file != NULL ) {
582             // 1. Get file content into buffer.
583             int fd = SafeOpen(file, O_RDONLY);
584             if (fd < 0) {
585                 return msg;
586             }
587             struct stat buf;
588             if (SafeFStat(fd, &buf) < 0) {
589                 close(fd);
590                 return msg;
591             }
592  
593             mbuf.buffer = new char[buf.st_size];
594             if (!mbuf.buffer) {
595                 close(fd);
596                 return msg;
597             }
598  
599             if (SafeRead(fd, mbuf.buffer, 
600                                 (unsigned int) buf.st_size) != buf.st_size) {
601                 delete [] mbuf.buffer;
602                 close(fd);
603                 return msg;
604             }
605             mbuf.size = (unsigned long)buf.st_size;
606         }
607         DtMailEnv error;
608         DtMail::Session *d_session = theRoamApp.session()->session();
609         DtMail::Message *msgHandle = d_session->messageConstruct(
610                                                         error,
611                                                         DtMailBufferObject,
612                                                         &mbuf,
613                                                         NULL,
614                                                         NULL,
615                                                         NULL);
616         if ( error.isSet() || !msgHandle ) {
617             return msg;
618         }
619
620         // Send the message
621         theRoamApp.default_transport()->submit(error, msgHandle,
622                                                (DtMailBoolean)FALSE);
623
624     } else if ( op == TTME_MAIL_COMPOSE ) {
625         // Bring up blank compose window.
626         status = tt_message_reply(msg);
627         dieFromTtError(status,
628                 "tooltalk_msg_handler.tt_message_reply", NULL, NULL);
629
630         compose = theCompose.getWin();
631         if ( docname ) {
632             compose->setTitle(docname);
633             compose->setIconTitle(docname);
634         }
635     } else if ( op == TTME_MAIL_EDIT ) {
636         // Bring up compose window with given data filled in.
637         status = tt_message_reply(msg);
638         dieFromTtError(status,
639                 "tooltalk_msg_handler.tt_message_reply", NULL, NULL);
640
641         // Parse data (coming in as buffer or file)
642         if ( len > 0 ) {    // Pass by content
643             compose = theCompose.getWin();
644             compose->parseNplace((char *)contents, len);
645             if ( docname ) {
646                 compose->setTitle(docname);
647                 compose->setIconTitle(docname);
648             }
649         } else if ( file != NULL ) {    // Pass by filename
650             compose = theCompose.getWin();
651             compose->parseNplace(file);
652             if ( docname ) {
653                 compose->setTitle(docname);
654                 compose->setIconTitle(docname);
655             }
656         } else {
657             // DO NOTHING
658             // This is not an entry point to bring up a blank compose window
659             // because ttMediaLoadPatCB() returns with diagnosis =
660             // TT_DESKTOP_ENODATA. For INOUT mode, it expects file or buffer.
661         }
662     } else if ( op == TTME_DISPLAY ) {
663
664         char *opname = tt_message_op(msg);
665
666         // It is the Display message.  
667         // Since the compose window can be started independent of
668         // RoamMenuWindow, a DtMail process may be around
669         // (because of a compose window).  Then if a DtMail process
670         // exists (ToolTalk will not start another DtMail process),
671         // that process needs to respond to this DISPLAY message. 
672         // If for some reason, the view of the specified mail folder
673         // is unmapped, then need to map it.  Otherwise, create the
674         // view for the specified mail folder.  Need to remove self
675         // destruct when using an existing DtMail process if that
676         // process was started by compose.
677
678         // This is the wrong place to reply to this message.
679         // Replying here causes the action layer to think that
680         // it is ok to remove the file it passed in, but we
681         // are not done with it yet.  Calling tt_message_reply
682         // at the end of this function doesn't work either.  We
683         // are just not going to reply to this tooltalk message
684         // (tooltalk folks say this is ok) for the time being.
685         if (! strcmp("Display", opname)) {
686             MailSession *ses = theRoamApp.session();
687             RoamMenuWindow *roamwin = NULL;
688
689             if ( theApplication == NULL ) {
690 #ifdef WM_TT
691                 fprintf(stdout, "%s: theApplication is NULL\n", thisFcn);
692 #endif
693                 return 0;
694             }
695
696 #ifdef WM_TT
697             fprintf(stdout, "%s: TTME_DISPLAY\n", thisFcn);
698 #endif
699
700             if ( theCompose.getTimeOutId() != 0 )
701               XtRemoveTimeOut(theCompose.getTimeOutId());
702
703
704             if (len > 0)
705             {
706                 DtMailEnv       mail_error;
707                 DtMailObjectSpace space;
708                 DtMail::Session *d_session = theRoamApp.session()->session();
709
710                 char *str = (char*) malloc(len + 1);
711
712                 memcpy(str, contents, len);
713                 str[len] = '\0';
714
715                 if (0 == strcmp("DTMAIL_INBOX", str))
716                 {
717                     mail_error.clear();
718                     d_session->queryImpl(
719                                         mail_error,
720                                         d_session->getDefaultImpl(mail_error),
721                                         DtMailCapabilityInboxName,
722                                         &space,
723                                         &file);
724                 }
725
726                 free(str);
727
728                 if (NULL == file)
729                 {
730                     file = tooltalk_save_buffer_to_file(contents, len);
731                     if (NULL == file) return msg;
732                 }
733             }
734
735             // Check to see if the mbox is already open.  If it is, we will
736             // simply make sure it's displayed in the current workspace.
737             if (ses->isMboxOpen(file))
738             {
739                 Widget w = NULL;
740                 roamwin = ses->getRMW(file);
741                 ses->activateRMW(roamwin);
742                 if (NULL != roamwin) roamwin->displayInCurrentWorkspace();
743             }
744             else
745             {
746                 roamwin = new RoamMenuWindow(file);
747                 roamwin->initialize();
748                 roamwin->manage();
749             }
750
751             MsgHandlerDoneCbData *cbData;
752             
753             cbData = (MsgHandlerDoneCbData*)
754                         XtMalloc(sizeof(MsgHandlerDoneCbData));
755             cbData->msg = msg;
756             cbData->roamApp = roamapp;
757             cbData->roamWin = NULL;
758             XtAddCallback(roamwin->GetMainWin(),
759                         XtNdestroyCallback,
760                         tooltalk_msg_handler_done_cb,
761                         (XtPointer) cbData);
762             roamapp->registerPendingTask();
763
764             // Set this to True so Self_destruct()
765             // would not be started by Compose.
766             dtmail_mapped = 1;
767         } else if (! strcmp("Print", opname)) {
768             char *p;
769
770             if ( len > 0 ) {
771                 p = tooltalk_save_buffer_to_file(contents, len);
772                 if (NULL == p) return msg;
773             } else if (file != NULL) {
774                p = strdup(file);
775             } else {
776                return msg;
777             }
778                 
779 #if 1
780             MsgHandlerDoneCbData *cbData = NULL;
781             DtMailEnv            error;
782             DmxPrintJob         *pjob = NULL;
783             RoamMenuWindow      *roamwin = NULL;
784             MailSession         *ses = theRoamApp.session();
785
786             cbData =
787               (MsgHandlerDoneCbData*) XtMalloc(sizeof(MsgHandlerDoneCbData));
788             if (ses->isMboxOpen(file))
789             {
790                 Widget w = NULL;
791                 roamwin = ses->getRMW(file);
792                 ses->activateRMW(roamwin);
793                 if (NULL != roamwin) roamwin->displayInCurrentWorkspace();
794
795                 cbData->roamWin = NULL;
796             }
797             else
798             {
799                 roamwin = new RoamMenuWindow(file);
800                 roamwin->initialize();
801                 roamwin->manage();
802                 roamwin->list()->select_all_and_display_last(error);
803
804                 cbData->roamWin = roamwin;
805             }
806
807             pjob = new DmxPrintJob(p, DTM_FALSE, (MainWindow*) roamwin);
808
809             cbData->msg = msg;
810             cbData->roamApp = roamapp;
811             XtAddCallback(pjob->baseWidget(),
812                         XtNdestroyCallback,
813                         tooltalk_msg_handler_done_cb,
814                         (XtPointer) cbData);
815             roamapp->registerPendingTask();
816
817             pjob->execute();
818 #else
819             MainWindow *mw = roamapp->defaultStatusWindow();
820
821             if (mw != NULL) {
822               char *buf = new char[1024];
823               sprintf(buf, "Printing %s", p);
824               mw->setStatus(buf);
825               delete [] buf;
826             }
827
828             status = tt_message_reply(msg);
829             dieFromTtError(status,
830                 "tooltalk_msg_handler.tt_message_reply", NULL, NULL);
831 #endif
832         }
833     } else {
834         status = tttk_message_fail( msg, TT_DESKTOP_ENOTSUP, 0, 1 );
835         dieFromTtError(status,
836                 "tooltalk_msg_handler.tttk_message_fail", NULL, NULL);
837     }
838
839     //     tt_free( (caddr_t)contents );
840     //     tt_free( file );
841     //     tt_free( docname );
842     return 0;
843 }
844
845 Tt_message
846 quit_message_cb(Tt_message m,
847         void *client_data,
848         Tt_message)
849 {
850     static const char *thisFcn = "quit_message_cb()";
851     int silent, force;
852     Tt_status status;
853
854     // int mark = tt_mark();
855
856     char *op = tt_message_op(m);
857     if ( tttk_string_op(op) == TTDT_QUIT ) {
858
859         /* silent? */
860         status = tt_message_arg_ival(m, 0, &silent);
861         if ( status != TT_OK ) {
862             fprintf(stderr, "DEBUG: %s: tt_message_arg_ival(0): %s\n",
863                     thisFcn, tt_status_message(status));
864             return m;
865         }
866         if ( silent ) {
867         }
868
869         /* force? */
870         status = tt_message_arg_ival(m, 1, &force);
871         if ( status != TT_OK ) {
872             fprintf(stderr, "DEBUG: %s: tt_message_arg_ival(1): %s\n",
873                     thisFcn, tt_status_message(status));
874             return m;
875         }
876         if ( force ) {
877             tt_message_reply(m);
878             free(op);
879             // tt_release(mark);
880             RoamApp *app = (RoamApp *)client_data;
881             // Need to make the following call; otherwise, will 
882             // run into Phase2Destroy
883             XtRemoveAllCallbacks(app->baseWidget(), 
884                                  XmNdestroyCallback);
885             app->shutdown();
886         }
887         // tt_release(mark);
888         return m; 
889     } else {
890         // tt_release(mark);
891         free(op);
892         return m;
893     }
894 }
895
896 static void
897 reload_notify_cb(XtPointer)
898 {
899     DebugPrintf(2, "reload_notify_cb:  Reloading types database\n");
900     DtDbLoad();
901 }
902 #endif   /* DTMAIL_TOOLTALK */
903
904 #ifdef DEAD_WOOD
905 static void
906 SigChldHandler(int)
907 {
908     int status;
909     int pid = (int) wait(&status);
910
911     ChildExitNotify(pid, status);
912 }
913 #endif /* DEAD_WOOD */
914
915
916 void pspace_signal( int )
917 {
918    Widget parent=theApplication->baseWidget();
919    if(parent)
920    {
921         DtMailGenDialog *genDialog = new DtMailGenDialog("Dialog",parent);
922
923         char *errMsg = (char *) XtCalloc(1,10240);
924
925                 // Serious error here -- No Space on Filesystem --
926         sprintf(errMsg,"Insufficient paging space, \n Mailer cannot perform any operations.\n Please contact the System Administrator to \n correct the paging space problem ");
927         genDialog->setToErrorDialog(
928                             GETMSG(DT_catd, 1, 6, "Mailer"),
929                             errMsg);
930         XtFree(errMsg);
931
932         genDialog->post_and_return(
933                             GETMSG(DT_catd, 3, 9, "OK"),
934                             NULL);
935         delete genDialog;
936     }
937     else
938         printf("Insufficient paging space, \n Mailer cannot perform any operations.\n Please contact the System Administrator to \n correct the paging space problem ");
939
940
941 }
942
943
944 void
945 Usage(char *progname)
946 {
947    printf("Usage:  %s\n", progname);
948    printf("[-h]  Help\n");
949    printf("[-c]  A blank compose window comes up.\n");
950    printf("[-a file1 ... fileN]  Compose window comes up with file1 through fileN as attachments.\n"); 
951    printf("[-f mailfile]  The specified mail folder is displayed instead of INBOX.\n");
952    printf("[-l]  Start the compose window on a dead letter file.\n");
953
954    // There is also a "-e" option which would run dtmail with the XmTextEditor
955    // instead of DtEditor.  Right now it is an undocumented feature.
956 }
957
958 nl_catd DT_catd = (nl_catd) -1;    // catgets file descriptor
959
960 #if defined(reallyoldsun) || defined(USL)
961 #define SA_HANDLER_TYPE void (*)(void)
962 #else
963 #define SA_HANDLER_TYPE void (*)(int)
964 #endif
965
966 void RoamApp::initialize(int *argcp, char **argv)
967 {
968     char                **av = argv;
969     struct sigaction    *action;
970     struct sigaction    action_buf;
971     Tt_status           status;
972
973     _busy_count = 0;
974     _firstSaveYourselfArrived = FALSE;
975     _options = NULL;
976     _optionsHandle = NULL;
977     _quitSilently = FALSE;
978     _quitQuickly = FALSE;
979     _shutdownWorkprocID = 0;
980
981     int n = 1;
982     char * mail_file = NULL;
983     char * dead_letter = NULL;
984     char *session_file = NULL;
985     int opt;
986     char *helpId;
987
988 #ifdef hpV4
989     signal(SIGUSR1, (void(*)(int ...))SigUsr1);
990 #else
991     signal(SIGUSR1, SigUsr1);
992 #endif
993
994 #ifdef _AIX
995     (void)signal( SIGDANGER, pspace_signal );
996 #endif /* _AIX */
997
998     action = &action_buf;
999     memset((void*) action, 0, sizeof(struct sigaction));
1000     action->sa_handler = (SA_HANDLER_TYPE) panicQuitSignalHandler;
1001     action->sa_flags = 0;
1002     sigaction(SIGHUP, action, NULL);
1003     sigaction(SIGINT, action, NULL);
1004     sigaction(SIGQUIT, action, NULL);
1005     sigaction(SIGILL, action, NULL);
1006     sigaction(SIGABRT, action, NULL);
1007     sigaction(SIGBUS, action, NULL);
1008     sigaction(SIGSEGV, action, NULL);
1009     sigaction(SIGTERM, action, NULL);
1010
1011     // Must be called before XtAppInitialize.
1012     XtSetLanguageProc(NULL, NULL, NULL);
1013
1014     // Set up environment variable (including NLSPATH) before calling 
1015     // XtAppInitialize() (cmvc 6576). 
1016     _DtEnvControl (DT_ENV_SET);
1017
1018     // This will take care of standard arguments such as -display.
1019     Application::initialize(argcp,argv);
1020
1021     // If -session arg is present remove it and return session file name. 
1022     session_file = parseSessionArg(argcp, argv);
1023
1024     // export display and locale settings
1025
1026     static char displayenv[256];        /* Needs to be static for putenv */
1027     sprintf(displayenv, "DISPLAY=%s", XDisplayString(_display));
1028     putenv(displayenv);
1029
1030     // Process dtmail opt args.
1031
1032     int num_legit_args = 0;
1033
1034     while((opt = getopt(*argcp, argv, "a:cef:hl:t")) != EOF) {
1035         switch (opt) {
1036           case 'a':
1037                 just_compose = 1;
1038                 num_legit_args++;
1039                 break;
1040           case 'c':
1041                 just_compose = 1;
1042                 num_legit_args++;
1043                 break;
1044
1045           case 'e':
1046                 use_XmTextEditor = 1;
1047                 num_legit_args++;
1048                 break;
1049
1050           case 'f':
1051                 mail_file = strdup(optarg);
1052                 num_legit_args++;
1053                 break;
1054
1055           case 'l':
1056                 just_compose = 1;
1057                 num_legit_args++;
1058                 dead_letter = optarg;
1059                 break;
1060
1061           case 't':
1062                 // Started by ToolTalk
1063                 started_by_tt++;
1064                 num_legit_args++;
1065                 n = 2;
1066                 break;
1067
1068           case 'h':
1069           default:
1070                 Usage(argv[0]);
1071                 // Remove cbs else face Phase2Destroy!
1072                 XtRemoveAllCallbacks(baseWidget(), XmNdestroyCallback);
1073                 exit (1);
1074         }
1075     }
1076
1077     initDebug();
1078
1079     if(!just_compose && !started_by_tt && !mail_file && session_file)
1080         openSessionFile(session_file); // Open the session file
1081
1082     MdbgChainCheck();
1083     getResources( _resources, XtNumber ( _resources ) );
1084
1085     DtInitialize(XtDisplay(baseWidget()), baseWidget(), argv[0], "Dtmail");
1086
1087     // Must be called after XtSetLanguageProc and DtInitialize because
1088     // DtInitialize sets the environment variable NLSPATH which is used
1089     // in catopen(). That's why we have to take out catopen from
1090     // Application::initialize and put a new mathod open->catalog for
1091     // both Application and RoamApp
1092     this->open_catalog();
1093
1094     // Initialize the mail_error. This also has to be done after DtInitialize 
1095     // because the DtMailEnv consturctor calls catopen as well.
1096     DtMailEnv mail_error;
1097     mail_error.clear();
1098
1099     DtDbLoad();
1100
1101     //
1102     // Some of the scrolling lists contain formatted text and thus
1103     // require a fixed width font to display the text correctly.
1104     // We want to use the fixed width font defined by dt.userFont as
1105     // it will be internationalized correctly.  If it is not there
1106     // we fall back to a fixed width lucida font.  If that fails
1107     // we let the widget fallback to what ever it thinks is best.
1108     //
1109
1110     // Default font in case we can't find anything else.
1111     XrmDatabase db = XtScreenDatabase(XtScreen(baseWidget()));
1112
1113     if (db) {
1114         FontType        userfont;
1115         char            *buf = new char[1024];
1116
1117
1118         // Need to check fixed width font mailrc variable.  Assume
1119         // True for now
1120 #if 0
1121         if (True) {
1122
1123             XrmPutStringResource(
1124                 &db, "*Work_Area*XmText.fontList", _user_font);
1125             XrmPutStringResource(
1126                 &db, "*Work_Area*XmTextField.fontList", _user_font);
1127 #endif
1128             XrmPutStringResource(
1129                 &db, "*Work_Area*DtEditor.textFontList", _user_font);
1130             XrmPutStringResource(
1131                 &db, "*Work_Area*iconGadget.fontList", _user_font);
1132             XrmPutStringResource(
1133                 &db, "*XmDialogShell*XmList.FontList", _user_font);
1134
1135                 // Convert the user fontlist to a font.
1136             if (!fontlist_to_font(_user_fontlist, &userfont)) {
1137                 if (!(userfont.f.cf_font = XLoadQueryFont(
1138                                                         XtDisplay(baseWidget()),
1139                                                         "fixed")) )
1140             /* Couldn't convert the user fontlist to a font */
1141                     fprintf(
1142                         stderr,
1143                         "RoamApp::initialize : error loading fixed font\n");
1144                 else
1145                     userfont.cf_type = XmFONT_IS_FONT;
1146             }
1147
1148 #if 0
1149         } else {
1150             FontType    systemfont;
1151
1152             XrmPutStringResource(
1153                 &db, "*Work_Area*Text*fontList", _system_font);
1154             XrmPutStringResource(
1155                 &db, "*Work_Area*iconGadget*fontList", _system_font);
1156             XrmPutStringResource(
1157                 &db, "*Work_Area*Text*textFontList", _system_font);
1158             XrmPutStringResource(
1159                 &db, "*XmDialogShell*XmList*FontList", _system_font);
1160
1161             if (!fontlist_to_font(_system_fontlist, &systemfont)) {
1162                 /* Couldn't convert the system fontlist to a font */
1163                 if (!(systemfont.f.cf_font = XLoadQueryFont(
1164                                                         XtDisplay(baseWidget()),
1165                                                         "variable")) )
1166                     fprintf(
1167                         stderr,
1168                         "RoamApp::initialize : error loading variable font\n");
1169                 else
1170                     systemfont.cf_type = XmFONT_IS_FONT;
1171             }
1172                 
1173         }
1174 #endif
1175
1176         // If the glyph font is specified in a resource file, use it.
1177         if (_glyph_font)
1178           _glyph_name = XtNewString(_glyph_font);
1179         else
1180           _glyph_name = NULL;
1181         
1182         // If the glyph font hasn't been specified, try to match it with
1183         // the user font.
1184         if (!_glyph_name) {
1185             // Get the font name that matches the user font pixel size.
1186             load_app_font(baseWidget(), &userfont, &_glyph_name);
1187         }
1188
1189         // Create a fontlist that contains the glyph and user fonts
1190         strcpy(buf, _user_font);
1191 #if 0
1192         // Never refer to the "plain" tag so dont add it.
1193         if (strchr(_user_font, '=') == NULL) {
1194             // No tag.  Add one
1195             strcat(buf, "=plain, ");
1196         }
1197 #endif
1198         // If the symbol font can't be found, use user font above
1199         if (_glyph_name) {
1200             strcat(buf, ", ");
1201             strcat(buf, _glyph_name);
1202             strcat(buf, "=attach");
1203         }
1204
1205         // Loosely bind font to message list.  This lets users override
1206         // with a more strongly bound name (ie "Dtmail*Message_List*FontList");
1207         //
1208         // Matches the MsgScrollingList in the RoamMenuWindows.
1209         XrmPutStringResource(&db, "*Message_List*FontList", buf);
1210         //
1211         // CDExc19318
1212         // Matches the UndelMsgScrollingList in the UndelFromListDialog
1213         // and overrides the "*XmDialogShell*XmList*FontList" spec above.
1214         XrmPutStringResource(&db, "*XmDialogShell*Message_List*FontList", buf);
1215
1216         delete [] buf;
1217     }
1218
1219     // If we don't have a mail file yet, then we need to retrieve the
1220     // Initialize Tooltalk
1221     // NOTE:  For now, must make the FIRST ttdt_open call to be the proc_id 
1222     //        that will respond to the start message.  Therefore, call gui's 
1223     //        ttdt_open before libDtMail calls its.
1224     char *sess = NULL;
1225
1226     sess = (char *)getenv("TT_SESSION");
1227     if (!sess || (*sess == '\0')) {
1228       sess = getenv("_SUN_TT_SESSION");
1229     }
1230     if (!sess || (*sess == '\0')) {
1231       tt_default_session_set(tt_X_session(XDisplayString(_display)));
1232     }
1233
1234     roam_tt_procid = ttdt_open( &roam_tt_fd, "DTMAIL", "SunSoft", "%I", 1 );
1235     dieFromTtError(tt_ptr_error(roam_tt_procid),
1236                 "initialize.ttdt_open",
1237                 GETMSG(DT_catd, 2, 2, "ToolTalk is not initialized.  Mailer cannot run without ToolTalk.\nTry starting /usr/dt/bin/dtsession, or contact your System Administrator."),
1238                 DTMAILHELPCANTINITTOOLTALK);
1239
1240     // This is for supporting old ptype where RFC_822_Message is
1241     // in lower case.
1242     status = ttmedia_ptype_declare( "RFC_822_Message",
1243                                     0,
1244                                     tooltalk_msg_handler,
1245                                     this,
1246                                     1);
1247     dieFromTtError(status,
1248                 "initialize.ttmedia_ptype_declare.RFC_822_Message", NULL, NULL);
1249
1250     status = ttmedia_ptype_declare( "RFC_822_MESSAGE",
1251                                     0,
1252                                     tooltalk_msg_handler,
1253                                     this,
1254                                     1);
1255     dieFromTtError(status,
1256                 "initialize.ttmedia_ptype_declare.RFC_822_MESSAGE", NULL, NULL);
1257
1258     status = ttmedia_ptype_declare( "MAIL_TYPE",
1259                                     0,
1260                                     attachmt_msg_handler,
1261                                     this,
1262                                     1);
1263     dieFromTtError(status,
1264                 "initialize.ttmedia_ptype_declare.MAIL_TYPE", NULL, NULL);
1265
1266     /* Join the default session -- This should have been done by default */
1267     roam_tt_pattern = ttdt_session_join(
1268                                 (const char *)0,
1269                                 (Ttdt_contract_cb)quit_message_cb,
1270                                 baseWidget(), this, 1);
1271     dieFromTtError(tt_ptr_error(roam_tt_pattern),
1272                 "initialize.ttdt_session_join", NULL, NULL);
1273
1274     _mail_session = new MailSession(mail_error, Application::appContext());
1275     if (mail_error.isSet()) {
1276         // what do we do here?  there are no windows for dialogs
1277         // should we just register a syslog() and exit? ugggh
1278         // SR
1279         fprintf(stderr, "RoamApp::initialize : error creating MailSession\n");
1280         exit (1);
1281     }
1282
1283     //
1284     // .mailrc parsing error checking
1285     //
1286     DtMail::Session * d_session = _mail_session->session();
1287     DtMail::MailRc* mailRc = d_session->mailRc(mail_error);
1288     DTMailError_t pErr = mailRc->getParseError();
1289     if (pErr != DTME_NoError) {
1290         int answer = 0;
1291         DtMailGenDialog *mailrc_dialog = new DtMailGenDialog(
1292                                                 "MailRcDialog",
1293                                                 theApplication->baseWidget());
1294         mailrc_dialog->setToQuestionDialog(
1295                                 GETMSG(DT_catd, 2, 1, "Mailer"),
1296                                 GETMSG(DT_catd, 2, 22,
1297                                         "There were unrecoverable syntax errors found in the ~/.mailrc file.\nCheck for more messages on terminal. Fix the errors and restart dtmail.\nIf you choose to continue you will not be able to save any changes made\nin the options dialog to file.") );
1298         helpId = DTMAILHELPERROR;
1299         answer = mailrc_dialog->post_and_return(
1300                                         GETMSG(DT_catd, 2, 23, "Continue"),
1301                                         GETMSG(DT_catd, 2, 24, "Exit"),
1302                                         helpId);
1303         if (answer == 2) {
1304             XtRemoveAllCallbacks(
1305                 theApplication->baseWidget(),
1306                 XmNdestroyCallback);
1307             exit(1);
1308         }
1309     }
1310
1311
1312     //
1313     // Check if mailer is installed properly if we are not in debugging mode.
1314     //
1315     const char *value = NULL;
1316     mail_error.clear();
1317     d_session->mailRc(mail_error)->getValue(mail_error, 
1318                                             "__ignore_group_permissions", 
1319                                             &value);
1320     if (mail_error.isSet()) {
1321         char grp_name[100];
1322
1323         mail_error.clear();
1324         theApplication->enableGroupPrivileges();
1325         GetGroupName(grp_name);
1326         theApplication->disableGroupPrivileges();
1327         if (strcmp(grp_name, DTMAIL_DEFAULT_CREATE_MAILGROUP))
1328           mail_error.setError(DTME_BadRunGroup);
1329     
1330
1331         if ((DTMailError_t)mail_error == DTME_BadRunGroup) {
1332             char        buf[512];
1333             int answer = 0;
1334
1335             DtMailGenDialog *install_errDialog =
1336                 new DtMailGenDialog("Dialog", theApplication->baseWidget());
1337             sprintf(buf, "%s", GETMSG(DT_catd, 2, 4,
1338                 "Mailer has not been properly installed,\n\
1339 and cannot run because the execution group\n\
1340 is incorrectly set."));
1341
1342             install_errDialog->setToErrorDialog(
1343                                 GETMSG(DT_catd, 1, 6, "Mailer"),
1344                                 buf);
1345
1346             // No choice at this state other than to OK.
1347             helpId = DTMAILHELPBADGROUPID;
1348             answer = install_errDialog->post_and_return(
1349                                 GETMSG(DT_catd, 3, 9, "OK"),
1350                                 helpId);
1351             XtRemoveAllCallbacks(
1352                 theApplication->baseWidget(),
1353                 XmNdestroyCallback);
1354
1355             exit(1);
1356         }
1357     }
1358     if (NULL != value)
1359       free((void*) value);
1360
1361     _options = new OptCmd("Mail Options...",
1362                           GETMSG(DT_catd, 1, 2,"Mail Options..."),
1363                           TRUE,
1364                           _w);
1365
1366     if ( just_compose ) {
1367         // Need to install self destruct
1368         SendMsgDialog *compose = theCompose.getWin();
1369
1370         if (dead_letter) {
1371             char *ttl = GETMSG(DT_catd, 1, 262, "Dead Letter Message");
1372             compose->loadDeadLetter(dead_letter);
1373             compose->setTitle(ttl);
1374             compose->setIconTitle(ttl);
1375         }
1376         else {
1377             if ( *argcp > num_legit_args + 1) {    // Have attachment(s)
1378                 for ( int k = num_legit_args + 1;  k < *argcp;  k++ ) {
1379                     compose->inclAsAttmt(argv[k], NULL);
1380                 }
1381             }
1382         }
1383     }
1384     
1385     // inbox. We do this by querying the current implementation for
1386     // the path to its inbox.
1387     //
1388     if (!mail_file) {
1389         DtMailObjectSpace space;
1390
1391         d_session->queryImpl(mail_error,
1392                              d_session->getDefaultImpl(mail_error),
1393                              DtMailCapabilityInboxName,
1394                              &space,
1395                              &mail_file);
1396     }
1397
1398     // We need to determine which transport to use. The order of preference
1399     // is:
1400     //
1401     // 1) The value of DEFAULT_TRANSPORT in the .mailrc file.
1402     //
1403     // 2) The value of DEFAULT_TRANSPORT in the environment.
1404     //
1405     // 3) The transport, if available for the default implementation.
1406     //
1407     // 4) The first implementation that provides a transport.
1408     //
1409     const char * trans_impl = NULL;
1410
1411     d_session->mailRc(mail_error)->getValue(mail_error, 
1412                                             "DEFAULT_TRANSPORT", 
1413                                             &trans_impl);
1414     if (mail_error.isSet()) {
1415         trans_impl = NULL;
1416     }
1417     
1418     
1419     if (!trans_impl) {
1420         trans_impl = getenv("DEFAULT_TRANSPORT");
1421     }
1422
1423     if (!trans_impl) {
1424         trans_impl = d_session->getDefaultImpl(mail_error);
1425
1426         DtMailBoolean trans;
1427
1428         d_session->queryImpl(mail_error, trans_impl, 
1429                              DtMailCapabilityTransport, &trans);
1430         if (mail_error.isSet() || trans == DTM_FALSE) {
1431             trans_impl = NULL;
1432         }
1433     }
1434
1435     if (!trans_impl) {
1436         const char ** impls = d_session->enumerateImpls(mail_error);
1437
1438         for (int impl = 0; impls[impl]; impl++) {
1439             DtMailBoolean trans;
1440             d_session->queryImpl(mail_error, impls[impl],
1441                                  DtMailCapabilityTransport, &trans);
1442             if (!mail_error.isSet() || trans == DTM_TRUE) {
1443                 trans_impl = impls[impl];
1444                 break;
1445             }
1446         }
1447     }
1448
1449     if (trans_impl) {
1450         _mail_transport = d_session->transportConstruct(mail_error,
1451                                                         trans_impl,
1452                                                         statusCallback,
1453                                                         this);
1454     }
1455     else {
1456         _mail_transport = NULL;
1457     }
1458
1459     // Register all callbacks the backend may have to deal with
1460     //
1461     d_session->registerLastInteractiveEventTimeCallback(lastInteractiveEventTime, this);
1462
1463     // These callbacks are only needed on systems where sendmail is run as 
1464     // 'root', and so has access permissions to any file on the system. 
1465     d_session->registerDisableGroupPrivilegesCallback(disableGroupPrivileges, this);
1466     d_session->registerEnableGroupPrivilegesCallback(enableGroupPrivileges, this);
1467     
1468     if (d_session->pollRequired(mail_error) == DTM_TRUE) {
1469         d_session->registerBusyCallback(mail_error, setBusyState, this);
1470         _appTimeoutId = XtAppAddTimeOut(
1471                                 XtWidgetToApplicationContext( _w ),
1472                                 15000,
1473                                 &RoamApp::applicationTimeout,
1474                                 (XtPointer) this );
1475     }
1476
1477     // Process the message that started us, if any.  Should I pass in
1478     // roam_tt_procid or NULL.
1479     tttk_Xt_input_handler( 0, 0, 0 );
1480     XtAppAddInput( Application::appContext(), roam_tt_fd,
1481                    (XtPointer)XtInputReadMask,
1482                    tttk_Xt_input_handler, roam_tt_procid );
1483
1484     // Get the vacation handle before the new RoamMenuWindow
1485     // This is for setting the Vacation title stripe on the window
1486     _vacation = new VacationCmd("Vacation", GETMSG(DT_catd, 1, 3, "Vacation"));
1487
1488     // DtMail only supports the "Mail" message.  
1489     // If DtMail is started by ToolTalk, then we assume that the 
1490     // client wants a Compose window.  Therefore, we do not
1491     // bring up a RoamMenuWindow.
1492     //
1493     if ( !session_fp && !started_by_tt && !just_compose) {
1494         dtmail_mapped = 1;
1495         _mailview = new RoamMenuWindow(mail_file);
1496         _mailview->initialize();
1497         _mailview->manage();
1498     } else {
1499         if(session_fp ) 
1500                 restoreSession();
1501         else
1502                 _mailview = NULL;
1503     }
1504
1505     initSession();
1506  
1507     _dialog = new DtMailGenDialog("Dialog", _w);
1508
1509     DtDbReloadNotify(reload_notify_cb, (XtPointer) NULL);
1510     _default_x_error_handler = XSetErrorHandler(x_error_handler);
1511     if (NULL != mail_file)
1512       free((void*) mail_file);
1513
1514
1515 RoamApp::RoamApp(char *name) : Application (name), _activePrintDisplays(5)
1516 {
1517     DebugPrintf(2, "RoamApp::RoamApp(%p \"%s\")\n", name, name);
1518     theRoamApp = *this;
1519     
1520     _busy_count = 0;
1521     _dialog = NULL;
1522     _errorPrintDisplay = NULL;
1523     _firstSaveYourselfArrived = false;
1524     _options = NULL;
1525     _optionsHandle = NULL;
1526     _mailview = NULL;
1527     _quitSilently = false;
1528     _quitQuickly = false;
1529     _vacation = NULL;
1530     session_fp = NULL;
1531     _shutdownWorkprocID = 0;
1532     _appTimeoutId = 0;
1533     _default_mailbox = NULL;
1534     _glyph_font = NULL;
1535     _glyph_name = NULL;
1536     _mailfiles_folder = NULL;
1537     _mail_session = NULL;
1538     _mail_transport = NULL;
1539     _print_script = NULL;
1540     _system_font = NULL;
1541     _system_fontlist = NULL;
1542     _tt_fd = 0;
1543     _user_font = NULL;
1544     _user_fontlist = NULL;
1545     _default_x_error_handler = NULL;
1546 }
1547
1548 // Let the destructor of parent Application class handle the
1549 // exit().
1550
1551 RoamApp::~RoamApp()
1552 {
1553 #ifdef DTMAIL_TOOLTALK
1554     if ( roam_tt_procid != NULL ) {
1555         //
1556         // Temporary workaround to get PrintToFile to work.
1557         // Currently, PrintToFile forks a child which is
1558         // calling ~RoamApp when it exits.  This clobbers
1559         // the tooltalk connection causing the parent to hang.
1560         //
1561         ttdt_close(0, 0, 1);
1562         roam_tt_procid = NULL;
1563     }
1564 #endif
1565
1566     catclose(DT_catd);
1567     if (_appTimeoutId)
1568       XtRemoveTimeOut(_appTimeoutId);
1569 }
1570
1571 Boolean
1572 RoamApp::shutdownWorkproc(XtPointer client_data)
1573 {
1574     RoamApp *thisApp = (RoamApp*) client_data;
1575
1576     if (thisApp->_numPendingTasks > 0)
1577       return FALSE;
1578     
1579     if (thisApp->_shutdownWorkprocID != 0)
1580     {
1581         XtRemoveWorkProc(thisApp->_shutdownWorkprocID);
1582         thisApp->_shutdownWorkprocID = 0;
1583     }
1584
1585     delete thisApp->_mail_session;
1586
1587     // Delete this and let the parent class's destructor call exit(0)
1588     delete thisApp;
1589
1590     return TRUE;
1591 }
1592
1593 void
1594 RoamApp::shutdown()
1595 {
1596     if (_numPendingTasks > 0)
1597     {
1598       if (_shutdownWorkprocID == 0)
1599         _shutdownWorkprocID = XtAppAddWorkProc(
1600                                         _appContext,
1601                                         &RoamApp::shutdownWorkproc,
1602                                         (XtPointer) this);
1603     }
1604     else
1605       shutdownWorkproc((XtPointer) this);
1606 }
1607
1608 void
1609 RoamApp::checkForShutdown()
1610 {
1611     int nappwin, ncompunused, nrmwunused;
1612
1613     if (! theApplication->isEnabledShutdown()) return;
1614     nappwin = theApplication->num_windows();
1615     ncompunused = theCompose.numUnusedWindows();
1616     nrmwunused = theRoamApp.session()->numDeactivatedRMW();
1617
1618     if (nappwin == ncompunused + nrmwunused)
1619     {
1620         theCompose.~Compose();
1621         while (_numWindows > 0)
1622         {
1623             delete _windows[0];
1624         }
1625     }
1626 }
1627
1628 void
1629 RoamApp::applicationTimeout( XtPointer client_data,
1630                                    XtIntervalId *id )
1631 {
1632   RoamApp *app = ( RoamApp * ) client_data;
1633   app->timeout( id );
1634 }
1635
1636 void
1637 RoamApp::timeout( XtIntervalId * )
1638 {
1639     DtMail::Session * d_session = _mail_session->session();
1640     DtMailEnv error;
1641
1642     d_session->poll(error);
1643
1644     _appTimeoutId = XtAppAddTimeOut(
1645                                 XtWidgetToApplicationContext( _w ),
1646                                 15000,
1647                                 &RoamApp::applicationTimeout,
1648                                 (XtPointer) this );
1649 }
1650
1651 long
1652 RoamApp::lastInteractiveEventTime(void * client_data)
1653 {
1654     RoamApp * self = (RoamApp *)client_data;
1655     return (theApplication->lastInteractiveEventTime());
1656 }
1657
1658 void
1659 RoamApp::disableGroupPrivileges(void * client_data)
1660 {
1661     RoamApp * self = (RoamApp *)client_data;
1662     theApplication->disableGroupPrivileges();
1663     return;
1664 }
1665
1666 void
1667 RoamApp::enableGroupPrivileges(void * client_data)
1668 {
1669     RoamApp * self = (RoamApp *)client_data;
1670     theApplication->enableGroupPrivileges();
1671     return;
1672 }
1673
1674
1675 void
1676 RoamApp::setBusyState(
1677                 DtMailEnv       &error,
1678                 DtMailBusyState busy_state,
1679                 void            *client_data)
1680 {
1681     static const int            MAXBUSYSTATES=20;
1682     static int                  busyStateStackTop = 0;
1683     static DtMailBusyState      busyStateStack[MAXBUSYSTATES] =
1684                                                 {DtMailBusyState_NotBusy};
1685     RoamApp                     *self = (RoamApp*) client_data;
1686
1687     switch (busy_state)
1688     {
1689         case DtMailBusyState_AutoSave:
1690         case DtMailBusyState_NewMail:
1691         default:
1692           showBusyState(error, busy_state, client_data);
1693 //        if (busyStateStackTop)        // Already busy from previous message?
1694 //          self->_busy_count--;        // Decrement busy count for
1695                                         // displaying this message
1696           busyStateStack[++busyStateStackTop] = busy_state;
1697           assert(busyStateStackTop < MAXBUSYSTATES);
1698           break;
1699
1700         case DtMailBusyState_NotBusy:
1701           if (busyStateStackTop)
1702           {
1703             if (error.isSet())
1704               self->_mailview->postErrorDialog(error);
1705            
1706             busy_state = busyStateStack[--busyStateStackTop];
1707             showBusyState(error, busy_state, client_data);
1708 //          if (busyStateStackTop)
1709 //            self->_busy_count--;      // Decrement busy count for
1710                                         // redisplaying this message
1711           }
1712           else
1713             showBusyState(error, busy_state, client_data);
1714           break;
1715     }
1716     assert(busyStateStack[0] == DtMailBusyState_NotBusy);
1717 }
1718
1719 void
1720 RoamApp::showBusyState(
1721                         DtMailEnv &,
1722                         DtMailBusyState busy_state,
1723                         void * client_data
1724                       )
1725 {
1726     RoamApp * self = (RoamApp *)client_data;
1727
1728     switch (busy_state)
1729     {
1730     case DtMailBusyState_AutoSave:
1731       self->busyAllWindows(GETMSG(DT_catd, 3, 1, "Auto-saving..."));
1732       break;
1733
1734     case DtMailBusyState_NewMail:
1735       self->busyAllWindows(GETMSG(DT_catd, 3, 86, "Checking for new mail..."));
1736       break;
1737
1738     case DtMailBusyState_NotBusy:
1739     default:
1740       self->unbusyAllWindows();
1741       break;
1742     }
1743 }
1744
1745 void
1746 RoamApp::busyAllWindows(const char * msg)
1747 {
1748   for (int win = 0; win < _numWindows; win++) {
1749 //  if (_busy_count == 0) {
1750     _windows[win]->busyCursor();
1751 //  }
1752     if (msg) {
1753       _windows[win]->setStatus(msg);
1754     }
1755   }
1756 //  _busy_count++;
1757 }
1758
1759 void
1760 RoamApp::unbusyAllWindows(void)
1761 {
1762 //  _busy_count--;
1763 //  if (_busy_count == 0) {
1764       for (int win = 0; win < _numWindows; win++) {
1765         _windows[win]->normalCursor();
1766         _windows[win]->clearStatus();
1767       }
1768 //  }
1769 }
1770
1771 void
1772 RoamApp::globalAddToCachedContainerList(char *destname)
1773 {
1774     const char *cname;
1775     RoamMenuWindow *rmw;
1776
1777     for (int win = 0; win < _numWindows; win++) {
1778         cname = _windows[win]->className(); 
1779         if (strcmp(cname, "RoamMenuWindow") == 0) { 
1780             rmw = (RoamMenuWindow *)_windows[win];
1781             rmw->addToCachedContainerList(destname);
1782         }
1783     }
1784 }
1785
1786 void
1787 RoamApp::globalPropChange(void)
1788 {
1789     busyAllWindows(GETMSG(DT_catd, 1, 4, "Updating properties..."));
1790
1791     for (int win = 0; win < _numWindows; win++) {
1792         _windows[win]->propsChanged();
1793     }
1794
1795     unbusyAllWindows();
1796 }
1797
1798 DtMailGenDialog *
1799 RoamApp::genDialog()
1800 {
1801     return(_dialog);
1802 }
1803
1804
1805 Boolean
1806 RoamApp::startVacation(
1807     Widget subject_tf,
1808     Widget text_tp
1809 )
1810 {
1811     XmString subj;
1812     XmString text;
1813     RoamMenuWindow *rmw;
1814     const char *cname;
1815     Boolean status;
1816
1817     XtVaGetValues(subject_tf,
1818         XmNvalue, &subj,
1819         NULL);
1820
1821     XtVaGetValues(text_tp,
1822         XmNvalue, &text,
1823         NULL);
1824         
1825     status = _vacation->startVacation((char *)subj, (char *) text);
1826
1827     for (int win = 0; win < _numWindows; win++) {
1828         cname = _windows[win]->className();
1829         if (strcmp(cname, "RoamMenuWindow") == 0) {
1830             rmw = (RoamMenuWindow *)_windows[win];
1831             rmw->setVacationTitle();
1832         }
1833     }
1834     return status;
1835 }
1836
1837 void
1838 RoamApp::stopVacation()
1839 {
1840     const char *cname;
1841     RoamMenuWindow *rmw;
1842
1843     _vacation->stopVacation();
1844
1845     for (int win = 0; win < _numWindows; win++) {
1846         cname = _windows[win]->className(); 
1847         if (strcmp(cname, "RoamMenuWindow") == 0) { 
1848             rmw = (RoamMenuWindow *)_windows[win];
1849             rmw->removeVacationTitle();
1850         }
1851     }
1852 }
1853
1854 char*
1855 getPropStringValue(DtVirtArray<PropStringPair *> &results, const char *label)
1856 {
1857         for (int mrc = 0; mrc < results.length(); mrc++) {
1858             PropStringPair * psp = results[mrc];
1859             if (strcmp(label, psp->label) == 0)
1860                 return strdup(psp->value);
1861         }
1862         return (char*)NULL;
1863 }
1864
1865 void
1866 parsePropString(const char * input, DtVirtArray<PropStringPair *> & results)
1867 {
1868   if (input == NULL)
1869         return;
1870
1871     // If it's not multibyte, use the regular string function
1872   if (MB_CUR_MAX <= 1) {
1873      const char * start = input;
1874      const char * end;
1875      char *ptr;
1876
1877     do {
1878         while (isspace(*start)) {
1879             start += 1;
1880         }
1881         
1882         for (end = start; end && *end; end++) 
1883             if (isspace(*end) && *(end-1) != '\\')
1884                 break;
1885
1886         PropStringPair * new_pair = new PropStringPair;
1887
1888         int len = end - start;
1889         char * label = new char[len + 5];
1890         strncpy(label, start, len);
1891         label[len] = 0;
1892         for (ptr = label; (ptr = strstr(ptr, "\\ ")); ptr++) 
1893                 strcpy(ptr, ptr+1); 
1894         
1895         char * file = strchr(label, ':');
1896         if (!file) {
1897             new_pair->label = strdup(label);
1898             new_pair->value = NULL;
1899         }
1900         else {
1901             *file = 0;
1902             new_pair->label = strdup(label);
1903
1904             file += 1;
1905             if (strlen(file) == 0) {
1906                 new_pair->value = NULL;
1907             }
1908             else {
1909                 new_pair->value = strdup(file);
1910             }
1911         }
1912
1913         results.append(new_pair);
1914         delete [] label;
1915
1916         start = end;
1917         
1918         while (*start && isspace(*start)) {
1919             start += 1;
1920         }
1921         
1922     } while(*start && *end);
1923   }
1924
1925   else {
1926     // The string can contain multibyte characters and it must be converted to 
1927     // wide character string before parsing
1928     int len = strlen(input);
1929     wchar_t *wc_input= new wchar_t[len+1];
1930     mbstowcs(wc_input, input, len+1);
1931     const wchar_t *start = wc_input;
1932     const wchar_t *end;
1933     wchar_t *ptr;
1934
1935     do {
1936         while (iswspace(*start)) {             
1937             start += 1;             
1938         }
1939
1940         for (end = start; end && *end; end++)
1941             if (iswspace(*end) && *(end-1) != (wint_t)'\\')
1942                 break;
1943         PropStringPair * new_pair = new PropStringPair;
1944
1945         int wclen = end - start;
1946         wchar_t *wc_label = new wchar_t[wclen+1];
1947         wcsncpy(wc_label, start, wclen);
1948         wc_label[wclen] = (wint_t)'\0';
1949
1950         // Search the string "\ " and take out the back slash
1951         wchar_t esc_space[3];
1952         mbstowcs(esc_space, "\\ ", 3);
1953         for (ptr = wc_label; (ptr = wcswcs(ptr, esc_space)); ptr++)
1954                 wcscpy(ptr, ptr+1);
1955
1956         wchar_t *file = wcschr(wc_label, (wint_t)':');
1957         if (!file) {
1958             new_pair->label = new char[(wclen+1)*MB_CUR_MAX];
1959             wcstombs(new_pair->label, wc_label, wclen+1);
1960             new_pair->value = NULL;
1961
1962         }
1963
1964         else {
1965             *file = (wint_t)'\0';
1966             new_pair->label = new char[(wclen+1)*MB_CUR_MAX];
1967             wcstombs(new_pair->label, wc_label, (wclen+1)*MB_CUR_MAX);
1968
1969             file += 1;
1970             int filelen = wcslen(file);
1971             if (filelen == 0) {
1972                 new_pair->value = NULL;
1973             }
1974             else {
1975                 new_pair->value = new char[(filelen+1)*MB_CUR_MAX];
1976                 wcstombs(new_pair->value, file, (filelen+1)*MB_CUR_MAX);
1977
1978             }
1979         }
1980         results.append(new_pair);
1981         delete []wc_label;
1982         start = end;
1983
1984         while (*start && iswspace(*start)) {
1985             start += 1;
1986         }
1987
1988     } while(*start && *end);
1989
1990     delete []wc_input;
1991 }
1992 }
1993
1994 PropStringPair::PropStringPair(void)
1995 {
1996     label = NULL;
1997     value = NULL;
1998 }
1999
2000 PropStringPair::PropStringPair(const PropStringPair & other)
2001 {
2002     label = NULL;
2003     value = NULL;
2004
2005     if (other.label) {
2006         label = strdup(other.label);
2007     }
2008
2009     if (other.value) {
2010         value = strdup(other.value);
2011     }
2012 }
2013
2014 PropStringPair::~PropStringPair(void)
2015 {
2016     if (label) {
2017         free(label);
2018     }
2019
2020     if (value) {
2021         free(value);
2022     }
2023 }
2024
2025 void
2026 RoamApp::setSession( MailSession *session )
2027 {
2028    _mail_session = session; 
2029 }
2030
2031
2032 VacationCmd*
2033 RoamApp::vacation()
2034 {
2035     return (_vacation);
2036 }
2037
2038 char *formatPropPair(char * key, const void * data)
2039 {
2040     char *formatted_str = NULL;
2041     char *white_space = NULL;
2042     int i, num_spaces = 0;
2043     int key_len = strlen(key);
2044     int m_size;
2045
2046     if (data == NULL)
2047         data = (void *)"";
2048
2049     // figure out whitespace for formatting
2050     // assume 13 for normal sized alias name
2051
2052     if(key_len < 13)  // need to add spaces
2053       {
2054         num_spaces = 13 - key_len;
2055
2056         white_space = (char *)malloc(num_spaces + 1);
2057         strcpy(white_space, "\0");
2058
2059         for(i = 0; i < num_spaces; i++)
2060           strcat(white_space, " ");
2061
2062         m_size = key_len + strlen((char *)white_space)
2063                  + strlen((char *)data) + strlen(" = ") + 1;
2064         formatted_str = (char *)malloc(m_size);
2065
2066         sprintf(formatted_str, "%s%s = %s",key, white_space, (const char *) data);
2067
2068       }
2069     else
2070       {
2071                 /* make an alias string */
2072         formatted_str = (char *)malloc(key_len + strlen((const char *)data) + 2);
2073     
2074         m_size = key_len + strlen((const char *)data) + strlen(" = ") + 1;
2075         formatted_str = (char *)malloc(m_size);
2076
2077         sprintf(formatted_str, "%s = %s",key, (const char *) data);
2078
2079       }
2080     
2081     return formatted_str;
2082
2083 }
2084
2085 RoamMenuWindow*
2086 RoamApp::inboxWindow()
2087 {
2088     RoamMenuWindow *rmw;
2089     const char *cname;
2090
2091     for (int win = 0; win < _numWindows; win++) {
2092         cname = _windows[win]->className();
2093         if (strcmp(cname, "RoamMenuWindow") == 0) {
2094             rmw = (RoamMenuWindow *)_windows[win];
2095             if (rmw->inbox()) {
2096                 return(rmw);
2097             }
2098         }
2099     }
2100     return(NULL);
2101 }
2102
2103 RoamMenuWindow*
2104 RoamApp::nextRoamMenuWindow(RoamMenuWindow *last)
2105 {
2106     RoamMenuWindow *looking_for = last;
2107     RoamMenuWindow *rmw;
2108     const char *cname;
2109
2110     for (int win = 0; win < _numWindows; win++) {
2111         cname = _windows[win]->className();
2112         if (strcmp(cname, "RoamMenuWindow") == 0) {
2113             rmw = (RoamMenuWindow *)_windows[win];
2114             if (NULL == looking_for)
2115               return(rmw);
2116             if (rmw == looking_for)
2117               looking_for = NULL;
2118         }
2119     }
2120     return(NULL);
2121 }
2122
2123 void
2124 RoamApp::closeInactiveRoamMenuWindows(void)
2125 {
2126     MailSession *ses = theRoamApp.session();
2127     RoamMenuWindow **rmws;
2128     const char *cname;
2129     int win=0, nrmws=0;
2130
2131     rmws = (RoamMenuWindow**) malloc(_numWindows * sizeof(RoamMenuWindow *));
2132     for (win=0; win<_numWindows; win++)
2133     {
2134         cname = _windows[win]->className();
2135         if (strcmp(cname, "RoamMenuWindow") == 0)
2136           rmws[nrmws++] = (RoamMenuWindow*) _windows[win];
2137     }
2138
2139     for (win=0; win<nrmws; win++)
2140       if (! ses->isActiveRMW(rmws[win])) rmws[win]->quit(TRUE);
2141
2142     free(rmws);
2143 }
2144
2145 void
2146 RoamApp::reopenRoamMenuWindows(void)
2147 {
2148     RoamMenuWindow *rmw, **rmws;
2149     const char *cname;
2150     int win=0, nrmws=0;
2151
2152     theApplication->disableShutdown();
2153
2154     rmws = (RoamMenuWindow**) malloc(_numWindows * sizeof(RoamMenuWindow *));
2155     for (win=0; win<_numWindows; win++)
2156     {
2157         cname = _windows[win]->className();
2158         if (strcmp(cname, "RoamMenuWindow") == 0)
2159           rmws[nrmws++] = (RoamMenuWindow*) _windows[win];
2160     }
2161
2162     for (win=0; win<nrmws; win++)
2163     {
2164         char *name;
2165         rmw = rmws[win];
2166         name = strdup(rmw->mailboxName());
2167         rmw->quit(TRUE);
2168
2169         rmw = new RoamMenuWindow(name);
2170         rmw->initialize();
2171         rmw->manage();
2172         free(name);
2173     }
2174     free(rmws);
2175
2176     theApplication->enableShutdown();
2177 }
2178
2179 MainWindow*
2180 RoamApp::defaultStatusWindow()
2181 {
2182     MainWindow *mw = NULL;
2183
2184     if ((mw = (MainWindow *) _mailview) != NULL)
2185       return mw;
2186     if ((mw = (MainWindow *) inboxWindow()) != NULL)
2187       return mw;
2188     if (_numWindows > 0)
2189       mw = (MainWindow *) _windows[0];
2190
2191     return mw;
2192 }
2193
2194 void
2195 RoamApp::open_catalog()
2196 {
2197     Application::open_catalog();      // Open Motif Application message catalog file
2198     DT_catd = catopen(DTMAIL_CAT, NL_CAT_LOCALE); // Open DtMail message catalog file
2199 }