Convert uses of XKeycodeToKeysym (deprecated) to XkbKeycodeToKeysym
[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(__linux__) || defined(CSRG_BASED)
74 #define wcswcs wcsstr
75 #include <wctype.h>
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 occurred 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 (char*) 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 #define SA_HANDLER_TYPE void (*)(int)
961
962 void RoamApp::initialize(int *argcp, char **argv)
963 {
964     char                **av = argv;
965     struct sigaction    *action;
966     struct sigaction    action_buf;
967     Tt_status           status;
968
969     _busy_count = 0;
970     _firstSaveYourselfArrived = FALSE;
971     _options = NULL;
972     _optionsHandle = NULL;
973     _quitSilently = FALSE;
974     _quitQuickly = FALSE;
975     _shutdownWorkprocID = 0;
976
977     int n = 1;
978     char * mail_file = NULL;
979     char * dead_letter = NULL;
980     char *session_file = NULL;
981     int opt;
982     char *helpId;
983
984 #ifdef hpV4
985     signal(SIGUSR1, (void(*)(int ...))SigUsr1);
986 #else
987     signal(SIGUSR1, SigUsr1);
988 #endif
989
990 #ifdef _AIX
991     (void)signal( SIGDANGER, pspace_signal );
992 #endif /* _AIX */
993
994     action = &action_buf;
995     memset((void*) action, 0, sizeof(struct sigaction));
996     action->sa_handler = (SA_HANDLER_TYPE) panicQuitSignalHandler;
997     action->sa_flags = 0;
998     sigaction(SIGHUP, action, NULL);
999     sigaction(SIGINT, action, NULL);
1000     sigaction(SIGQUIT, action, NULL);
1001     sigaction(SIGILL, action, NULL);
1002     sigaction(SIGABRT, action, NULL);
1003     sigaction(SIGBUS, action, NULL);
1004     sigaction(SIGSEGV, action, NULL);
1005     sigaction(SIGTERM, action, NULL);
1006
1007     // Must be called before XtAppInitialize.
1008     XtSetLanguageProc(NULL, NULL, NULL);
1009
1010     // Set up environment variable (including NLSPATH) before calling 
1011     // XtAppInitialize() (cmvc 6576). 
1012     _DtEnvControl (DT_ENV_SET);
1013
1014     // This will take care of standard arguments such as -display.
1015     Application::initialize(argcp,argv);
1016
1017     // If -session arg is present remove it and return session file name. 
1018     session_file = parseSessionArg(argcp, argv);
1019
1020     // export display and locale settings
1021
1022     static char displayenv[256];        /* Needs to be static for putenv */
1023     sprintf(displayenv, "DISPLAY=%s", XDisplayString(_display));
1024     putenv(displayenv);
1025
1026     // Process dtmail opt args.
1027
1028     int num_legit_args = 0;
1029
1030     while((opt = getopt(*argcp, argv, "a:cef:hl:t")) != EOF) {
1031         switch (opt) {
1032           case 'a':
1033                 just_compose = 1;
1034                 num_legit_args++;
1035                 break;
1036           case 'c':
1037                 just_compose = 1;
1038                 num_legit_args++;
1039                 break;
1040
1041           case 'e':
1042                 use_XmTextEditor = 1;
1043                 num_legit_args++;
1044                 break;
1045
1046           case 'f':
1047                 mail_file = strdup(optarg);
1048                 num_legit_args++;
1049                 break;
1050
1051           case 'l':
1052                 just_compose = 1;
1053                 num_legit_args++;
1054                 dead_letter = optarg;
1055                 break;
1056
1057           case 't':
1058                 // Started by ToolTalk
1059                 started_by_tt++;
1060                 num_legit_args++;
1061                 n = 2;
1062                 break;
1063
1064           case 'h':
1065           default:
1066                 Usage(argv[0]);
1067                 // Remove cbs else face Phase2Destroy!
1068                 XtRemoveAllCallbacks(baseWidget(), XmNdestroyCallback);
1069                 exit (1);
1070         }
1071     }
1072
1073     initDebug();
1074
1075     if(!just_compose && !started_by_tt && !mail_file && session_file)
1076         openSessionFile(session_file); // Open the session file
1077
1078     MdbgChainCheck();
1079     getResources( _resources, XtNumber ( _resources ) );
1080
1081     DtInitialize(XtDisplay(baseWidget()), baseWidget(), argv[0], "Dtmail");
1082
1083     // Must be called after XtSetLanguageProc and DtInitialize because
1084     // DtInitialize sets the environment variable NLSPATH which is used
1085     // in catopen(). That's why we have to take out catopen from
1086     // Application::initialize and put a new mathod open->catalog for
1087     // both Application and RoamApp
1088     this->open_catalog();
1089
1090     // Initialize the mail_error. This also has to be done after DtInitialize 
1091     // because the DtMailEnv consturctor calls catopen as well.
1092     DtMailEnv mail_error;
1093     mail_error.clear();
1094
1095     DtDbLoad();
1096
1097     //
1098     // Some of the scrolling lists contain formatted text and thus
1099     // require a fixed width font to display the text correctly.
1100     // We want to use the fixed width font defined by dt.userFont as
1101     // it will be internationalized correctly.  If it is not there
1102     // we fall back to a fixed width lucida font.  If that fails
1103     // we let the widget fallback to what ever it thinks is best.
1104     //
1105
1106     // Default font in case we can't find anything else.
1107     XrmDatabase db = XtScreenDatabase(XtScreen(baseWidget()));
1108
1109     if (db) {
1110         FontType        userfont;
1111         char            *buf = new char[1024];
1112
1113
1114         // Need to check fixed width font mailrc variable.  Assume
1115         // True for now
1116 #if 0
1117         if (True) {
1118
1119             XrmPutStringResource(
1120                 &db, "*Work_Area*XmText.fontList", _user_font);
1121             XrmPutStringResource(
1122                 &db, "*Work_Area*XmTextField.fontList", _user_font);
1123 #endif
1124             XrmPutStringResource(
1125                 &db, "*Work_Area*DtEditor.textFontList", _user_font);
1126             XrmPutStringResource(
1127                 &db, "*Work_Area*iconGadget.fontList", _user_font);
1128             XrmPutStringResource(
1129                 &db, "*XmDialogShell*XmList.FontList", _user_font);
1130
1131                 // Convert the user fontlist to a font.
1132             if (!fontlist_to_font(_user_fontlist, &userfont)) {
1133                 if (!(userfont.f.cf_font = XLoadQueryFont(
1134                                                         XtDisplay(baseWidget()),
1135                                                         "fixed")) )
1136             /* Couldn't convert the user fontlist to a font */
1137                     fprintf(
1138                         stderr,
1139                         "RoamApp::initialize : error loading fixed font\n");
1140                 else
1141                     userfont.cf_type = XmFONT_IS_FONT;
1142             }
1143
1144 #if 0
1145         } else {
1146             FontType    systemfont;
1147
1148             XrmPutStringResource(
1149                 &db, "*Work_Area*Text*fontList", _system_font);
1150             XrmPutStringResource(
1151                 &db, "*Work_Area*iconGadget*fontList", _system_font);
1152             XrmPutStringResource(
1153                 &db, "*Work_Area*Text*textFontList", _system_font);
1154             XrmPutStringResource(
1155                 &db, "*XmDialogShell*XmList*FontList", _system_font);
1156
1157             if (!fontlist_to_font(_system_fontlist, &systemfont)) {
1158                 /* Couldn't convert the system fontlist to a font */
1159                 if (!(systemfont.f.cf_font = XLoadQueryFont(
1160                                                         XtDisplay(baseWidget()),
1161                                                         "variable")) )
1162                     fprintf(
1163                         stderr,
1164                         "RoamApp::initialize : error loading variable font\n");
1165                 else
1166                     systemfont.cf_type = XmFONT_IS_FONT;
1167             }
1168                 
1169         }
1170 #endif
1171
1172         // If the glyph font is specified in a resource file, use it.
1173         if (_glyph_font)
1174           _glyph_name = XtNewString(_glyph_font);
1175         else
1176           _glyph_name = NULL;
1177         
1178         // If the glyph font hasn't been specified, try to match it with
1179         // the user font.
1180         if (!_glyph_name) {
1181             // Get the font name that matches the user font pixel size.
1182             load_app_font(baseWidget(), &userfont, &_glyph_name);
1183         }
1184
1185         // Create a fontlist that contains the glyph and user fonts
1186         strcpy(buf, _user_font);
1187 #if 0
1188         // Never refer to the "plain" tag so don't add it.
1189         if (strchr(_user_font, '=') == NULL) {
1190             // No tag.  Add one
1191             strcat(buf, "=plain, ");
1192         }
1193 #endif
1194         // If the symbol font can't be found, use user font above
1195         if (_glyph_name) {
1196             strcat(buf, ", ");
1197             strcat(buf, _glyph_name);
1198             strcat(buf, "=attach");
1199         }
1200
1201         // Loosely bind font to message list.  This lets users override
1202         // with a more strongly bound name (ie "Dtmail*Message_List*FontList");
1203         //
1204         // Matches the MsgScrollingList in the RoamMenuWindows.
1205         XrmPutStringResource(&db, "*Message_List*FontList", buf);
1206         //
1207         // CDExc19318
1208         // Matches the UndelMsgScrollingList in the UndelFromListDialog
1209         // and overrides the "*XmDialogShell*XmList*FontList" spec above.
1210         XrmPutStringResource(&db, "*XmDialogShell*Message_List*FontList", buf);
1211
1212         delete [] buf;
1213     }
1214
1215     // If we don't have a mail file yet, then we need to retrieve the
1216     // Initialize Tooltalk
1217     // NOTE:  For now, must make the FIRST ttdt_open call to be the proc_id 
1218     //        that will respond to the start message.  Therefore, call gui's 
1219     //        ttdt_open before libDtMail calls its.
1220     char *sess = NULL;
1221
1222     sess = (char *)getenv("TT_SESSION");
1223     if (!sess || (*sess == '\0')) {
1224       sess = getenv("_SUN_TT_SESSION");
1225     }
1226     if (!sess || (*sess == '\0')) {
1227       tt_default_session_set(tt_X_session(XDisplayString(_display)));
1228     }
1229
1230     roam_tt_procid = ttdt_open( &roam_tt_fd, "DTMAIL", "SunSoft", "%I", 1 );
1231     dieFromTtError(tt_ptr_error(roam_tt_procid),
1232                 "initialize.ttdt_open",
1233                 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."),
1234                 DTMAILHELPCANTINITTOOLTALK);
1235
1236     // This is for supporting old ptype where RFC_822_Message is
1237     // in lower case.
1238     status = ttmedia_ptype_declare( "RFC_822_Message",
1239                                     0,
1240                                     tooltalk_msg_handler,
1241                                     this,
1242                                     1);
1243     dieFromTtError(status,
1244                 "initialize.ttmedia_ptype_declare.RFC_822_Message", NULL, NULL);
1245
1246     status = ttmedia_ptype_declare( "RFC_822_MESSAGE",
1247                                     0,
1248                                     tooltalk_msg_handler,
1249                                     this,
1250                                     1);
1251     dieFromTtError(status,
1252                 "initialize.ttmedia_ptype_declare.RFC_822_MESSAGE", NULL, NULL);
1253
1254     status = ttmedia_ptype_declare( "MAIL_TYPE",
1255                                     0,
1256                                     attachmt_msg_handler,
1257                                     this,
1258                                     1);
1259     dieFromTtError(status,
1260                 "initialize.ttmedia_ptype_declare.MAIL_TYPE", NULL, NULL);
1261
1262     /* Join the default session -- This should have been done by default */
1263     roam_tt_pattern = ttdt_session_join(
1264                                 (const char *)0,
1265                                 (Ttdt_contract_cb)quit_message_cb,
1266                                 baseWidget(), this, 1);
1267     dieFromTtError(tt_ptr_error(roam_tt_pattern),
1268                 "initialize.ttdt_session_join", NULL, NULL);
1269
1270     _mail_session = new MailSession(mail_error, Application::appContext());
1271     if (mail_error.isSet()) {
1272         // what do we do here?  there are no windows for dialogs
1273         // should we just register a syslog() and exit? ugggh
1274         // SR
1275         fprintf(stderr, "RoamApp::initialize : error creating MailSession\n");
1276         exit (1);
1277     }
1278
1279     //
1280     // .mailrc parsing error checking
1281     //
1282     DtMail::Session * d_session = _mail_session->session();
1283     DtMail::MailRc* mailRc = d_session->mailRc(mail_error);
1284     DTMailError_t pErr = mailRc->getParseError();
1285     if (pErr != DTME_NoError) {
1286         int answer = 0;
1287         DtMailGenDialog *mailrc_dialog = new DtMailGenDialog(
1288                                                 "MailRcDialog",
1289                                                 theApplication->baseWidget());
1290         mailrc_dialog->setToQuestionDialog(
1291                                 GETMSG(DT_catd, 2, 1, "Mailer"),
1292                                 GETMSG(DT_catd, 2, 22,
1293                                         "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.") );
1294         helpId = DTMAILHELPERROR;
1295         answer = mailrc_dialog->post_and_return(
1296                                         GETMSG(DT_catd, 2, 23, "Continue"),
1297                                         GETMSG(DT_catd, 2, 24, "Exit"),
1298                                         helpId);
1299         if (answer == 2) {
1300             XtRemoveAllCallbacks(
1301                 theApplication->baseWidget(),
1302                 XmNdestroyCallback);
1303             exit(1);
1304         }
1305     }
1306
1307
1308     //
1309     // Check if mailer is installed properly if we are not in debugging mode.
1310     //
1311     const char *value = NULL;
1312     mail_error.clear();
1313     d_session->mailRc(mail_error)->getValue(mail_error, 
1314                                             "__ignore_group_permissions", 
1315                                             &value);
1316     if (mail_error.isSet()) {
1317         char grp_name[100];
1318
1319         mail_error.clear();
1320         theApplication->enableGroupPrivileges();
1321         GetGroupName(grp_name);
1322         theApplication->disableGroupPrivileges();
1323         if (strcmp(grp_name, DTMAIL_DEFAULT_CREATE_MAILGROUP))
1324           mail_error.setError(DTME_BadRunGroup);
1325     
1326
1327         if ((DTMailError_t)mail_error == DTME_BadRunGroup) {
1328             char        buf[512];
1329             int answer = 0;
1330
1331             DtMailGenDialog *install_errDialog =
1332                 new DtMailGenDialog("Dialog", theApplication->baseWidget());
1333             sprintf(buf, "%s", GETMSG(DT_catd, 2, 4,
1334                 "Mailer has not been properly installed,\n\
1335 and cannot run because the execution group\n\
1336 is incorrectly set."));
1337
1338             install_errDialog->setToErrorDialog(
1339                                 GETMSG(DT_catd, 1, 6, "Mailer"),
1340                                 buf);
1341
1342             // No choice at this state other than to OK.
1343             helpId = DTMAILHELPBADGROUPID;
1344             answer = install_errDialog->post_and_return(
1345                                 GETMSG(DT_catd, 3, 9, "OK"),
1346                                 helpId);
1347             XtRemoveAllCallbacks(
1348                 theApplication->baseWidget(),
1349                 XmNdestroyCallback);
1350
1351             exit(1);
1352         }
1353     }
1354     if (NULL != value)
1355       free((void*) value);
1356
1357     _options = new OptCmd("Mail Options...",
1358                           GETMSG(DT_catd, 1, 2,"Mail Options..."),
1359                           TRUE,
1360                           _w);
1361
1362     if ( just_compose ) {
1363         // Need to install self destruct
1364         SendMsgDialog *compose = theCompose.getWin();
1365
1366         if (dead_letter) {
1367             char *ttl = GETMSG(DT_catd, 1, 262, "Dead Letter Message");
1368             compose->loadDeadLetter(dead_letter);
1369             compose->setTitle(ttl);
1370             compose->setIconTitle(ttl);
1371         }
1372         else {
1373             if ( *argcp > num_legit_args + 1) {    // Have attachment(s)
1374                 for ( int k = num_legit_args + 1;  k < *argcp;  k++ ) {
1375                     compose->inclAsAttmt(argv[k], NULL);
1376                 }
1377             }
1378         }
1379     }
1380     
1381     // inbox. We do this by querying the current implementation for
1382     // the path to its inbox.
1383     //
1384     if (!mail_file) {
1385         DtMailObjectSpace space;
1386
1387         d_session->queryImpl(mail_error,
1388                              d_session->getDefaultImpl(mail_error),
1389                              DtMailCapabilityInboxName,
1390                              &space,
1391                              &mail_file);
1392     }
1393
1394     // We need to determine which transport to use. The order of preference
1395     // is:
1396     //
1397     // 1) The value of DEFAULT_TRANSPORT in the .mailrc file.
1398     //
1399     // 2) The value of DEFAULT_TRANSPORT in the environment.
1400     //
1401     // 3) The transport, if available for the default implementation.
1402     //
1403     // 4) The first implementation that provides a transport.
1404     //
1405     const char * trans_impl = NULL;
1406
1407     d_session->mailRc(mail_error)->getValue(mail_error, 
1408                                             "DEFAULT_TRANSPORT", 
1409                                             &trans_impl);
1410     if (mail_error.isSet()) {
1411         trans_impl = NULL;
1412     }
1413     
1414     
1415     if (!trans_impl) {
1416         trans_impl = getenv("DEFAULT_TRANSPORT");
1417     }
1418
1419     if (!trans_impl) {
1420         trans_impl = d_session->getDefaultImpl(mail_error);
1421
1422         DtMailBoolean trans;
1423
1424         d_session->queryImpl(mail_error, trans_impl, 
1425                              DtMailCapabilityTransport, &trans);
1426         if (mail_error.isSet() || trans == DTM_FALSE) {
1427             trans_impl = NULL;
1428         }
1429     }
1430
1431     if (!trans_impl) {
1432         const char ** impls = d_session->enumerateImpls(mail_error);
1433
1434         for (int impl = 0; impls[impl]; impl++) {
1435             DtMailBoolean trans;
1436             d_session->queryImpl(mail_error, impls[impl],
1437                                  DtMailCapabilityTransport, &trans);
1438             if (!mail_error.isSet() || trans == DTM_TRUE) {
1439                 trans_impl = impls[impl];
1440                 break;
1441             }
1442         }
1443     }
1444
1445     if (trans_impl) {
1446         _mail_transport = d_session->transportConstruct(mail_error,
1447                                                         trans_impl,
1448                                                         statusCallback,
1449                                                         this);
1450     }
1451     else {
1452         _mail_transport = NULL;
1453     }
1454
1455     // Register all callbacks the backend may have to deal with
1456     //
1457     d_session->registerLastInteractiveEventTimeCallback(lastInteractiveEventTime, this);
1458
1459     // These callbacks are only needed on systems where sendmail is run as 
1460     // 'root', and so has access permissions to any file on the system. 
1461     d_session->registerDisableGroupPrivilegesCallback(disableGroupPrivileges, this);
1462     d_session->registerEnableGroupPrivilegesCallback(enableGroupPrivileges, this);
1463     
1464     if (d_session->pollRequired(mail_error) == DTM_TRUE) {
1465         d_session->registerBusyCallback(mail_error, setBusyState, this);
1466         _appTimeoutId = XtAppAddTimeOut(
1467                                 XtWidgetToApplicationContext( _w ),
1468                                 15000,
1469                                 &RoamApp::applicationTimeout,
1470                                 (XtPointer) this );
1471     }
1472
1473     // Process the message that started us, if any.  Should I pass in
1474     // roam_tt_procid or NULL.
1475     tttk_Xt_input_handler( 0, 0, 0 );
1476     XtAppAddInput( Application::appContext(), roam_tt_fd,
1477                    (XtPointer)XtInputReadMask,
1478                    tttk_Xt_input_handler, roam_tt_procid );
1479
1480     // Get the vacation handle before the new RoamMenuWindow
1481     // This is for setting the Vacation title stripe on the window
1482     _vacation = new VacationCmd("Vacation", GETMSG(DT_catd, 1, 3, "Vacation"));
1483
1484     // DtMail only supports the "Mail" message.  
1485     // If DtMail is started by ToolTalk, then we assume that the 
1486     // client wants a Compose window.  Therefore, we do not
1487     // bring up a RoamMenuWindow.
1488     //
1489     if ( !session_fp && !started_by_tt && !just_compose) {
1490         dtmail_mapped = 1;
1491         _mailview = new RoamMenuWindow(mail_file);
1492         _mailview->initialize();
1493         _mailview->manage();
1494     } else {
1495         if(session_fp ) 
1496                 restoreSession();
1497         else
1498                 _mailview = NULL;
1499     }
1500
1501     initSession();
1502  
1503     _dialog = new DtMailGenDialog("Dialog", _w);
1504
1505     DtDbReloadNotify(reload_notify_cb, (XtPointer) NULL);
1506     _default_x_error_handler = XSetErrorHandler(x_error_handler);
1507     if (NULL != mail_file)
1508       free((void*) mail_file);
1509
1510
1511 RoamApp::RoamApp(char *name) : Application (name), _activePrintDisplays(5)
1512 {
1513     DebugPrintf(2, "RoamApp::RoamApp(%p \"%s\")\n", name, name);
1514     theRoamApp = *this;
1515     
1516     _busy_count = 0;
1517     _dialog = NULL;
1518     _errorPrintDisplay = NULL;
1519     _firstSaveYourselfArrived = false;
1520     _options = NULL;
1521     _optionsHandle = NULL;
1522     _mailview = NULL;
1523     _quitSilently = false;
1524     _quitQuickly = false;
1525     _vacation = NULL;
1526     session_fp = NULL;
1527     _shutdownWorkprocID = 0;
1528     _appTimeoutId = 0;
1529     _default_mailbox = NULL;
1530     _glyph_font = NULL;
1531     _glyph_name = NULL;
1532     _mailfiles_folder = NULL;
1533     _mail_session = NULL;
1534     _mail_transport = NULL;
1535     _print_script = NULL;
1536     _system_font = NULL;
1537     _system_fontlist = NULL;
1538     _tt_fd = 0;
1539     _user_font = NULL;
1540     _user_fontlist = NULL;
1541     _default_x_error_handler = NULL;
1542 }
1543
1544 // Let the destructor of parent Application class handle the
1545 // exit().
1546
1547 RoamApp::~RoamApp()
1548 {
1549 #ifdef DTMAIL_TOOLTALK
1550     if ( roam_tt_procid != NULL ) {
1551         //
1552         // Temporary workaround to get PrintToFile to work.
1553         // Currently, PrintToFile forks a child which is
1554         // calling ~RoamApp when it exits.  This clobbers
1555         // the tooltalk connection causing the parent to hang.
1556         //
1557         ttdt_close(0, 0, 1);
1558         roam_tt_procid = NULL;
1559     }
1560 #endif
1561
1562     catclose(DT_catd);
1563     if (_appTimeoutId)
1564       XtRemoveTimeOut(_appTimeoutId);
1565 }
1566
1567 Boolean
1568 RoamApp::shutdownWorkproc(XtPointer client_data)
1569 {
1570     RoamApp *thisApp = (RoamApp*) client_data;
1571
1572     if (thisApp->_numPendingTasks > 0)
1573       return FALSE;
1574     
1575     if (thisApp->_shutdownWorkprocID != 0)
1576     {
1577         XtRemoveWorkProc(thisApp->_shutdownWorkprocID);
1578         thisApp->_shutdownWorkprocID = 0;
1579     }
1580
1581     delete thisApp->_mail_session;
1582
1583     // Delete this and let the parent class's destructor call exit(0)
1584     delete thisApp;
1585
1586     return TRUE;
1587 }
1588
1589 void
1590 RoamApp::shutdown()
1591 {
1592     if (_numPendingTasks > 0)
1593     {
1594       if (_shutdownWorkprocID == 0)
1595         _shutdownWorkprocID = XtAppAddWorkProc(
1596                                         _appContext,
1597                                         &RoamApp::shutdownWorkproc,
1598                                         (XtPointer) this);
1599     }
1600     else
1601       shutdownWorkproc((XtPointer) this);
1602 }
1603
1604 void
1605 RoamApp::checkForShutdown()
1606 {
1607     int nappwin, ncompunused, nrmwunused;
1608
1609     if (! theApplication->isEnabledShutdown()) return;
1610     nappwin = theApplication->num_windows();
1611     ncompunused = theCompose.numUnusedWindows();
1612     nrmwunused = theRoamApp.session()->numDeactivatedRMW();
1613
1614     if (nappwin == ncompunused + nrmwunused)
1615     {
1616         theCompose.~Compose();
1617         while (_numWindows > 0)
1618         {
1619             delete _windows[0];
1620         }
1621     }
1622 }
1623
1624 void
1625 RoamApp::applicationTimeout( XtPointer client_data,
1626                                    XtIntervalId *id )
1627 {
1628   RoamApp *app = ( RoamApp * ) client_data;
1629   app->timeout( id );
1630 }
1631
1632 void
1633 RoamApp::timeout( XtIntervalId * )
1634 {
1635     DtMail::Session * d_session = _mail_session->session();
1636     DtMailEnv error;
1637
1638     d_session->poll(error);
1639
1640     _appTimeoutId = XtAppAddTimeOut(
1641                                 XtWidgetToApplicationContext( _w ),
1642                                 15000,
1643                                 &RoamApp::applicationTimeout,
1644                                 (XtPointer) this );
1645 }
1646
1647 long
1648 RoamApp::lastInteractiveEventTime(void * client_data)
1649 {
1650     RoamApp * self = (RoamApp *)client_data;
1651     return (theApplication->lastInteractiveEventTime());
1652 }
1653
1654 void
1655 RoamApp::disableGroupPrivileges(void * client_data)
1656 {
1657     RoamApp * self = (RoamApp *)client_data;
1658     theApplication->disableGroupPrivileges();
1659     return;
1660 }
1661
1662 void
1663 RoamApp::enableGroupPrivileges(void * client_data)
1664 {
1665     RoamApp * self = (RoamApp *)client_data;
1666     theApplication->enableGroupPrivileges();
1667     return;
1668 }
1669
1670
1671 void
1672 RoamApp::setBusyState(
1673                 DtMailEnv       &error,
1674                 DtMailBusyState busy_state,
1675                 void            *client_data)
1676 {
1677     static const int            MAXBUSYSTATES=20;
1678     static int                  busyStateStackTop = 0;
1679     static DtMailBusyState      busyStateStack[MAXBUSYSTATES] =
1680                                                 {DtMailBusyState_NotBusy};
1681     RoamApp                     *self = (RoamApp*) client_data;
1682
1683     switch (busy_state)
1684     {
1685         case DtMailBusyState_AutoSave:
1686         case DtMailBusyState_NewMail:
1687         default:
1688           showBusyState(error, busy_state, client_data);
1689 //        if (busyStateStackTop)        // Already busy from previous message?
1690 //          self->_busy_count--;        // Decrement busy count for
1691                                         // displaying this message
1692           busyStateStack[++busyStateStackTop] = busy_state;
1693           assert(busyStateStackTop < MAXBUSYSTATES);
1694           break;
1695
1696         case DtMailBusyState_NotBusy:
1697           if (busyStateStackTop)
1698           {
1699             if (error.isSet())
1700               self->_mailview->postErrorDialog(error);
1701            
1702             busy_state = busyStateStack[--busyStateStackTop];
1703             showBusyState(error, busy_state, client_data);
1704 //          if (busyStateStackTop)
1705 //            self->_busy_count--;      // Decrement busy count for
1706                                         // redisplaying this message
1707           }
1708           else
1709             showBusyState(error, busy_state, client_data);
1710           break;
1711     }
1712     assert(busyStateStack[0] == DtMailBusyState_NotBusy);
1713 }
1714
1715 void
1716 RoamApp::showBusyState(
1717                         DtMailEnv &,
1718                         DtMailBusyState busy_state,
1719                         void * client_data
1720                       )
1721 {
1722     RoamApp * self = (RoamApp *)client_data;
1723
1724     switch (busy_state)
1725     {
1726     case DtMailBusyState_AutoSave:
1727       self->busyAllWindows(GETMSG(DT_catd, 3, 1, "Auto-saving..."));
1728       break;
1729
1730     case DtMailBusyState_NewMail:
1731       self->busyAllWindows(GETMSG(DT_catd, 3, 86, "Checking for new mail..."));
1732       break;
1733
1734     case DtMailBusyState_NotBusy:
1735     default:
1736       self->unbusyAllWindows();
1737       break;
1738     }
1739 }
1740
1741 void
1742 RoamApp::busyAllWindows(const char * msg)
1743 {
1744   for (int win = 0; win < _numWindows; win++) {
1745 //  if (_busy_count == 0) {
1746     _windows[win]->busyCursor();
1747 //  }
1748     if (msg) {
1749       _windows[win]->setStatus(msg);
1750     }
1751   }
1752 //  _busy_count++;
1753 }
1754
1755 void
1756 RoamApp::unbusyAllWindows(void)
1757 {
1758 //  _busy_count--;
1759 //  if (_busy_count == 0) {
1760       for (int win = 0; win < _numWindows; win++) {
1761         _windows[win]->normalCursor();
1762         _windows[win]->clearStatus();
1763       }
1764 //  }
1765 }
1766
1767 void
1768 RoamApp::globalAddToCachedContainerList(char *destname)
1769 {
1770     const char *cname;
1771     RoamMenuWindow *rmw;
1772
1773     for (int win = 0; win < _numWindows; win++) {
1774         cname = _windows[win]->className(); 
1775         if (strcmp(cname, "RoamMenuWindow") == 0) { 
1776             rmw = (RoamMenuWindow *)_windows[win];
1777             rmw->addToCachedContainerList(destname);
1778         }
1779     }
1780 }
1781
1782 void
1783 RoamApp::globalPropChange(void)
1784 {
1785     busyAllWindows(GETMSG(DT_catd, 1, 4, "Updating properties..."));
1786
1787     for (int win = 0; win < _numWindows; win++) {
1788         _windows[win]->propsChanged();
1789     }
1790
1791     unbusyAllWindows();
1792 }
1793
1794 DtMailGenDialog *
1795 RoamApp::genDialog()
1796 {
1797     return(_dialog);
1798 }
1799
1800
1801 Boolean
1802 RoamApp::startVacation(
1803     Widget subject_tf,
1804     Widget text_tp
1805 )
1806 {
1807     XmString subj;
1808     XmString text;
1809     RoamMenuWindow *rmw;
1810     const char *cname;
1811     Boolean status;
1812
1813     XtVaGetValues(subject_tf,
1814         XmNvalue, &subj,
1815         NULL);
1816
1817     XtVaGetValues(text_tp,
1818         XmNvalue, &text,
1819         NULL);
1820         
1821     status = _vacation->startVacation((char *)subj, (char *) text);
1822
1823     for (int win = 0; win < _numWindows; win++) {
1824         cname = _windows[win]->className();
1825         if (strcmp(cname, "RoamMenuWindow") == 0) {
1826             rmw = (RoamMenuWindow *)_windows[win];
1827             rmw->setVacationTitle();
1828         }
1829     }
1830     return status;
1831 }
1832
1833 void
1834 RoamApp::stopVacation()
1835 {
1836     const char *cname;
1837     RoamMenuWindow *rmw;
1838
1839     _vacation->stopVacation();
1840
1841     for (int win = 0; win < _numWindows; win++) {
1842         cname = _windows[win]->className(); 
1843         if (strcmp(cname, "RoamMenuWindow") == 0) { 
1844             rmw = (RoamMenuWindow *)_windows[win];
1845             rmw->removeVacationTitle();
1846         }
1847     }
1848 }
1849
1850 char*
1851 getPropStringValue(DtVirtArray<PropStringPair *> &results, const char *label)
1852 {
1853         for (int mrc = 0; mrc < results.length(); mrc++) {
1854             PropStringPair * psp = results[mrc];
1855             if (strcmp(label, psp->label) == 0)
1856                 return strdup(psp->value);
1857         }
1858         return (char*)NULL;
1859 }
1860
1861 void
1862 parsePropString(const char * input, DtVirtArray<PropStringPair *> & results)
1863 {
1864   if (input == NULL)
1865         return;
1866
1867     // If it's not multibyte, use the regular string function
1868   if (MB_CUR_MAX <= 1) {
1869      const char * start = input;
1870      const char * end;
1871      char *ptr;
1872
1873     do {
1874         while (isspace(*start)) {
1875             start += 1;
1876         }
1877         
1878         for (end = start; end && *end; end++) 
1879             if (isspace(*end) && *(end-1) != '\\')
1880                 break;
1881
1882         PropStringPair * new_pair = new PropStringPair;
1883
1884         int len = end - start;
1885         char * label = new char[len + 5];
1886         strncpy(label, start, len);
1887         label[len] = 0;
1888         for (ptr = label; (ptr = strstr(ptr, "\\ ")); ptr++) 
1889                 strcpy(ptr, ptr+1); 
1890         
1891         char * file = strchr(label, ':');
1892         if (!file) {
1893             new_pair->label = strdup(label);
1894             new_pair->value = NULL;
1895         }
1896         else {
1897             *file = 0;
1898             new_pair->label = strdup(label);
1899
1900             file += 1;
1901             if (strlen(file) == 0) {
1902                 new_pair->value = NULL;
1903             }
1904             else {
1905                 new_pair->value = strdup(file);
1906             }
1907         }
1908
1909         results.append(new_pair);
1910         delete [] label;
1911
1912         start = end;
1913         
1914         while (*start && isspace(*start)) {
1915             start += 1;
1916         }
1917         
1918     } while(*start && *end);
1919   }
1920
1921   else {
1922     // The string can contain multibyte characters and it must be converted to 
1923     // wide character string before parsing
1924     int len = strlen(input);
1925     wchar_t *wc_input= new wchar_t[len+1];
1926     mbstowcs(wc_input, input, len+1);
1927     const wchar_t *start = wc_input;
1928     const wchar_t *end;
1929     wchar_t *ptr;
1930
1931     do {
1932         while (iswspace(*start)) {             
1933             start += 1;             
1934         }
1935
1936         for (end = start; end && *end; end++)
1937             if (iswspace(*end) && *(end-1) != (wint_t)'\\')
1938                 break;
1939         PropStringPair * new_pair = new PropStringPair;
1940
1941         int wclen = end - start;
1942         wchar_t *wc_label = new wchar_t[wclen+1];
1943         wcsncpy(wc_label, start, wclen);
1944         wc_label[wclen] = (wint_t)'\0';
1945
1946         // Search the string "\ " and take out the back slash
1947         wchar_t esc_space[3];
1948         mbstowcs(esc_space, "\\ ", 3);
1949         for (ptr = wc_label; (ptr = wcswcs(ptr, esc_space)); ptr++)
1950                 wcscpy(ptr, ptr+1);
1951
1952         wchar_t *file = wcschr(wc_label, (wint_t)':');
1953         if (!file) {
1954             new_pair->label = new char[(wclen+1)*MB_CUR_MAX];
1955             wcstombs(new_pair->label, wc_label, wclen+1);
1956             new_pair->value = NULL;
1957
1958         }
1959
1960         else {
1961             *file = (wint_t)'\0';
1962             new_pair->label = new char[(wclen+1)*MB_CUR_MAX];
1963             wcstombs(new_pair->label, wc_label, (wclen+1)*MB_CUR_MAX);
1964
1965             file += 1;
1966             int filelen = wcslen(file);
1967             if (filelen == 0) {
1968                 new_pair->value = NULL;
1969             }
1970             else {
1971                 new_pair->value = new char[(filelen+1)*MB_CUR_MAX];
1972                 wcstombs(new_pair->value, file, (filelen+1)*MB_CUR_MAX);
1973
1974             }
1975         }
1976         results.append(new_pair);
1977         delete []wc_label;
1978         start = end;
1979
1980         while (*start && iswspace(*start)) {
1981             start += 1;
1982         }
1983
1984     } while(*start && *end);
1985
1986     delete []wc_input;
1987 }
1988 }
1989
1990 PropStringPair::PropStringPair(void)
1991 {
1992     label = NULL;
1993     value = NULL;
1994 }
1995
1996 PropStringPair::PropStringPair(const PropStringPair & other)
1997 {
1998     label = NULL;
1999     value = NULL;
2000
2001     if (other.label) {
2002         label = strdup(other.label);
2003     }
2004
2005     if (other.value) {
2006         value = strdup(other.value);
2007     }
2008 }
2009
2010 PropStringPair::~PropStringPair(void)
2011 {
2012     if (label) {
2013         free(label);
2014     }
2015
2016     if (value) {
2017         free(value);
2018     }
2019 }
2020
2021 void
2022 RoamApp::setSession( MailSession *session )
2023 {
2024    _mail_session = session; 
2025 }
2026
2027
2028 VacationCmd*
2029 RoamApp::vacation()
2030 {
2031     return (_vacation);
2032 }
2033
2034 char *formatPropPair(char * key, const void * data)
2035 {
2036     char *formatted_str = NULL;
2037     char *white_space = NULL;
2038     int i, num_spaces = 0;
2039     int key_len = strlen(key);
2040     int m_size;
2041
2042     if (data == NULL)
2043         data = (void *)"";
2044
2045     // figure out whitespace for formatting
2046     // assume 13 for normal sized alias name
2047
2048     if(key_len < 13)  // need to add spaces
2049       {
2050         num_spaces = 13 - key_len;
2051
2052         white_space = (char *)malloc(num_spaces + 1);
2053         strcpy(white_space, "\0");
2054
2055         for(i = 0; i < num_spaces; i++)
2056           strcat(white_space, " ");
2057
2058         m_size = key_len + strlen((char *)white_space)
2059                  + strlen((char *)data) + strlen(" = ") + 1;
2060         formatted_str = (char *)malloc(m_size);
2061
2062         sprintf(formatted_str, "%s%s = %s",key, white_space, (const char *) data);
2063
2064       }
2065     else
2066       {
2067                 /* make an alias string */
2068         formatted_str = (char *)malloc(key_len + strlen((const char *)data) + 2);
2069     
2070         m_size = key_len + strlen((const char *)data) + strlen(" = ") + 1;
2071         formatted_str = (char *)malloc(m_size);
2072
2073         sprintf(formatted_str, "%s = %s",key, (const char *) data);
2074
2075       }
2076     
2077     return formatted_str;
2078
2079 }
2080
2081 RoamMenuWindow*
2082 RoamApp::inboxWindow()
2083 {
2084     RoamMenuWindow *rmw;
2085     const char *cname;
2086
2087     for (int win = 0; win < _numWindows; win++) {
2088         cname = _windows[win]->className();
2089         if (strcmp(cname, "RoamMenuWindow") == 0) {
2090             rmw = (RoamMenuWindow *)_windows[win];
2091             if (rmw->inbox()) {
2092                 return(rmw);
2093             }
2094         }
2095     }
2096     return(NULL);
2097 }
2098
2099 RoamMenuWindow*
2100 RoamApp::nextRoamMenuWindow(RoamMenuWindow *last)
2101 {
2102     RoamMenuWindow *looking_for = last;
2103     RoamMenuWindow *rmw;
2104     const char *cname;
2105
2106     for (int win = 0; win < _numWindows; win++) {
2107         cname = _windows[win]->className();
2108         if (strcmp(cname, "RoamMenuWindow") == 0) {
2109             rmw = (RoamMenuWindow *)_windows[win];
2110             if (NULL == looking_for)
2111               return(rmw);
2112             if (rmw == looking_for)
2113               looking_for = NULL;
2114         }
2115     }
2116     return(NULL);
2117 }
2118
2119 void
2120 RoamApp::closeInactiveRoamMenuWindows(void)
2121 {
2122     MailSession *ses = theRoamApp.session();
2123     RoamMenuWindow **rmws;
2124     const char *cname;
2125     int win=0, nrmws=0;
2126
2127     rmws = (RoamMenuWindow**) malloc(_numWindows * sizeof(RoamMenuWindow *));
2128     for (win=0; win<_numWindows; win++)
2129     {
2130         cname = _windows[win]->className();
2131         if (strcmp(cname, "RoamMenuWindow") == 0)
2132           rmws[nrmws++] = (RoamMenuWindow*) _windows[win];
2133     }
2134
2135     for (win=0; win<nrmws; win++)
2136       if (! ses->isActiveRMW(rmws[win])) rmws[win]->quit(TRUE);
2137
2138     free(rmws);
2139 }
2140
2141 void
2142 RoamApp::reopenRoamMenuWindows(void)
2143 {
2144     RoamMenuWindow *rmw, **rmws;
2145     const char *cname;
2146     int win=0, nrmws=0;
2147
2148     theApplication->disableShutdown();
2149
2150     rmws = (RoamMenuWindow**) malloc(_numWindows * sizeof(RoamMenuWindow *));
2151     for (win=0; win<_numWindows; win++)
2152     {
2153         cname = _windows[win]->className();
2154         if (strcmp(cname, "RoamMenuWindow") == 0)
2155           rmws[nrmws++] = (RoamMenuWindow*) _windows[win];
2156     }
2157
2158     for (win=0; win<nrmws; win++)
2159     {
2160         char *name;
2161         rmw = rmws[win];
2162         name = strdup(rmw->mailboxName());
2163         rmw->quit(TRUE);
2164
2165         rmw = new RoamMenuWindow(name);
2166         rmw->initialize();
2167         rmw->manage();
2168         free(name);
2169     }
2170     free(rmws);
2171
2172     theApplication->enableShutdown();
2173 }
2174
2175 MainWindow*
2176 RoamApp::defaultStatusWindow()
2177 {
2178     MainWindow *mw = NULL;
2179
2180     if ((mw = (MainWindow *) _mailview) != NULL)
2181       return mw;
2182     if ((mw = (MainWindow *) inboxWindow()) != NULL)
2183       return mw;
2184     if (_numWindows > 0)
2185       mw = (MainWindow *) _windows[0];
2186
2187     return mw;
2188 }
2189
2190 void
2191 RoamApp::open_catalog()
2192 {
2193     Application::open_catalog();      // Open Motif Application message catalog file
2194     DT_catd = catopen(DTMAIL_CAT, NL_CAT_LOCALE); // Open DtMail message catalog file
2195 }