dtmail: resolve 'deference before null check' errors related to if(!NULL) checks...
[oweals/cde.git] / cde / programs / dtmail / dtmail / ComposeCmds.C
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /*
24  *+SNOTICE
25  *
26  *      $TOG: ComposeCmds.C /main/11 1998/10/21 17:23:13 mgreess $
27  *
28  *      RESTRICTED CONFIDENTIAL INFORMATION:
29  *      
30  *      The information in this document is subject to special
31  *      restrictions in a confidential disclosure agreement between
32  *      HP, IBM, Sun, USL, SCO and Univel.  Do not distribute this
33  *      document outside HP, IBM, Sun, USL, SCO, or Univel without
34  *      Sun's specific written approval.  This document and all copies
35  *      and derivative works thereof must be returned or destroyed at
36  *      Sun's request.
37  *
38  *      Copyright 1993 Sun Microsystems, Inc.  All rights reserved.
39  *
40  *+ENOTICE
41  */
42
43 #include <EUSCompat.h>
44 #include <unistd.h>
45 #include <sys/types.h>
46
47 #if defined(NEED_MMAP_WRAPPER)
48 extern "C" {
49 #endif
50 #include <sys/mman.h>
51 #if defined(NEED_MMAP_WRAPPER)
52 }
53 #endif
54
55 #include <pwd.h>
56 #include <Xm/Text.h>
57 #include <Xm/FileSBP.h>
58 #include <Xm/PushB.h>
59 #include <Xm/ToggleB.h>
60 #include <Xm/PushBG.h>
61 #include <Xm/PanedW.h>
62 #include <Xm/Form.h>
63 #include <Dt/Dts.h>
64 #include <Dt/Action.h>
65 #include <DtMail/IO.hh>
66 #include "RoamMenuWindow.h"
67 #include "SendMsgDialog.h"
68 #include "Undelete.hh"
69 #include "RoamCmds.h"
70 #include "ComposeCmds.hh"
71 #include "Application.h"
72 #include "RoamApp.h"
73 #include "DtMailWDM.hh"
74 #include "FindDialog.h"
75 #include "MsgScrollingList.hh"
76 #include "MsgHndArray.hh"
77 #include "MemUtils.hh"
78 #include "MailMsg.h"
79 #include "EUSDebug.hh"
80 #include "DtMailGenDialog.hh"
81 #include "DtMailHelp.hh"
82 #include <DtMail/DtMailError.hh>
83 #include "Help.hh"
84 #include <Dt/Help.h>
85 #include "Attachment.h"
86 #include "str_utils.h"
87
88 ComposeFamily::ComposeFamily(char *name,
89                              char *label,
90                              int active,
91                              RoamMenuWindow *window)
92 : RoamCmd(name, label, active, window)
93 {
94     _parent = window;
95 }
96
97 #ifndef CAN_INLINE_VIRTUALS
98 ComposeFamily::~ComposeFamily( void )
99 {
100 }
101 #endif /* ! CAN_INLINE_VIRTUALS */
102
103 // Append a formatted message to Compose's Text area.
104 // This routine is essentially the same as MsgScollingList::display_message()
105 // except for two major differences:
106 // 1. No RoamMenuWindow reference (so that Compose can be standalone).
107 // 2. Indent string can be used for "include" and "forward".
108 void
109 ComposeFamily::Display_entire_msg(DtMailMessageHandle msgno,
110                                   SendMsgDialog *compose, 
111                                   char *format 
112                                   )
113 {
114     DtMailEnv error;
115     
116     int num_bodyParts;
117     DtMail::MailBox *mbox = _menuwindow->mailbox();
118     DtMail::Message *msg = mbox->getMessage(error, msgno);
119     DtMail::Envelope *env = msg->getEnvelope(error);
120     DtMail::BodyPart *tmpBP = NULL;
121     DtMailBuffer tmpBuffer;
122     void *buffer = NULL;
123     unsigned long size = 0;
124     
125     Editor::InsertFormat ins_format = Editor::IF_NONE;
126     Editor::BracketFormat brackets = Editor::BF_NONE;
127     
128     // Do not need to wrap "include", "forward", and "indent" with
129     // catgets().
130     if ( strcmp(format, "include") == 0 ) {
131         ins_format = Editor::IF_BRACKETED;
132         brackets = Editor::BF_INCLUDE;
133     } else if ( strcmp(format, "forward") == 0 ) {
134         ins_format = Editor::IF_BRACKETED;
135         brackets = Editor::BF_FORWARD;
136     } else if ( strcmp(format, "indent") == 0 ) {
137         ins_format = Editor::IF_INDENTED;
138     }
139     
140     // Get the editor to display the body of message with the appropriate
141     // insert/bracket formatting.
142     // We only include the first body part of the message. Attachments, 
143     // etc. are "FORWARD"-ed but not "INCLUDE"-ed
144     
145     char * status_string;
146     DtMailBoolean firstBPHandled =
147         compose->get_editor()->textEditor()->set_message(
148                                          msg, 
149                                          &status_string, 
150                                          Editor::HF_ABBREV,
151                                          ins_format, 
152                                          brackets);
153
154     // Now need to handle the unhandled body parts of the message.
155     
156     num_bodyParts = msg->getBodyCount(error);
157     if (error.isSet()) {
158         // do something
159     }
160     
161     if (strcmp(format, "forward") == 0) {
162         // If the message has attachments, then let the attach pane
163         // handle attachments but not the first bodyPart (which has
164         // already been handled here).
165         
166         if ((num_bodyParts > 1) || (!firstBPHandled)) {
167
168             tmpBP = msg->getFirstBodyPart(error);
169             if (firstBPHandled) {
170                 //  The first bodyPart has already been handled.
171                 // The others, beginning from the second, need to be parsed 
172                 // and put into the attachPane.
173
174                 compose->setInclMsgHnd(msg, TRUE);
175                 tmpBP = msg->getNextBodyPart(error, tmpBP);
176
177             } else {
178                 // The first bodyPart was not handled.
179                 // It may not have been of type text.
180                 // The attachment pane needs to handle all the bodyParts
181                 // beginning with the first.
182             
183                 compose->setInclMsgHnd(msg, FALSE);
184             }
185             
186             char *name;
187             while (tmpBP != NULL) {
188                 tmpBP->getContents(
189                                     error, (const void **) &tmpBuffer.buffer,
190                                     &tmpBuffer.size,
191                                     NULL,
192                                     &name,
193                                     NULL,
194                                     NULL);
195                 if (error.isSet()) {
196                     // Do something
197                 }
198                 // It's possible for an attachment to not have a name.
199                 if (!name) {
200                     name = "NoName";
201                 }
202
203                 compose->add_att(name, tmpBuffer);
204                 tmpBP = msg->getNextBodyPart(error, tmpBP);
205                 if (error.isSet()) {
206                     // do something
207                 }
208
209                 if (strcmp(name, "NoName") != 0) {
210                     free(name);
211                 }
212             }
213             if (error.isSet()) {
214                 
215                 // do something
216             }
217         
218             // Need to call this after calling parseAttachments().
219
220             compose->get_editor()->manageAttachArea();
221         
222             // This message has attachment and is being included/forwarded,
223             // so need to fill the Compose Message Handle with attachment 
224             // BodyParts.
225             // See function for further details.
226
227             // compose->updateMsgHndAtt();
228         }
229     } 
230     else
231     {
232         // If the message has attachments, then let the attach pane
233         // handle attachments but not the first bodyPart (which has
234         // already been handled here).
235         
236         if ((num_bodyParts > 1) || (!firstBPHandled))
237         {
238             char        *att;
239             Editor      *editor = compose->get_editor()->textEditor();
240             
241             att = GETMSG(
242                         DT_catd, 1, 255,
243                         "------------------ Attachments ------------------\n");
244
245             tmpBP = msg->getFirstBodyPart(error);
246             if (firstBPHandled)
247               tmpBP = msg->getNextBodyPart(error, tmpBP);
248             
249             editor->append_to_contents(att, strlen(att));
250             while (tmpBP != NULL)
251             {
252                 editor->set_attachment(tmpBP, ins_format, brackets);
253                 tmpBP = msg->getNextBodyPart(error, tmpBP);
254                 if (error.isSet()) {
255                     // do something
256                 }
257             }
258         }
259     } 
260     
261     // Leave it up to check point routine for update or do it now???
262     compose->updateMsgHnd();
263 }
264
265 void
266 ComposeFamily::appendSignature(SendMsgDialog * compose)
267 {
268     DtMailEnv error;
269     DtMail::Session * d_session = theRoamApp.session()->session();
270     DtMail::MailRc * mail_rc = d_session->mailRc(error);
271
272     const char * value = NULL;
273     mail_rc->getValue(error, "signature", &value);
274     if (error.isSet()) {
275         return;
276     }
277
278     char * fullpath = d_session->expandPath(error, value);
279     compose->get_editor()->textEditor()->append_to_contents(fullpath);
280     if (NULL != fullpath)
281       free((void*) fullpath);
282     if (NULL != value)
283       free((void*) value);
284
285     compose->get_editor()->textEditor()->set_to_top();
286 }
287
288 char *
289 ComposeFamily::valueToAddrString(DtMailValueSeq & value)
290 {
291     int max_len = 0;
292     int count;
293
294     for (count = 0; count < value.length(); count++) {
295         max_len += strlen(*(value[count]));
296     }
297
298     char * str = new char[max_len + count + 1];
299     str[0] = 0;
300
301     DtMailBoolean need_comma = DTM_FALSE;
302
303     for (int cat = 0; cat < value.length(); cat++)
304     {
305         DtMailValue * val = value[cat];
306         DtMailAddressSeq *addr_seq = val->toAddress();
307
308         for (int ad = 0; ad < addr_seq->length(); ad++)
309         {
310             DtMailValueAddress * addr = (*addr_seq)[ad];
311
312             // Deal with mail address parser shortcomings
313             if ( strcmp(addr->dtm_address, ",") == 0 )
314                 continue ;
315
316             if (need_comma) {
317                 strcat(str, ", ");
318             }
319
320             need_comma = DTM_TRUE;
321
322             strcat(str, addr->dtm_address);
323         }
324
325         delete addr_seq;
326     }
327
328     return(str);
329 }
330
331
332 // Container menu "Compose==>New Message"
333 ComposeCmd::ComposeCmd( 
334                         char *name,
335                         char *label,
336                         int active,
337                         RoamMenuWindow *window
338                         ) : ComposeFamily( name, label, active, window )
339 {
340 }
341
342 // Put up a blank compose window.
343 void
344 ComposeCmd::doit()
345 {
346     SendMsgDialog * newsend = theCompose.getWin();
347     if (newsend == NULL) {
348         DtMailGenDialog * dialog = _parent->genDialog();
349         
350         dialog->setToErrorDialog(GETMSG(DT_catd, 1, 203, "Mailer"),
351                                  GETMSG(DT_catd, 1, 204, "Unable to create a compose window."));
352         char * helpId = DTMAILHELPNOCOMPOSE;
353         int answer = dialog->post_and_return(helpId);
354     }
355
356     appendSignature(newsend);
357 }
358
359 // Container menu "Compose==>New, Include All" and "Compose==>Forward Message"
360 // The last parameter is a switch for "include" or "forward" format.
361 ForwardCmd::ForwardCmd( 
362                         char *name,
363                         char *label,
364                         int active,
365                         RoamMenuWindow *window, 
366                         int forward
367                         ) : ComposeFamily(name, label, active, window)
368 {
369     _forward = forward;
370 }
371
372 // Forward or Include selected messages.
373 // For Include message(s), all Compose window header fields are left blank.
374 // For Forward message(s), the Compose window "Subject" header field is filled
375 // with the subject of the last selected message.
376 void
377 ForwardCmd::doit()
378 {
379     FORCE_SEGV_DECL(MsgHndArray, msgList);
380     FORCE_SEGV_DECL(MsgStruct, tmpMS);
381     DtMailMessageHandle msgno;
382     
383     // Get a Compose window.
384     SendMsgDialog *newsend = theCompose.getWin();
385     if ( newsend == NULL ) {
386         DtMailGenDialog * dialog = _parent->genDialog();
387         
388         dialog->setToErrorDialog(GETMSG(DT_catd, 1, 205, "Mailer"),
389                                  GETMSG(DT_catd, 1, 206, "Unable to create a compose window."));
390         char * helpId = DTMAILHELPNOCOMPOSE;
391         int answer = dialog->post_and_return(helpId);
392     }
393
394     // Put the signature above the message.
395     //
396     appendSignature(newsend);
397
398     // For Forwarding subject
399     DtMail::MailBox * mbox = _menuwindow->mailbox();
400     DtMail::Message * msg;
401     DtMail::Envelope * env;
402     DtMailValueSeq      value;
403     DtMailEnv error;
404     
405     // For each selected message, put it in the Compose window.
406     if ( msgList = _menuwindow->list()->selected() ) {
407         for ( int k = 0;  k < msgList->length();  k++ ) {
408             tmpMS = msgList->at(k);
409             msgno = tmpMS->message_handle;
410             if ( _forward ) {
411                 msg = mbox->getMessage(error, msgno);
412                 env = msg->getEnvelope(error);
413                 value.clear();
414                 env->getHeader(error, DtMailMessageSubject, DTM_TRUE, value);
415                 if (!error.isSet()) {
416                     const char *subject = *(value[0]);
417                     newsend->setHeader("Subject", subject);
418                     newsend->setTitle((char*) subject);
419                     newsend->setIconTitle((char*) subject);
420                 }
421                 Display_entire_msg(msgno, newsend, "forward");
422             } else {
423                 Display_entire_msg(msgno, newsend, "indent");
424             }
425         }
426     }
427     newsend->get_editor()->textEditor()->set_to_top();
428 }
429
430 // Container menu "Compose==>Reply to Semder" and
431 // "Compose==>Reply to Sender, Include"
432 // The last parameter is a switch for including the selected message or not.
433 ReplyCmd::ReplyCmd ( 
434                      char *name, 
435                      char *label,
436                      int active, 
437                      RoamMenuWindow *window, 
438                      int include 
439                      ) : ComposeFamily ( name, label, active, window )
440 {
441     _include = include;
442 }
443
444 // For each message selected, reply to sender.
445 void
446 ReplyCmd::doit()
447 {
448     FORCE_SEGV_DECL(MsgHndArray, msgList);
449     FORCE_SEGV_DECL(MsgStruct, tmpMS);
450     DtMailMessageHandle msgno;
451     FORCE_SEGV_DECL(char, from);
452     FORCE_SEGV_DECL(char, subject);
453     FORCE_SEGV_DECL(char, cc);
454     DtMailEnv error;
455     DtMail::MailBox * mbox = _menuwindow->mailbox();
456     
457     // Initialize the error.
458     error.clear();
459     
460     if (msgList = _menuwindow->list()->selected())
461     {
462         for ( int i=0; i < msgList->length(); i++ ) {
463             tmpMS = msgList->at(i);
464             msgno = tmpMS->message_handle;
465             SendMsgDialog *newsend = theCompose.getWin();
466             if ( newsend == NULL ) {
467                 DtMailGenDialog * dialog = _parent->genDialog();
468                 
469                 dialog->setToErrorDialog(GETMSG(DT_catd, 1, 207, "Mailer"),
470                                          GETMSG(DT_catd, 1, 208, "Unable to create a compose window."));
471                 char * helpId = DTMAILHELPNOCOMPOSE;
472                 int answer = dialog->post_and_return(helpId);
473             }
474             XmUpdateDisplay( newsend->baseWidget() );
475             
476             DtMail::Message * msg = mbox->getMessage(error, msgno);
477             DtMail::Envelope * env = msg->getEnvelope(error);
478             
479             DtMailValueSeq      value;
480             
481             env->getHeader(error, DtMailMessageSender, DTM_TRUE, value);
482             if (error.isSet()) {
483                 newsend->setHeader("To", "nobody@nowhere");
484             }
485             else {
486                 char * addr_str = valueToAddrString(value);
487                 newsend->setHeader("To", addr_str);
488                 delete [] addr_str;
489             }
490             
491             value.clear();
492             env->getHeader(error, DtMailMessageSubject, DTM_TRUE, value);
493             if (error.isSet()) {
494                 subject = new char[200];
495                 strcpy(subject, "Re: ");
496                 DtMailValueSeq sent;
497                 env->getHeader(error,
498                                DtMailMessageSentTime,
499                                DTM_TRUE,
500                                sent);
501                 if (error.isSet()) {
502                     strcat(subject, "Your Message");
503                 }
504                 else {
505                     strcat(subject, "Your Message Sent on ");
506                     strcat(subject, *(sent[0]));
507                 }
508                 newsend->setHeader("Subject", subject);
509             }
510             else {
511                 // Get the BE store of header.  It may contain newlines or 
512                 // tab chars which can munge the scrolling list's display!
513
514                 const char * orig = *(value[0]);
515
516                 int fc;
517                 int orig_length;
518                 char *tmp_subj;
519
520                 // Check if BE store contains the funky chars.
521
522                 for (fc = 0, orig_length = strlen(orig), 
523                       tmp_subj = (char *) orig; 
524                     fc < orig_length; 
525                     fc++, tmp_subj++) {
526
527                     char c = *tmp_subj;
528                     if ((c == '\n') 
529                      || (c == '\t') 
530                      || (c == '\r')) {
531
532                         break;
533                     }
534                 }
535
536                 subject = new char[fc+6];
537                 
538                 if (strncasecmp(orig, "Re:", 3)) {
539                     strcpy(subject, "Re: ");
540                 }
541                 else {
542                     *subject = 0;
543                 }
544                 
545                 strncat((char *)subject, orig, fc);
546
547                 newsend->setHeader("Subject", subject);
548             }
549             
550             newsend->setTitle(subject);
551             newsend->setIconTitle(subject);
552             delete [] subject;
553
554             if ( _include ) {
555                 Display_entire_msg(msgno, newsend, "indent");
556                 newsend->get_editor()->textEditor()->set_to_top();
557             }
558             appendSignature(newsend);
559                 newsend->setInputFocus(1);
560         }
561
562         delete msgList;
563     }
564 }
565
566 // Container menu "Compose==>Reply to All" and "Compose==>Reply to All, Include"
567 // The last parameter is a switch for including the selected message or not.
568 ReplyAllCmd::ReplyAllCmd( 
569                           char *name,
570                           char *label,
571                           int active,
572                           RoamMenuWindow *window, 
573                           int include 
574                           ) : ComposeFamily( name, label, active, window )
575 {
576     _include = include;
577 }
578
579 // For each message selected, reply to everybody.
580 void
581 ReplyAllCmd::doit()
582 {
583     FORCE_SEGV_DECL(MsgHndArray, msgList);
584     FORCE_SEGV_DECL(MsgStruct, tmpMS);
585     FORCE_SEGV_DECL(char, subject);
586     FORCE_SEGV_DECL(char, to);
587     FORCE_SEGV_DECL(char, buffer);
588     DtMailMessageHandle msgno;
589     DtMail::MailBox *mbox = _menuwindow->mailbox();
590     DtMailEnv error;
591     char *currentCcValue;
592     SendMsgDialog *newsend;
593     DtMailGenDialog * dialog;
594     DtMail::Message *msg;
595     DtMail::Envelope *env;
596
597
598     // Initialize the mail_error.
599     error.clear();
600     
601     
602     if ( msgList = _menuwindow->list()->selected() )
603         for ( int k = 0;  k < msgList->length();  k++ ) {
604             DtMailValueSeq      value ;
605             
606             tmpMS = msgList->at(k);
607             msgno = tmpMS->message_handle;
608             newsend = theCompose.getWin();
609             if ( newsend == NULL ) {
610                 dialog = _parent->genDialog();
611                 
612                 dialog->setToErrorDialog(GETMSG(DT_catd, 1, 209, "Mailer"),
613                                          GETMSG(DT_catd, 1, 210, "Unable to create a compose window."));
614                 char * helpId = DTMAILHELPNOCOMPOSE;
615                 int answer = dialog->post_and_return(helpId);
616             }
617             msg = mbox->getMessage(error, msgno);
618             env = msg->getEnvelope(error);
619             
620             env->getHeader(
621                            error, 
622                            DtMailMessageToReply,
623                            DTM_TRUE, 
624                            value);
625             
626             env->getHeader(
627                            error, 
628                            DtMailMessageSender, 
629                            DTM_TRUE, 
630                            value);
631
632             char * addr_str = valueToAddrString(value);
633             newsend->setHeader("To", addr_str);
634             delete [] addr_str;
635             value.clear();
636             env->getHeader(
637                            error, 
638                            DtMailMessageSubject, 
639                            DTM_TRUE, 
640                            value);
641             if ( error.isSet() ) {
642                 subject = new char[200];
643                 strcpy(subject, "Re: ");
644                 DtMailValueSeq sent;
645                 env->getHeader(error,
646                                DtMailMessageSentTime,
647                                DTM_TRUE,
648                                sent);
649                 if (error.isSet()) {
650                     strcat(subject, "Your Message");
651                 }
652                 else {
653                     strcat(subject, "Your Message Sent on ");
654                     strcat(subject, *(sent[0]));
655                 }
656                 newsend->setHeader("Subject", subject);
657             } else {
658                 // Get the BE store of header.  It may contain newlines or 
659                 // tab chars which can munge the scrolling list's display!
660
661                 const char * orig = *(value[0]);
662
663
664                 int fc = 0;
665                 int orig_length;
666                 char *tmp_subj;
667
668                 // Check if BE store contains the funky chars.
669
670                 for (fc = 0, orig_length = strlen(orig), 
671                       tmp_subj = (char *)orig; 
672                      fc < orig_length; 
673                      fc++, tmp_subj++) {
674
675                     char c = *tmp_subj;
676                     if ((c == '\n') 
677                      || (c == '\t') 
678                      || (c == '\r')) {
679
680                         break;
681                     }
682                 }
683
684                 subject = new char[fc+6];
685                 
686                 if (strncasecmp(orig, "Re:", 3)) {
687                     strcpy(subject, "Re: ");
688                 }
689                 else {
690                     *subject = 0;
691                 }
692                 
693                 strncat((char *)subject, orig, fc);
694
695                 newsend->setHeader("Subject", subject);
696             }
697             value.clear();
698             env->getHeader(
699                            error, 
700                            DtMailMessageCcReply, 
701                            DTM_TRUE, 
702                            value);
703             if (!error.isSet()) {
704                 // Strip out newlines from the cc line.  They *may* be 
705                 // present.
706                 currentCcValue = valueToAddrString(value);
707
708                 newsend->setHeader("Cc", currentCcValue);
709                 delete [] currentCcValue ;
710             }
711
712             newsend->setTitle(subject);
713             newsend->setIconTitle(subject);
714             delete [] subject;
715
716             if ( _include ) {
717                 Display_entire_msg(msgno, newsend, "indent");
718                 newsend->get_editor()->textEditor()->set_to_top();
719             }
720             appendSignature(newsend);
721                 newsend->setInputFocus(1);
722         }
723 }
724
725 TemplateCmd::TemplateCmd(char *name,
726                          char *label,
727                          int active,
728                          SendMsgDialog * compose,
729                          const char * file)
730 : NoUndoCmd(name, label, active)
731 {
732     _compose = compose;
733
734     if (*file != '/' && *file != '~') {
735         // Relative path.  Should be relative to home directory
736         _file = (char *)malloc(strlen(file) + 4);
737         if (_file != NULL) {
738                 strcpy(_file, "~/");
739                 strcat(_file, file);
740         }
741     } else {
742         _file = strdup(file);
743     }
744 }
745
746 TemplateCmd::~TemplateCmd(void)
747 {
748     free(_file);
749 }
750
751 void
752 TemplateCmd::doit()
753 {
754     DtMailEnv error;
755     DtMail::Session * d_session = theRoamApp.session()->session();
756     DtMailGenDialog * dialog = _compose->genDialog();
757     DtMailBuffer mbuf;
758
759     char * fullpath = d_session->expandPath(error, _file);
760
761     // Map the file and try to parse it as a message. If it is a message,
762     // then load it with headers. Otherwise, throw everything into the
763     // editor.
764     //
765     int fd = SafeOpen(fullpath, O_RDONLY);
766     if (fd < 0) {
767         dialog->setToErrorDialog(GETMSG(DT_catd, 1, 211, "Mailer"),
768                                  GETMSG(DT_catd, 1, 212, "The template does not exist."));
769         char * helpId = DTMAILHELPNOTEMPLATE;
770         int answer = dialog->post_and_return(helpId);
771         free(fullpath);
772         return;
773     }
774
775     struct stat buf;
776     if (SafeFStat(fd, &buf) < 0) {
777         dialog->setToErrorDialog(GETMSG(DT_catd, 1, 213, "Mailer"),
778                                  GETMSG(DT_catd, 1, 214, "The template appears to be corrupt."));
779         char * helpId = DTMAILHELPCORRUPTTEMPLATE;
780         int answer = dialog->post_and_return(helpId);
781         SafeClose(fd);
782         free(fullpath);
783         return;
784     }
785
786     int page_size = (int)sysconf(_SC_PAGESIZE);
787     size_t map_size = (size_t) (buf.st_size + 
788                                 (page_size - (buf.st_size % page_size)));
789
790     int free_buf = 0;
791     mbuf.size = buf.st_size;
792 #ifdef __osf__
793     // This version of mmap does NOT allow requested length to be
794     // greater than the file size ...  in contradiction to the
795     // documentation (don't round up).
796     mbuf.buffer = mmap(0, buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
797 #else
798     mbuf.buffer = mmap(0, map_size, PROT_READ, MAP_PRIVATE, fd, 0);
799 #endif
800     if (mbuf.buffer == (char *)-1) {
801         free_buf = 1;
802         mbuf.buffer = new char[mbuf.size];
803         if (mbuf.buffer == NULL) {
804             dialog->setToErrorDialog(GETMSG(DT_catd, 1, 215, "Mailer"),
805                                      GETMSG(DT_catd, 1, 216, "There is not enough memory to load the template."));
806             char * helpId = DTMAILHELPNOMEMTEMPLATE;
807             int answer = dialog->post_and_return(helpId);
808             SafeClose(fd);
809             free(fullpath);
810             return;
811         }
812
813         if (SafeRead(fd, mbuf.buffer, (unsigned int)mbuf.size) < mbuf.size) {
814             dialog->setToErrorDialog(GETMSG(DT_catd, 1, 217, "Mailer"),
815                                      GETMSG(DT_catd, 1, 218, "The template appears to be corrupt."));
816             char * helpId = DTMAILHELPERROR;
817             int answer = dialog->post_and_return(helpId);
818             SafeClose(fd);
819             delete [] mbuf.buffer;
820             free(fullpath);
821             return;
822         }
823     }
824
825     // Now we ask the library to parse it. If this fails for any reason, this
826     // is not a message, so we give up.
827     //
828     DtMail::Message * msg = d_session->messageConstruct(error,
829                                                         DtMailBufferObject,
830                                                         &mbuf,
831                                                         NULL,
832                                                         NULL,
833                                                         NULL);
834     if (error.isSet()) {
835         _compose->get_editor()->textEditor()->append_to_contents((char *)mbuf.buffer,
836                                                                mbuf.size);
837     }
838     else {
839         _compose->loadHeaders(msg, DTM_TRUE);
840
841         DtMail::BodyPart * bp = msg->getFirstBodyPart(error);
842         if (error.isNotSet()) {
843             const void * contents;
844             unsigned long length;
845
846             bp->getContents(error,
847                             &contents,
848                             &length,
849                             NULL,
850                             NULL,
851                             NULL,
852                             NULL);
853
854             _compose->get_editor()->textEditor()->append_to_contents(
855                                                 (char *)contents, length);
856         }
857     }
858
859     free(fullpath);
860
861     if (free_buf) {
862         free(mbuf.buffer);
863     }
864     else {
865         munmap((char *)mbuf.buffer, map_size);
866     }
867
868     SafeClose(fd);
869 }
870
871 HideShowCmd::HideShowCmd(char *name,
872                          char *widgetlabel,
873                          int active,
874                          SendMsgDialog * compose,
875                          const char * label)
876 : NoUndoCmd(name, (char *)widgetlabel, active)
877 {
878     _compose = compose;
879     _header = strdup(label);
880 }
881
882 HideShowCmd::~HideShowCmd(void)
883 {
884     if (_header) {
885         free(_header);
886     }
887 }
888
889 void
890 HideShowCmd::doit(void)
891 {
892     _compose->changeHeaderState(_header);
893 }