/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/NPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): */ #include "rosetta.h" #include "msg.h" #include "errcode.h" #include "error.h" #include "mime.h" #include "shist.h" #include "xlate.h" #include "libi18n.h" #include "xpgetstr.h" #include "msgcpane.h" #include "msgprefs.h" #include "msgcflds.h" #include "dirprefs.h" #include "msgurlq.h" #include "edt.h" // to invoke save on the html compose pane #include "mhtmlstm.h" #include "prefapi.h" #include "htmldlgs.h" #include "xp_qsort.h" #include "intl_csi.h" extern "C" { extern int MK_MSG_MSG_COMPOSITION; extern int MK_COMMUNICATIONS_ERROR; extern int MK_OUT_OF_MEMORY; extern int MK_MSG_EMPTY_MESSAGE; extern int MK_MSG_DOUBLE_INCLUDE; extern int MK_MSG_WHY_QUEUE_SPECIAL; extern int MK_MSG_WHY_QUEUE_SPECIAL_OLD; extern int MK_MSG_NOT_AS_SENT_FOLDER; extern int MK_MSG_MISSING_SUBJECT; HG65256 extern int MK_MSG_SEND; extern int MK_MSG_SEND_LATER; extern int MK_MSG_ATTACH_ETC; extern int MK_MSG_QUOTE_MESSAGE; extern int MK_MSG_FROM; extern int MK_MSG_REPLY_TO; extern int MK_MSG_MAIL_TO; extern int MK_MSG_MAIL_CC; extern int MK_MSG_MAIL_BCC; extern int MK_MSG_FILE_CC; extern int MK_MSG_POST_TO; extern int MK_MSG_FOLLOWUPS_TO; extern int MK_MSG_SUBJECT; extern int MK_MSG_ATTACHMENT; extern int MK_MSG_ATTACH_AS_TEXT; extern int MK_MSG_SAVE_DRAFT; extern int MK_ADDR_BOOK_CARD; extern int MK_MSG_ASK_HTML_MAIL; extern int MK_MSG_ASK_HTML_MAIL_TITLE; extern int MK_MSG_HTML_RECIPIENTS; extern int MK_MSG_HTML_RECIPIENTS_TITLE; extern int MK_MSG_EVERYONE; extern int MK_MSG_ENTER_NAME_FOR_TEMPLATE; #include "xp_help.h" } HG33234 #define ALL_HEADERS (MSG_FROM_HEADER_MASK | \ MSG_REPLY_TO_HEADER_MASK | \ MSG_TO_HEADER_MASK | \ MSG_CC_HEADER_MASK | \ MSG_BCC_HEADER_MASK | \ MSG_FCC_HEADER_MASK | \ MSG_NEWSGROUPS_HEADER_MASK | \ MSG_FOLLOWUP_TO_HEADER_MASK | \ MSG_SUBJECT_HEADER_MASK | \ MSG_ATTACHMENTS_HEADER_MASK) enum RecipientType { Address = 1, Domain = 2, Newsgroup = 3, GroupHierarchy = 4 }; class RecipientEntry : public MSG_ZapIt { public: RecipientEntry(const char* name, const char* description, RecipientType type, XP_Bool htmlok); ~RecipientEntry(); char* GetName() {return m_name;} char* GetDescription() {return m_description;} RecipientType GetType() {return m_type;} XP_Bool GetHTMLOk() {return m_htmlok;} XP_Bool GetNewHTMLOk() {return m_newhtmlok;} void SetNewOK(XP_Bool value); XP_Bool GetTouched() {return m_touched;} protected: char* m_name; char* m_description; RecipientType m_type; XP_Bool m_htmlok; XP_Bool m_newhtmlok; XP_Bool m_touched; }; RecipientEntry::RecipientEntry(const char* name, const char* description, RecipientType type, XP_Bool htmlok) { m_name = XP_STRDUP(name); m_description = XP_STRDUP(description); if (!m_description) { FREEIF(m_name); // Checking for name being NULL is the hack // used to see if we're out of memory. } m_type = type; m_htmlok = htmlok; } RecipientEntry::~RecipientEntry() { FREEIF(m_name); FREEIF(m_description); } void RecipientEntry::SetNewOK(XP_Bool value) { XP_ASSERT(!m_touched); m_touched = TRUE; m_newhtmlok = value; } class MSG_HTMLRecipients : public MSG_ZapIt { public: MSG_HTMLRecipients(); ~MSG_HTMLRecipients(); int AddOne(const char* name, const char* description, RecipientType type, XP_Bool htmlok); MSG_RecipientList* GetList(XP_Bool htmlok); int SetNewList(int32* notoklist, int32* oklist); char** GetChangedList(RecipientType type, XP_Bool htmlok); void FreeChangedList(char** list); int GetNum() {return m_num;} protected: RecipientEntry** m_list; int32 m_num; int32 m_max; MSG_RecipientList* m_generatedList[2]; }; MSG_HTMLRecipients::MSG_HTMLRecipients() { } MSG_HTMLRecipients::~MSG_HTMLRecipients() { delete m_generatedList[0]; delete m_generatedList[1]; for (int32 i=0 ; iGetType() == type && XP_STRCMP(m_list[i]->GetName(), name) == 0) return 0; } if (m_num >= m_max) { RecipientEntry** tmp = new RecipientEntry* [m_max + 10]; if (!tmp) return MK_OUT_OF_MEMORY; m_max += 10; for (i=0 ; iGetName()) { delete m_list[m_num]; return MK_OUT_OF_MEMORY; } m_num++; return 0; } MSG_RecipientList* MSG_HTMLRecipients::GetList(XP_Bool htmlok) { int32 i, j; if (m_generatedList[0] == NULL) { // Sort the entries in the list. Within a given type, we want to // keep things in the order they were generated, but they need to // be grouped by type. So, it's bubble-sort time. Whee... for (i=1 ; i 0 && m_list[j]->GetType() < m_list[j-1]->GetType(); j--) { RecipientEntry* tmp = m_list[j]; m_list[j] = m_list[j-1]; m_list[j-1] = tmp; } } m_generatedList[0] = new MSG_RecipientList [m_num + 1]; if (!m_generatedList[0]) return NULL; m_generatedList[1] = new MSG_RecipientList [m_num + 1]; if (!m_generatedList[1]) { delete [] m_generatedList[0]; return NULL; } int32 cur[2]; cur[0] = cur[1] = 0; for (i=0 ; iGetHTMLOk()); m_generatedList[w][cur[w]].name = m_list[i]->GetDescription(); m_generatedList[w][cur[w]].value = i; (cur[w])++; } for (i=0 ; i<2 ; i++) { m_generatedList[i][cur[i]].name = NULL; m_generatedList[i][cur[i]].value = -1; } } return m_generatedList[int(htmlok)]; } int MSG_HTMLRecipients::SetNewList(int32* notoklist, int32* oklist) { int32 i; #ifdef DEBUG for (i=0 ; iGetTouched()); } #endif for (int w=0 ; w<2 ; w++) { XP_Bool ok = (w == 1); int32* list = ok ? oklist : notoklist; XP_ASSERT(list); if (!list) continue; for ( ; *list >= 0 ; list++) { XP_ASSERT(*list < m_num); if (*list >= m_num) break; m_list[*list]->SetNewOK(ok); } } int status = 0; for (i=0 ; iGetTouched()); if (!m_list[i]->GetTouched()) { status = -1; } } return status; } char** MSG_HTMLRecipients::GetChangedList(RecipientType type, XP_Bool htmlok) { char** result = new char * [m_num + 1]; if (!result) return NULL; char** tmp = result; for (int32 i=0 ; iGetType() == type && m_list[i]->GetNewHTMLOk() == htmlok && m_list[i]->GetHTMLOk() != htmlok) { *tmp = m_list[i]->GetName(); tmp++; } } *tmp = NULL; return result; } void MSG_HTMLRecipients::FreeChangedList(char** list) { delete [] list; } static void msg_free_attachment_list(struct MSG_AttachmentData *list); static void msg_delete_attached_files(struct MSG_AttachedFile *attachments) { struct MSG_AttachedFile *tmp; if (!attachments) return; for (tmp = attachments; tmp->orig_url; tmp++) { FREEIF(tmp->orig_url); FREEIF(tmp->type); FREEIF(tmp->real_name); FREEIF(tmp->encoding); FREEIF(tmp->description); FREEIF(tmp->x_mac_type); FREEIF(tmp->x_mac_creator); if (tmp->file_name) { XP_FileRemove(tmp->file_name, xpFileToPost); XP_FREE(tmp->file_name); } } XP_FREEIF(attachments); } MSG_CompositionPane::MSG_CompositionPane(MWContext* context, MWContext* old_context, MSG_Prefs* prefs, MSG_CompositionFields* fields, MSG_Master* master) : MSG_Pane(context, master) { m_prefs = prefs; m_htmlaction = MSG_HTMLAskUser; Initialize(old_context, fields); } MSG_CompositionPane::MSG_CompositionPane(MWContext* context, MSG_Prefs* prefs, MSG_Master* master) : MSG_Pane(context, master) { m_prefs = prefs; } int MSG_CompositionPane::Initialize(MWContext* old_context, MSG_CompositionFields* fields) { m_print = new PrintSetup; InitializeHeaders(old_context, fields); m_visible_headers = GetInterestingHeaders(); m_deliver_mode = MSG_DeliverNow; m_haveAttachedVcard = FALSE; m_fields->SetForcePlainText(FALSE); // Coming into us, this field meant // "bring up the editor in plaintext // mode". Well, that's already been // done at this point. Now, we want // it to mean "convert this message // to plaintext on send". Which we // do only if DetermineHTMLAction() // tells us to. return 0; } MSG_CompositionPane::~MSG_CompositionPane() { // Don't interrupt if there's nothing to interrupt because we might lose // mocha messages. if (NET_AreThereActiveConnectionsForWindow(m_context)) msg_InterruptContext (m_context, FALSE); if (m_textContext != NULL) { msg_InterruptContext(m_textContext, TRUE); } msg_delete_attached_files (m_attachedFiles); FREEIF(m_defaultUrl); FREEIF(m_attachmentString); msg_free_attachment_list(m_attachData); delete m_print; m_print = NULL; #if XP_UNIX if (m_context) FE_DestroyMailCompositionContext(m_context); #endif //XP_UNIX m_context = NULL; delete m_fields; m_fields = NULL; delete m_initfields; m_initfields = NULL; delete m_htmlrecip; m_htmlrecip = NULL; FREEIF(m_quotedText); FREEIF(m_messageId); } MSG_PaneType MSG_CompositionPane::GetPaneType() { return MSG_COMPOSITIONPANE; } void MSG_CompositionPane::NotifyPrefsChange(NotifyCode) { // ###tw Write me! } char* MSG_CompositionPane::FigureBcc(XP_Bool newsBcc) { char* result = NULL; FREEIF(result); const char* tmp = GetPrefs()->GetDefaultHeaderContents( newsBcc ? MSG_NEWS_BCC_HEADER_MASK : MSG_BCC_HEADER_MASK); if (!GetPrefs()->GetDefaultBccSelf(newsBcc)) { result = XP_STRDUP(tmp ? tmp : ""); } else if (!tmp || !*tmp) { result = XP_STRDUP(FE_UsersMailAddress()); } else { result = PR_smprintf("%s, %s", FE_UsersMailAddress(), tmp); } return result; } MsgERR MSG_CompositionPane::GetCommandStatus(MSG_CommandType command, const MSG_ViewIndex* indices, int32 numindices, XP_Bool *selectable_pP, MSG_COMMAND_CHECK_STATE *selected_pP, const char **display_stringP, XP_Bool *plural_pP) { const char *display_string = 0; XP_Bool plural_p = FALSE; // N.B. default is TRUE, so you don't need to set it in each case XP_Bool selectable_p = TRUE; XP_Bool selected_p = FALSE; XP_Bool selected_used_p = FALSE; switch (command) { case MSG_AttachAsText: // the WinFE uses this for lots of update, so pretend we handle it. display_string = XP_GetString(MK_MSG_ATTACH_AS_TEXT); break; case MSG_SendMessage: display_string = XP_GetString(MK_MSG_SEND); if (m_attachmentInProgress) selectable_p = FALSE; break; case MSG_SendMessageLater: display_string = XP_GetString(MK_MSG_SEND_LATER); if (m_attachmentInProgress) selectable_p = FALSE; break; case MSG_SaveDraft: case MSG_SaveDraftThenClose: case MSG_SaveTemplate: display_string = XP_GetString(MK_MSG_SAVE_DRAFT); if (m_attachmentInProgress) selectable_p = FALSE; break; case MSG_Attach: display_string = XP_GetString(MK_MSG_ATTACH_ETC); break; case MSG_ShowFrom: display_string = XP_GetString(MK_MSG_FROM); selected_p = ShowingCompositionHeader(MSG_FROM_HEADER_MASK); selected_used_p = TRUE; break; case MSG_ShowReplyTo: display_string = XP_GetString(MK_MSG_REPLY_TO); selected_p = ShowingCompositionHeader(MSG_REPLY_TO_HEADER_MASK); selected_used_p = TRUE; break; case MSG_ShowTo: display_string = XP_GetString(MK_MSG_MAIL_TO); selected_p = ShowingCompositionHeader(MSG_TO_HEADER_MASK); selected_used_p = TRUE; break; case MSG_ShowCC: display_string = XP_GetString(MK_MSG_MAIL_CC); selected_p = ShowingCompositionHeader(MSG_CC_HEADER_MASK); selected_used_p = TRUE; break; case MSG_ShowBCC: display_string = XP_GetString(MK_MSG_MAIL_BCC); selected_p = ShowingCompositionHeader(MSG_BCC_HEADER_MASK); selected_used_p = TRUE; break; case MSG_ShowFCC: display_string = XP_GetString(MK_MSG_FILE_CC); selected_p = ShowingCompositionHeader(MSG_FCC_HEADER_MASK); selected_used_p = TRUE; break; case MSG_ShowPostTo: display_string = XP_GetString(MK_MSG_POST_TO); selected_p = ShowingCompositionHeader(MSG_NEWSGROUPS_HEADER_MASK); selected_used_p = TRUE; break; case MSG_ShowFollowupTo: display_string = XP_GetString(MK_MSG_FOLLOWUPS_TO); selected_p = ShowingCompositionHeader(MSG_FOLLOWUP_TO_HEADER_MASK); selected_used_p = TRUE; break; case MSG_ShowSubject: display_string = XP_GetString(MK_MSG_SUBJECT); selected_p = ShowingCompositionHeader(MSG_SUBJECT_HEADER_MASK); selected_used_p = TRUE; break; case MSG_ShowAttachments: display_string = XP_GetString(MK_MSG_ATTACHMENT); selected_p = ShowingCompositionHeader(MSG_ATTACHMENTS_HEADER_MASK); selected_used_p = TRUE; break; default: selectable_p = FALSE; return MSG_Pane::GetCommandStatus(command, indices, numindices, selectable_pP, selected_pP, display_stringP, plural_pP); } if (selectable_pP) *selectable_pP = selectable_p; if (selected_pP) { if (selected_used_p) { if (selected_p) *selected_pP = MSG_Checked; else *selected_pP = MSG_Unchecked; } else { *selected_pP = MSG_NotUsed; } } if (display_stringP) *display_stringP = display_string; if (plural_pP) *plural_pP = plural_p; return 0; } MsgERR MSG_CompositionPane::DoCommand(MSG_CommandType command, MSG_ViewIndex* indices, int32 numindices) { MsgERR status = 0; InterruptContext(FALSE); switch (command) { case MSG_SendMessage: status = SendMessageNow(); /* ###tw Error-return-type mismatch! */ break; case MSG_SendMessageLater: status = QueueMessageForLater();/* ###tw Error-return-type mismatch! */ break; case MSG_SaveDraft: case MSG_SaveDraftThenClose: if (command == MSG_SaveDraftThenClose) m_closeAfterSave = TRUE; status = SaveMessageAsDraft(); /* ### Error-return-type mismatch! */ break; case MSG_SaveTemplate: status = SaveMessageAsTemplate(); break; case MSG_ShowPostTo: // how to do this? ToggleCompositionHeader(MSG_NEWSGROUPS_HEADER_MASK); break; HG82762 case MSG_ShowFrom: ToggleCompositionHeader(MSG_FROM_HEADER_MASK); break; case MSG_ShowReplyTo: ToggleCompositionHeader(MSG_REPLY_TO_HEADER_MASK); break; case MSG_ShowTo: ToggleCompositionHeader(MSG_TO_HEADER_MASK); break; case MSG_ShowCC: ToggleCompositionHeader(MSG_CC_HEADER_MASK); break; case MSG_ShowBCC: ToggleCompositionHeader(MSG_BCC_HEADER_MASK); break; case MSG_ShowFCC: ToggleCompositionHeader(MSG_FCC_HEADER_MASK); break; case MSG_ShowFollowupTo: ToggleCompositionHeader(MSG_FOLLOWUP_TO_HEADER_MASK); break; case MSG_ShowSubject: ToggleCompositionHeader(MSG_SUBJECT_HEADER_MASK); break; case MSG_ShowAttachments: ToggleCompositionHeader(MSG_ATTACHMENTS_HEADER_MASK); break; default: status = MSG_Pane::DoCommand(command, indices, numindices); break; } return status; } extern "C" void FE_MsgShowHeaders(MSG_Pane *pPane, MSG_HEADER_SET mhsHeaders); void MSG_CompositionPane::ToggleCompositionHeader(uint32 header) { #if XP_UNIX if (m_visible_headers & header) { m_visible_headers &= ~header; } else { m_visible_headers |= header; } FE_MsgShowHeaders(this, m_visible_headers); #endif //XP_UNIX } XP_Bool MSG_CompositionPane::ShowingAllCompositionHeaders() { return m_visible_headers == ALL_HEADERS; } XP_Bool MSG_CompositionPane::ShowingCompositionHeader(uint32 mask) { return (m_visible_headers & mask) == mask; } int MSG_CompositionPane::SetCallbacks(MSG_CompositionPaneCallbacks* callbacks, void* closure) { m_callbacks = *callbacks; m_callbackclosure = closure; return 0; } void MSG_CompositionPane::InitializeHeaders(MWContext* old_context, MSG_CompositionFields* fields) { #ifdef XP_UNIX XP_ASSERT(m_fields == NULL); XP_ASSERT(m_initfields == NULL); const char *real_addr = FE_UsersMailAddress (); char *real_return_address; const char* sig; XP_Bool forward_quoted; forward_quoted = FALSE; m_fields = new MSG_CompositionFields(fields); // ###tw Should check for failure! /* hack for forward quoted. Checks the attachment field for a cookie string indicating that this is a forward quoted operation. If a cookie is found, the attachment string is slid back down over the cookie. This will put the original string back in tact. */ const char* attachment = m_fields->GetAttachments(); if (attachment) { if (!XP_STRNCMP(attachment, MSG_FORWARD_COOKIE, strlen(MSG_FORWARD_COOKIE))) { attachment += XP_STRLEN(MSG_FORWARD_COOKIE); forward_quoted = TRUE; /* set forward with quote flag */ m_fields->SetAttachments(attachment); attachment = m_fields->GetAttachments(); } } m_status = -1; if (MISC_ValidateReturnAddress(old_context, real_addr) < 0) { return; } real_return_address = MIME_MakeFromField(); XP_ASSERT (m_context->type == MWContextMessageComposition); XP_ASSERT (XP_FindContextOfType(0, MWContextMessageComposition)); XP_ASSERT (!m_context->msg_cframe); int32 count = m_fields->GetNumForwardURL(); if (count > 0) { // if forwarding one or more messages XP_ASSERT(*attachment == '\0'); MSG_AttachmentData *alist = (struct MSG_AttachmentData *) XP_ALLOC((count + 1) * sizeof(MSG_AttachmentData)); if (alist) { XP_MEMSET(alist, 0, (count + 1) * sizeof(*alist)); for (count--; count >= 0; count--) { alist[count].url = (char*) m_fields->GetForwardURL(count); alist[count].real_name = (char*) m_fields->GetForwardURL(count); } SetAttachmentList(alist); XP_FREE(alist); } } else if (*attachment) { // forwarding a single url // typically a web page MSG_AttachmentData *alist; count = 1; alist = (struct MSG_AttachmentData *) XP_ALLOC((count + 1) * sizeof(MSG_AttachmentData)); if (alist) { XP_MEMSET(alist, 0, (count + 1) * sizeof(*alist)); alist[0].url = (char *)attachment; alist[0].real_name = XP_STRDUP ((char *)attachment); SetAttachmentList(alist); } } // else if (*attachment) if (*attachment) { if (*attachment != '(') { m_defaultUrl = XP_STRDUP(attachment); } } else if (old_context) { History_entry *h = SHIST_GetCurrent(&old_context->hist); if (h && h->address) { m_defaultUrl = XP_STRDUP(h->address); } } if (!*m_fields->GetFrom()) { m_fields->SetFrom(real_return_address); } /* Guess what kind of reply this is based on the headers we passed in. */ const char* newsgroups = m_fields->GetNewsgroups(); const char* to = m_fields->GetTo(); const char* cc = m_fields->GetCc(); const char* references = m_fields->GetReferences(); if (count > 0 || *attachment) { /* if an attachment exists and the forward_quoted flag is set, this is a forward quoted operation. */ if (forward_quoted) { m_replyType = MSG_ForwardMessageQuoted; /* clear out the attachment list for forward quoted messages. */ SetAttachmentList(NULL); m_pendingAttachmentsCount = 0; } else { m_replyType = MSG_ForwardMessageAttachment; } } else if (*references && *newsgroups && (*to || *cc)) { m_replyType = MSG_PostAndMailReply; } else if (*references && *newsgroups) { m_replyType = MSG_PostReply; } else if (*references && *cc) { m_replyType = MSG_ReplyToAll; } else if (*references && *to) { m_replyType = MSG_ReplyToSender; } else if (*newsgroups) { m_replyType = MSG_PostNew; } else { m_replyType = MSG_MailNew; } if (!*m_fields->GetOrganization()) { m_fields->SetOrganization(FE_UsersOrganization()); } if (!*m_fields->GetReplyTo()) { m_fields-> SetReplyTo(GetPrefs()-> GetDefaultHeaderContents(MSG_REPLY_TO_HEADER_MASK)); } if (!*m_fields->GetFcc()) { XP_Bool useDefaultFcc = TRUE; /*int prefError =*/ PREF_GetBoolPref(*newsgroups ? "news.use_fcc" : "mail.use_fcc", &useDefaultFcc); if (useDefaultFcc) { m_fields->SetFcc(GetPrefs()-> GetDefaultHeaderContents(*newsgroups ? MSG_NEWS_FCC_HEADER_MASK : MSG_FCC_HEADER_MASK)); } } if (!*m_fields->GetBcc()) { char* bcc = FigureBcc(*newsgroups); m_fields->SetBcc(bcc); FREEIF(bcc); } { const char *body = m_fields->GetDefaultBody(); if (body && *body) { m_fields->AppendBody(body); m_fields->AppendBody(LINEBREAK); /* m_bodyEdited = TRUE; */ } } HG93653 sig = FE_UsersSignature (); if (sig && *sig) { m_fields->AppendBody(LINEBREAK); /* If the sig doesn't begin with "--" followed by whitespace or a newline, insert "-- \n" (the pseudo-standard sig delimiter.) */ if (sig[0] != '-' || sig[1] != '-' || (sig[2] != ' ' && sig[2] != CR && sig[2] != LF)) { m_fields->AppendBody("-- " LINEBREAK); } m_fields->AppendBody(sig); } FREEIF (real_return_address); FE_SetDocTitle(m_context, (char*) GetWindowTitle()); m_initfields = new MSG_CompositionFields(m_fields); // ###tw Should check for failure! #endif //XP_UNIX } XP_Bool MSG_CompositionPane::ShouldAutoQuote() { if (m_haveQuoted) return FALSE; if (m_replyType == MSG_ForwardMessageQuoted || GetPrefs()->GetAutoQuoteReply()) { switch (m_replyType) { case MSG_ForwardMessageQuoted: case MSG_PostAndMailReply: case MSG_PostReply: case MSG_ReplyToAll: case MSG_ReplyToSender: return TRUE; default: break; } } return FALSE; } const char* MSG_CompositionPane::GetDefaultURL() { return m_defaultUrl; } MSG_CompositionFields* MSG_CompositionPane::GetInitialFields() { return m_initfields; } #define ALL_HEADERS (MSG_FROM_HEADER_MASK | \ MSG_REPLY_TO_HEADER_MASK | \ MSG_TO_HEADER_MASK | \ MSG_CC_HEADER_MASK | \ MSG_BCC_HEADER_MASK | \ MSG_FCC_HEADER_MASK | \ MSG_NEWSGROUPS_HEADER_MASK | \ MSG_FOLLOWUP_TO_HEADER_MASK | \ MSG_SUBJECT_HEADER_MASK | \ MSG_ATTACHMENTS_HEADER_MASK) MSG_HEADER_SET MSG_CompositionPane::GetInterestingHeaders() { MSG_HEADER_SET desired_mask = 0; /* The FE has requested the list of "interesting" header fields. The logic here is a bit complicated, in the interest of DWIMity. */ /* Cc, Subject, and Attachments are always interesting. */ desired_mask |= (MSG_CC_HEADER_MASK | MSG_SUBJECT_HEADER_MASK /* | MSG_ATTACHMENTS_HEADER_MASK */); /* To is interesting if: - it is non-empty, or - this composition window was brought up with a "mail sending" command (Mail New, Reply-*, Forward, or Post and Mail). */ if (*m_fields->GetTo() || m_replyType == MSG_MailNew || m_replyType == MSG_ReplyToSender || m_replyType == MSG_ReplyToAll || m_replyType == MSG_PostAndMailReply || m_replyType == MSG_ForwardMessageAttachment || m_replyType == MSG_ForwardMessageQuoted) desired_mask |= MSG_TO_HEADER_MASK; /* CC is interesting if: - it is non-empty, or - this composition window was brought up as a reply to another mail message. (Should mail-and-post do this too?) */ if ((*m_fields->GetCc()) || m_replyType == MSG_ReplyToSender || m_replyType == MSG_ReplyToAll) desired_mask |= MSG_CC_HEADER_MASK; /* Reply-To and BCC are interesting if: - they are non-empty, AND - they are different from the default value (meaning the user has edited them this session.) */ const char* reply_to = m_fields->GetReplyTo(); const char* default_reply_to = GetPrefs()->GetDefaultHeaderContents(MSG_REPLY_TO_HEADER_MASK); if (reply_to && *reply_to && ((default_reply_to && *default_reply_to) ? !!XP_STRCMP (reply_to, default_reply_to) : TRUE)) desired_mask |= MSG_REPLY_TO_HEADER_MASK; /* (see above.) */ const char* bcc = m_fields->GetBcc(); const char* default_bcc = GetPrefs()->GetDefaultHeaderContents(MSG_BCC_HEADER_MASK); if (bcc && *bcc && ((default_bcc && *default_bcc) ? !!XP_STRCMP (bcc, default_bcc) : TRUE)) desired_mask |= MSG_BCC_HEADER_MASK; /* FCC is never interesting. */ /* Newsgroups is interesting if: - it is non-empty, or - this composition window was brought up with a "news posting" command (Post New, Post Reply, or Post and Mail). */ const char* newsgroups = m_fields->GetNewsgroups(); if ((newsgroups && *newsgroups) || m_replyType == MSG_PostNew || m_replyType == MSG_PostReply || m_replyType == MSG_PostAndMailReply) desired_mask |= MSG_NEWSGROUPS_HEADER_MASK; /* Followup-To is interesting if: - it is non-empty, AND - it differs from the Newsgroups field. */ const char* followup_to = m_fields->GetFollowupTo(); if (followup_to && *followup_to && (newsgroups ? XP_STRCMP (followup_to, newsgroups) : TRUE)) desired_mask |= MSG_FOLLOWUP_TO_HEADER_MASK; return desired_mask; } void MSG_CompositionPane::GetUrlDone_S(PrintSetup* pptr) { ((MSG_CompositionPane*) (pptr->carg))->GetUrlDone(pptr); } #define QUOTE_BUFFER_SIZE 10240 void MSG_CompositionPane::GetUrlDone(PrintSetup* /*pptr*/) { #ifdef XP_UNIX XP_File file; FREEIF(m_quoteUrl); m_textContext = NULL; /* since this is called as a result of TXFE_AllConnectionsComplete, we know this context is going away by natural means */ int bufSize = QUOTE_BUFFER_SIZE; XP_FileClose(m_print->out); XP_StatStruct stat; char* curquote = NULL; int32 replyOnTop = 0, replyWithExtraLines = 0; PREF_GetIntPref("mailnews.reply_on_top", &replyOnTop); PREF_GetIntPref("mailnews.reply_with_extra_lines", &replyWithExtraLines); int32 extra = (m_markup ? 0 : (replyWithExtraLines ? LINEBREAK_LEN * replyWithExtraLines : 0)); if (XP_Stat(m_print->filename, &stat, xpTemporary) == 0) { m_quotedText = (char*) XP_ALLOC(stat.st_size + 1 + extra); /* Insert two line break at the begining of the quoted text */ if (!m_quotedText) return; curquote = m_quotedText; if (!m_markup && extra && replyOnTop == 1) { for (; replyWithExtraLines > 0; replyWithExtraLines--) { XP_STRCPY(curquote, LINEBREAK); curquote += LINEBREAK_LEN; if (m_quotefunc) (*m_quotefunc)(m_quoteclosure, LINEBREAK); } } } /* Open hateful temporary file as input */ file = XP_FileOpen (m_print->filename, xpTemporary, XP_FILE_READ); if (file) { char* buf = NULL; while (!buf && (bufSize >= 512)) { buf = (char*)XP_ALLOC(bufSize + 1); if (!buf) bufSize /= 2; } if (buf) { int32 bufferLen; CCCDataObject conv; int doConv; INTL_CharSetInfo c = LO_GetDocumentCharacterSetInfo(m_context); int16 win_csid = INTL_GetCSIWinCSID(c); /* * We aren't actually converting character encodings here. * (Note that both the "from" and "to" are the win_csid.) * This makes it call a special routine that makes sure we * deal with whole multibyte characters instead of partial * ones that happen to lie on the boundary of the buffer. -- erik */ conv = INTL_CreateCharCodeConverter(); if (conv) { doConv = INTL_GetCharCodeConverter(win_csid, win_csid, conv); } else { doConv = 0; } while (0 < (bufferLen = XP_FileRead(buf, bufSize, file))) { char *newBuf; buf[bufferLen] = '\0'; if (doConv) { newBuf = (char *) INTL_CallCharCodeConverter(conv, (unsigned char *) buf, bufferLen); if (!newBuf) { newBuf = buf; } } else { newBuf = buf; } if (m_quotefunc) { (*m_quotefunc)(m_quoteclosure, newBuf); } if (m_quotedText && curquote) { XP_ASSERT(curquote + bufferLen <= m_quotedText + stat.st_size + extra); if (curquote + bufferLen <= m_quotedText + stat.st_size + extra) { XP_STRCPY(curquote, newBuf); curquote += bufferLen; } } if (newBuf != buf) { XP_FREE(newBuf); } } if (!m_markup && extra && replyOnTop == 0) { for (; replyWithExtraLines > 1; replyWithExtraLines--) { XP_STRCPY(curquote, LINEBREAK); curquote += LINEBREAK_LEN; if (m_quotefunc) (*m_quotefunc)(m_quoteclosure, LINEBREAK); } } XP_FREE(buf); if (conv) { INTL_DestroyCharCodeConverter(conv); } } XP_FileClose(file); } if (curquote) *curquote = '\0'; m_cited = TRUE; XP_FileRemove(m_print->filename, xpTemporary); FREEIF(m_print->filename); if (m_exitQuoting) { (*m_exitQuoting)(m_dummyUrl, 0, m_context); m_exitQuoting = NULL; m_dummyUrl = NULL; /* hack that manages to get the cursor back to normal. */ NET_SilentInterruptWindow(m_context); } if (m_quotefunc) { (*m_quotefunc)(m_quoteclosure, NULL); m_quotefunc = NULL; } /* Re-enable the UI. */ FE_UpdateCompToolbar (this); #endif //XP_UNIX } class QuotePlainIntoHTML : public MSG_ZapIt { public: QuotePlainIntoHTML(MWContext* context); ~QuotePlainIntoHTML(); int DoQuote(const char* data); static int32 QuoteLine_s(char* line, uint32 line_length, void* closure); int32 QuoteLine(char* line, uint32 line_length); protected: MWContext* m_context; char* m_buffer; uint32 m_size; uint32 m_fp; XP_Bool m_insertedpre; char* m_outbuf; int32 m_outbufsize; int m_maxLineWidth; int32 m_replyOnTop; int32 m_replyWithExtraLines; }; static int MyQuoteFunc(void* closure, const char* data) { return ((QuotePlainIntoHTML*) closure)->DoQuote(data); } QuotePlainIntoHTML::QuotePlainIntoHTML(MWContext* context) { m_context = context; if (EDT_PasteQuoteBegin(m_context, TRUE) != EDT_COP_OK) { m_context = NULL; } PREF_GetIntPref("mailnews.reply_on_top", &m_replyOnTop); PREF_GetIntPref("mailnews.reply_with_extra_lines", &m_replyWithExtraLines); } QuotePlainIntoHTML::~QuotePlainIntoHTML() { FREEIF(m_buffer); delete [] m_outbuf; } int QuotePlainIntoHTML::DoQuote(const char* data) { if (data) { if (!m_context) return 0; return msg_LineBuffer(data, XP_STRLEN(data), &m_buffer, &m_size, &m_fp, FALSE, #ifdef XP_OS2 (int32 (_Optlink*) (char*,uint32,void*)) #endif QuoteLine_s, this); } else { if (m_context) { if (m_fp > 0) { QuoteLine(m_buffer, m_fp); } if (m_insertedpre) { EDT_PasteQuote(m_context, ""); } if ( 0 == m_replyOnTop && m_replyWithExtraLines) for (;m_replyWithExtraLines > 0; m_replyWithExtraLines--) EDT_PasteQuote(m_context, "
"); EDT_PasteQuoteEnd(m_context); MSG_CompositionPane *cpane = (MSG_CompositionPane *) MSG_FindPane(m_context, MSG_COMPOSITIONPANE); if (cpane) cpane->SetLineWidth(m_maxLineWidth); } delete this; return 0; } } int32 QuotePlainIntoHTML::QuoteLine_s(char* line, uint32 length, void* closure) { return ((QuotePlainIntoHTML*)closure)->QuoteLine(line, length); } int32 QuotePlainIntoHTML::QuoteLine(char* line, uint32 length) { if (length > m_maxLineWidth) m_maxLineWidth = length; if (length >= 2 && line[0] == '>' && line[1] == ' ') { line += 2; length -= 2; if (!m_insertedpre) { EDT_PasteQuote(m_context, "
");
			m_insertedpre = TRUE;
		}
	}
	else if (!m_insertedpre) {
		if (1 == m_replyOnTop && m_replyWithExtraLines)
		  for (; m_replyWithExtraLines > 0; m_replyWithExtraLines--)
				EDT_PasteQuote(m_context, "
"); } int l = length * 2 + 50; if (l > m_outbufsize) { if (l < 512) l = 512; m_outbufsize = l; delete [] m_outbuf; m_outbuf = new char [m_outbufsize]; } if (m_outbuf) { *m_outbuf = '\0'; NET_ScanForURLs(NULL, line, length, m_outbuf, m_outbufsize, TRUE); EDT_PasteQuote(m_context, m_outbuf); } return 0; } void MSG_CompositionPane::QuoteHTMLDone_S(URL_Struct* url, int /*status*/, MWContext* /*context*/) { MSG_CompositionPane *pane = (MSG_CompositionPane *) url->fe_data; if (pane) { FREEIF(pane->m_quoteUrl); pane->m_quotefunc = NULL; if (pane->m_quoteclosure) { delete pane->m_quoteclosure; pane->m_quoteclosure = NULL; } } NET_FreeURLStruct(url); } MsgERR MSG_CompositionPane::QuoteMessage(int (*func)(void* closure, const char* data), void* closure) { MsgERR status = 0; char* ptr; m_haveQuoted = TRUE; if (!m_defaultUrl) return 0; /* Nothing to quote. */ if (m_quoteUrl) return 0; /* Currently already quoting! */ XP_ASSERT(m_quotefunc == NULL); if (m_markup) { func = MyQuoteFunc; closure = new QuotePlainIntoHTML(GetContext()); if (!closure) return MK_OUT_OF_MEMORY; } const char* htmlpart = m_fields->GetHTMLPart(); XP_Bool quotehtml = (m_markup && htmlpart != NULL && *htmlpart != '\0'); if (m_quotedText) { if (func) { #ifdef EXTRA_QUOTE_BEGIN if (m_markup) { if (EDT_PasteQuoteBegin(GetContext(), TRUE) != EDT_COP_OK) { return eUNKNOWN; } } #endif (*func)(closure, m_quotedText); (*func)(closure, NULL); } return 0; } m_quotefunc = func; m_quoteclosure = closure; m_quoteUrl = XP_STRDUP(m_defaultUrl); if (!m_quoteUrl) return eOUT_OF_MEMORY; /* remove any position information from the url */ ptr = XP_STRCHR(m_quoteUrl, '#'); if (ptr) *ptr = '\0'; if (quotehtml) { URL_Struct* url = NET_CreateURLStruct(m_quoteUrl, NET_DONT_RELOAD); if (!url) return MK_OUT_OF_MEMORY; // This is a hack, really should be url->msg_pane, but this prevents mail // quoting from working at all. We just need SOME sort of way to give // the msg_page to the completion function. url->fe_data = (void *)this; // Set this because when quoting messages that might have been // downloaded through IMAP MIME Parts on Demand (with some parts left out) // we think it is OK to only quote the inline parts that have been // downloaded. // (That is, we don't think it's necessary to re-download the entire message // from the server. We might be wrong about this, but so far haven't seen // any examples to the contrary.) url->allow_content_change = TRUE; MSG_UrlQueue::AddUrlToPane (url, QuoteHTMLDone_S, this, TRUE, FO_QUOTE_HTML_MESSAGE); return 0; } XL_InitializeTextSetup(m_print); m_print->out = NULL; m_print->prefix = "> "; if (m_markup) { if (htmlpart && *htmlpart) { // Quoting html into html // We are kind of doomed on this case when force sending plain text // if there are hard line breaks. m_print->width = 999; } else { // Quoting plain text into html; there are hard line breaks // We should not reformat the line when force sending plain text // message // This is done via recording the max line width when we quote each plain // text line and then use the max line width if it is greater than default // wraplinewidth. This prevent from reformatting the qutoed line in an // unwanted fashion. m_print->width = 998; } // m_print->width = 999; // Cheap hack. The EDT_PasteQuote routine // generates HTML that is better at wrapping // than the TextFE is, so try and let it do the // wrapping instead. } else { if (htmlpart && *htmlpart) { // We are quoting html message into plain text message // Use wrapline width from preference int32 width = 72; PREF_GetIntPref("mailnews.wraplength", &width); if (width == 0) width = 72; else if (width < 10) width = 10; else if (width > 30000) width = 30000; m_print->width = width - 2; } else { // We are quoting plain text message.to plain text // We shouldn't reformat the original message since everyline already // has hard line break we simply set the m_print->width to 999. m_print->width = 997; } // m_print->width = 70; // The default window is 72 wide; subtract 2 for "> ". } m_print->carg = this; m_print->url = NET_CreateURLStruct(m_defaultUrl, NET_DONT_RELOAD); if (!m_print->url) { status = eOUT_OF_MEMORY; goto FAIL; } HG98265 m_print->url->position_tag = 0; m_print->completion = MSG_CompositionPane::GetUrlDone_S; m_print->filename = WH_TempName(xpTemporary, "ns"); if (!m_print->filename) { status = eOUT_OF_MEMORY; goto FAIL; } m_print->out = XP_FileOpen(m_print->filename, xpTemporary, XP_FILE_WRITE); if (!m_print->out) { status = 9999; /* ###tw Need the right error code! */ goto FAIL; } m_print->cx = m_context; m_exitQuoting = NULL; m_dummyUrl = NET_CreateURLStruct("about:", NET_DONT_RELOAD); m_dummyUrl->internal_url = TRUE; if (m_dummyUrl) { FE_SetWindowLoading(m_context, m_dummyUrl, &m_exitQuoting); XP_ASSERT(m_exitQuoting != NULL); } /* Start the URL loading... (msg_get_url_done gets called later.) */ m_textContext = (MWContext*) XL_TranslateText(m_context, m_print->url, m_print); // ###tw I'm not at all sure this cast is the // right thing to do here... return 0; FAIL: FREEIF(m_print->filename); FREEIF(m_quoteUrl); if (m_print->out) { XP_FileClose(m_print->out); m_print->out = NULL; } if (m_print->url) { NET_FreeURLStruct(m_print->url); m_print->url = NULL; } return status; } int MSG_CompositionPane::PastePlaintextQuotation(const char* str) { if (str && *str) { if (EDT_PasteQuoteBegin(m_context, TRUE) != EDT_COP_OK) { return -1; } EDT_PasteQuote(m_context, "
");
		EDT_PasteQuote(m_context, (char*) str);
		EDT_PasteQuote(m_context, "
"); EDT_PasteQuoteEnd(m_context); } return 0; } int MSG_CompositionPane::SetAttachmentList(struct MSG_AttachmentData* list) { int count = 0; MSG_AttachmentData *tmp; MSG_AttachmentData *tmp2; int status = 0; ClearCompositionMessageID(); /* Since the attachment list has changed, the message has changed, so make sure we're using a fresh message-id when we try to send it. */ msg_free_attachment_list(m_attachData); m_attachData = NULL; for (tmp = list; tmp && tmp->url; tmp++) count++; if (count > 0) { m_attachData = (MSG_AttachmentData*) XP_ALLOC((count + 1) * sizeof(MSG_AttachmentData)); if (!m_attachData) { FE_Alert(m_context, XP_GetString(MK_OUT_OF_MEMORY)); return MK_OUT_OF_MEMORY; } XP_MEMSET(m_attachData, 0, (count + 1) * sizeof(MSG_AttachmentData)); } if (count > 0) { for (tmp = list, tmp2 = m_attachData; tmp->url; tmp++, tmp2++) { tmp2->url = XP_STRDUP(tmp->url); if (tmp->desired_type) { tmp2->desired_type = XP_STRDUP(tmp->desired_type); } if (tmp->real_type) { tmp2->real_type = XP_STRDUP(tmp->real_type); } if (tmp->real_encoding) { tmp2->real_encoding = XP_STRDUP(tmp->real_encoding); } if (tmp->real_name) { tmp2->real_name = XP_STRDUP(tmp->real_name); } if (tmp->description) { tmp2->description = XP_STRDUP(tmp->description); } if (tmp->x_mac_type) { tmp2->x_mac_type = XP_STRDUP(tmp->x_mac_type); } if (tmp->x_mac_creator) { tmp2->x_mac_creator = XP_STRDUP(tmp->x_mac_creator); } } } status = DownloadAttachments(); return status; } const struct MSG_AttachmentData * MSG_CompositionPane::GetAttachmentList() { if (m_attachData && m_attachData[0].url != NULL) return m_attachData; return NULL; } static void msg_free_attachment_list(struct MSG_AttachmentData *list) { MSG_AttachmentData* tmp; if (!list) return; for (tmp = list ; tmp->url ; tmp++) { XP_FREE((char*) tmp->url); if (tmp->desired_type) XP_FREE((char*) tmp->desired_type); if (tmp->real_type) XP_FREE((char*) tmp->real_type); if (tmp->real_encoding) XP_FREE((char*) tmp->real_encoding); if (tmp->real_name) XP_FREE((char*) tmp->real_name); if (tmp->description) XP_FREE((char*) tmp->description); if (tmp->x_mac_type) XP_FREE((char*) tmp->x_mac_type); if (tmp->x_mac_creator) XP_FREE((char*) tmp->x_mac_creator); } XP_FREEIF(list); } /* Whether the given saved-attachment-file thing is a match for the given URL (in source and type-conversion.) */ static XP_Bool msg_attachments_match (MSG_AttachmentData *attachment, MSG_AttachedFile *file) { const char *dt; XP_ASSERT(attachment && file); if (!attachment || !file) return FALSE; XP_ASSERT(attachment->url && file->orig_url); if (!attachment->url || !file->orig_url) return FALSE; XP_ASSERT(file->type); if (!file->type) return FALSE; XP_ASSERT(file->file_name); if (XP_STRCMP(attachment->url, file->orig_url)) return FALSE; /* If the attachment has a conversion type specified (and it's not the "no conversion" type) then this is only a match if the saved document ended up with that type as well. */ dt = ((attachment->desired_type && *attachment->desired_type) ? attachment->desired_type : 0); if (dt && !strcasecomp(dt, TEXT_HTML)) dt = 0; /* dt only has a value if it's "not `As Is', ie, text/plain or app/ps. */ if (dt && XP_STRCMP(dt, file->type)) return FALSE; return TRUE; } int MSG_CompositionPane::DownloadAttachments() { int returnValue = 0; #ifdef XP_UNIX int attachment_count = 0; int new_download_count = 0; int download_overlap_count = 0; MSG_AttachmentData *tmp; MSG_AttachmentData *downloads = 0; MSG_AttachedFile *tmp2; // *** Relax the rule a little bit to enable resume downloading at // *** send time. // XP_ASSERT(!m_deliveryInProgress); // Make sure we do not have an attachement already pending. If we do, // then we do not want to interrupt it. The new attachement will be picked up // when we go to send the message. if (m_attachmentInProgress) return MK_INTERRUPTED; // this status value is ignored by the caller m_pendingAttachmentsCount = 0; // reset m_pendingAttachmentsCount // in case the attachmentlist has been // cleared if (m_attachData) for (tmp = m_attachData; tmp->url; tmp++) attachment_count++; /* First, go through the list of desired attachments, and the list of currently-saved attachments, and delete the files (and data) of the ones which were attached/saved but are no longer. */ tmp2 = m_attachedFiles; while (tmp2 && tmp2->orig_url) { XP_Bool match = FALSE; for (tmp = m_attachData; tmp && tmp->url; tmp++) { if (msg_attachments_match(tmp, tmp2)) { match = TRUE; break; } } if (match) { tmp2++; download_overlap_count++; } else { /* Delete the file, free the strings, and pull the other entries forward to cover this one. */ int i = 0; if (tmp2->file_name) { XP_FileRemove(tmp2->file_name, xpFileToPost); XP_FREE(tmp2->file_name); } FREEIF(tmp2->orig_url); FREEIF(tmp2->type); FREEIF(tmp2->encoding); FREEIF(tmp2->description); FREEIF(tmp2->x_mac_type); FREEIF(tmp2->x_mac_creator); FREEIF(tmp2->real_name); do { tmp2[i]=tmp2[i+1]; } while (tmp2[i++].orig_url); } } /* Now download any new files that are in the list. */ if (download_overlap_count != attachment_count) { MSG_AttachmentData *dfp; new_download_count = attachment_count - download_overlap_count; m_pendingAttachmentsCount = new_download_count; downloads = (MSG_AttachmentData *) XP_ALLOC(sizeof(MSG_AttachmentData) * (new_download_count + 1)); if (!downloads) { FE_Alert(m_context, XP_GetString(MK_OUT_OF_MEMORY)); return MK_OUT_OF_MEMORY; } XP_MEMSET(downloads, 0, sizeof(*downloads) * (new_download_count + 1)); dfp = downloads; for (tmp = m_attachData; tmp && tmp->url; tmp++) { XP_Bool match = FALSE; if (m_attachedFiles) for (tmp2 = m_attachedFiles; tmp2->orig_url; tmp2++) { if (msg_attachments_match(tmp, tmp2)) { match = TRUE; break; } } if (!match) { *dfp = *tmp; dfp++; } } if (!downloads[0].url) return 0; // *** Relax the rule a little bit to enable resume downloading at // *** send time. // XP_ASSERT(!m_deliveryInProgress); XP_ASSERT(!m_attachmentInProgress); m_attachmentInProgress = TRUE; FE_UpdateCompToolbar (this); returnValue = msg_DownloadAttachments(this, this, downloads, #ifdef XP_OS2 (void (_Optlink*) (MWContext*,void*,int,const char*,MSG_AttachedFile*)) #endif MSG_CompositionPane::DownloadAttachmentsDone_S); XP_FREE(downloads); } #endif //XP_UNIX return returnValue; } void MSG_CompositionPane::DownloadAttachmentsDone_S(MWContext *context, void *fe_data, int status, const char *error_message, struct MSG_AttachedFile *attachments) { ((MSG_CompositionPane*) fe_data)->DownloadAttachmentsDone(context, status, error_message, attachments); } void MSG_CompositionPane::DownloadAttachmentsDone(MWContext* context, int status, const char* error_message, struct MSG_AttachedFile *attachments) { #ifdef XP_UNIX XP_ASSERT(context == m_context); int old_count = 0; int new_count = 0; struct MSG_AttachedFile *tmp; MSG_AttachedFile *newd; // *** Relax the rule a little bit to enable resume downloading at // *** send time. // XP_ASSERT(!m_deliveryInProgress); if (m_attachmentInProgress) { m_attachmentInProgress = FALSE; FE_UpdateCompToolbar (this); } if (status < 0) goto FAIL; status = MK_INTERRUPTED; if (!m_attachData) goto FAIL; HG98229 if (m_attachedFiles) { for (tmp = m_attachedFiles; tmp->orig_url; tmp++) old_count++; } if (attachments) { for (tmp = attachments; tmp->orig_url; tmp++) new_count++; } if (old_count + new_count == 0) goto FAIL; newd = (MSG_AttachedFile *) XP_REALLOC(m_attachedFiles, ((old_count + new_count + 1) * sizeof(MSG_AttachedFile))); if (!newd) { status = MK_OUT_OF_MEMORY; error_message = XP_GetString(status); goto FAIL; } m_attachedFiles = newd; XP_MEMCPY(newd + old_count, attachments, sizeof(MSG_AttachedFile) * (new_count + 1)); XP_ASSERT (m_pendingAttachmentsCount >= new_count); m_pendingAttachmentsCount -= new_count; if (m_deliveryInProgress) { m_deliveryInProgress = FALSE; DoneComposeMessage(m_deliver_mode); } return; FAIL: XP_ASSERT(status < 0); if (error_message) { FE_Alert(context, error_message); } else if (status != MK_INTERRUPTED) { char *errmsg; errmsg = PR_smprintf(XP_GetString(MK_COMMUNICATIONS_ERROR), status); if (errmsg) { FE_Alert(context, errmsg); XP_FREE(errmsg); } } /* Since we weren't able to store it, ditch the files and the strings describing them. */ msg_delete_attached_files(attachments); #endif //XP_UNIX } /* How many implementations of this are there now? 4? */ static void msg_mid_truncate_string (const char *input, char *output, int max_length) { int L = XP_STRLEN(input); if (L <= max_length) { XP_MEMCPY(output, input, L+1); } else { int mid = (max_length - 3) / 2; char *tmp = 0; if (input == output) { tmp = output; output = (char *) XP_ALLOC(max_length + 1); *tmp = 0; if (!output) return; } XP_MEMCPY(output, input, mid); XP_MEMCPY(output + mid, "...", 3); XP_MEMCPY(output + mid + 3, input + L - mid, mid + 1); if (tmp) { XP_MEMCPY(tmp, output, max_length + 1); XP_FREE(output); } } } char * MSG_CompositionPane::GetAttachmentString() { /* #### bug 8688 */ MSG_AttachmentData *tmp; int count; int chars_per_attachment; int default_field_width = 63; /* 72 - some space for the word "Attachment" */ count = 0; for (tmp = m_attachData; tmp && tmp->url ; tmp++) count++; if (count <= 0) return 0; chars_per_attachment = (default_field_width - (count * 2)) / count; if (chars_per_attachment < 15) chars_per_attachment = 15; FREEIF(m_attachmentString); m_attachmentString = (char *) XP_ALLOC(count * (chars_per_attachment + 3) + 20); if (!m_attachmentString) return 0; *m_attachmentString = 0; for (tmp = m_attachData ; tmp && tmp->url ; tmp++) { const char *url = tmp->real_name ? tmp->real_name : tmp->url; const char *ptr = XP_STRCHR(url, ':'); char *result = 0; if (!ptr) { /* No colon? Must be a file name. */ ptr = url; goto DO_FILE; } if (!XP_STRNCMP(url, "news:", 5) || !XP_STRNCMP(url, "snews:", 6) || !XP_STRNCMP(url, "IMAP:", 5) || !XP_STRNCMP(url, "mailbox:", 8)) { /* ###tw Unfortunately, I don't think this stuff quite ports directly to the new world, so I'm gonna disable it for now... */ goto DONE; } /* Ok, so it must be something vaguely file-name-like. Look for a slash. */ DO_FILE: { char *ptr2 = XP_STRDUP(ptr); if (!ptr2) goto DONE; char* s = XP_STRCHR(ptr2, '?'); if (s) *s = 0; s = XP_STRCHR(ptr2, '#'); if (s) *s = 0; s = XP_STRRCHR(ptr2, '/'); if(!s) { XP_FREE(ptr2); goto DONE; } s++; if (!*s || !strcasecomp(s,"index.html") || !strcasecomp(s,"index.htm")) { /* This had a useless file name; take the last directory name. */ char *s2 = s-1; if (*s2 == '/') s2--; while (s2 > ptr2 && *s2 != '/') s2--; if (*s2 == ':' || *s2 == '/') s2++; result = (char *) XP_ALLOC (s - s2 + 1); XP_MEMCPY (result, s2, s - s2); result[s - s2] = 0; } else { /* The file name is ok; use it. */ result = XP_STRDUP (s); } NET_UnEscape (result); XP_FREE(ptr2); goto DONE; } DONE: if (tmp != m_attachData) { XP_STRCAT(m_attachmentString, "; "); } if (!result) { if (!XP_STRNCMP(url, "news:", 5) || !XP_STRNCMP(url, "snews:", 6) || !XP_STRNCMP(url, "IMAP:", 5) || !XP_STRNCMP(url, "mailbox:", 8)) { result = XP_STRDUP(""); } else { result = XP_STRDUP(url); } if (!result) break; } msg_mid_truncate_string(result, (m_attachmentString + XP_STRLEN(m_attachmentString)), chars_per_attachment); XP_FREE(result); } return m_attachmentString; } char* MSG_CompositionPane::UpdateHeaderContents(MSG_HEADER_SET which_header, const char* ) { switch (which_header) { case MSG_TO_HEADER_MASK: case MSG_CC_HEADER_MASK: case MSG_BCC_HEADER_MASK: case MSG_REPLY_TO_HEADER_MASK: break; } return NULL; } int MSG_CompositionPane::SetCompHeader(MSG_HEADER_SET header, const char *value) { XP_ASSERT(header != MSG_ATTACHMENTS_HEADER_MASK); ClearCompositionMessageID(); m_fields->SetHeader(header, value); return 0; } const char* MSG_CompositionPane::GetCompHeader(MSG_HEADER_SET header) { if (header == MSG_ATTACHMENTS_HEADER_MASK) { return GetAttachmentString(); } else { return m_fields ? m_fields->GetHeader(header) : (char *)NULL; } } int MSG_CompositionPane::SetCompBoolHeader(MSG_BOOL_HEADER_SET header, XP_Bool bValue) { return m_fields->SetBoolHeader(header, bValue); } XP_Bool MSG_CompositionPane::GetCompBoolHeader(MSG_BOOL_HEADER_SET header) { return m_fields ? m_fields->GetBoolHeader(header) : FALSE; } const char* MSG_CompositionPane::GetCompBody() { return m_fields ? m_fields->GetBody() : (char *)NULL; } int MSG_CompositionPane::SetCompBody(const char* value) { return m_fields->SetBody(value); } const char* MSG_CompositionPane::GetWindowTitle() { const char *s; if (*m_fields->GetSubject()) { s = m_fields->GetSubject(); } else if (*m_fields->GetTo()) { s = m_fields->GetTo(); } else if (*m_fields->GetNewsgroups()) { s = m_fields->GetNewsgroups(); } else { s = XP_GetString(MK_MSG_MSG_COMPOSITION); } return s; } int MSG_CompositionPane::SanityCheck(int skippast) { const char* body = m_fields->GetBody(); const char* sub = m_fields->GetSubject(); if (skippast == MK_MSG_DOUBLE_INCLUDE) goto AFTER_DOUBLE_INCLUDE; if (skippast == MK_MSG_EMPTY_MESSAGE) goto AFTER_EMPTY_MESSAGE; if (skippast == MK_MSG_MISSING_SUBJECT) goto AFTER_MISSING_SUBJECT; // Check if they have quoted a document and not edited it, and also // attached the same document. if (m_quotedText && XP_STRNCMP(body, m_quotedText, XP_STRLEN(m_quotedText)) == 0 && m_attachData && m_attachData[0].url && m_defaultUrl && !XP_STRCMP (m_attachData[0].url, m_defaultUrl)) { return MK_MSG_DOUBLE_INCLUDE; } AFTER_DOUBLE_INCLUDE: // Check if this message has no attachments, and the body has not been // edited. if (XP_STRCMP(body, m_initfields->GetBody()) == 0 && (!m_attachData || !m_attachData[0].url)) { return MK_MSG_EMPTY_MESSAGE; } AFTER_EMPTY_MESSAGE: // Check if they neglected to type a subject. if (sub) { while (XP_IS_SPACE(*sub)) sub++; } if (!sub || !*sub) { return MK_MSG_MISSING_SUBJECT; } AFTER_MISSING_SUBJECT: return 0; } void MSG_CompositionPane::DeliveryDoneCB_s(MWContext *context, void *fe_data, int status, const char *error_message) { ((MSG_CompositionPane*) fe_data)->DeliveryDoneCB(context, status, error_message); } void MSG_CompositionPane::DeliveryDoneCB(MWContext* context, int status, const char* error_message) { XP_ASSERT(context == m_context); // *** We don't want to set m_status to status. The default value // of m_status (-1) prevents the composition pane from closing down // once we done with saving draft. The composition pane should remain up. if ((m_deliver_mode != MSG_SaveAsDraft && m_deliver_mode != MSG_SaveAsTemplate) || (m_deliver_mode == MSG_SaveAsDraft && m_closeAfterSave)) m_status = status; XP_ASSERT(!m_attachmentInProgress); if (m_deliveryInProgress) { m_deliveryInProgress = FALSE; #if 0 //#ifndef XP_UNIX /* Does not need this function call for UNIX. This will prevent toolbar to be enabled after msg sent on UNIX. */ FE_UpdateCompToolbar (this); #endif } if (status < 0) { if (error_message) { FE_Alert(context, error_message); } else if (status != MK_INTERRUPTED) { char *errmsg; HG92755 errmsg = PR_smprintf(XP_GetString(MK_COMMUNICATIONS_ERROR), status); if (errmsg) { FE_Alert(context, errmsg); XP_FREE(errmsg); } } } else { /* ### jht bug 45220 -- do following only when successfully deliver the message */ /* time to delete message from draft/outbox */ } } void MSG_CompositionPane::MailCompositionAllConnectionsComplete () { #ifdef XP_UNIX /* This may be redundant, I'm not sure... */ if (m_deliveryInProgress) { m_deliveryInProgress = FALSE; FE_UpdateCompToolbar(this); } if (m_attachmentInProgress) { m_attachmentInProgress = FALSE; FE_UpdateCompToolbar(this); } if (m_status >= 0) { delete this; } #endif //XP_UNIX } XP_Bool MSG_CompositionPane::DeliveryInProgress () { /* Disable the UI if delivery, attachment loading, or quoting is in progress. */ return m_deliveryInProgress || m_attachmentInProgress || (m_quoteUrl != 0); } /* This function sets the markup flag to indicate that the message body is HTML. Returns the previously set value. */ void MSG_CompositionPane::SetHTMLMarkup(XP_Bool flag) { m_markup = flag; } XP_Bool MSG_CompositionPane::GetHTMLMarkup(void) { return m_markup; } int MSG_CompositionPane::DoneComposeMessage( MSG_Deliver_Mode deliver_mode ) { int status = 0; #ifdef XP_UNIX int attachment_count = 0; XP_Bool digest_p = FALSE; if (m_pendingAttachmentsCount) { m_deliveryInProgress = TRUE; // so that DoneComposeMessage is called again status = DownloadAttachments(); return status; } HG92762 if (m_markup && (deliver_mode != MSG_SaveAsDraft && deliver_mode != MSG_SaveAsTemplate)) { MSG_HTMLComposeAction action = DetermineHTMLAction(); if (action == MSG_HTMLAskUser) { status = -1; #ifndef XP_UNIX // Unix will have to make this dialog a blocking dialog for this logic to work if (m_callbacks.CreateAskHTMLDialog) { status = (*m_callbacks.CreateAskHTMLDialog)(this, m_callbackclosure); // if status == 0, then user wants to Send, so we continue // if status < 0, do the HTML dialogs // if status > 0, cancel if (status == 0) { action = DetermineHTMLAction(); // reget the new action XP_ASSERT(action != MSG_HTMLAskUser); if (action == MSG_HTMLAskUser) { // Boy, the FE is busted. Use our own. status = -1; } } if (status > 0) return 0; // we have to return 0, even in the cancel case so that an error won't get displayed } #endif // if status still negative, do the HTML thing if (status < 0) { static XPDialogInfo dialogInfo = { 0, // I'll provide all my own buttons, thanks. #ifdef XP_OS2 (PRBool (_Optlink*) (XPDialogState*,char**,int,unsigned int)) #endif MSG_CompositionPane::AskDialogDone_s, 500, 300 }; XPDialogStrings* strings = XP_GetDialogStrings(MK_MSG_ASK_HTML_MAIL); if (!strings) return MK_OUT_OF_MEMORY; XP_MakeHTMLDialog(GetContext(), &dialogInfo, MK_MSG_ASK_HTML_MAIL_TITLE, strings, this, PR_FALSE); return 0; } } switch (action) { case MSG_HTMLUseMultipartAlternative: m_fields->SetUseMultipartAlternative(TRUE); break; case MSG_HTMLConvertToPlaintext: m_fields->SetForcePlainText(TRUE); break; case MSG_HTMLSendAsHTML: break; default: XP_ASSERT(0); return -1; } } const char* body = m_fields->GetBody(); uint32 body_length = XP_STRLEN(body); for (attachment_count = 0; m_attachData && m_attachData[attachment_count].url; attachment_count++) ; if (m_attachData && m_attachData[0].url && m_attachData[1].url ) { MSG_AttachmentData* s; digest_p = TRUE; for (s = m_attachData ; s->url ; s++) { /* When there are attachments, start out assuming it is a digest, and then decide that it is not if any of the attached URLs are not mail or news messages. */ if (XP_STRNCMP(s->url, "news:", 5) != 0 && XP_STRNCMP(s->url, "snews:", 6) != 0 && XP_STRNCMP(s->url, "IMAP:", 5) != 0 && XP_STRNCMP(s->url, "mailbox:", 8) != 0) { digest_p = FALSE; } } } XP_ASSERT(!m_attachmentInProgress); XP_ASSERT(!m_deliveryInProgress); m_deliveryInProgress = TRUE; FE_UpdateCompToolbar(this); if (m_messageId == NULL) { m_messageId = msg_generate_message_id(); m_duplicatePost = FALSE; } else { m_duplicatePost = TRUE; } m_fields->SetMessageId(m_messageId); MSG_MimeRelatedSaver *fs = NULL; #ifdef MSG_SEND_MULTIPART_RELATED if (m_markup) { char *pRootPartName = NULL; // set to NULL so that we're given a temp filename fs = new MSG_MimeRelatedSaver(this, m_context, m_fields, digest_p, deliver_mode, body, body_length, m_attachedFiles, DeliveryDoneCB_s, &pRootPartName); if (fs) { EDT_SaveFileTo(m_context, ((deliver_mode == MSG_SaveAsDraft && !m_closeAfterSave) || deliver_mode == MSG_SaveAsTemplate) ? ED_FINISHED_SAVE_DRAFT : ED_FINISHED_MAIL_SEND, pRootPartName, fs, TRUE, TRUE); // Note: EDT_SaveFileTo will delete fs, even if it returns an error. So // it is incorrect to delete it here. Also, we ignore the result, because // it calls FE_Alert itself. } FREEIF(pRootPartName); } else #endif // MSG_SEND_MULTIPART_RELATED { msg_StartMessageDeliveryWithAttachments(this, this, m_fields, digest_p, FALSE, deliver_mode, (m_markup ? TEXT_HTML : TEXT_PLAIN), body, body_length, m_attachedFiles, NULL, #ifdef XP_OS2 (void (_Optlink*) (MWContext*,void*,int,const char*)) #endif DeliveryDoneCB_s); } #endif //XP_UNIX return 0; // Always success, because Errors were reported and handled by EDT_SaveFileTo. } int MSG_CompositionPane::SendMessageNow() { PREF_SetBoolPref("network.online", TRUE); // make sure we're online. // remember if we're queued so we know which folder m_deliver_mode = MSG_DeliverNow; HG73943 // counts we need to update. return DoneComposeMessage(MSG_DeliverNow); } int MSG_CompositionPane::QueueMessageForLater() { // remember if we're queued so we know which folder m_deliver_mode = MSG_QueueForLater; // counts we need to update. return DoneComposeMessage(MSG_QueueForLater); } int MSG_CompositionPane::SaveMessageAsDraft() { m_deliver_mode = MSG_SaveAsDraft; // remember if we're saved as draft so we know which // folder counts we need to update. return DoneComposeMessage(MSG_SaveAsDraft); } int MSG_CompositionPane::SaveMessageAsTemplate() { m_deliver_mode = MSG_SaveAsTemplate; #ifdef SUPPORT_X_TEMPLATE_NAME char *defaultName = NULL; defaultName = FE_Prompt (GetContext(), XP_GetString(MK_MSG_ENTER_NAME_FOR_TEMPLATE), m_fields->GetSubject()); if (defaultName && *defaultName) { m_fields->SetTemplateName(defaultName); XP_FREEIF(defaultName); } #endif /* SUPPORT_X_TEMPLATE_NAME */ return DoneComposeMessage(MSG_SaveAsTemplate); } static int StuffParams(char** params, const char* name, int32 value) { char* escaped = NET_EscapeHTML(name); if (!escaped) return MK_OUT_OF_MEMORY; char* tmp = PR_smprintf("