-fpermissive to allow GCC to compile old C++
[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 != NULL)
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
293     for (int count = 0; count < value.length(); count++) {
294         max_len += strlen(*(value[count]));
295     }
296
297     char * str = new char[max_len + count + 1];
298     str[0] = 0;
299
300     DtMailBoolean need_comma = DTM_FALSE;
301
302     for (int cat = 0; cat < value.length(); cat++)
303     {
304         DtMailValue * val = value[cat];
305         DtMailAddressSeq *addr_seq = val->toAddress();
306
307         for (int ad = 0; ad < addr_seq->length(); ad++)
308         {
309             DtMailValueAddress * addr = (*addr_seq)[ad];
310
311             // Deal with mail address parser shortcomings
312             if ( strcmp(addr->dtm_address, ",") == 0 )
313                 continue ;
314
315             if (need_comma) {
316                 strcat(str, ", ");
317             }
318
319             need_comma = DTM_TRUE;
320
321             strcat(str, addr->dtm_address);
322         }
323
324         delete addr_seq;
325     }
326
327     return(str);
328 }
329
330
331 // Container menu "Compose==>New Message"
332 ComposeCmd::ComposeCmd( 
333                         char *name,
334                         char *label,
335                         int active,
336                         RoamMenuWindow *window
337                         ) : ComposeFamily( name, label, active, window )
338 {
339 }
340
341 // Put up a blank compose window.
342 void
343 ComposeCmd::doit()
344 {
345     SendMsgDialog * newsend = theCompose.getWin();
346     if (newsend == NULL) {
347         DtMailGenDialog * dialog = _parent->genDialog();
348         
349         dialog->setToErrorDialog(GETMSG(DT_catd, 1, 203, "Mailer"),
350                                  GETMSG(DT_catd, 1, 204, "Unable to create a compose window."));
351         char * helpId = DTMAILHELPNOCOMPOSE;
352         int answer = dialog->post_and_return(helpId);
353     }
354
355     appendSignature(newsend);
356 }
357
358 // Container menu "Compose==>New, Include All" and "Compose==>Forward Message"
359 // The last parameter is a switch for "include" or "forward" format.
360 ForwardCmd::ForwardCmd( 
361                         char *name,
362                         char *label,
363                         int active,
364                         RoamMenuWindow *window, 
365                         int forward
366                         ) : ComposeFamily(name, label, active, window)
367 {
368     _forward = forward;
369 }
370
371 // Forward or Include selected messages.
372 // For Include message(s), all Compose window header fields are left blank.
373 // For Forward message(s), the Compose window "Subject" header field is filled
374 // with the subject of the last selected message.
375 void
376 ForwardCmd::doit()
377 {
378     FORCE_SEGV_DECL(MsgHndArray, msgList);
379     FORCE_SEGV_DECL(MsgStruct, tmpMS);
380     DtMailMessageHandle msgno;
381     
382     // Get a Compose window.
383     SendMsgDialog *newsend = theCompose.getWin();
384     if ( newsend == NULL ) {
385         DtMailGenDialog * dialog = _parent->genDialog();
386         
387         dialog->setToErrorDialog(GETMSG(DT_catd, 1, 205, "Mailer"),
388                                  GETMSG(DT_catd, 1, 206, "Unable to create a compose window."));
389         char * helpId = DTMAILHELPNOCOMPOSE;
390         int answer = dialog->post_and_return(helpId);
391     }
392
393     // Put the signature above the message.
394     //
395     appendSignature(newsend);
396
397     // For Forwarding subject
398     DtMail::MailBox * mbox = _menuwindow->mailbox();
399     DtMail::Message * msg;
400     DtMail::Envelope * env;
401     DtMailValueSeq      value;
402     DtMailEnv error;
403     
404     // For each selected message, put it in the Compose window.
405     if ( msgList = _menuwindow->list()->selected() ) {
406         for ( int k = 0;  k < msgList->length();  k++ ) {
407             tmpMS = msgList->at(k);
408             msgno = tmpMS->message_handle;
409             if ( _forward ) {
410                 msg = mbox->getMessage(error, msgno);
411                 env = msg->getEnvelope(error);
412                 value.clear();
413                 env->getHeader(error, DtMailMessageSubject, DTM_TRUE, value);
414                 if (!error.isSet()) {
415                     const char *subject = *(value[0]);
416                     newsend->setHeader("Subject", subject);
417                     newsend->setTitle((char*) subject);
418                     newsend->setIconTitle((char*) subject);
419                 }
420                 Display_entire_msg(msgno, newsend, "forward");
421             } else {
422                 Display_entire_msg(msgno, newsend, "indent");
423             }
424         }
425     }
426     newsend->get_editor()->textEditor()->set_to_top();
427 }
428
429 // Container menu "Compose==>Reply to Semder" and
430 // "Compose==>Reply to Sender, Include"
431 // The last parameter is a switch for including the selected message or not.
432 ReplyCmd::ReplyCmd ( 
433                      char *name, 
434                      char *label,
435                      int active, 
436                      RoamMenuWindow *window, 
437                      int include 
438                      ) : ComposeFamily ( name, label, active, window )
439 {
440     _include = include;
441 }
442
443 // For each message selected, reply to sender.
444 void
445 ReplyCmd::doit()
446 {
447     FORCE_SEGV_DECL(MsgHndArray, msgList);
448     FORCE_SEGV_DECL(MsgStruct, tmpMS);
449     DtMailMessageHandle msgno;
450     FORCE_SEGV_DECL(char, from);
451     FORCE_SEGV_DECL(char, subject);
452     FORCE_SEGV_DECL(char, cc);
453     DtMailEnv error;
454     DtMail::MailBox * mbox = _menuwindow->mailbox();
455     
456     // Initialize the error.
457     error.clear();
458     
459     if (msgList = _menuwindow->list()->selected())
460     {
461         for ( int i=0; i < msgList->length(); i++ ) {
462             tmpMS = msgList->at(i);
463             msgno = tmpMS->message_handle;
464             SendMsgDialog *newsend = theCompose.getWin();
465             if ( newsend == NULL ) {
466                 DtMailGenDialog * dialog = _parent->genDialog();
467                 
468                 dialog->setToErrorDialog(GETMSG(DT_catd, 1, 207, "Mailer"),
469                                          GETMSG(DT_catd, 1, 208, "Unable to create a compose window."));
470                 char * helpId = DTMAILHELPNOCOMPOSE;
471                 int answer = dialog->post_and_return(helpId);
472             }
473             XmUpdateDisplay( newsend->baseWidget() );
474             
475             DtMail::Message * msg = mbox->getMessage(error, msgno);
476             DtMail::Envelope * env = msg->getEnvelope(error);
477             
478             DtMailValueSeq      value;
479             
480             env->getHeader(error, DtMailMessageSender, DTM_TRUE, value);
481             if (error.isSet()) {
482                 newsend->setHeader("To", "nobody@nowhere");
483             }
484             else {
485                 char * addr_str = valueToAddrString(value);
486                 newsend->setHeader("To", addr_str);
487                 delete [] addr_str;
488             }
489             
490             value.clear();
491             env->getHeader(error, DtMailMessageSubject, DTM_TRUE, value);
492             if (error.isSet()) {
493                 subject = new char[200];
494                 strcpy(subject, "Re: ");
495                 DtMailValueSeq sent;
496                 env->getHeader(error,
497                                DtMailMessageSentTime,
498                                DTM_TRUE,
499                                sent);
500                 if (error.isSet()) {
501                     strcat(subject, "Your Message");
502                 }
503                 else {
504                     strcat(subject, "Your Message Sent on ");
505                     strcat(subject, *(sent[0]));
506                 }
507                 newsend->setHeader("Subject", subject);
508             }
509             else {
510                 // Get the BE store of header.  It may contain newlines or 
511                 // tab chars which can munge the scrolling list's display!
512
513                 const char * orig = *(value[0]);
514
515                 int fc;
516                 int orig_length;
517                 char *tmp_subj;
518
519                 // Check if BE store contains the funky chars.
520
521                 for (fc = 0, orig_length = strlen(orig), 
522                       tmp_subj = (char *) orig; 
523                     fc < orig_length; 
524                     fc++, tmp_subj++) {
525
526                     char c = *tmp_subj;
527                     if ((c == '\n') 
528                      || (c == '\t') 
529                      || (c == '\r')) {
530
531                         break;
532                     }
533                 }
534
535                 subject = new char[fc+6];
536                 
537                 if (strncasecmp(orig, "Re:", 3)) {
538                     strcpy(subject, "Re: ");
539                 }
540                 else {
541                     *subject = 0;
542                 }
543                 
544                 strncat((char *)subject, orig, fc);
545
546                 newsend->setHeader("Subject", subject);
547             }
548             
549             newsend->setTitle(subject);
550             newsend->setIconTitle(subject);
551             delete [] subject;
552
553             if ( _include ) {
554                 Display_entire_msg(msgno, newsend, "indent");
555                 newsend->get_editor()->textEditor()->set_to_top();
556             }
557             appendSignature(newsend);
558                 newsend->setInputFocus(1);
559         }
560
561         delete msgList;
562     }
563 }
564
565 // Container menu "Compose==>Reply to All" and "Compose==>Reply to All, Include"
566 // The last parameter is a switch for including the selected message or not.
567 ReplyAllCmd::ReplyAllCmd( 
568                           char *name,
569                           char *label,
570                           int active,
571                           RoamMenuWindow *window, 
572                           int include 
573                           ) : ComposeFamily( name, label, active, window )
574 {
575     _include = include;
576 }
577
578 // For each message selected, reply to everybody.
579 void
580 ReplyAllCmd::doit()
581 {
582     FORCE_SEGV_DECL(MsgHndArray, msgList);
583     FORCE_SEGV_DECL(MsgStruct, tmpMS);
584     FORCE_SEGV_DECL(char, subject);
585     FORCE_SEGV_DECL(char, to);
586     FORCE_SEGV_DECL(char, buffer);
587     DtMailMessageHandle msgno;
588     DtMail::MailBox *mbox = _menuwindow->mailbox();
589     DtMailEnv error;
590     char *currentCcValue;
591     SendMsgDialog *newsend;
592     DtMailGenDialog * dialog;
593     DtMail::Message *msg;
594     DtMail::Envelope *env;
595
596
597     // Initialize the mail_error.
598     error.clear();
599     
600     
601     if ( msgList = _menuwindow->list()->selected() )
602         for ( int k = 0;  k < msgList->length();  k++ ) {
603             DtMailValueSeq      value ;
604             
605             tmpMS = msgList->at(k);
606             msgno = tmpMS->message_handle;
607             newsend = theCompose.getWin();
608             if ( newsend == NULL ) {
609                 dialog = _parent->genDialog();
610                 
611                 dialog->setToErrorDialog(GETMSG(DT_catd, 1, 209, "Mailer"),
612                                          GETMSG(DT_catd, 1, 210, "Unable to create a compose window."));
613                 char * helpId = DTMAILHELPNOCOMPOSE;
614                 int answer = dialog->post_and_return(helpId);
615             }
616             msg = mbox->getMessage(error, msgno);
617             env = msg->getEnvelope(error);
618             
619             env->getHeader(
620                            error, 
621                            DtMailMessageToReply,
622                            DTM_TRUE, 
623                            value);
624             
625             env->getHeader(
626                            error, 
627                            DtMailMessageSender, 
628                            DTM_TRUE, 
629                            value);
630
631             char * addr_str = valueToAddrString(value);
632             newsend->setHeader("To", addr_str);
633             delete [] addr_str;
634             value.clear();
635             env->getHeader(
636                            error, 
637                            DtMailMessageSubject, 
638                            DTM_TRUE, 
639                            value);
640             if ( error.isSet() ) {
641                 subject = new char[200];
642                 strcpy(subject, "Re: ");
643                 DtMailValueSeq sent;
644                 env->getHeader(error,
645                                DtMailMessageSentTime,
646                                DTM_TRUE,
647                                sent);
648                 if (error.isSet()) {
649                     strcat(subject, "Your Message");
650                 }
651                 else {
652                     strcat(subject, "Your Message Sent on ");
653                     strcat(subject, *(sent[0]));
654                 }
655                 newsend->setHeader("Subject", subject);
656             } else {
657                 // Get the BE store of header.  It may contain newlines or 
658                 // tab chars which can munge the scrolling list's display!
659
660                 const char * orig = *(value[0]);
661
662
663                 int fc = 0;
664                 int orig_length;
665                 char *tmp_subj;
666
667                 // Check if BE store contains the funky chars.
668
669                 for (fc = 0, orig_length = strlen(orig), 
670                       tmp_subj = (char *)orig; 
671                      fc < orig_length; 
672                      fc++, tmp_subj++) {
673
674                     char c = *tmp_subj;
675                     if ((c == '\n') 
676                      || (c == '\t') 
677                      || (c == '\r')) {
678
679                         break;
680                     }
681                 }
682
683                 subject = new char[fc+6];
684                 
685                 if (strncasecmp(orig, "Re:", 3)) {
686                     strcpy(subject, "Re: ");
687                 }
688                 else {
689                     *subject = 0;
690                 }
691                 
692                 strncat((char *)subject, orig, fc);
693
694                 newsend->setHeader("Subject", subject);
695             }
696             value.clear();
697             env->getHeader(
698                            error, 
699                            DtMailMessageCcReply, 
700                            DTM_TRUE, 
701                            value);
702             if (!error.isSet()) {
703                 // Strip out newlines from the cc line.  They *may* be 
704                 // present.
705                 currentCcValue = valueToAddrString(value);
706
707                 newsend->setHeader("Cc", currentCcValue);
708                 delete [] currentCcValue ;
709             }
710
711             newsend->setTitle(subject);
712             newsend->setIconTitle(subject);
713             delete [] subject;
714
715             if ( _include ) {
716                 Display_entire_msg(msgno, newsend, "indent");
717                 newsend->get_editor()->textEditor()->set_to_top();
718             }
719             appendSignature(newsend);
720                 newsend->setInputFocus(1);
721         }
722 }
723
724 TemplateCmd::TemplateCmd(char *name,
725                          char *label,
726                          int active,
727                          SendMsgDialog * compose,
728                          const char * file)
729 : NoUndoCmd(name, label, active)
730 {
731     _compose = compose;
732
733     if (*file != '/' && *file != '~') {
734         // Relative path.  Should be relative to home directory
735         _file = (char *)malloc(strlen(file) + 4);
736         if (_file != NULL) {
737                 strcpy(_file, "~/");
738                 strcat(_file, file);
739         }
740     } else {
741         _file = strdup(file);
742     }
743 }
744
745 TemplateCmd::~TemplateCmd(void)
746 {
747     free(_file);
748 }
749
750 void
751 TemplateCmd::doit()
752 {
753     DtMailEnv error;
754     DtMail::Session * d_session = theRoamApp.session()->session();
755     DtMailGenDialog * dialog = _compose->genDialog();
756     DtMailBuffer mbuf;
757
758     char * fullpath = d_session->expandPath(error, _file);
759
760     // Map the file and try to parse it as a message. If it is a message,
761     // then load it with headers. Otherwise, throw everything into the
762     // editor.
763     //
764     int fd = SafeOpen(fullpath, O_RDONLY);
765     if (fd < 0) {
766         dialog->setToErrorDialog(GETMSG(DT_catd, 1, 211, "Mailer"),
767                                  GETMSG(DT_catd, 1, 212, "The template does not exist."));
768         char * helpId = DTMAILHELPNOTEMPLATE;
769         int answer = dialog->post_and_return(helpId);
770         free(fullpath);
771         return;
772     }
773
774     struct stat buf;
775     if (SafeFStat(fd, &buf) < 0) {
776         dialog->setToErrorDialog(GETMSG(DT_catd, 1, 213, "Mailer"),
777                                  GETMSG(DT_catd, 1, 214, "The template appears to be corrupt."));
778         char * helpId = DTMAILHELPCORRUPTTEMPLATE;
779         int answer = dialog->post_and_return(helpId);
780         SafeClose(fd);
781         free(fullpath);
782         return;
783     }
784
785     int page_size = (int)sysconf(_SC_PAGESIZE);
786     size_t map_size = (size_t) (buf.st_size + 
787                                 (page_size - (buf.st_size % page_size)));
788
789     int free_buf = 0;
790     mbuf.size = buf.st_size;
791 #ifdef __osf__
792     // This version of mmap does NOT allow requested length to be
793     // greater than the file size ...  in contradiction to the
794     // documentation (don't round up).
795     mbuf.buffer = mmap(0, buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
796 #else
797     mbuf.buffer = mmap(0, map_size, PROT_READ, MAP_PRIVATE, fd, 0);
798 #endif
799     if (mbuf.buffer == (char *)-1) {
800         free_buf = 1;
801         mbuf.buffer = new char[mbuf.size];
802         if (mbuf.buffer == NULL) {
803             dialog->setToErrorDialog(GETMSG(DT_catd, 1, 215, "Mailer"),
804                                      GETMSG(DT_catd, 1, 216, "There is not enough memory to load the template."));
805             char * helpId = DTMAILHELPNOMEMTEMPLATE;
806             int answer = dialog->post_and_return(helpId);
807             SafeClose(fd);
808             free(fullpath);
809             return;
810         }
811
812         if (SafeRead(fd, mbuf.buffer, (unsigned int)mbuf.size) < mbuf.size) {
813             dialog->setToErrorDialog(GETMSG(DT_catd, 1, 217, "Mailer"),
814                                      GETMSG(DT_catd, 1, 218, "The template appears to be corrupt."));
815             char * helpId = DTMAILHELPERROR;
816             int answer = dialog->post_and_return(helpId);
817             SafeClose(fd);
818             delete [] mbuf.buffer;
819             free(fullpath);
820             return;
821         }
822     }
823
824     // Now we ask the library to parse it. If this fails for any reason, this
825     // is not a message, so we give up.
826     //
827     DtMail::Message * msg = d_session->messageConstruct(error,
828                                                         DtMailBufferObject,
829                                                         &mbuf,
830                                                         NULL,
831                                                         NULL,
832                                                         NULL);
833     if (error.isSet()) {
834         _compose->get_editor()->textEditor()->append_to_contents((char *)mbuf.buffer,
835                                                                mbuf.size);
836     }
837     else {
838         _compose->loadHeaders(msg, DTM_TRUE);
839
840         DtMail::BodyPart * bp = msg->getFirstBodyPart(error);
841         if (error.isNotSet()) {
842             const void * contents;
843             unsigned long length;
844
845             bp->getContents(error,
846                             &contents,
847                             &length,
848                             NULL,
849                             NULL,
850                             NULL,
851                             NULL);
852
853             _compose->get_editor()->textEditor()->append_to_contents(
854                                                 (char *)contents, length);
855         }
856     }
857
858     free(fullpath);
859
860     if (free_buf) {
861         free(mbuf.buffer);
862     }
863     else {
864         munmap((char *)mbuf.buffer, map_size);
865     }
866
867     SafeClose(fd);
868 }
869
870 HideShowCmd::HideShowCmd(char *name,
871                          char *widgetlabel,
872                          int active,
873                          SendMsgDialog * compose,
874                          const char * label)
875 : NoUndoCmd(name, (char *)widgetlabel, active)
876 {
877     _compose = compose;
878     _header = strdup(label);
879 }
880
881 HideShowCmd::~HideShowCmd(void)
882 {
883     if (_header) {
884         free(_header);
885     }
886 }
887
888 void
889 HideShowCmd::doit(void)
890 {
891     _compose->changeHeaderState(_header);
892 }