/* * CDE - Common Desktop Environment * * Copyright (c) 1993-2012, The Open Group. All rights reserved. * * These libraries and programs are free software; you can * redistribute them and/or modify them under the terms of the GNU * Lesser General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. * * These libraries and programs are distributed in the hope that * they will be useful, but WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public * License along with these librararies and programs; if not, write * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth * Floor, Boston, MA 02110-1301 USA */ /* *+SNOTICE * * $TOG: ComposeCmds.C /main/11 1998/10/21 17:23:13 mgreess $ * * RESTRICTED CONFIDENTIAL INFORMATION: * * The information in this document is subject to special * restrictions in a confidential disclosure agreement between * HP, IBM, Sun, USL, SCO and Univel. Do not distribute this * document outside HP, IBM, Sun, USL, SCO, or Univel without * Sun's specific written approval. This document and all copies * and derivative works thereof must be returned or destroyed at * Sun's request. * * Copyright 1993 Sun Microsystems, Inc. All rights reserved. * *+ENOTICE */ #include #include #include #if defined(NEED_MMAP_WRAPPER) extern "C" { #endif #include #if defined(NEED_MMAP_WRAPPER) } #endif #include #include #include #include #include #include #include #include #include
#include
#include #include "RoamMenuWindow.h" #include "SendMsgDialog.h" #include "Undelete.hh" #include "RoamCmds.h" #include "ComposeCmds.hh" #include "Application.h" #include "RoamApp.h" #include "DtMailWDM.hh" #include "FindDialog.h" #include "MsgScrollingList.hh" #include "MsgHndArray.hh" #include "MemUtils.hh" #include "MailMsg.h" #include "EUSDebug.hh" #include "DtMailGenDialog.hh" #include "DtMailHelp.hh" #include #include "Help.hh" #include
#include "Attachment.h" #include "str_utils.h" ComposeFamily::ComposeFamily(char *name, char *label, int active, RoamMenuWindow *window) : RoamCmd(name, label, active, window) { _parent = window; } #ifndef CAN_INLINE_VIRTUALS ComposeFamily::~ComposeFamily( void ) { } #endif /* ! CAN_INLINE_VIRTUALS */ // Append a formatted message to Compose's Text area. // This routine is essentially the same as MsgScollingList::display_message() // except for two major differences: // 1. No RoamMenuWindow reference (so that Compose can be standalone). // 2. Indent string can be used for "include" and "forward". void ComposeFamily::Display_entire_msg(DtMailMessageHandle msgno, SendMsgDialog *compose, char *format ) { DtMailEnv error; int num_bodyParts; DtMail::MailBox *mbox = _menuwindow->mailbox(); DtMail::Message *msg = mbox->getMessage(error, msgno); DtMail::Envelope *env = msg->getEnvelope(error); DtMail::BodyPart *tmpBP = NULL; DtMailBuffer tmpBuffer; void *buffer = NULL; unsigned long size = 0; Editor::InsertFormat ins_format = Editor::IF_NONE; Editor::BracketFormat brackets = Editor::BF_NONE; // Do not need to wrap "include", "forward", and "indent" with // catgets(). if ( strcmp(format, "include") == 0 ) { ins_format = Editor::IF_BRACKETED; brackets = Editor::BF_INCLUDE; } else if ( strcmp(format, "forward") == 0 ) { ins_format = Editor::IF_BRACKETED; brackets = Editor::BF_FORWARD; } else if ( strcmp(format, "indent") == 0 ) { ins_format = Editor::IF_INDENTED; } // Get the editor to display the body of message with the appropriate // insert/bracket formatting. // We only include the first body part of the message. Attachments, // etc. are "FORWARD"-ed but not "INCLUDE"-ed char * status_string; DtMailBoolean firstBPHandled = compose->get_editor()->textEditor()->set_message( msg, &status_string, Editor::HF_ABBREV, ins_format, brackets); // Now need to handle the unhandled body parts of the message. num_bodyParts = msg->getBodyCount(error); if (error.isSet()) { // do something } if (strcmp(format, "forward") == 0) { // If the message has attachments, then let the attach pane // handle attachments but not the first bodyPart (which has // already been handled here). if ((num_bodyParts > 1) || (!firstBPHandled)) { tmpBP = msg->getFirstBodyPart(error); if (firstBPHandled) { // The first bodyPart has already been handled. // The others, beginning from the second, need to be parsed // and put into the attachPane. compose->setInclMsgHnd(msg, TRUE); tmpBP = msg->getNextBodyPart(error, tmpBP); } else { // The first bodyPart was not handled. // It may not have been of type text. // The attachment pane needs to handle all the bodyParts // beginning with the first. compose->setInclMsgHnd(msg, FALSE); } char *name; while (tmpBP != NULL) { tmpBP->getContents( error, (const void **) &tmpBuffer.buffer, &tmpBuffer.size, NULL, &name, NULL, NULL); if (error.isSet()) { // Do something } // It's possible for an attachment to not have a name. if (!name) { name = "NoName"; } compose->add_att(name, tmpBuffer); tmpBP = msg->getNextBodyPart(error, tmpBP); if (error.isSet()) { // do something } if (strcmp(name, "NoName") != 0) { free(name); } } if (error.isSet()) { // do something } // Need to call this after calling parseAttachments(). compose->get_editor()->manageAttachArea(); // This message has attachment and is being included/forwarded, // so need to fill the Compose Message Handle with attachment // BodyParts. // See function for further details. // compose->updateMsgHndAtt(); } } else { // If the message has attachments, then let the attach pane // handle attachments but not the first bodyPart (which has // already been handled here). if ((num_bodyParts > 1) || (!firstBPHandled)) { char *att; Editor *editor = compose->get_editor()->textEditor(); att = GETMSG( DT_catd, 1, 255, "------------------ Attachments ------------------\n"); tmpBP = msg->getFirstBodyPart(error); if (firstBPHandled) tmpBP = msg->getNextBodyPart(error, tmpBP); editor->append_to_contents(att, strlen(att)); while (tmpBP != NULL) { editor->set_attachment(tmpBP, ins_format, brackets); tmpBP = msg->getNextBodyPart(error, tmpBP); if (error.isSet()) { // do something } } } } // Leave it up to check point routine for update or do it now??? compose->updateMsgHnd(); } void ComposeFamily::appendSignature(SendMsgDialog * compose) { DtMailEnv error; DtMail::Session * d_session = theRoamApp.session()->session(); DtMail::MailRc * mail_rc = d_session->mailRc(error); const char * value = NULL; mail_rc->getValue(error, "signature", &value); if (error.isSet()) { return; } char * fullpath = d_session->expandPath(error, value); compose->get_editor()->textEditor()->append_to_contents(fullpath); if (NULL != fullpath) free((void*) fullpath); if (NULL != NULL) free((void*) value); compose->get_editor()->textEditor()->set_to_top(); } char * ComposeFamily::valueToAddrString(DtMailValueSeq & value) { int max_len = 0; for (int count = 0; count < value.length(); count++) { max_len += strlen(*(value[count])); } char * str = new char[max_len + count + 1]; str[0] = 0; DtMailBoolean need_comma = DTM_FALSE; for (int cat = 0; cat < value.length(); cat++) { DtMailValue * val = value[cat]; DtMailAddressSeq *addr_seq = val->toAddress(); for (int ad = 0; ad < addr_seq->length(); ad++) { DtMailValueAddress * addr = (*addr_seq)[ad]; // Deal with mail address parser shortcomings if ( strcmp(addr->dtm_address, ",") == 0 ) continue ; if (need_comma) { strcat(str, ", "); } need_comma = DTM_TRUE; strcat(str, addr->dtm_address); } delete addr_seq; } return(str); } // Container menu "Compose==>New Message" ComposeCmd::ComposeCmd( char *name, char *label, int active, RoamMenuWindow *window ) : ComposeFamily( name, label, active, window ) { } // Put up a blank compose window. void ComposeCmd::doit() { SendMsgDialog * newsend = theCompose.getWin(); if (newsend == NULL) { DtMailGenDialog * dialog = _parent->genDialog(); dialog->setToErrorDialog(GETMSG(DT_catd, 1, 203, "Mailer"), GETMSG(DT_catd, 1, 204, "Unable to create a compose window.")); char * helpId = DTMAILHELPNOCOMPOSE; int answer = dialog->post_and_return(helpId); } appendSignature(newsend); } // Container menu "Compose==>New, Include All" and "Compose==>Forward Message" // The last parameter is a switch for "include" or "forward" format. ForwardCmd::ForwardCmd( char *name, char *label, int active, RoamMenuWindow *window, int forward ) : ComposeFamily(name, label, active, window) { _forward = forward; } // Forward or Include selected messages. // For Include message(s), all Compose window header fields are left blank. // For Forward message(s), the Compose window "Subject" header field is filled // with the subject of the last selected message. void ForwardCmd::doit() { FORCE_SEGV_DECL(MsgHndArray, msgList); FORCE_SEGV_DECL(MsgStruct, tmpMS); DtMailMessageHandle msgno; // Get a Compose window. SendMsgDialog *newsend = theCompose.getWin(); if ( newsend == NULL ) { DtMailGenDialog * dialog = _parent->genDialog(); dialog->setToErrorDialog(GETMSG(DT_catd, 1, 205, "Mailer"), GETMSG(DT_catd, 1, 206, "Unable to create a compose window.")); char * helpId = DTMAILHELPNOCOMPOSE; int answer = dialog->post_and_return(helpId); } // Put the signature above the message. // appendSignature(newsend); // For Forwarding subject DtMail::MailBox * mbox = _menuwindow->mailbox(); DtMail::Message * msg; DtMail::Envelope * env; DtMailValueSeq value; DtMailEnv error; // For each selected message, put it in the Compose window. if ( msgList = _menuwindow->list()->selected() ) { for ( int k = 0; k < msgList->length(); k++ ) { tmpMS = msgList->at(k); msgno = tmpMS->message_handle; if ( _forward ) { msg = mbox->getMessage(error, msgno); env = msg->getEnvelope(error); value.clear(); env->getHeader(error, DtMailMessageSubject, DTM_TRUE, value); if (!error.isSet()) { const char *subject = *(value[0]); newsend->setHeader("Subject", subject); newsend->setTitle((char*) subject); newsend->setIconTitle((char*) subject); } Display_entire_msg(msgno, newsend, "forward"); } else { Display_entire_msg(msgno, newsend, "indent"); } } } newsend->get_editor()->textEditor()->set_to_top(); } // Container menu "Compose==>Reply to Semder" and // "Compose==>Reply to Sender, Include" // The last parameter is a switch for including the selected message or not. ReplyCmd::ReplyCmd ( char *name, char *label, int active, RoamMenuWindow *window, int include ) : ComposeFamily ( name, label, active, window ) { _include = include; } // For each message selected, reply to sender. void ReplyCmd::doit() { FORCE_SEGV_DECL(MsgHndArray, msgList); FORCE_SEGV_DECL(MsgStruct, tmpMS); DtMailMessageHandle msgno; FORCE_SEGV_DECL(char, from); FORCE_SEGV_DECL(char, subject); FORCE_SEGV_DECL(char, cc); DtMailEnv error; DtMail::MailBox * mbox = _menuwindow->mailbox(); // Initialize the error. error.clear(); if (msgList = _menuwindow->list()->selected()) { for ( int i=0; i < msgList->length(); i++ ) { tmpMS = msgList->at(i); msgno = tmpMS->message_handle; SendMsgDialog *newsend = theCompose.getWin(); if ( newsend == NULL ) { DtMailGenDialog * dialog = _parent->genDialog(); dialog->setToErrorDialog(GETMSG(DT_catd, 1, 207, "Mailer"), GETMSG(DT_catd, 1, 208, "Unable to create a compose window.")); char * helpId = DTMAILHELPNOCOMPOSE; int answer = dialog->post_and_return(helpId); } XmUpdateDisplay( newsend->baseWidget() ); DtMail::Message * msg = mbox->getMessage(error, msgno); DtMail::Envelope * env = msg->getEnvelope(error); DtMailValueSeq value; env->getHeader(error, DtMailMessageSender, DTM_TRUE, value); if (error.isSet()) { newsend->setHeader("To", "nobody@nowhere"); } else { char * addr_str = valueToAddrString(value); newsend->setHeader("To", addr_str); delete [] addr_str; } value.clear(); env->getHeader(error, DtMailMessageSubject, DTM_TRUE, value); if (error.isSet()) { subject = new char[200]; strcpy(subject, "Re: "); DtMailValueSeq sent; env->getHeader(error, DtMailMessageSentTime, DTM_TRUE, sent); if (error.isSet()) { strcat(subject, "Your Message"); } else { strcat(subject, "Your Message Sent on "); strcat(subject, *(sent[0])); } newsend->setHeader("Subject", subject); } else { // Get the BE store of header. It may contain newlines or // tab chars which can munge the scrolling list's display! const char * orig = *(value[0]); int fc; int orig_length; char *tmp_subj; // Check if BE store contains the funky chars. for (fc = 0, orig_length = strlen(orig), tmp_subj = (char *) orig; fc < orig_length; fc++, tmp_subj++) { char c = *tmp_subj; if ((c == '\n') || (c == '\t') || (c == '\r')) { break; } } subject = new char[fc+6]; if (strncasecmp(orig, "Re:", 3)) { strcpy(subject, "Re: "); } else { *subject = 0; } strncat((char *)subject, orig, fc); newsend->setHeader("Subject", subject); } newsend->setTitle(subject); newsend->setIconTitle(subject); delete [] subject; if ( _include ) { Display_entire_msg(msgno, newsend, "indent"); newsend->get_editor()->textEditor()->set_to_top(); } appendSignature(newsend); newsend->setInputFocus(1); } delete msgList; } } // Container menu "Compose==>Reply to All" and "Compose==>Reply to All, Include" // The last parameter is a switch for including the selected message or not. ReplyAllCmd::ReplyAllCmd( char *name, char *label, int active, RoamMenuWindow *window, int include ) : ComposeFamily( name, label, active, window ) { _include = include; } // For each message selected, reply to everybody. void ReplyAllCmd::doit() { FORCE_SEGV_DECL(MsgHndArray, msgList); FORCE_SEGV_DECL(MsgStruct, tmpMS); FORCE_SEGV_DECL(char, subject); FORCE_SEGV_DECL(char, to); FORCE_SEGV_DECL(char, buffer); DtMailMessageHandle msgno; DtMail::MailBox *mbox = _menuwindow->mailbox(); DtMailEnv error; char *currentCcValue; SendMsgDialog *newsend; DtMailGenDialog * dialog; DtMail::Message *msg; DtMail::Envelope *env; // Initialize the mail_error. error.clear(); if ( msgList = _menuwindow->list()->selected() ) for ( int k = 0; k < msgList->length(); k++ ) { DtMailValueSeq value ; tmpMS = msgList->at(k); msgno = tmpMS->message_handle; newsend = theCompose.getWin(); if ( newsend == NULL ) { dialog = _parent->genDialog(); dialog->setToErrorDialog(GETMSG(DT_catd, 1, 209, "Mailer"), GETMSG(DT_catd, 1, 210, "Unable to create a compose window.")); char * helpId = DTMAILHELPNOCOMPOSE; int answer = dialog->post_and_return(helpId); } msg = mbox->getMessage(error, msgno); env = msg->getEnvelope(error); env->getHeader( error, DtMailMessageToReply, DTM_TRUE, value); env->getHeader( error, DtMailMessageSender, DTM_TRUE, value); char * addr_str = valueToAddrString(value); newsend->setHeader("To", addr_str); delete [] addr_str; value.clear(); env->getHeader( error, DtMailMessageSubject, DTM_TRUE, value); if ( error.isSet() ) { subject = new char[200]; strcpy(subject, "Re: "); DtMailValueSeq sent; env->getHeader(error, DtMailMessageSentTime, DTM_TRUE, sent); if (error.isSet()) { strcat(subject, "Your Message"); } else { strcat(subject, "Your Message Sent on "); strcat(subject, *(sent[0])); } newsend->setHeader("Subject", subject); } else { // Get the BE store of header. It may contain newlines or // tab chars which can munge the scrolling list's display! const char * orig = *(value[0]); int fc = 0; int orig_length; char *tmp_subj; // Check if BE store contains the funky chars. for (fc = 0, orig_length = strlen(orig), tmp_subj = (char *)orig; fc < orig_length; fc++, tmp_subj++) { char c = *tmp_subj; if ((c == '\n') || (c == '\t') || (c == '\r')) { break; } } subject = new char[fc+6]; if (strncasecmp(orig, "Re:", 3)) { strcpy(subject, "Re: "); } else { *subject = 0; } strncat((char *)subject, orig, fc); newsend->setHeader("Subject", subject); } value.clear(); env->getHeader( error, DtMailMessageCcReply, DTM_TRUE, value); if (!error.isSet()) { // Strip out newlines from the cc line. They *may* be // present. currentCcValue = valueToAddrString(value); newsend->setHeader("Cc", currentCcValue); delete [] currentCcValue ; } newsend->setTitle(subject); newsend->setIconTitle(subject); delete [] subject; if ( _include ) { Display_entire_msg(msgno, newsend, "indent"); newsend->get_editor()->textEditor()->set_to_top(); } appendSignature(newsend); newsend->setInputFocus(1); } } TemplateCmd::TemplateCmd(char *name, char *label, int active, SendMsgDialog * compose, const char * file) : NoUndoCmd(name, label, active) { _compose = compose; if (*file != '/' && *file != '~') { // Relative path. Should be relative to home directory _file = (char *)malloc(strlen(file) + 4); if (_file != NULL) { strcpy(_file, "~/"); strcat(_file, file); } } else { _file = strdup(file); } } TemplateCmd::~TemplateCmd(void) { free(_file); } void TemplateCmd::doit() { DtMailEnv error; DtMail::Session * d_session = theRoamApp.session()->session(); DtMailGenDialog * dialog = _compose->genDialog(); DtMailBuffer mbuf; char * fullpath = d_session->expandPath(error, _file); // Map the file and try to parse it as a message. If it is a message, // then load it with headers. Otherwise, throw everything into the // editor. // int fd = SafeOpen(fullpath, O_RDONLY); if (fd < 0) { dialog->setToErrorDialog(GETMSG(DT_catd, 1, 211, "Mailer"), GETMSG(DT_catd, 1, 212, "The template does not exist.")); char * helpId = DTMAILHELPNOTEMPLATE; int answer = dialog->post_and_return(helpId); free(fullpath); return; } struct stat buf; if (SafeFStat(fd, &buf) < 0) { dialog->setToErrorDialog(GETMSG(DT_catd, 1, 213, "Mailer"), GETMSG(DT_catd, 1, 214, "The template appears to be corrupt.")); char * helpId = DTMAILHELPCORRUPTTEMPLATE; int answer = dialog->post_and_return(helpId); SafeClose(fd); free(fullpath); return; } int page_size = (int)sysconf(_SC_PAGESIZE); size_t map_size = (size_t) (buf.st_size + (page_size - (buf.st_size % page_size))); int free_buf = 0; mbuf.size = buf.st_size; #ifdef __osf__ // This version of mmap does NOT allow requested length to be // greater than the file size ... in contradiction to the // documentation (don't round up). mbuf.buffer = mmap(0, buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0); #else mbuf.buffer = mmap(0, map_size, PROT_READ, MAP_PRIVATE, fd, 0); #endif if (mbuf.buffer == (char *)-1) { free_buf = 1; mbuf.buffer = new char[mbuf.size]; if (mbuf.buffer == NULL) { dialog->setToErrorDialog(GETMSG(DT_catd, 1, 215, "Mailer"), GETMSG(DT_catd, 1, 216, "There is not enough memory to load the template.")); char * helpId = DTMAILHELPNOMEMTEMPLATE; int answer = dialog->post_and_return(helpId); SafeClose(fd); free(fullpath); return; } if (SafeRead(fd, mbuf.buffer, (unsigned int)mbuf.size) < mbuf.size) { dialog->setToErrorDialog(GETMSG(DT_catd, 1, 217, "Mailer"), GETMSG(DT_catd, 1, 218, "The template appears to be corrupt.")); char * helpId = DTMAILHELPERROR; int answer = dialog->post_and_return(helpId); SafeClose(fd); delete [] mbuf.buffer; free(fullpath); return; } } // Now we ask the library to parse it. If this fails for any reason, this // is not a message, so we give up. // DtMail::Message * msg = d_session->messageConstruct(error, DtMailBufferObject, &mbuf, NULL, NULL, NULL); if (error.isSet()) { _compose->get_editor()->textEditor()->append_to_contents((char *)mbuf.buffer, mbuf.size); } else { _compose->loadHeaders(msg, DTM_TRUE); DtMail::BodyPart * bp = msg->getFirstBodyPart(error); if (error.isNotSet()) { const void * contents; unsigned long length; bp->getContents(error, &contents, &length, NULL, NULL, NULL, NULL); _compose->get_editor()->textEditor()->append_to_contents( (char *)contents, length); } } free(fullpath); if (free_buf) { free(mbuf.buffer); } else { munmap((char *)mbuf.buffer, map_size); } SafeClose(fd); } HideShowCmd::HideShowCmd(char *name, char *widgetlabel, int active, SendMsgDialog * compose, const char * label) : NoUndoCmd(name, (char *)widgetlabel, active) { _compose = compose; _header = strdup(label); } HideShowCmd::~HideShowCmd(void) { if (_header) { free(_header); } } void HideShowCmd::doit(void) { _compose->changeHeaderState(_header); }