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