Even more spelling fixed
[oweals/cde.git] / cde / programs / dtmail / dtmail / Attachment.C
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these libraries and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $TOG: Attachment.C /main/16 1998/07/23 17:58:44 mgreess $
24  *
25  * (c) Copyright 1996 Digital Equipment Corporation.
26  * (c) Copyright 1996 Hewlett-Packard Company.
27  * (c) Copyright 1996 International Business Machines Corp.
28  * (c) Copyright 1993-1996 Sun Microsystems, Inc.
29  * (c) Copyright 1996 Novell, Inc. 
30  * (c) Copyright 1996 FUJITSU LIMITED.
31  * (c) Copyright 1996 Hitachi.
32  */
33 /*
34  *+SNOTICE
35  *
36  *      RESTRICTED CONFIDENTIAL INFORMATION:
37  *      
38  *      The information in this document is subject to special
39  *      restrictions in a confidential disclosure agreement between
40  *      HP, IBM, Sun, USL, SCO and Univel.  Do not distribute this
41  *      document outside HP, IBM, Sun, USL, SCO, or Univel without
42  *      Sun's specific written approval.  This document and all copies
43  *      and derivative works thereof must be returned or destroyed at
44  *      Sun's request.
45  *
46  *+ENOTICE
47  */
48
49 #include <EUSCompat.h>   // For strcasecmp()
50 #include <stdio.h>
51 #include <unistd.h>
52 #include <sys/types.h>
53 #include <sys/stat.h>
54 #include <stdlib.h>
55 #include <fcntl.h>
56 #include <string.h>
57 #include <ctype.h>
58 #include <memory.h>
59 #include <errno.h>
60 #include <sys/utsname.h>
61 #include <stdio.h>
62
63 #include <Xm/Xm.h>
64 #include <Xm/Form.h>
65 #include <Xm/Label.h>
66 #include <Xm/BulletinB.h>
67 #include <Xm/AtomMgr.h>
68 #include <Xm/DragDrop.h>
69 #include <Xm/Screen.h>
70 #include <Xm/PushB.h>
71 #include <Xm/PushBG.h>
72 #include <Xm/LabelG.h>
73 #include <X11/IntrinsicP.h>
74 #include <X11/Xatom.h>
75
76 #include "Attachment.h"
77 #include "Icon.h"
78 #include "RoamMenuWindow.h"
79 #include "RoamApp.h"
80 #include "InfoDialogManager.h"
81 #include "ViewMsgDialog.h"
82 #include "RoamMenuWindow.h"
83 #include "MailMsg.h"
84 #include "MailSession.hh"
85 #include <Dt/Dts.h>
86 #include <DtMail/DtMailError.hh>
87 #include <DtMail/IO.hh>                 // SafeAccess...
88 #include "MemUtils.hh"
89 #include "DtMailHelp.hh"
90 #include "str_utils.h"
91
92
93 extern "C" {
94 extern XtPointer _XmStringUngenerate (
95                                 XmString string,
96                                 XmStringTag tag,
97                                 XmTextType tag_type,
98                                 XmTextType output_type);
99 }
100
101 extern nl_catd  DtMailMsgCat;
102
103 unsigned char validbits[] = {
104    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
105    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x07, 0x00,
106    0x00, 0xfc, 0x3f, 0x00, 0x00, 0xfe, 0x7f, 0x00, 0x00, 0x1f, 0xf8, 0x00,
107    0x80, 0x07, 0xe0, 0x01, 0x80, 0x03, 0xc0, 0x01, 0xc0, 0x01, 0x80, 0x03,
108    0xc0, 0x01, 0x80, 0x03, 0xe0, 0x00, 0x00, 0x07, 0xe0, 0x80, 0x01, 0x07,
109    0xe0, 0xc0, 0x03, 0x07, 0xe0, 0xc0, 0x03, 0x07, 0xe0, 0x80, 0x01, 0x07,
110    0xe0, 0x00, 0x00, 0x07, 0xc0, 0x01, 0x80, 0x03, 0xc0, 0x01, 0x80, 0x03,
111    0x80, 0x03, 0xc0, 0x01, 0x80, 0x07, 0xe0, 0x01, 0x00, 0x1f, 0xf8, 0x00,
112    0x00, 0xfe, 0x7f, 0x00, 0x00, 0xfc, 0x3f, 0x00, 0x00, 0xf0, 0x0f, 0x00,
113    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
114    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
115
116 unsigned char invalidbits[] = {
117    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
118    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x07, 0x00,
119    0x00, 0xfc, 0x3f, 0x00, 0x00, 0xfe, 0x7f, 0x00, 0x00, 0x1f, 0xf8, 0x00,
120    0x80, 0x0f, 0xe0, 0x01, 0x80, 0x1f, 0xc0, 0x01, 0xc0, 0x3f, 0x80, 0x03,
121    0xc0, 0x7d, 0x80, 0x03, 0xe0, 0xf8, 0x00, 0x07, 0xe0, 0xf0, 0x01, 0x07,
122    0xe0, 0xe0, 0x03, 0x07, 0xe0, 0xc0, 0x07, 0x07, 0xe0, 0x80, 0x0f, 0x07,
123    0xe0, 0x00, 0x1f, 0x07, 0xc0, 0x01, 0xbe, 0x03, 0xc0, 0x01, 0xfc, 0x03,
124    0x80, 0x03, 0xf8, 0x01, 0x80, 0x07, 0xf0, 0x01, 0x00, 0x1f, 0xf8, 0x00,
125    0x00, 0xfe, 0x7f, 0x00, 0x00, 0xfc, 0x3f, 0x00, 0x00, 0xf0, 0x0f, 0x00,
126    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
127    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
128
129 #define equal(a, b) (!strcasecmp(a,b))
130
131 String typeName;
132
133 #ifdef DEAD_WOOD
134 void runit_callback( int *data );
135 void norunit_callback( int *data );
136 #endif /* DEAD_WOOD */
137 static void okcb(XtPointer);
138
139
140 /*
141  * CDExc17304
142  *
143  * A note on this bug, several related bugs, design of C++ programs and
144  * general coding style and proficiency.  This module, as well as most
145  * others in the dtmail application, violate many elements of good
146  * OO design.  Poor compartmentalization, poor abstraction, ambiguous data
147  * ownership, ambiguous data model, lack of a clear Model/View/Controller
148  * structure are all examples of things going wrong or being done badly.
149  * In the case at hand, we have a much larger problem (inadequate design
150  * of mmap()'d file handling) causing numerous small bugs from a
151  * collaboration with these design problems.  I will workaround the
152  * bug and the design flaw by moving data ownership into this Class thus
153  * adding stones to the Chinese Wall that separates this UI-like class
154  * from the model-like classes in RFC.  However, it is in no way a
155  * real fix for the problems dtmail is experiencing.
156  */
157
158 Attachment::Attachment(
159     AttachArea *classparent,
160     String name,
161     DtMail::BodyPart *body_part,
162     int indx
163 )
164     : UIComponent (
165         name
166 ),
167     _parent(classparent),
168     _body_part(body_part),
169     _index(indx),
170     _canKillSelf(TRUE),
171     _myActionIds(5),
172     _myAllocContents(NULL),
173     _myContents(NULL),
174     _myContentsSize(0),
175     _haveContents(FALSE),
176     _myType(NULL),
177     _subtype(NULL),
178     _myActionsList(NULL)
179 {
180     if(strchr(name, '/') == NULL) // The name does not include a slash
181         _label = XmStringCreateLocalized(name);
182     else                           // The name does include a slash
183         _label = XmStringCreateLocalized(strrchr(name, '/')+1);
184
185     _key = theRoamApp.session()->session()->newObjectKey();
186     
187     myIcon = NULL;
188     _background = 0;
189     _foreground = 0;
190     _attachmentWidth = 0;
191     _attachmentHeight = 0;
192     _positionX = 0;
193     _positionY = 0;
194     _deleted = false;
195     _selected = false;
196     _row = 0;
197     _saveAsFilename = NULL;
198     _ce_name = NULL;
199     _ce_type = NULL;
200     _type = 0;
201     _binary = false;
202     _executable = false;
203     
204 }
205
206 Attachment::~Attachment(
207 )
208 {
209     
210     theRoamApp.session()->session()->removeObjectKey(_key);
211
212     if (_label) {
213       XmStringFree (_label);
214     }
215     if (_subtype) {
216       free (_subtype);
217     }
218     if (_myType) {
219       free (_myType);
220     }
221
222     delete myIcon;
223     if (_myActionsList) {
224         delete []_myActionsList;
225     }
226     if (_myAllocContents) {
227         delete [] _myAllocContents;
228     }
229     _myAllocContents = NULL;
230     _myContents = NULL;
231     _myContentsSize = 0;
232 }
233
234
235 void
236 Attachment::setAttachArea(AttachArea *aa)
237 {
238     _parent = aa;
239 }
240     
241
242 void
243 Attachment::initialize()
244 {
245     Widget widgetparent;
246
247     assert( _parent != NULL);
248
249     this->setContents();
250
251     widgetparent = _parent->getClipWindow();
252
253     _foreground = _parent->owner()->textEditor()->get_text_foreground();
254     _background = _parent->owner()->textEditor()->get_text_background();
255
256     _selected = FALSE;
257
258     // We already have the name from the classing engine.  Now map
259     // the name to the MIME type and subtype
260     name_to_type();
261
262     myIcon = new Icon(this, _name, _label, _type, widgetparent, _index);
263     
264     _w = myIcon->baseWidget();
265
266      _deleted = FALSE;
267      XmUpdateDisplay(_w);
268
269     {
270     Arg args[2];
271     int n;
272     Dimension aaHeight;
273     Widget sw;
274
275     sw = _parent->getSWWindow();
276
277     n = 0;
278     XtSetArg(args[n], XmNwidth, &_attachmentWidth);  n++;
279     XtSetArg(args[n], XmNheight, &_attachmentHeight);  n++;
280     XtGetValues(_w, args, n);
281     XtSetArg(args[0], XmNheight, &aaHeight);  n++;
282     XtGetValues(sw, args, 1);
283     if( aaHeight < (_attachmentHeight+20) )
284     {
285         XtSetArg(args[0],XmNheight,(_attachmentHeight+20));
286         XtSetValues(sw, args, 1);
287     
288     }
289     }
290     
291     installDestroyHandler();
292 }
293
294 #ifdef DEAD_WOOD
295 Boolean
296 Attachment::check_if_binary(String contents, unsigned long size)
297 {
298     int i;
299
300     for(i=0;i<size;i++) {
301         if((!isprint(contents[i])) && (!isspace(contents[i])))
302             return True;
303     }
304     return False;
305 }
306 #endif /* DEAD_WOOD */
307
308 //
309 // Map the name (which we got from the classing engine) to a 
310 // type and subtype
311 //
312
313 void
314 Attachment::name_to_type()
315 {
316     char *subtype = new char[128];
317
318     // Hack hack!
319     // Hardcode ce_name to be text for now.
320     // It should actually be determined dynamically from the type of
321     // the attachment.
322
323     _ce_name = "text";
324
325     // If the type and subtype are already set then we don't need to
326     // map a classing engine type to a MIME type; We already have
327     // the MIME type, so just return
328     if(_subtype != NULL)
329         return;
330     if(equal(_ce_name, "text")) {                       // text
331         _type = TYPETEXT;
332         _subtype = strdup("plain");
333     } else if(equal(_ce_name, "richtext")) {            // richtext
334         _type = TYPETEXT;
335         sprintf(subtype, "X-sun-%s",_ce_name);
336         _subtype = strdup(subtype);
337     } else if(equal(_ce_name, "audio-file")) {          // audio-file
338         _type = TYPEAUDIO;
339         _subtype = strdup("basic");
340     } else if(equal(_ce_name, "default")) {             // default
341         _type = TYPEAPPLICATION;
342         sprintf(subtype, "X-sun-%s",_ce_name);
343         _subtype = strdup(subtype);
344     } else if(equal(_ce_name, "oda")) {                 // oda
345         _type = TYPEAPPLICATION;
346         _subtype = strdup("oda");
347     } else if(equal(_ce_name, "postscript-file")) {     // postscript
348         _type = TYPEAPPLICATION;
349         _subtype = strdup("PostScript");
350     } else if(equal(_ce_name, "sun-raster")) {          // sun-raster
351         _type = TYPEIMAGE;
352         sprintf(subtype, "X-%s",_ce_name);
353         _subtype = strdup(subtype);
354     } else if(equal(_ce_name, "jpeg-file")) {           // jpeg-file
355         _type = TYPEIMAGE;
356         _subtype = strdup("jpeg");
357     } else if(equal(_ce_name, "g3-file")) {             // g3-file
358         _type = TYPEIMAGE;
359         sprintf(subtype, "X-sun-%s",_ce_name);
360         _subtype = strdup(subtype);
361     } else if(equal(_ce_name, "gif-file")) {            // gif-file
362         _type = TYPEIMAGE;
363         _subtype = strdup("gif");
364     } else if(equal(_ce_name, "pbm-file")) {            // pbm-file
365         _type = TYPEIMAGE;
366         sprintf(subtype, "X-sun-%s",_ce_name);
367         _subtype = strdup(subtype);
368     } else if(equal(_ce_name, "pgm-file")) {            // pgm-file
369         _type = TYPEIMAGE;
370         sprintf(subtype, "X-sun-%s",_ce_name);
371         _subtype = strdup(subtype);
372     } else if(equal(_ce_name, "ppm-file")) {            // ppm-file
373         _type = TYPEIMAGE;
374         sprintf(subtype, "X-sun-%s",_ce_name);
375         _subtype = strdup(subtype);
376     } else if(equal(_ce_name, "xpm-file")) {            // xpm-file
377         _type = TYPEIMAGE;
378         sprintf(subtype, "X-sun-%s",_ce_name);
379         _subtype = strdup(subtype);
380     } else if(equal(_ce_name, "tiff-file")) {           // tiff-file
381         _type = TYPEIMAGE;
382         sprintf(subtype, "X-sun-%s",_ce_name);
383         _subtype = strdup(subtype);
384     } else if(equal(_ce_name, "troff")) {               // troff
385         _type = TYPETEXT;
386         sprintf(subtype, "X-sun-%s",_ce_name);
387         _subtype = strdup(subtype);
388     } else if(equal(_ce_name, "nroff")) {               // nroff
389         _type = TYPETEXT;
390         sprintf(subtype, "X-sun-%s",_ce_name);
391         _subtype = strdup(subtype);
392     } else if(equal(_ce_name, "h-file")) {              // h-file
393         _type = TYPETEXT;
394         sprintf(subtype, "X-sun-%s",_ce_name);
395         _subtype = strdup(subtype);
396     } else if(equal(_ce_name, "c-file")) {              // c-file
397         _type = TYPETEXT;
398         sprintf(subtype, "X-sun-%s",_ce_name);
399         _subtype = strdup(subtype);
400     } else if(equal(_ce_name, "makefile")) {            // makefile
401         _type = TYPETEXT;
402         sprintf(subtype, "X-sun-%s",_ce_name);
403         _subtype = strdup(subtype);
404     } else if(equal(_ce_name, "mail-file")) {           // mail-file
405         _type = TYPETEXT;
406         sprintf(subtype, "X-sun-%s",_ce_name);
407         _subtype = strdup(subtype);
408     } else if(equal(_ce_name, "mail-message")) {        // mail-message
409         _type = TYPETEXT;
410         sprintf(subtype, "X-sun-%s",_ce_name);
411         _subtype = strdup(subtype);
412     } else {
413         _type = TYPEAPPLICATION;
414         sprintf(subtype, "X-sun-%s",_ce_name);
415         _subtype = strdup(subtype);
416     }
417     delete [] subtype;
418 }
419
420 void
421 Attachment::invokeAction(int index)
422 {
423     char                *actionCommand = NULL;
424     DtActionArg         *actionArg = NULL;
425     DtActionBuffer       bufArg;
426
427     char                *exble = NULL;
428     char                *type = NULL;
429
430     if (_myActionsList == NULL || NULL == _myActionsList[index]) return;
431
432     this->setContents();
433     actionCommand = _myActionsList[index];
434     memset(&bufArg, 0, sizeof(bufArg));
435
436     bufArg.bp = (void *)_myContents;
437     bufArg.size = (int) _myContentsSize;
438
439     // Determine the type based on the contents.
440     // This is to compensate for errors that other MUAs could have
441     // generated:  some claim a bodyPart is rfc822 but deliver something else.  
442     type = bufArg.type = DtDtsBufferToDataType(
443                                         (char*) _myContents,
444                                         (const int) _myContentsSize,
445                                         _name);
446
447     if (_parent->isOwnerShellEditable())
448       bufArg.writable = TRUE;
449     else
450       bufArg.writable = FALSE;
451
452     // If this is the default action (aka Run) and the attachment is executable,
453     // display a dialog informing the user of the risks of executing it.
454     exble = DtDtsDataTypeToAttributeValue(type, DtDTS_DA_IS_EXECUTABLE, _name);
455     if (0 == index && NULL != exble && DtDtsIsTrue(exble))
456     {
457         int answer;
458         char *buf = new char[2048];
459
460         sprintf(buf, "%s",
461                 GETMSG(DT_catd, 3, 81, "This attachment may contain commands that can cause serious\ndamage.  It is recommended that you only execute it after you\nare certain it is safe to do so.\n\nPress OK if you are certain it is safe,\nCancel to cancel execution."));
462
463         answer = parent()->handleQuestionDialog(
464                                         GETMSG(DT_catd, 1, 86, "Mailer"),
465                                         buf,
466                                         DTMAILHELPEXECUTEOK);
467         delete [] buf;
468         if (answer == 2) return;
469     }
470
471     // Passing a buffer
472     actionArg = (DtActionArg *) malloc(sizeof(DtActionArg) * 1);
473     memset(actionArg, 0, sizeof(DtActionArg));
474     actionArg->argClass = 2;
475     actionArg->u.buffer = bufArg;
476         
477     ActionCallback *acb = new ActionCallback(_key,  this);
478     DtActionInvoke(
479                 _parent->ownerShellWidget(),
480                 actionCommand, actionArg,
481                 1, NULL, NULL, NULL, 1,
482                (DtActionCallbackProc)& Attachment::actionCallback, acb);
483 }
484
485 void
486 Attachment::handleDoubleClick()
487 {
488     _parent->attachmentFeedback(TRUE);
489     invokeAction(0);
490     _parent->attachmentFeedback(FALSE);
491 }
492
493 static void okcb( XtPointer )
494 {
495     //Empty
496     // This function exists so that the OK button will appear on the
497     // Info Dialog. It doesn't have to do anything because the dialog
498     // automatically pops down. It is for information only.
499 }
500
501 #ifdef DEAD_WOOD
502 void
503 runit_callback(int *data)
504 {
505     *data=1;
506 }
507
508 void
509 norunit_callback(int *data)
510 {
511     *data=2;
512 }
513 #endif /* DEAD_WOOD */
514
515 int
516 Attachment::operator==
517 (
518 const Attachment &
519 )
520 {
521     return 1;
522 }
523
524 void
525 Attachment::set_selected()
526 {
527     char        *actions_list = NULL;   //Comma separated list of actions
528     char*       anAction=NULL;
529     int         numActions = 0;
530
531     _selected = TRUE;
532
533     parent()->attachmentSelected(this);
534
535     // Crude Hack.
536     // Assuming that only 10 actions are possible.
537     // Need a mechanism to query, determine how many possible and
538     // allocate memory for that many.
539
540     _myActionsList = new char*[10];
541
542     // Retrieve the actions list.  Walk through the list and
543     // for each item in it, ask the parent to create a menu item
544     // in its parent's menubar.
545
546     actions_list = DtDtsDataTypeToAttributeValue(
547                                 _myType,
548                                 DtDTS_DA_ACTION_LIST,
549                                 NULL
550                    );
551
552     char **tmpActionCommand = _myActionsList;
553
554     if (actions_list != NULL)
555         anAction = strtok(actions_list, ",");
556     if (anAction == NULL) {
557         return;
558     }
559
560     *tmpActionCommand = strdup(anAction);
561
562     while (*tmpActionCommand != NULL) {
563         
564         // strtok() requires that calls other than the first have NULL as
565         // the first arg..
566
567         tmpActionCommand++;
568         numActions++;
569
570         anAction = strtok(NULL, ",");
571         if (anAction == NULL) {
572             *tmpActionCommand = NULL;
573         }
574         else {
575             *tmpActionCommand = strdup(anAction);
576         }
577     }
578     parent()->addAttachmentActions(
579                         _myActionsList,
580                         numActions
581                 );
582
583     free((void*) actions_list);
584 }
585
586 void
587 Attachment::unselect()
588 {
589     _selected = FALSE;
590     myIcon->unselect(); 
591 }
592
593 // Save the attachment to the specified file.
594 void
595 Attachment::saveToFile(DtMailEnv &, char *filename)
596 {
597     int answer;
598     char *buf = new char[2048];
599     char *helpId = NULL;
600
601     if (SafeAccess(filename, F_OK) == 0) {
602
603         sprintf(buf, GETMSG(DT_catd, 3, 42, "%s already exists. Replace?"),
604                 filename);
605         helpId = DTMAILHELPALREADYEXISTS;
606
607         answer = parent()->handleQuestionDialog(GETMSG(DT_catd,
608                                                         1, 85,
609                                                         "Mailer"), 
610                                                 buf,
611                                                 helpId);
612
613         if (answer == 2) { // Pressed cancel
614             delete [] buf;
615             return;
616         }
617
618         if (unlink(filename) < 0) {
619             sprintf(buf, GETMSG(DT_catd, 3, 43, "Unable to replace %s."), filename);
620             helpId = DTMAILHELPNOREPLACE;
621             answer = parent()->handleErrorDialog(GETMSG(DT_catd,
622                                                          1, 86,
623                                                          "Mailer"), 
624                                                  buf,
625                                                  helpId);
626             delete [] buf;
627             return;
628         }
629     }
630
631
632 //     if (_myContentsSize == 0) {// Oops! BE thinks attachment is of size 0.
633 //      sprintf(buf, "Mailer BE thinks attachment is size 0.\nPlease call a Dtmail engineer to attach a debugger\n to this process to begin debugging.  Only after attaching the debugger should you click OK.\n");
634 //      answer = parent()->handleErrorDialog("BUG!", buf);
635 //      
636 //      // Call setContents again.  
637 //      // This will help us debug why the body part has bogus contents.
638 //      this->setContents();
639 //     }
640
641     // Create or truncate, and then write the bits.
642     //
643     int fd = SafeOpen(filename, O_RDWR | O_CREAT | O_TRUNC, 0600);
644     if (fd < 0) {
645         sprintf(buf, GETMSG(DT_catd, 3, 44, "Unable to create %s."), filename);
646         helpId = DTMAILHELPNOCREATE;
647         answer = parent()->handleErrorDialog(GETMSG(DT_catd, 1, 87, "Mailer"), 
648                                              buf,
649                                              helpId);
650         delete [] buf;
651         return;
652     }
653
654     if (SafeWrite(fd, _myContents, (unsigned int)_myContentsSize) < _myContentsSize) {
655         sprintf(buf, GETMSG(DT_catd, 3, 45, "Unable to create %s."), 
656                 filename);
657         helpId = DTMAILHELPNOCREATE;
658         answer = parent()->handleErrorDialog(GETMSG(DT_catd, 1, 88, "Mailer"), 
659                                              buf,
660                                              helpId);
661         SafeClose(fd);
662         unlink(filename);
663         delete [] buf;
664         return;
665     }
666     
667     SafeClose(fd);
668
669     // Stat the created file and see if it is of size 0.
670     // If it is, engage the user in a dialog and involve a dtmail engineer
671     // to attach a debugger to this process.
672
673 //     struct stat sbuf;
674 //     SafeStat(filename, &sbuf);
675 // 
676 //     if (sbuf.st_size == 0) {
677 //      sprintf(buf, "Mailer produced a zero length file.\nPlease call a Dtmail engineer to attach a debugger\n to this process to begin debugging.  Only after attaching the debugger should you click OK.\n");
678 //      answer = parent()->handleErrorDialog("BUG!", buf);
679 //     }
680     delete [] buf;
681 }
682
683 void
684 Attachment::setX(
685     Position x
686 )
687 {
688     Arg args[2];
689     int n = 0;
690     Boolean was_managed;
691
692     _positionX = x;
693
694     was_managed = isManaged();
695     if (was_managed) unmanageIconWidget();
696       
697     n = 0;
698     XtSetArg(args[n], XtNx, x); n++;
699     XtSetValues( _w, args,n);
700
701     if (was_managed) manageIconWidget();
702 }
703
704 void
705 Attachment::setY(
706     Position y
707 )
708 {
709     Arg args[2];
710     int n = 0;
711     Boolean was_managed;
712
713     _positionY = y;
714
715     was_managed = isManaged();
716     if (was_managed) unmanageIconWidget();
717       
718     n = 0;
719     XtSetArg(args[n], XtNy, y); n++;
720     XtSetValues( _w, args,n);
721
722     if (was_managed) manageIconWidget();
723 }
724
725 #ifdef DEAD_WOOD
726 void
727 Attachment::setRow(
728     int row
729 )
730 {
731     _row = row;
732 }
733 #endif /* DEAD_WOOD */
734
735
736 void 
737 Attachment::actionCallback(
738   DtActionInvocationID id,
739   XtPointer     clientData,
740   DtActionArg   *action_arg,
741   int           argCount,
742   DtActionStatus status 
743 )
744 {
745     ActionCallback *acb = (ActionCallback *) clientData;
746     if (theRoamApp.session()->session()->validObjectKey(
747                         acb->_myKey) == DTM_FALSE) {
748         // Error out. Post an error?
749         return;
750     }
751     else {
752         acb->_myAttachment->action(id, action_arg, argCount, status);
753     }
754 }    
755
756 void
757 Attachment::action(
758   DtActionInvocationID id,
759   DtActionArg   *actionArg,
760   int,          // argCount,
761   int           status
762 )
763 {
764     DtActionBuffer      bufArg;
765     DtMailEnv   mail_error;
766     int answer;
767     char *buf = new char[2048];
768     const void * lclContents(NULL);
769     unsigned long lclContentsSize(0);
770
771     // Initialize the mail_error.
772     mail_error.clear();
773
774     if (status == DtACTION_INVOKED) {
775         registerAction(id);
776         _parent->setPendingAction(TRUE);
777     }
778     else if (status == DtACTION_DONE) {
779         unregisterAction(id);   
780         _parent->setPendingAction(FALSE);
781         
782         // Check first if ownerShell is an SMD.
783         // Secondly, check if the SMD is still available.  If the user had
784         // sent the message while this attachment was up, the SMD's reset()
785         // method would have set the attachments' _deleted flag to TRUE.
786         // So, check to see that the attachment is still valid before setting
787         // its contents.
788         //
789         // Note: actionArg can be NULL if there were no actions associated
790         // with the data type.  Is this an Actions bug? Could be, but 
791         // we check for it anyway.
792
793         if (actionArg != NULL && _parent->isOwnerShellEditable() && !_deleted) {
794             if (actionArg->argClass == DtACTION_BUFFER) {
795                 bufArg = actionArg->u.buffer;
796                 if (bufArg.writable) {
797                     // Assume user edited launched attachment -- as
798                     // optimization, we can compare buffers to see if
799                     // content actually changed.  For now, assume it changed.
800                     // Make a copy of incoming buffer and set body part's
801                     // contents, size. Let BE determine type.
802                     // Reset private variables.
803                     _body_part->setContents(
804                                         mail_error, 
805                                         (char *)bufArg.bp,
806                                         bufArg.size,
807                                         NULL, NULL, 0, NULL);
808                         
809 //                  if (mail_error.isSet()) {
810 //                      //handle error
811 //                  }
812
813                     assert(mail_error.isNotSet());
814
815 //
816 // CDExc17304.  Note the following curiosity.  The caller is considered
817 // the owner of the _myType argument but the called retains ownership
818 // of the contents.
819 //
820                     if (_myType) {
821                         free(_myType);
822                         _myType = NULL;
823                     }
824                     _body_part->getContents(
825                                         mail_error,
826                                         &lclContents,
827                                         &lclContentsSize,
828                                         &_myType, 
829                                         NULL, 0, NULL);
830 //                  if (mail_error.isSet()) {
831 //                      //handle error
832 //                  }
833                     assert(mail_error.isNotSet());
834                     _setMyContents(lclContents, int(lclContentsSize));
835                 }
836                 // Free the buffer...
837                 XtFree((char *)bufArg.bp);
838             }
839             else {
840                 // DtACTION_FILE
841                 // Read the file into a buffer and do the same stuff
842                 // as above.
843
844                 int tmp_file;
845                 DtActionFile fileArg = actionArg->u.file;
846                 struct stat stat_buf;
847
848                 if ( SafeStat ( fileArg.name, &stat_buf ) == -1 ) {
849                     mail_error.setError(DTME_ObjectAccessFailed);
850                     mail_error.logError(DTM_FALSE, "Mailer: Unable to process action, stat failed on file %s.\n", fileArg.name);
851                     delete [] buf;
852                     return;
853                 }
854                 tmp_file = SafeOpen(fileArg.name, O_RDONLY);
855                 char *tmp_buf = (char*) malloc((size_t) stat_buf.st_size);
856                 SafeRead(tmp_file, (void *)tmp_buf, (size_t) stat_buf.st_size);
857                 SafeClose(tmp_file);
858
859                 _body_part->setContents(mail_error,
860                                         tmp_buf,
861                                         stat_buf.st_size,
862                                         NULL, NULL, 0, NULL);
863                         
864                 assert(mail_error.isNotSet());
865
866                 // Free the buffer 
867                 free(tmp_buf);
868
869                 if (_myType) {
870                     free(_myType);
871                     _myType = NULL;
872                 }
873
874                 _body_part->getContents(mail_error,
875                                         &lclContents,
876                                         &lclContentsSize,
877                                         &_myType,
878                                         NULL, 0, NULL);
879
880                 assert(mail_error.isNotSet());
881                 _setMyContents(lclContents, int(lclContentsSize));
882             }
883         }
884     }
885     else if (status == DtACTION_INVALID_ID) {
886         /* NL_COMMENT
887          * Post a dialog explaining that the action was invalid
888          */
889         sprintf(buf, "%s",
890                 GETMSG(
891                         DT_catd, 3, 91, "Cannot execute invalid action."));
892
893         answer = parent()->handleErrorDialog(GETMSG(DT_catd, 1, 86, "Mailer"),
894                                              buf);
895
896         unregisterAction(id);   
897         _parent->setPendingAction(FALSE);
898     }
899     else if (status == DtACTION_FAILED) {
900
901         /* NL_COMMENT 
902          * Post a dialog explaining that the action failed.
903          */
904         sprintf(buf, "%s",
905                 GETMSG(DT_catd, 3, 92, "Executing action failed!"));
906
907         answer = parent()->handleErrorDialog(GETMSG(DT_catd, 1, 86, "Mailer"),
908                                              buf);
909
910         unregisterAction(id);   
911         _parent->setPendingAction(FALSE);
912     }
913     else if (status == DtACTION_STATUS_UPDATE) {
914         // Check if ownerShell is an SMD.  Make sure the message has not
915         // been sent before attempting to update things.
916
917         if(actionArg != NULL && _parent->isOwnerShellEditable() && !_deleted) {
918             if (actionArg->argClass == DtACTION_BUFFER) {
919                 bufArg = actionArg->u.buffer;
920                 if (bufArg.writable) {
921                     // Assume user edited launched attachment -- as
922                     // optimization, we can compare buffers to see if
923                     // content actually changed.  For now, assume it changed.
924                     // Make a copy of incoming buffer and set body part's
925                     // contents, size. Let BE determine type.
926                     // Reset private variables.
927                     _body_part->setContents(
928                                         mail_error, 
929                                         (char *)bufArg.bp,
930                                         bufArg.size,
931                                         NULL, NULL, 0, NULL);
932                         
933 //                  if (mail_error.isSet()) {
934 //                      //handle error
935 //                  }
936
937                     assert(mail_error.isNotSet());
938
939                     if (_myType) {
940                         free(_myType);
941                         _myType = NULL;
942                     }
943                     _body_part->getContents(
944                                         mail_error, 
945                                         &lclContents,
946                                         &lclContentsSize,
947                                         &_myType, 
948                                         NULL, 0, NULL);
949 //                  if (mail_error.isSet()) {
950 //                      //handle error
951 //                  }
952
953                     assert(mail_error.isNotSet());
954                     _setMyContents(lclContents, int(lclContentsSize));
955                 }
956                 // Free the buffer 
957                 XtFree((char *)bufArg.bp);
958             }
959             else {
960                 // DtACTION_FILE
961                 // Read the file into a buffer and do the same stuff
962                 // as above.
963
964                 int tmp_file;
965                 DtActionFile fileArg = actionArg->u.file;
966                 struct stat stat_buf;
967
968                 if ( SafeStat ( fileArg.name, &stat_buf ) == -1 ) {
969                     mail_error.setError(DTME_ObjectAccessFailed);
970                     mail_error.logError(DTM_FALSE, "Mailer: Unable to process action, stat failed on file %s.\n", fileArg.name);
971                     delete [] buf;
972                     return;
973                 }
974                 tmp_file = SafeOpen(fileArg.name, O_RDONLY);
975                 char *tmp_buf = (char*) malloc((size_t) stat_buf.st_size);
976                 SafeRead(tmp_file, (void *)tmp_buf, (size_t) stat_buf.st_size);
977                 SafeClose(tmp_file);
978
979                 _body_part->setContents(mail_error,
980                                         (char *)tmp_buf,
981                                         (int) stat_buf.st_size,
982                                         NULL, NULL, 0, NULL);
983                         
984                 assert(mail_error.isNotSet());
985
986                 if (_myType) {
987                     free(_myType);
988                     _myType = NULL;
989                 }
990
991                 _body_part->getContents(mail_error,
992                                         &lclContents,
993                                         &lclContentsSize,
994                                         &_myType,
995                                         NULL, 0, NULL);
996
997                 assert(mail_error.isNotSet());
998                 _setMyContents(lclContents, int(lclContentsSize));
999
1000                 // Free the buffer 
1001                 free(tmp_buf);
1002             }
1003         }
1004     }
1005     else if (status == DtACTION_CANCELED) {
1006         unregisterAction(id);   
1007         _parent->setPendingAction(FALSE);
1008
1009         if (actionArg != NULL) {
1010                 XtFree((char *)(actionArg->u.buffer.bp));
1011         }
1012     }
1013     delete [] buf;
1014 }
1015
1016 void
1017 Attachment::deleteIt()
1018 {
1019     DtMailEnv mail_error;
1020
1021     // Initialize the mail_error.
1022     
1023     mail_error.clear();
1024
1025     _deleted = TRUE;
1026
1027     // Need to remove the view from display
1028
1029     // Get the BE to mark the bodyPart as deleted
1030     _body_part->setFlag(mail_error, DtMailBodyPartDeletePending);
1031
1032 }
1033
1034 void
1035 Attachment::undeleteIt()
1036 {
1037     DtMailEnv mail_error;
1038
1039     // Initialize the mail_error.
1040
1041     mail_error.clear();
1042
1043     _deleted = FALSE;
1044
1045     _body_part->resetFlag(mail_error, DtMailBodyPartDeletePending);
1046
1047 }
1048
1049 void
1050 Attachment::registerAction(
1051     DtActionInvocationID id
1052 )
1053 {
1054     _canKillSelf = FALSE;
1055     _myActionIds.append(id);
1056
1057 }       
1058
1059
1060 void
1061 Attachment::unregisterAction(
1062     DtActionInvocationID id
1063 )
1064 {
1065     if (_myActionIds.length() == 0) {
1066         // error.  Choke!
1067     }
1068     else {
1069         _myActionIds.remove(id);
1070         if (_canKillSelf) {
1071             // See copious documentation above.
1072             delete this;
1073         }
1074     }
1075 }
1076
1077 void
1078 Attachment::quit()
1079 {
1080     _canKillSelf = TRUE;
1081     if (_myActionIds.length() == 0) {
1082         delete this;
1083     }
1084 }
1085
1086 // ActionCallback
1087
1088 ActionCallback::ActionCallback(
1089     DtMailObjectKey key,
1090     Attachment *att
1091 )
1092 {
1093     _myKey = key;
1094     _myAttachment = att;
1095 }
1096
1097 ActionCallback::~ActionCallback()
1098 {
1099
1100 }
1101
1102
1103 void
1104 Attachment::unmanageIconWidget(void)
1105 {
1106
1107     XtUnmanageChild(_w);
1108 }
1109
1110 void
1111 Attachment::manageIconWidget(void)
1112 {
1113
1114     XtManageChild(_w);
1115 }
1116
1117 void
1118 Attachment::setLabel(XmString str)
1119 {
1120         Arg args[2];
1121         int n;
1122         _label = XmStringCopy(str);
1123
1124         n = 0;
1125         XtSetArg(args[n], XmNstring, _label); n++;
1126    //The Icon widget needs to be unmanaged first before involking XtSetValues
1127    // Otherwise, the parent of the Icon widget does not allow the icon widget
1128    // resize. The Icon widget will be remanaged after its dimensions (w and h)
1129    // are obtained by XtGetValues. This is a fix of the defect 176690.
1130         unmanageIconWidget();
1131         XtSetValues(_w, args, n);
1132
1133         n = 0;
1134         XtSetArg(args[n], XmNwidth, &_attachmentWidth); n++;
1135         XtSetArg(args[n], XmNheight, &_attachmentHeight); n++;
1136         XtGetValues(_w, args, n);
1137         manageIconWidget();
1138 }
1139
1140 void
1141 Attachment::primitive_select()
1142 {
1143         _selected = TRUE;
1144         myIcon->primitiveSelect();
1145
1146 }
1147
1148 void
1149 Attachment::rename(
1150     XmString new_name
1151 )
1152 {
1153     char *name = NULL;
1154     DtMailEnv mail_error;
1155     
1156     mail_error.clear();
1157
1158     name = (char *) _XmStringUngenerate(new_name, NULL,
1159                                         XmMULTIBYTE_TEXT, XmMULTIBYTE_TEXT);
1160     
1161     _body_part->setContents(
1162                         mail_error,
1163                         NULL, 1, NULL,
1164                         name, 0, NULL);
1165
1166     this->setLabel(new_name);
1167 }
1168
1169 void
1170 Attachment::setContents()
1171 {
1172     DtMailEnv mail_error;
1173     const void * lclContents;
1174     unsigned long lclContentsSize;
1175
1176     // Initialize the mail_error.
1177     mail_error.clear();
1178
1179     if (_myType) {
1180         free(_myType);
1181         _myType = NULL;
1182     }
1183     _body_part->getContents(
1184                         mail_error,
1185                         &lclContents,
1186                         &lclContentsSize,
1187                         &_myType,
1188                         NULL, 0, NULL);
1189
1190     // BE has returned an error condition...
1191
1192     // It would be nice to popup a dialog to let the user know that 
1193     // dtmail has run into a problem that it can't resolve.  Unfortunately,
1194     // the code for postFatalErrorDialog() has been ifdef'ed out.  Perhaps
1195     // the error dialog can be enabled at some point in the future.
1196
1197     // if (mail_error.isSet())
1198     //   parent()->myRMW->postFatalErrorDialog(mail_error);
1199     assert ( mail_error.isNotSet() );
1200     _setMyContents(lclContents, int(lclContentsSize));
1201
1202     _haveContents = TRUE;
1203
1204     // If it is a rfc822 message, check if it has From stuffing.
1205     // From stuffing is ">From".
1206     // If it has, advance the pointer to step past the ">" character.
1207
1208     if (_myType && !strcmp(_myType,"DTMAIL_FILE")) {
1209
1210         // If it has a From header, return.  The classing engine uses
1211         // that to determine the client that needs to be launched.
1212
1213         if (_myContents &&
1214             0 == strncasecmp((char *)_myContents, "From", 4)) {
1215             return;
1216         }
1217
1218         // Message doesn't begin with From. It may have From stuffing --
1219         // ">From".  Or it may have no From header at all. The MIME 
1220         // specs are vague on what headers an Message/RFC822 body part
1221         // has to have.  We need From to help the classing engine and
1222         // therefore, we will by force (or hack) make it have a From :-)
1223
1224         if (_myContents &&
1225             0 == strncasecmp((char *)_myContents, ">From", 5)) {
1226
1227             // Has From stuffing.
1228
1229             char *ptr = (char *)_myContents;
1230             ptr++;                          // Step past the first char
1231             _myContents = (void *) ptr;    // Reset contents
1232             _myContentsSize--;
1233         }
1234
1235         else {
1236         
1237             // No "From" header.
1238             // Generate a new content string by prepending an
1239             // "From UNKNOWN" header.
1240
1241             char        *buffer = NULL;
1242             char        *from_hdr = "From UNKNOWN";
1243             int         size = 0;
1244             
1245             // Allocate a buffer for the new contents.
1246             // One for the '\0' and two for the extra newlines.
1247             size = strlen(from_hdr) + int(_myContentsSize) + 3;
1248             buffer = new char[size];
1249
1250             // Look for the first occurance of a colon or a newline.
1251             char *sptr;
1252             for (sptr = (char*) _myContents;
1253                  *sptr && *sptr != '\n' && *sptr != ':';
1254                  sptr++) {;}
1255
1256             // Copy in the default From header.
1257             // Add a second newline if there are no rfc822 message headers.
1258             // Assume that finding a colon prior to a newline indicates
1259             // an rfc822 message header.
1260             if (*sptr == ':')
1261               sprintf(buffer, "%s\n", from_hdr);
1262             else
1263               sprintf(buffer, "%s\n\n", from_hdr);
1264
1265             // Copy in the original contents.
1266             size = strlen(buffer);
1267             memcpy(&buffer[size], (char *) _myContents, (int) _myContentsSize);
1268             size += (int) _myContentsSize;
1269             buffer[size] = '\0';
1270
1271             _setMyContents(buffer, size);
1272             delete [] buffer;
1273         }
1274     }
1275 }
1276
1277 void *
1278 Attachment::getContents()
1279 {
1280     if (_myContents) {
1281         return ( (void *)_myContents ); 
1282     }
1283     else {
1284         this->setContents();
1285         return ( (void *)_myContents ); 
1286     }
1287 }
1288
1289 /*
1290  * _setMyContents
1291  *
1292  * Makes a real copy of the specified data to be used as the
1293  * attachments's data.  Note that a substring of the existing value
1294  * can be passed in as an argument and the right thing will happen.
1295  */
1296 void
1297 Attachment::_setMyContents(const void * data, int size)
1298 {
1299     char * new_contents;
1300     int new_size;
1301
1302     if (size > 0) {
1303         new_size = size;
1304         new_contents = new char [new_size + 1];
1305         memcpy(new_contents, data, new_size);
1306         new_contents[new_size] = '\0';
1307     }
1308     else {
1309         new_size = 0;
1310         new_contents = NULL;
1311     }
1312     if (_myAllocContents != NULL) {
1313         delete [] _myAllocContents;
1314     }
1315     _myAllocContents = new_contents;
1316     _myContents = new_contents;
1317     _myContentsSize = new_size;
1318     return;
1319 }