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