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)
294 for (count = 0; count < value.length(); count++) {
295 max_len += strlen(*(value[count]));
298 char * str = new char[max_len + count + 1];
301 DtMailBoolean need_comma = DTM_FALSE;
303 for (int cat = 0; cat < value.length(); cat++)
305 DtMailValue * val = value[cat];
306 DtMailAddressSeq *addr_seq = val->toAddress();
308 for (int ad = 0; ad < addr_seq->length(); ad++)
310 DtMailValueAddress * addr = (*addr_seq)[ad];
312 // Deal with mail address parser shortcomings
313 if ( strcmp(addr->dtm_address, ",") == 0 )
320 need_comma = DTM_TRUE;
322 strcat(str, addr->dtm_address);
332 // Container menu "Compose==>New Message"
333 ComposeCmd::ComposeCmd(
337 RoamMenuWindow *window
338 ) : ComposeFamily( name, label, active, window )
342 // Put up a blank compose window.
346 SendMsgDialog * newsend = theCompose.getWin();
347 if (newsend == NULL) {
348 DtMailGenDialog * dialog = _parent->genDialog();
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);
356 appendSignature(newsend);
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(
365 RoamMenuWindow *window,
367 ) : ComposeFamily(name, label, active, window)
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.
379 FORCE_SEGV_DECL(MsgHndArray, msgList);
380 FORCE_SEGV_DECL(MsgStruct, tmpMS);
381 DtMailMessageHandle msgno;
383 // Get a Compose window.
384 SendMsgDialog *newsend = theCompose.getWin();
385 if ( newsend == NULL ) {
386 DtMailGenDialog * dialog = _parent->genDialog();
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);
394 // Put the signature above the message.
396 appendSignature(newsend);
398 // For Forwarding subject
399 DtMail::MailBox * mbox = _menuwindow->mailbox();
400 DtMail::Message * msg;
401 DtMail::Envelope * env;
402 DtMailValueSeq value;
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;
411 msg = mbox->getMessage(error, msgno);
412 env = msg->getEnvelope(error);
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);
421 Display_entire_msg(msgno, newsend, "forward");
423 Display_entire_msg(msgno, newsend, "indent");
427 newsend->get_editor()->textEditor()->set_to_top();
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.
437 RoamMenuWindow *window,
439 ) : ComposeFamily ( name, label, active, window )
444 // For each message selected, reply to sender.
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);
455 DtMail::MailBox * mbox = _menuwindow->mailbox();
457 // Initialize the error.
460 if (msgList = _menuwindow->list()->selected())
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();
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);
474 XmUpdateDisplay( newsend->baseWidget() );
476 DtMail::Message * msg = mbox->getMessage(error, msgno);
477 DtMail::Envelope * env = msg->getEnvelope(error);
479 DtMailValueSeq value;
481 env->getHeader(error, DtMailMessageSender, DTM_TRUE, value);
483 newsend->setHeader("To", "nobody@nowhere");
486 char * addr_str = valueToAddrString(value);
487 newsend->setHeader("To", addr_str);
492 env->getHeader(error, DtMailMessageSubject, DTM_TRUE, value);
494 subject = new char[200];
495 strcpy(subject, "Re: ");
497 env->getHeader(error,
498 DtMailMessageSentTime,
502 strcat(subject, "Your Message");
505 strcat(subject, "Your Message Sent on ");
506 strcat(subject, *(sent[0]));
508 newsend->setHeader("Subject", subject);
511 // Get the BE store of header. It may contain newlines or
512 // tab chars which can munge the scrolling list's display!
514 const char * orig = *(value[0]);
520 // Check if BE store contains the funky chars.
522 for (fc = 0, orig_length = strlen(orig),
523 tmp_subj = (char *) orig;
536 subject = new char[fc+6];
538 if (strncasecmp(orig, "Re:", 3)) {
539 strcpy(subject, "Re: ");
545 strncat((char *)subject, orig, fc);
547 newsend->setHeader("Subject", subject);
550 newsend->setTitle(subject);
551 newsend->setIconTitle(subject);
555 Display_entire_msg(msgno, newsend, "indent");
556 newsend->get_editor()->textEditor()->set_to_top();
558 appendSignature(newsend);
559 newsend->setInputFocus(1);
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(
572 RoamMenuWindow *window,
574 ) : ComposeFamily( name, label, active, window )
579 // For each message selected, reply to everybody.
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();
591 char *currentCcValue;
592 SendMsgDialog *newsend;
593 DtMailGenDialog * dialog;
594 DtMail::Message *msg;
595 DtMail::Envelope *env;
598 // Initialize the mail_error.
602 if ( msgList = _menuwindow->list()->selected() )
603 for ( int k = 0; k < msgList->length(); k++ ) {
604 DtMailValueSeq value ;
606 tmpMS = msgList->at(k);
607 msgno = tmpMS->message_handle;
608 newsend = theCompose.getWin();
609 if ( newsend == NULL ) {
610 dialog = _parent->genDialog();
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);
617 msg = mbox->getMessage(error, msgno);
618 env = msg->getEnvelope(error);
622 DtMailMessageToReply,
632 char * addr_str = valueToAddrString(value);
633 newsend->setHeader("To", addr_str);
638 DtMailMessageSubject,
641 if ( error.isSet() ) {
642 subject = new char[200];
643 strcpy(subject, "Re: ");
645 env->getHeader(error,
646 DtMailMessageSentTime,
650 strcat(subject, "Your Message");
653 strcat(subject, "Your Message Sent on ");
654 strcat(subject, *(sent[0]));
656 newsend->setHeader("Subject", subject);
658 // Get the BE store of header. It may contain newlines or
659 // tab chars which can munge the scrolling list's display!
661 const char * orig = *(value[0]);
668 // Check if BE store contains the funky chars.
670 for (fc = 0, orig_length = strlen(orig),
671 tmp_subj = (char *)orig;
684 subject = new char[fc+6];
686 if (strncasecmp(orig, "Re:", 3)) {
687 strcpy(subject, "Re: ");
693 strncat((char *)subject, orig, fc);
695 newsend->setHeader("Subject", subject);
700 DtMailMessageCcReply,
703 if (!error.isSet()) {
704 // Strip out newlines from the cc line. They *may* be
706 currentCcValue = valueToAddrString(value);
708 newsend->setHeader("Cc", currentCcValue);
709 delete [] currentCcValue ;
712 newsend->setTitle(subject);
713 newsend->setIconTitle(subject);
717 Display_entire_msg(msgno, newsend, "indent");
718 newsend->get_editor()->textEditor()->set_to_top();
720 appendSignature(newsend);
721 newsend->setInputFocus(1);
725 TemplateCmd::TemplateCmd(char *name,
728 SendMsgDialog * compose,
730 : NoUndoCmd(name, label, active)
734 if (*file != '/' && *file != '~') {
735 // Relative path. Should be relative to home directory
736 _file = (char *)malloc(strlen(file) + 4);
742 _file = strdup(file);
746 TemplateCmd::~TemplateCmd(void)
755 DtMail::Session * d_session = theRoamApp.session()->session();
756 DtMailGenDialog * dialog = _compose->genDialog();
759 char * fullpath = d_session->expandPath(error, _file);
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
765 int fd = SafeOpen(fullpath, O_RDONLY);
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);
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);
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)));
791 mbuf.size = buf.st_size;
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);
798 mbuf.buffer = mmap(0, map_size, PROT_READ, MAP_PRIVATE, fd, 0);
800 if (mbuf.buffer == (char *)-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);
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);
819 delete [] mbuf.buffer;
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.
828 DtMail::Message * msg = d_session->messageConstruct(error,
835 _compose->get_editor()->textEditor()->append_to_contents((char *)mbuf.buffer,
839 _compose->loadHeaders(msg, DTM_TRUE);
841 DtMail::BodyPart * bp = msg->getFirstBodyPart(error);
842 if (error.isNotSet()) {
843 const void * contents;
844 unsigned long length;
846 bp->getContents(error,
854 _compose->get_editor()->textEditor()->append_to_contents(
855 (char *)contents, length);
865 munmap((char *)mbuf.buffer, map_size);
871 HideShowCmd::HideShowCmd(char *name,
874 SendMsgDialog * compose,
876 : NoUndoCmd(name, (char *)widgetlabel, active)
879 _header = strdup(label);
882 HideShowCmd::~HideShowCmd(void)
890 HideShowCmd::doit(void)
892 _compose->changeHeaderState(_header);