2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
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)
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
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
26 * $TOG: ComposeCmds.C /main/11 1998/10/21 17:23:13 mgreess $
28 * RESTRICTED CONFIDENTIAL INFORMATION:
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
38 * Copyright 1993 Sun Microsystems, Inc. All rights reserved.
43 #include <EUSCompat.h>
45 #include <sys/types.h>
47 #if defined(NEED_MMAP_WRAPPER)
51 #if defined(NEED_MMAP_WRAPPER)
57 #include <Xm/FileSBP.h>
59 #include <Xm/ToggleB.h>
60 #include <Xm/PushBG.h>
61 #include <Xm/PanedW.h>
64 #include <Dt/Action.h>
65 #include <DtMail/IO.hh>
66 #include "RoamMenuWindow.h"
67 #include "SendMsgDialog.h"
68 #include "Undelete.hh"
70 #include "ComposeCmds.hh"
71 #include "Application.h"
73 #include "DtMailWDM.hh"
74 #include "FindDialog.h"
75 #include "MsgScrollingList.hh"
76 #include "MsgHndArray.hh"
77 #include "MemUtils.hh"
79 #include "EUSDebug.hh"
80 #include "DtMailGenDialog.hh"
81 #include "DtMailHelp.hh"
82 #include <DtMail/DtMailError.hh>
85 #include "Attachment.h"
86 #include "str_utils.h"
88 ComposeFamily::ComposeFamily(char *name,
91 RoamMenuWindow *window)
92 : RoamCmd(name, label, active, window)
97 #ifndef CAN_INLINE_VIRTUALS
98 ComposeFamily::~ComposeFamily( void )
101 #endif /* ! CAN_INLINE_VIRTUALS */
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".
109 ComposeFamily::Display_entire_msg(DtMailMessageHandle msgno,
110 SendMsgDialog *compose,
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;
123 unsigned long size = 0;
125 Editor::InsertFormat ins_format = Editor::IF_NONE;
126 Editor::BracketFormat brackets = Editor::BF_NONE;
128 // Do not need to wrap "include", "forward", and "indent" with
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;
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
145 char * status_string;
146 DtMailBoolean firstBPHandled =
147 compose->get_editor()->textEditor()->set_message(
154 // Now need to handle the unhandled body parts of the message.
156 num_bodyParts = msg->getBodyCount(error);
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).
166 if ((num_bodyParts > 1) || (!firstBPHandled)) {
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.
174 compose->setInclMsgHnd(msg, TRUE);
175 tmpBP = msg->getNextBodyPart(error, tmpBP);
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.
183 compose->setInclMsgHnd(msg, FALSE);
187 while (tmpBP != NULL) {
189 error, (const void **) &tmpBuffer.buffer,
198 // It's possible for an attachment to not have a name.
203 compose->add_att(name, tmpBuffer);
204 tmpBP = msg->getNextBodyPart(error, tmpBP);
209 if (strcmp(name, "NoName") != 0) {
218 // Need to call this after calling parseAttachments().
220 compose->get_editor()->manageAttachArea();
222 // This message has attachment and is being included/forwarded,
223 // so need to fill the Compose Message Handle with attachment
225 // See function for further details.
227 // compose->updateMsgHndAtt();
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).
236 if ((num_bodyParts > 1) || (!firstBPHandled))
239 Editor *editor = compose->get_editor()->textEditor();
243 "------------------ Attachments ------------------\n");
245 tmpBP = msg->getFirstBodyPart(error);
247 tmpBP = msg->getNextBodyPart(error, tmpBP);
249 editor->append_to_contents(att, strlen(att));
250 while (tmpBP != NULL)
252 editor->set_attachment(tmpBP, ins_format, brackets);
253 tmpBP = msg->getNextBodyPart(error, tmpBP);
261 // Leave it up to check point routine for update or do it now???
262 compose->updateMsgHnd();
266 ComposeFamily::appendSignature(SendMsgDialog * compose)
269 DtMail::Session * d_session = theRoamApp.session()->session();
270 DtMail::MailRc * mail_rc = d_session->mailRc(error);
272 const char * value = NULL;
273 mail_rc->getValue(error, "signature", &value);
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);
285 compose->get_editor()->textEditor()->set_to_top();
289 ComposeFamily::valueToAddrString(DtMailValueSeq & value)
293 for (int count = 0; count < value.length(); count++) {
294 max_len += strlen(*(value[count]));
297 char * str = new char[max_len + count + 1];
300 DtMailBoolean need_comma = DTM_FALSE;
302 for (int cat = 0; cat < value.length(); cat++)
304 DtMailValue * val = value[cat];
305 DtMailAddressSeq *addr_seq = val->toAddress();
307 for (int ad = 0; ad < addr_seq->length(); ad++)
309 DtMailValueAddress * addr = (*addr_seq)[ad];
311 // Deal with mail address parser shortcomings
312 if ( strcmp(addr->dtm_address, ",") == 0 )
319 need_comma = DTM_TRUE;
321 strcat(str, addr->dtm_address);
331 // Container menu "Compose==>New Message"
332 ComposeCmd::ComposeCmd(
336 RoamMenuWindow *window
337 ) : ComposeFamily( name, label, active, window )
341 // Put up a blank compose window.
345 SendMsgDialog * newsend = theCompose.getWin();
346 if (newsend == NULL) {
347 DtMailGenDialog * dialog = _parent->genDialog();
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);
355 appendSignature(newsend);
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(
364 RoamMenuWindow *window,
366 ) : ComposeFamily(name, label, active, window)
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.
378 FORCE_SEGV_DECL(MsgHndArray, msgList);
379 FORCE_SEGV_DECL(MsgStruct, tmpMS);
380 DtMailMessageHandle msgno;
382 // Get a Compose window.
383 SendMsgDialog *newsend = theCompose.getWin();
384 if ( newsend == NULL ) {
385 DtMailGenDialog * dialog = _parent->genDialog();
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);
393 // Put the signature above the message.
395 appendSignature(newsend);
397 // For Forwarding subject
398 DtMail::MailBox * mbox = _menuwindow->mailbox();
399 DtMail::Message * msg;
400 DtMail::Envelope * env;
401 DtMailValueSeq value;
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;
410 msg = mbox->getMessage(error, msgno);
411 env = msg->getEnvelope(error);
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);
420 Display_entire_msg(msgno, newsend, "forward");
422 Display_entire_msg(msgno, newsend, "indent");
426 newsend->get_editor()->textEditor()->set_to_top();
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.
436 RoamMenuWindow *window,
438 ) : ComposeFamily ( name, label, active, window )
443 // For each message selected, reply to sender.
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);
454 DtMail::MailBox * mbox = _menuwindow->mailbox();
456 // Initialize the error.
459 if (msgList = _menuwindow->list()->selected())
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();
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);
473 XmUpdateDisplay( newsend->baseWidget() );
475 DtMail::Message * msg = mbox->getMessage(error, msgno);
476 DtMail::Envelope * env = msg->getEnvelope(error);
478 DtMailValueSeq value;
480 env->getHeader(error, DtMailMessageSender, DTM_TRUE, value);
482 newsend->setHeader("To", "nobody@nowhere");
485 char * addr_str = valueToAddrString(value);
486 newsend->setHeader("To", addr_str);
491 env->getHeader(error, DtMailMessageSubject, DTM_TRUE, value);
493 subject = new char[200];
494 strcpy(subject, "Re: ");
496 env->getHeader(error,
497 DtMailMessageSentTime,
501 strcat(subject, "Your Message");
504 strcat(subject, "Your Message Sent on ");
505 strcat(subject, *(sent[0]));
507 newsend->setHeader("Subject", subject);
510 // Get the BE store of header. It may contain newlines or
511 // tab chars which can munge the scrolling list's display!
513 const char * orig = *(value[0]);
519 // Check if BE store contains the funky chars.
521 for (fc = 0, orig_length = strlen(orig),
522 tmp_subj = (char *) orig;
535 subject = new char[fc+6];
537 if (strncasecmp(orig, "Re:", 3)) {
538 strcpy(subject, "Re: ");
544 strncat((char *)subject, orig, fc);
546 newsend->setHeader("Subject", subject);
549 newsend->setTitle(subject);
550 newsend->setIconTitle(subject);
554 Display_entire_msg(msgno, newsend, "indent");
555 newsend->get_editor()->textEditor()->set_to_top();
557 appendSignature(newsend);
558 newsend->setInputFocus(1);
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(
571 RoamMenuWindow *window,
573 ) : ComposeFamily( name, label, active, window )
578 // For each message selected, reply to everybody.
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();
590 char *currentCcValue;
591 SendMsgDialog *newsend;
592 DtMailGenDialog * dialog;
593 DtMail::Message *msg;
594 DtMail::Envelope *env;
597 // Initialize the mail_error.
601 if ( msgList = _menuwindow->list()->selected() )
602 for ( int k = 0; k < msgList->length(); k++ ) {
603 DtMailValueSeq value ;
605 tmpMS = msgList->at(k);
606 msgno = tmpMS->message_handle;
607 newsend = theCompose.getWin();
608 if ( newsend == NULL ) {
609 dialog = _parent->genDialog();
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);
616 msg = mbox->getMessage(error, msgno);
617 env = msg->getEnvelope(error);
621 DtMailMessageToReply,
631 char * addr_str = valueToAddrString(value);
632 newsend->setHeader("To", addr_str);
637 DtMailMessageSubject,
640 if ( error.isSet() ) {
641 subject = new char[200];
642 strcpy(subject, "Re: ");
644 env->getHeader(error,
645 DtMailMessageSentTime,
649 strcat(subject, "Your Message");
652 strcat(subject, "Your Message Sent on ");
653 strcat(subject, *(sent[0]));
655 newsend->setHeader("Subject", subject);
657 // Get the BE store of header. It may contain newlines or
658 // tab chars which can munge the scrolling list's display!
660 const char * orig = *(value[0]);
667 // Check if BE store contains the funky chars.
669 for (fc = 0, orig_length = strlen(orig),
670 tmp_subj = (char *)orig;
683 subject = new char[fc+6];
685 if (strncasecmp(orig, "Re:", 3)) {
686 strcpy(subject, "Re: ");
692 strncat((char *)subject, orig, fc);
694 newsend->setHeader("Subject", subject);
699 DtMailMessageCcReply,
702 if (!error.isSet()) {
703 // Strip out newlines from the cc line. They *may* be
705 currentCcValue = valueToAddrString(value);
707 newsend->setHeader("Cc", currentCcValue);
708 delete [] currentCcValue ;
711 newsend->setTitle(subject);
712 newsend->setIconTitle(subject);
716 Display_entire_msg(msgno, newsend, "indent");
717 newsend->get_editor()->textEditor()->set_to_top();
719 appendSignature(newsend);
720 newsend->setInputFocus(1);
724 TemplateCmd::TemplateCmd(char *name,
727 SendMsgDialog * compose,
729 : NoUndoCmd(name, label, active)
733 if (*file != '/' && *file != '~') {
734 // Relative path. Should be relative to home directory
735 _file = (char *)malloc(strlen(file) + 4);
741 _file = strdup(file);
745 TemplateCmd::~TemplateCmd(void)
754 DtMail::Session * d_session = theRoamApp.session()->session();
755 DtMailGenDialog * dialog = _compose->genDialog();
758 char * fullpath = d_session->expandPath(error, _file);
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
764 int fd = SafeOpen(fullpath, O_RDONLY);
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);
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);
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)));
790 mbuf.size = buf.st_size;
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);
797 mbuf.buffer = mmap(0, map_size, PROT_READ, MAP_PRIVATE, fd, 0);
799 if (mbuf.buffer == (char *)-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);
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);
818 delete [] mbuf.buffer;
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.
827 DtMail::Message * msg = d_session->messageConstruct(error,
834 _compose->get_editor()->textEditor()->append_to_contents((char *)mbuf.buffer,
838 _compose->loadHeaders(msg, DTM_TRUE);
840 DtMail::BodyPart * bp = msg->getFirstBodyPart(error);
841 if (error.isNotSet()) {
842 const void * contents;
843 unsigned long length;
845 bp->getContents(error,
853 _compose->get_editor()->textEditor()->append_to_contents(
854 (char *)contents, length);
864 munmap((char *)mbuf.buffer, map_size);
870 HideShowCmd::HideShowCmd(char *name,
873 SendMsgDialog * compose,
875 : NoUndoCmd(name, (char *)widgetlabel, active)
878 _header = strdup(label);
881 HideShowCmd::~HideShowCmd(void)
889 HideShowCmd::doit(void)
891 _compose->changeHeaderState(_header);