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