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 libraries 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 )
343 // Put up a blank compose window.
347 SendMsgDialog * newsend = theCompose.getWin();
348 if (newsend == NULL) {
349 DtMailGenDialog * dialog = _parent->genDialog();
351 dialog->setToErrorDialog(GETMSG(DT_catd, 1, 203, "Mailer"),
352 GETMSG(DT_catd, 1, 204, "Unable to create a compose window."));
353 char * helpId = DTMAILHELPNOCOMPOSE;
354 int answer = dialog->post_and_return(helpId);
357 appendSignature(newsend);
360 // Container menu "Compose==>New, Include All" and "Compose==>Forward Message"
361 // The last parameter is a switch for "include" or "forward" format.
362 ForwardCmd::ForwardCmd(
366 RoamMenuWindow *window,
368 ) : ComposeFamily(name, label, active, window)
374 // Forward or Include selected messages.
375 // For Include message(s), all Compose window header fields are left blank.
376 // For Forward message(s), the Compose window "Subject" header field is filled
377 // with the subject of the last selected message.
381 FORCE_SEGV_DECL(MsgHndArray, msgList);
382 FORCE_SEGV_DECL(MsgStruct, tmpMS);
383 DtMailMessageHandle msgno;
385 // Get a Compose window.
386 SendMsgDialog *newsend = theCompose.getWin();
387 if ( newsend == NULL ) {
388 DtMailGenDialog * dialog = _parent->genDialog();
390 dialog->setToErrorDialog(GETMSG(DT_catd, 1, 205, "Mailer"),
391 GETMSG(DT_catd, 1, 206, "Unable to create a compose window."));
392 char * helpId = DTMAILHELPNOCOMPOSE;
393 int answer = dialog->post_and_return(helpId);
396 // Put the signature above the message.
398 appendSignature(newsend);
400 // For Forwarding subject
401 DtMail::MailBox * mbox = _menuwindow->mailbox();
402 DtMail::Message * msg;
403 DtMail::Envelope * env;
404 DtMailValueSeq value;
407 // For each selected message, put it in the Compose window.
408 if ( msgList = _menuwindow->list()->selected() ) {
409 for ( int k = 0; k < msgList->length(); k++ ) {
410 tmpMS = msgList->at(k);
411 msgno = tmpMS->message_handle;
413 msg = mbox->getMessage(error, msgno);
414 env = msg->getEnvelope(error);
416 env->getHeader(error, DtMailMessageSubject, DTM_TRUE, value);
417 if (!error.isSet()) {
418 const char *subject = *(value[0]);
419 newsend->setHeader("Subject", subject);
420 newsend->setTitle((char*) subject);
421 newsend->setIconTitle((char*) subject);
423 Display_entire_msg(msgno, newsend, "forward");
425 Display_entire_msg(msgno, newsend, "indent");
429 newsend->get_editor()->textEditor()->set_to_top();
432 // Container menu "Compose==>Reply to Semder" and
433 // "Compose==>Reply to Sender, Include"
434 // The last parameter is a switch for including the selected message or not.
439 RoamMenuWindow *window,
441 ) : ComposeFamily ( name, label, active, window )
447 // For each message selected, reply to sender.
451 FORCE_SEGV_DECL(MsgHndArray, msgList);
452 FORCE_SEGV_DECL(MsgStruct, tmpMS);
453 DtMailMessageHandle msgno;
454 FORCE_SEGV_DECL(char, from);
455 FORCE_SEGV_DECL(char, subject);
456 FORCE_SEGV_DECL(char, cc);
458 DtMail::MailBox * mbox = _menuwindow->mailbox();
460 // Initialize the error.
463 if (msgList = _menuwindow->list()->selected())
465 for ( int i=0; i < msgList->length(); i++ ) {
466 tmpMS = msgList->at(i);
467 msgno = tmpMS->message_handle;
468 SendMsgDialog *newsend = theCompose.getWin();
469 if ( newsend == NULL ) {
470 DtMailGenDialog * dialog = _parent->genDialog();
472 dialog->setToErrorDialog(GETMSG(DT_catd, 1, 207, "Mailer"),
473 GETMSG(DT_catd, 1, 208, "Unable to create a compose window."));
474 char * helpId = DTMAILHELPNOCOMPOSE;
475 int answer = dialog->post_and_return(helpId);
477 XmUpdateDisplay( newsend->baseWidget() );
479 DtMail::Message * msg = mbox->getMessage(error, msgno);
480 DtMail::Envelope * env = msg->getEnvelope(error);
482 DtMailValueSeq value;
484 env->getHeader(error, DtMailMessageSender, DTM_TRUE, value);
486 newsend->setHeader("To", "nobody@nowhere");
489 char * addr_str = valueToAddrString(value);
490 newsend->setHeader("To", addr_str);
495 env->getHeader(error, DtMailMessageSubject, DTM_TRUE, value);
497 subject = new char[200];
498 strcpy(subject, "Re: ");
500 env->getHeader(error,
501 DtMailMessageSentTime,
505 strcat(subject, "Your Message");
508 strcat(subject, "Your Message Sent on ");
509 strcat(subject, *(sent[0]));
511 newsend->setHeader("Subject", subject);
514 // Get the BE store of header. It may contain newlines or
515 // tab chars which can munge the scrolling list's display!
517 const char * orig = *(value[0]);
523 // Check if BE store contains the funky chars.
525 for (fc = 0, orig_length = strlen(orig),
526 tmp_subj = (char *) orig;
539 subject = new char[fc+6];
541 if (strncasecmp(orig, "Re:", 3)) {
542 strcpy(subject, "Re: ");
548 strncat((char *)subject, orig, fc);
550 newsend->setHeader("Subject", subject);
553 newsend->setTitle(subject);
554 newsend->setIconTitle(subject);
558 Display_entire_msg(msgno, newsend, "indent");
559 newsend->get_editor()->textEditor()->set_to_top();
561 appendSignature(newsend);
562 newsend->setInputFocus(1);
569 // Container menu "Compose==>Reply to All" and "Compose==>Reply to All, Include"
570 // The last parameter is a switch for including the selected message or not.
571 ReplyAllCmd::ReplyAllCmd(
575 RoamMenuWindow *window,
577 ) : ComposeFamily( name, label, active, window )
583 // For each message selected, reply to everybody.
587 FORCE_SEGV_DECL(MsgHndArray, msgList);
588 FORCE_SEGV_DECL(MsgStruct, tmpMS);
589 FORCE_SEGV_DECL(char, subject);
590 FORCE_SEGV_DECL(char, to);
591 FORCE_SEGV_DECL(char, buffer);
592 DtMailMessageHandle msgno;
593 DtMail::MailBox *mbox = _menuwindow->mailbox();
595 char *currentCcValue;
596 SendMsgDialog *newsend;
597 DtMailGenDialog * dialog;
598 DtMail::Message *msg;
599 DtMail::Envelope *env;
602 // Initialize the mail_error.
606 if ( msgList = _menuwindow->list()->selected() )
607 for ( int k = 0; k < msgList->length(); k++ ) {
608 DtMailValueSeq value ;
610 tmpMS = msgList->at(k);
611 msgno = tmpMS->message_handle;
612 newsend = theCompose.getWin();
613 if ( newsend == NULL ) {
614 dialog = _parent->genDialog();
616 dialog->setToErrorDialog(GETMSG(DT_catd, 1, 209, "Mailer"),
617 GETMSG(DT_catd, 1, 210, "Unable to create a compose window."));
618 char * helpId = DTMAILHELPNOCOMPOSE;
619 int answer = dialog->post_and_return(helpId);
621 msg = mbox->getMessage(error, msgno);
622 env = msg->getEnvelope(error);
626 DtMailMessageToReply,
636 char * addr_str = valueToAddrString(value);
637 newsend->setHeader("To", addr_str);
642 DtMailMessageSubject,
645 if ( error.isSet() ) {
646 subject = new char[200];
647 strcpy(subject, "Re: ");
649 env->getHeader(error,
650 DtMailMessageSentTime,
654 strcat(subject, "Your Message");
657 strcat(subject, "Your Message Sent on ");
658 strcat(subject, *(sent[0]));
660 newsend->setHeader("Subject", subject);
662 // Get the BE store of header. It may contain newlines or
663 // tab chars which can munge the scrolling list's display!
665 const char * orig = *(value[0]);
672 // Check if BE store contains the funky chars.
674 for (fc = 0, orig_length = strlen(orig),
675 tmp_subj = (char *)orig;
688 subject = new char[fc+6];
690 if (strncasecmp(orig, "Re:", 3)) {
691 strcpy(subject, "Re: ");
697 strncat((char *)subject, orig, fc);
699 newsend->setHeader("Subject", subject);
704 DtMailMessageCcReply,
707 if (!error.isSet()) {
708 // Strip out newlines from the cc line. They *may* be
710 currentCcValue = valueToAddrString(value);
712 newsend->setHeader("Cc", currentCcValue);
713 delete [] currentCcValue ;
716 newsend->setTitle(subject);
717 newsend->setIconTitle(subject);
721 Display_entire_msg(msgno, newsend, "indent");
722 newsend->get_editor()->textEditor()->set_to_top();
724 appendSignature(newsend);
725 newsend->setInputFocus(1);
729 TemplateCmd::TemplateCmd(char *name,
732 SendMsgDialog * compose,
734 : NoUndoCmd(name, label, active)
738 if (*file != '/' && *file != '~') {
739 // Relative path. Should be relative to home directory
740 _file = (char *)malloc(strlen(file) + 4);
746 _file = strdup(file);
750 TemplateCmd::~TemplateCmd(void)
759 DtMail::Session * d_session = theRoamApp.session()->session();
760 DtMailGenDialog * dialog = _compose->genDialog();
763 char * fullpath = d_session->expandPath(error, _file);
765 // Map the file and try to parse it as a message. If it is a message,
766 // then load it with headers. Otherwise, throw everything into the
769 int fd = SafeOpen(fullpath, O_RDONLY);
771 dialog->setToErrorDialog(GETMSG(DT_catd, 1, 211, "Mailer"),
772 GETMSG(DT_catd, 1, 212, "The template does not exist."));
773 char * helpId = DTMAILHELPNOTEMPLATE;
774 int answer = dialog->post_and_return(helpId);
780 if (SafeFStat(fd, &buf) < 0) {
781 dialog->setToErrorDialog(GETMSG(DT_catd, 1, 213, "Mailer"),
782 GETMSG(DT_catd, 1, 214, "The template appears to be corrupt."));
783 char * helpId = DTMAILHELPCORRUPTTEMPLATE;
784 int answer = dialog->post_and_return(helpId);
790 int page_size = (int)sysconf(_SC_PAGESIZE);
791 size_t map_size = (size_t) (buf.st_size +
792 (page_size - (buf.st_size % page_size)));
795 mbuf.size = buf.st_size;
797 // This version of mmap does NOT allow requested length to be
798 // greater than the file size ... in contradiction to the
799 // documentation (don't round up).
800 mbuf.buffer = mmap(0, buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
802 mbuf.buffer = mmap(0, map_size, PROT_READ, MAP_PRIVATE, fd, 0);
804 if (mbuf.buffer == (char *)-1) {
806 mbuf.buffer = new char[mbuf.size];
807 if (mbuf.buffer == NULL) {
808 dialog->setToErrorDialog(GETMSG(DT_catd, 1, 215, "Mailer"),
809 GETMSG(DT_catd, 1, 216, "There is not enough memory to load the template."));
810 char * helpId = DTMAILHELPNOMEMTEMPLATE;
811 int answer = dialog->post_and_return(helpId);
817 if (SafeRead(fd, mbuf.buffer, (unsigned int)mbuf.size) < mbuf.size) {
818 dialog->setToErrorDialog(GETMSG(DT_catd, 1, 217, "Mailer"),
819 GETMSG(DT_catd, 1, 218, "The template appears to be corrupt."));
820 char * helpId = DTMAILHELPERROR;
821 int answer = dialog->post_and_return(helpId);
823 delete [] mbuf.buffer;
829 // Now we ask the library to parse it. If this fails for any reason, this
830 // is not a message, so we give up.
832 DtMail::Message * msg = d_session->messageConstruct(error,
839 _compose->get_editor()->textEditor()->append_to_contents((char *)mbuf.buffer,
843 _compose->loadHeaders(msg, DTM_TRUE);
845 DtMail::BodyPart * bp = msg->getFirstBodyPart(error);
846 if (error.isNotSet()) {
847 const void * contents;
848 unsigned long length;
850 bp->getContents(error,
858 _compose->get_editor()->textEditor()->append_to_contents(
859 (char *)contents, length);
869 munmap((char *)mbuf.buffer, map_size);
875 HideShowCmd::HideShowCmd(char *name,
878 SendMsgDialog * compose,
880 : NoUndoCmd(name, (char *)widgetlabel, active)
883 _header = strdup(label);
886 HideShowCmd::~HideShowCmd(void)
894 HideShowCmd::doit(void)
896 _compose->changeHeaderState(_header);