dtmail: resolve 'deference before null check' errors related to if(!NULL) checks...
[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 librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /*
24  *+SNOTICE
25  *
26  *      $TOG: 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     sprintf(tmpdir, "%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
1521 // Let the destructor of parent Application class handle the
1522 // exit().
1523
1524 RoamApp::~RoamApp()
1525 {
1526 #ifdef DTMAIL_TOOLTALK
1527     if ( roam_tt_procid != NULL ) {
1528         //
1529         // Temporary workaround to get PrintToFile to work.
1530         // Currently, PrintToFile forks a child which is
1531         // calling ~RoamApp when it exits.  This clobbers
1532         // the tooltalk connection causing the parent to hang.
1533         //
1534         ttdt_close(0, 0, 1);
1535         roam_tt_procid = NULL;
1536     }
1537 #endif
1538
1539     catclose(DT_catd);
1540     if (_appTimeoutId)
1541       XtRemoveTimeOut(_appTimeoutId);
1542 }
1543
1544 Boolean
1545 RoamApp::shutdownWorkproc(XtPointer client_data)
1546 {
1547     RoamApp *thisApp = (RoamApp*) client_data;
1548
1549     if (thisApp->_numPendingTasks > 0)
1550       return FALSE;
1551     
1552     if (thisApp->_shutdownWorkprocID != 0)
1553     {
1554         XtRemoveWorkProc(thisApp->_shutdownWorkprocID);
1555         thisApp->_shutdownWorkprocID = 0;
1556     }
1557
1558     delete thisApp->_mail_session;
1559
1560     // Delete this and let the parent class's destructor call exit(0)
1561     delete thisApp;
1562
1563     return TRUE;
1564 }
1565
1566 void
1567 RoamApp::shutdown()
1568 {
1569     if (_numPendingTasks > 0)
1570     {
1571       if (_shutdownWorkprocID == 0)
1572         _shutdownWorkprocID = XtAppAddWorkProc(
1573                                         _appContext,
1574                                         &RoamApp::shutdownWorkproc,
1575                                         (XtPointer) this);
1576     }
1577     else
1578       shutdownWorkproc((XtPointer) this);
1579 }
1580
1581 void
1582 RoamApp::checkForShutdown()
1583 {
1584     int nappwin, ncompunused, nrmwunused;
1585
1586     if (! theApplication->isEnabledShutdown()) return;
1587     nappwin = theApplication->num_windows();
1588     ncompunused = theCompose.numUnusedWindows();
1589     nrmwunused = theRoamApp.session()->numDeactivatedRMW();
1590
1591     if (nappwin == ncompunused + nrmwunused)
1592     {
1593         theCompose.~Compose();
1594         while (_numWindows > 0)
1595         {
1596             delete _windows[0];
1597         }
1598     }
1599 }
1600
1601 void
1602 RoamApp::applicationTimeout( XtPointer client_data,
1603                                    XtIntervalId *id )
1604 {
1605   RoamApp *app = ( RoamApp * ) client_data;
1606   app->timeout( id );
1607 }
1608
1609 void
1610 RoamApp::timeout( XtIntervalId * )
1611 {
1612     DtMail::Session * d_session = _mail_session->session();
1613     DtMailEnv error;
1614
1615     d_session->poll(error);
1616
1617     _appTimeoutId = XtAppAddTimeOut(
1618                                 XtWidgetToApplicationContext( _w ),
1619                                 15000,
1620                                 &RoamApp::applicationTimeout,
1621                                 (XtPointer) this );
1622 }
1623
1624 long
1625 RoamApp::lastInteractiveEventTime(void * client_data)
1626 {
1627     RoamApp * self = (RoamApp *)client_data;
1628     return (theApplication->lastInteractiveEventTime());
1629 }
1630
1631 void
1632 RoamApp::disableGroupPrivileges(void * client_data)
1633 {
1634     RoamApp * self = (RoamApp *)client_data;
1635     theApplication->disableGroupPrivileges();
1636     return;
1637 }
1638
1639 void
1640 RoamApp::enableGroupPrivileges(void * client_data)
1641 {
1642     RoamApp * self = (RoamApp *)client_data;
1643     theApplication->enableGroupPrivileges();
1644     return;
1645 }
1646
1647
1648 void
1649 RoamApp::setBusyState(
1650                 DtMailEnv       &error,
1651                 DtMailBusyState busy_state,
1652                 void            *client_data)
1653 {
1654     static const int            MAXBUSYSTATES=20;
1655     static int                  busyStateStackTop = 0;
1656     static DtMailBusyState      busyStateStack[MAXBUSYSTATES] =
1657                                                 {DtMailBusyState_NotBusy};
1658     RoamApp                     *self = (RoamApp*) client_data;
1659
1660     switch (busy_state)
1661     {
1662         case DtMailBusyState_AutoSave:
1663         case DtMailBusyState_NewMail:
1664         default:
1665           showBusyState(error, busy_state, client_data);
1666 //        if (busyStateStackTop)        // Already busy from previous message?
1667 //          self->_busy_count--;        // Decrement busy count for
1668                                         // displaying this message
1669           busyStateStack[++busyStateStackTop] = busy_state;
1670           assert(busyStateStackTop < MAXBUSYSTATES);
1671           break;
1672
1673         case DtMailBusyState_NotBusy:
1674           if (busyStateStackTop)
1675           {
1676             if (error.isSet())
1677               self->_mailview->postErrorDialog(error);
1678            
1679             busy_state = busyStateStack[--busyStateStackTop];
1680             showBusyState(error, busy_state, client_data);
1681 //          if (busyStateStackTop)
1682 //            self->_busy_count--;      // Decrement busy count for
1683                                         // redisplaying this message
1684           }
1685           else
1686             showBusyState(error, busy_state, client_data);
1687           break;
1688     }
1689     assert(busyStateStack[0] == DtMailBusyState_NotBusy);
1690 }
1691
1692 void
1693 RoamApp::showBusyState(
1694                         DtMailEnv &,
1695                         DtMailBusyState busy_state,
1696                         void * client_data
1697                       )
1698 {
1699     RoamApp * self = (RoamApp *)client_data;
1700
1701     switch (busy_state)
1702     {
1703     case DtMailBusyState_AutoSave:
1704       self->busyAllWindows(GETMSG(DT_catd, 3, 1, "Auto-saving..."));
1705       break;
1706
1707     case DtMailBusyState_NewMail:
1708       self->busyAllWindows(GETMSG(DT_catd, 3, 86, "Checking for new mail..."));
1709       break;
1710
1711     case DtMailBusyState_NotBusy:
1712     default:
1713       self->unbusyAllWindows();
1714       break;
1715     }
1716 }
1717
1718 void
1719 RoamApp::busyAllWindows(const char * msg)
1720 {
1721   for (int win = 0; win < _numWindows; win++) {
1722 //  if (_busy_count == 0) {
1723     _windows[win]->busyCursor();
1724 //  }
1725     if (msg) {
1726       _windows[win]->setStatus(msg);
1727     }
1728   }
1729 //  _busy_count++;
1730 }
1731
1732 void
1733 RoamApp::unbusyAllWindows(void)
1734 {
1735 //  _busy_count--;
1736 //  if (_busy_count == 0) {
1737       for (int win = 0; win < _numWindows; win++) {
1738         _windows[win]->normalCursor();
1739         _windows[win]->clearStatus();
1740       }
1741 //  }
1742 }
1743
1744 void
1745 RoamApp::globalAddToCachedContainerList(char *destname)
1746 {
1747     const char *cname;
1748     RoamMenuWindow *rmw;
1749
1750     for (int win = 0; win < _numWindows; win++) {
1751         cname = _windows[win]->className(); 
1752         if (strcmp(cname, "RoamMenuWindow") == 0) { 
1753             rmw = (RoamMenuWindow *)_windows[win];
1754             rmw->addToCachedContainerList(destname);
1755         }
1756     }
1757 }
1758
1759 void
1760 RoamApp::globalPropChange(void)
1761 {
1762     busyAllWindows(GETMSG(DT_catd, 1, 4, "Updating properties..."));
1763
1764     for (int win = 0; win < _numWindows; win++) {
1765         _windows[win]->propsChanged();
1766     }
1767
1768     unbusyAllWindows();
1769 }
1770
1771 DtMailGenDialog *
1772 RoamApp::genDialog()
1773 {
1774     return(_dialog);
1775 }
1776
1777
1778 Boolean
1779 RoamApp::startVacation(
1780     Widget subject_tf,
1781     Widget text_tp
1782 )
1783 {
1784     XmString subj;
1785     XmString text;
1786     RoamMenuWindow *rmw;
1787     const char *cname;
1788     Boolean status;
1789
1790     XtVaGetValues(subject_tf,
1791         XmNvalue, &subj,
1792         NULL);
1793
1794     XtVaGetValues(text_tp,
1795         XmNvalue, &text,
1796         NULL);
1797         
1798     status = _vacation->startVacation((char *)subj, (char *) text);
1799
1800     for (int win = 0; win < _numWindows; win++) {
1801         cname = _windows[win]->className();
1802         if (strcmp(cname, "RoamMenuWindow") == 0) {
1803             rmw = (RoamMenuWindow *)_windows[win];
1804             rmw->setVacationTitle();
1805         }
1806     }
1807     return status;
1808 }
1809
1810 void
1811 RoamApp::stopVacation()
1812 {
1813     const char *cname;
1814     RoamMenuWindow *rmw;
1815
1816     _vacation->stopVacation();
1817
1818     for (int win = 0; win < _numWindows; win++) {
1819         cname = _windows[win]->className(); 
1820         if (strcmp(cname, "RoamMenuWindow") == 0) { 
1821             rmw = (RoamMenuWindow *)_windows[win];
1822             rmw->removeVacationTitle();
1823         }
1824     }
1825 }
1826
1827 char*
1828 getPropStringValue(DtVirtArray<PropStringPair *> &results, const char *label)
1829 {
1830         for (int mrc = 0; mrc < results.length(); mrc++) {
1831             PropStringPair * psp = results[mrc];
1832             if (strcmp(label, psp->label) == 0)
1833                 return strdup(psp->value);
1834         }
1835         return (char*)NULL;
1836 }
1837
1838 void
1839 parsePropString(const char * input, DtVirtArray<PropStringPair *> & results)
1840 {
1841   if (input == NULL)
1842         return;
1843
1844     // If it's not multibyte, use the regular string function
1845   if (MB_CUR_MAX <= 1) {
1846      const char * start = input;
1847      const char * end;
1848      char *ptr;
1849
1850     do {
1851         while (isspace(*start)) {
1852             start += 1;
1853         }
1854         
1855         for (end = start; end && *end; end++) 
1856             if (isspace(*end) && *(end-1) != '\\')
1857                 break;
1858
1859         PropStringPair * new_pair = new PropStringPair;
1860
1861         int len = end - start;
1862         char * label = new char[len + 5];
1863         strncpy(label, start, len);
1864         label[len] = 0;
1865         for (ptr = label; (ptr = strstr(ptr, "\\ ")); ptr++) 
1866                 strcpy(ptr, ptr+1); 
1867         
1868         char * file = strchr(label, ':');
1869         if (!file) {
1870             new_pair->label = strdup(label);
1871             new_pair->value = NULL;
1872         }
1873         else {
1874             *file = 0;
1875             new_pair->label = strdup(label);
1876
1877             file += 1;
1878             if (strlen(file) == 0) {
1879                 new_pair->value = NULL;
1880             }
1881             else {
1882                 new_pair->value = strdup(file);
1883             }
1884         }
1885
1886         results.append(new_pair);
1887         delete [] label;
1888
1889         start = end;
1890         
1891         while (*start && isspace(*start)) {
1892             start += 1;
1893         }
1894         
1895     } while(*start && *end);
1896   }
1897
1898   else {
1899     // The string can contain multibyte characters and it must be converted to 
1900     // wide character string before parsing
1901     int len = strlen(input);
1902     wchar_t *wc_input= new wchar_t[len+1];
1903     mbstowcs(wc_input, input, len+1);
1904     const wchar_t *start = wc_input;
1905     const wchar_t *end;
1906     wchar_t *ptr;
1907
1908     do {
1909         while (iswspace(*start)) {             
1910             start += 1;             
1911         }
1912
1913         for (end = start; end && *end; end++)
1914             if (iswspace(*end) && *(end-1) != (wint_t)'\\')
1915                 break;
1916         PropStringPair * new_pair = new PropStringPair;
1917
1918         int wclen = end - start;
1919         wchar_t *wc_label = new wchar_t[wclen+1];
1920         wcsncpy(wc_label, start, wclen);
1921         wc_label[wclen] = (wint_t)'\0';
1922
1923         // Search the string "\ " and take out the back slash
1924         wchar_t esc_space[3];
1925         mbstowcs(esc_space, "\\ ", 3);
1926         for (ptr = wc_label; (ptr = wcswcs(ptr, esc_space)); ptr++)
1927                 wcscpy(ptr, ptr+1);
1928
1929         wchar_t *file = wcschr(wc_label, (wint_t)':');
1930         if (!file) {
1931             new_pair->label = new char[(wclen+1)*MB_CUR_MAX];
1932             wcstombs(new_pair->label, wc_label, wclen+1);
1933             new_pair->value = NULL;
1934
1935         }
1936
1937         else {
1938             *file = (wint_t)'\0';
1939             new_pair->label = new char[(wclen+1)*MB_CUR_MAX];
1940             wcstombs(new_pair->label, wc_label, (wclen+1)*MB_CUR_MAX);
1941
1942             file += 1;
1943             int filelen = wcslen(file);
1944             if (filelen == 0) {
1945                 new_pair->value = NULL;
1946             }
1947             else {
1948                 new_pair->value = new char[(filelen+1)*MB_CUR_MAX];
1949                 wcstombs(new_pair->value, file, (filelen+1)*MB_CUR_MAX);
1950
1951             }
1952         }
1953         results.append(new_pair);
1954         delete []wc_label;
1955         start = end;
1956
1957         while (*start && iswspace(*start)) {
1958             start += 1;
1959         }
1960
1961     } while(*start && *end);
1962
1963     delete []wc_input;
1964 }
1965 }
1966
1967 PropStringPair::PropStringPair(void)
1968 {
1969     label = NULL;
1970     value = NULL;
1971 }
1972
1973 PropStringPair::PropStringPair(const PropStringPair & other)
1974 {
1975     label = NULL;
1976     value = NULL;
1977
1978     if (other.label) {
1979         label = strdup(other.label);
1980     }
1981
1982     if (other.value) {
1983         value = strdup(other.value);
1984     }
1985 }
1986
1987 PropStringPair::~PropStringPair(void)
1988 {
1989     if (label) {
1990         free(label);
1991     }
1992
1993     if (value) {
1994         free(value);
1995     }
1996 }
1997
1998 void
1999 RoamApp::setSession( MailSession *session )
2000 {
2001    _mail_session = session; 
2002 }
2003
2004
2005 VacationCmd*
2006 RoamApp::vacation()
2007 {
2008     return (_vacation);
2009 }
2010
2011 char *formatPropPair(char * key, const void * data)
2012 {
2013     char *formatted_str = NULL;
2014     char *white_space = NULL;
2015     int i, num_spaces = 0;
2016     int key_len = strlen(key);
2017     int m_size;
2018
2019     if (data == NULL)
2020         data = (void *)"";
2021
2022     // figure out whitespace for formatting
2023     // assume 13 for normal sized alias name
2024
2025     if(key_len < 13)  // need to add spaces
2026       {
2027         num_spaces = 13 - key_len;
2028
2029         white_space = (char *)malloc(num_spaces + 1);
2030         strcpy(white_space, "\0");
2031
2032         for(i = 0; i < num_spaces; i++)
2033           strcat(white_space, " ");
2034
2035         m_size = key_len + strlen((char *)white_space)
2036                  + strlen((char *)data) + strlen(" = ") + 1;
2037         formatted_str = (char *)malloc(m_size);
2038
2039         sprintf(formatted_str, "%s%s = %s",key, white_space, (const char *) data);
2040
2041       }
2042     else
2043       {
2044                 /* make an alias string */
2045         formatted_str = (char *)malloc(key_len + strlen((const char *)data) + 2);
2046     
2047         m_size = key_len + strlen((const char *)data) + strlen(" = ") + 1;
2048         formatted_str = (char *)malloc(m_size);
2049
2050         sprintf(formatted_str, "%s = %s",key, (const char *) data);
2051
2052       }
2053     
2054     return formatted_str;
2055
2056 }
2057
2058 RoamMenuWindow*
2059 RoamApp::inboxWindow()
2060 {
2061     RoamMenuWindow *rmw;
2062     const char *cname;
2063
2064     for (int win = 0; win < _numWindows; win++) {
2065         cname = _windows[win]->className();
2066         if (strcmp(cname, "RoamMenuWindow") == 0) {
2067             rmw = (RoamMenuWindow *)_windows[win];
2068             if (rmw->inbox()) {
2069                 return(rmw);
2070             }
2071         }
2072     }
2073     return(NULL);
2074 }
2075
2076 RoamMenuWindow*
2077 RoamApp::nextRoamMenuWindow(RoamMenuWindow *last)
2078 {
2079     RoamMenuWindow *looking_for = last;
2080     RoamMenuWindow *rmw;
2081     const char *cname;
2082
2083     for (int win = 0; win < _numWindows; win++) {
2084         cname = _windows[win]->className();
2085         if (strcmp(cname, "RoamMenuWindow") == 0) {
2086             rmw = (RoamMenuWindow *)_windows[win];
2087             if (NULL == looking_for)
2088               return(rmw);
2089             if (rmw == looking_for)
2090               looking_for = NULL;
2091         }
2092     }
2093     return(NULL);
2094 }
2095
2096 void
2097 RoamApp::closeInactiveRoamMenuWindows(void)
2098 {
2099     MailSession *ses = theRoamApp.session();
2100     RoamMenuWindow **rmws;
2101     const char *cname;
2102     int win=0, nrmws=0;
2103
2104     rmws = (RoamMenuWindow**) malloc(_numWindows * sizeof(RoamMenuWindow *));
2105     for (win=0; win<_numWindows; win++)
2106     {
2107         cname = _windows[win]->className();
2108         if (strcmp(cname, "RoamMenuWindow") == 0)
2109           rmws[nrmws++] = (RoamMenuWindow*) _windows[win];
2110     }
2111
2112     for (win=0; win<nrmws; win++)
2113       if (! ses->isActiveRMW(rmws[win])) rmws[win]->quit(TRUE);
2114
2115     free(rmws);
2116 }
2117
2118 void
2119 RoamApp::reopenRoamMenuWindows(void)
2120 {
2121     RoamMenuWindow *rmw, **rmws;
2122     const char *cname;
2123     int win=0, nrmws=0;
2124
2125     theApplication->disableShutdown();
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     {
2137         char *name;
2138         rmw = rmws[win];
2139         name = strdup(rmw->mailboxName());
2140         rmw->quit(TRUE);
2141
2142         rmw = new RoamMenuWindow(name);
2143         rmw->initialize();
2144         rmw->manage();
2145         free(name);
2146     }
2147     free(rmws);
2148
2149     theApplication->enableShutdown();
2150 }
2151
2152 MainWindow*
2153 RoamApp::defaultStatusWindow()
2154 {
2155     MainWindow *mw = NULL;
2156
2157     if ((mw = (MainWindow *) _mailview) != NULL)
2158       return mw;
2159     if ((mw = (MainWindow *) inboxWindow()) != NULL)
2160       return mw;
2161     if (_numWindows > 0)
2162       mw = (MainWindow *) _windows[0];
2163
2164     return mw;
2165 }
2166
2167 void
2168 RoamApp::open_catalog()
2169 {
2170     Application::open_catalog();      // Open Motif Application message catalog file
2171     DT_catd = catopen(DTMAIL_CAT, NL_CAT_LOCALE); // Open DtMail message catalog file
2172 }