diff --git a/mail/base/content/msgHdrViewOverlay.js b/mail/base/content/msgHdrViewOverlay.js index cc8a3fcbb62e..0a86c6373b8e 100644 --- a/mail/base/content/msgHdrViewOverlay.js +++ b/mail/base/content/msgHdrViewOverlay.js @@ -1074,6 +1074,10 @@ function dofunc(aFunctionName, aFunctionArg) openAttachment(aFunctionArg); else if (aFunctionName == "printAttachment") printAttachment(aFunctionArg); + else if (aFunctionName == "deleteAttachment") + detachAttachment(aFunctionArg, false); + else if (aFunctionName == "detachAttachment") + detachAttachment(aFunctionArg, true); } function saveAttachment(aAttachment) @@ -1102,6 +1106,14 @@ function printAttachment(aAttachment) */ } +function detachAttachment(aAttachment, aSaveFirst) +{ + messenger.detachAttachment(aAttachment.contentType, + aAttachment.url, + encodeURIComponent(aAttachment.displayName), + aAttachment.messageUri, aSaveFirst); +} + function onShowAttachmentContextMenu() { // if no attachments are selected, disable the Open and Save... @@ -1133,7 +1145,7 @@ function attachmentListClick(event) var target = event.target; if (target.localName == "descriptionitem") { - dofunc("openAttachment", target.attachment); + dofunc("openAttachment", target.attachment); } } } @@ -1222,7 +1234,10 @@ function setApplicationIconForAttachment(attachment, listitem, largeView) { var iconSize = largeView ? kLargeIcon : kSmallIcon; // generate a moz-icon url for the attachment so we'll show a nice icon next to it. - listitem.setAttribute('image', "moz-icon:" + "//" + attachment.displayName + "?size=" + iconSize + "&contentType=" + attachment.contentType); + if ( attachment.contentType == 'text/x-moz-deleted' ) + listitem.setAttribute('image', 'chrome://messenger/skin/icons/message-mail-attach-del.gif'); + else + listitem.setAttribute('image', "moz-icon:" + "//" + attachment.displayName + "?size=" + iconSize + "&contentType=" + attachment.contentType); } // Public method called to generate a tooltip over an attachment @@ -1313,6 +1328,14 @@ function addAttachmentToPopup(popup, attachment, attachmentIndex) gOpenLabel = gMessengerBundle.getString("openLabel"); if (!gOpenLabelAccesskey) gOpenLabelAccesskey = gMessengerBundle.getString("openLabelAccesskey"); + if (!gDetachLabel) + gDetachLabel = gMessengerBundle.getString("detachLabel"); + if (!gDetachLabelAccesskey) + gDetachLabelAccesskey = gMessengerBundle.getString("detachLabelAccesskey"); + if (!gDeleteLabel) + gDeleteLabel = gMessengerBundle.getString("deleteLabel"); + if (!gDeleteLabelAccesskey) + gDeleteLabelAccesskey = gMessengerBundle.getString("deleteLabelAccesskey"); menuitementry.setAttribute('label', gOpenLabel); menuitementry.setAttribute('accesskey', gOpenLabelAccesskey); @@ -1326,12 +1349,32 @@ function addAttachmentToPopup(popup, attachment, attachmentIndex) menuitementry.setAttribute('oncommand', 'saveAttachment(this.attachment)'); menuitementry.setAttribute('label', gSaveLabel); menuitementry.setAttribute('accesskey', gSaveLabelAccesskey); + if (attachment.contentType == 'text/x-moz-deleted') + menuitementry.setAttribute('disabled', true); + menuitementry = openpopup.appendChild(menuitementry); + + menuitementry = document.createElement('menuitem'); + menuitementry.attachment = attachment; + menuitementry.setAttribute('oncommand', 'this.attachment.detachAttachment()'); + menuitementry.setAttribute('label', gDetachLabel); + menuitementry.setAttribute('accesskey', gDetachLabelAccesskey); + if (attachment.contentType == 'text/x-moz-deleted') + menuitementry.setAttribute('disabled', true); + menuitementry = openpopup.appendChild(menuitementry); + + menuitementry = document.createElement('menuitem'); + menuitementry.attachment = attachment; + menuitementry.setAttribute('oncommand', 'this.attachment.deleteAttachment()'); + menuitementry.setAttribute('label', gDeleteLabel); + menuitementry.setAttribute('accesskey', gDeleteLabelAccesskey); + if (attachment.contentType == 'text/x-moz-deleted') + menuitementry.setAttribute('disabled', true); menuitementry = openpopup.appendChild(menuitementry); } // if we created a menu item for this attachment... } // if we have a popup } -function SaveAllAttachments() +function HandleAllAttachments(action) { try { @@ -1342,24 +1385,43 @@ function SaveAllAttachments() var attachmentMessageUriArray = new Array(); // populate these arrays.. + var actionIndex = 0; for (index in currentAttachments) { + // exclude all attachments already deleted var attachment = currentAttachments[index]; - attachmentContentTypeArray[index] = attachment.contentType; - attachmentUrlArray[index] = attachment.url; - attachmentDisplayNameArray[index] = encodeURIComponent(attachment.displayName); - attachmentMessageUriArray[index] = attachment.uri; + if ( attachment.contentType != 'text/x-moz-deleted' ) + { + attachmentContentTypeArray[actionIndex] = attachment.contentType; + attachmentUrlArray[actionIndex] = attachment.url; + attachmentDisplayNameArray[actionIndex] = encodeURI(attachment.displayName); + attachmentMessageUriArray[actionIndex] = attachment.uri; + ++actionIndex; + } } - // okay the list has been built...now call our save all attachments code... - messenger.saveAllAttachments(attachmentContentTypeArray.length, - attachmentContentTypeArray, attachmentUrlArray, - attachmentDisplayNameArray, attachmentMessageUriArray); - } - catch (ex) - { - dump ("** failed to save all attachments **\n"); - } + // okay the list has been built... now call our action code... + if ( action == 'save' ) + messenger.saveAllAttachments(attachmentContentTypeArray.length, + attachmentContentTypeArray, attachmentUrlArray, + attachmentDisplayNameArray, attachmentMessageUriArray); + else if ( action == 'detach' ) + messenger.detachAllAttachments(attachmentContentTypeArray.length, + attachmentContentTypeArray, attachmentUrlArray, + attachmentDisplayNameArray, attachmentMessageUriArray, + true); // save + else if ( action == 'delete' ) + messenger.detachAllAttachments(attachmentContentTypeArray.length, + attachmentContentTypeArray, attachmentUrlArray, + attachmentDisplayNameArray, attachmentMessageUriArray, + false); // don't save + else + dump ("** unknown HandleAllAttachments action: " + action + "**\n"); + } + catch (ex) + { + dump ("** failed to handle all attachments **\n"); + } } function ClearAttachmentList() diff --git a/mail/base/content/msgHdrViewOverlay.xul b/mail/base/content/msgHdrViewOverlay.xul index ae9b856ec282..fbd7820fea26 100644 --- a/mail/base/content/msgHdrViewOverlay.xul +++ b/mail/base/content/msgHdrViewOverlay.xul @@ -62,12 +62,28 @@ - + + + + + + + + + @@ -171,7 +187,8 @@ diff --git a/mail/locales/en-US/chrome/messenger/messenger.properties b/mail/locales/en-US/chrome/messenger/messenger.properties index d360d8c6acab..c1335210137c 100644 --- a/mail/locales/en-US/chrome/messenger/messenger.properties +++ b/mail/locales/en-US/chrome/messenger/messenger.properties @@ -280,6 +280,15 @@ openLabel=Open openLabelAccesskey=O saveLabel=Save As... saveLabelAccesskey=A +detachLabel=Detach... +detachLabelAccesskey=D +deleteLabel=Delete +deleteLabelAccesskey=E +deleteAttachments=The following attachments will be permanently deleted from this message:\n%S\nThis action cannot be undone. Do you wish to continue? +detachAttachments=The following attachments have been successfully saved and will now be permanently deleted from this message:\n%S\nThis action cannot be undone. Do you wish to continue? +deleteAttachmentFailure=Failed to delete the selected attachments. +# LOCALIZATION NOTES(attachmentDeletePrefix): Do not translate until foreign language attachment names are fixed +attachmentDeletePrefix=Deleted: %S # This is the format for prepending accesskeys to the # each of the attachments in the file|attachments menu: diff --git a/mail/locales/en-US/chrome/messenger/msgHdrViewOverlay.dtd b/mail/locales/en-US/chrome/messenger/msgHdrViewOverlay.dtd index 2be06ab7b77e..c1b3fffaf9ac 100644 --- a/mail/locales/en-US/chrome/messenger/msgHdrViewOverlay.dtd +++ b/mail/locales/en-US/chrome/messenger/msgHdrViewOverlay.dtd @@ -61,8 +61,14 @@ + + + + + + diff --git a/mailnews/base/src/nsMessenger.cpp b/mailnews/base/src/nsMessenger.cpp index 32c131d8e5e4..a67aed6f4f7d 100644 --- a/mailnews/base/src/nsMessenger.cpp +++ b/mailnews/base/src/nsMessenger.cpp @@ -303,7 +303,8 @@ public: const char **urlArray, const char **displayNameArray, const char **messageUriArray, - const char *directoryName); + const char *directoryName, + PRBool detachingAttachments); virtual ~nsSaveAllAttachmentsState(); PRUint32 m_count; @@ -313,6 +314,8 @@ public: char** m_urlArray; char** m_displayNameArray; char** m_messageUriArray; + PRBool m_detachingAttachments; + nsCStringArray m_savedFiles; // if detaching first, remember where we saved to. }; // @@ -655,14 +658,33 @@ nsMessenger::SaveAttachment(nsIFileSpec * fileSpec, // whacky ref counting here...what's the deal? when does saveListener get released? it's not clear. nsSaveMsgListener *saveListener = new nsSaveMsgListener(fileSpec, this); if (!saveListener) - { return NS_ERROR_OUT_OF_MEMORY; - } + NS_ADDREF(saveListener); saveListener->m_contentType = contentType; if (saveState) - saveListener->m_saveAllAttachmentsState = saveState; + { + saveListener->m_saveAllAttachmentsState = saveState; + if (saveState->m_detachingAttachments) + { + + nsFileSpec realSpec; + fileSpec->GetFileSpec(&realSpec); + + // Create nsILocalFile from a nsFileSpec. + nsCOMPtr outputFile; + nsresult rv = NS_FileSpecToIFile(&realSpec, getter_AddRefs(outputFile)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr outputURI; + rv = NS_NewFileURI(getter_AddRefs(outputURI), outputFile); + NS_ENSURE_SUCCESS(rv, rv); + nsCAutoString fileUriSpec; + outputURI->GetSpec(fileUriSpec); + saveState->m_savedFiles.AppendCString(fileUriSpec); + } + } urlString = url; urlString.ReplaceSubstring("/;section", "?section"); @@ -717,7 +739,7 @@ nsMessenger::SaveAttachment(nsIFileSpec * fileSpec, NS_IF_RELEASE(saveListener); Alert("saveAttachmentFailed"); } - return rv; + return rv; } NS_IMETHODIMP @@ -834,6 +856,17 @@ nsMessenger::SaveAllAttachments(PRUint32 count, const char **urlArray, const char **displayNameArray, const char **messageUriArray) +{ + return SaveAllAttachments(count, contentTypeArray, urlArray, displayNameArray, messageUriArray, PR_FALSE); +} + +nsresult +nsMessenger::SaveAllAttachments(PRUint32 count, + const char **contentTypeArray, + const char **urlArray, + const char **displayNameArray, + const char **messageUriArray, + PRBool detaching) { nsresult rv = NS_ERROR_OUT_OF_MEMORY; nsCOMPtr filePicker = @@ -877,7 +910,7 @@ nsMessenger::SaveAllAttachments(PRUint32 count, urlArray, displayNameArray, messageUriArray, - (const char*) dirName); + (const char*) dirName, detaching); { nsFileSpec aFileSpec((const char *) dirName); @@ -888,7 +921,8 @@ nsMessenger::SaveAllAttachments(PRUint32 count, aFileSpec += unescapedName.get(); rv = PromptIfFileExists(aFileSpec); - if (NS_FAILED(rv)) return rv; + if (NS_FAILED(rv)) + return rv; fileSpec->SetFromFileSpec(aFileSpec); rv = SaveAttachment(fileSpec, urlArray[0], messageUriArray[0], contentTypeArray[0], (void *)saveState); @@ -1738,9 +1772,7 @@ done: realSpec.Delete(PR_FALSE); } if (m_messenger) - { m_messenger->Alert("saveMessageFailed"); - } } if (killSelf) Release(); // no more work needs to be done; kill ourself @@ -1973,8 +2005,20 @@ nsSaveMsgListener::OnStopRequest(nsIRequest* request, nsISupports* aSupport, } else { - delete m_saveAllAttachmentsState; - m_saveAllAttachmentsState = nsnull; + // check if we're saving attachments prior to detaching them. + if (m_saveAllAttachmentsState->m_detachingAttachments) + { + nsSaveAllAttachmentsState *state = m_saveAllAttachmentsState; + m_messenger->DetachAttachments(state->m_count, + (const char **) state->m_contentTypeArray, + (const char **) state->m_urlArray, + (const char **) state->m_displayNameArray, + (const char **) state->m_messageUriArray, + &state->m_savedFiles); + } + + delete m_saveAllAttachmentsState; + m_saveAllAttachmentsState = nsnull; } } @@ -2087,7 +2131,8 @@ nsSaveAllAttachmentsState::nsSaveAllAttachmentsState(PRUint32 count, const char **urlArray, const char **nameArray, const char **uriArray, - const char *dirName) + const char *dirName, + PRBool detachingAttachments) { PRUint32 i; NS_ASSERTION(count && urlArray && nameArray && uriArray && dirName, @@ -2107,6 +2152,7 @@ nsSaveAllAttachmentsState::nsSaveAllAttachmentsState(PRUint32 count, m_messageUriArray[i] = nsCRT::strdup(uriArray[i]); } m_directoryName = nsCRT::strdup(dirName); + m_detachingAttachments = detachingAttachments; } nsSaveAllAttachmentsState::~nsSaveAllAttachmentsState() @@ -2442,6 +2488,8 @@ public: // temp PRBool mWrittenExtra; + PRBool mDetaching; + nsCStringArray mDetachedFileUris; }; // @@ -2639,12 +2687,12 @@ nsDelAttachListener::~nsDelAttachListener() nsresult nsDelAttachListener::StartProcessing(nsMessenger * aMessenger, nsIMsgWindow * aMsgWindow, - nsAttachmentState * aAttach, PRBool aSaveFirst) + nsAttachmentState * aAttach, PRBool detaching) { aMessenger->QueryInterface(NS_GET_IID(nsIMessenger), getter_AddRefs(mMessenger)); mMsgWindow = aMsgWindow; mAttach = aAttach; - mSaveFirst = aSaveFirst; + mDetaching = detaching; nsresult rv; @@ -2694,15 +2742,24 @@ nsDelAttachListener::StartProcessing(nsMessenger * aMessenger, nsIMsgWindow * aM const char * partId; const char * nextField; nsCAutoString sHeader("attach&del="); + nsCAutoString detachToHeader("&detachTo="); for (PRUint32 u = 0; u < mAttach->mCount; ++u) { if (u > 0) + { sHeader.Append(","); + if (detaching) + detachToHeader.Append(","); + } partId = GetAttachmentPartId(mAttach->mAttachmentArray[u].mUrl); nextField = PL_strchr(partId, '&'); sHeader.Append(partId, nextField ? nextField - partId : -1); + if (detaching) + detachToHeader.Append(mDetachedFileUris.CStringAt(u)->get()); } + if (detaching) + sHeader.Append(detachToHeader); // stream this message to our listener converting it via the attachment mime // converter. The listener will just write the converted message straight to disk. nsCOMPtr listenerSupports; @@ -2753,8 +2810,25 @@ nsMessenger::DetachAllAttachments(PRUint32 aCount, NS_ENSURE_ARG_POINTER(aDisplayNameArray); NS_ENSURE_ARG_POINTER(aMessageUriArray); + if (aSaveFirst) + return SaveAllAttachments(aCount, aContentTypeArray, aUrlArray, aDisplayNameArray, aMessageUriArray, PR_TRUE); + else + return DetachAttachments(aCount, aContentTypeArray, aUrlArray, aDisplayNameArray, aMessageUriArray, nsnull); +} + +nsresult +nsMessenger::DetachAttachments(PRUint32 aCount, + const char ** aContentTypeArray, + const char ** aUrlArray, + const char ** aDisplayNameArray, + const char ** aMessageUriArray, + nsCStringArray *saveFileUris) +{ + if (NS_FAILED(PromptIfDeleteAttachments(saveFileUris != nsnull, aCount, aDisplayNameArray))) + return NS_OK; nsresult rv = NS_OK; + // ensure that our arguments are valid // char * partId; for (PRUint32 u = 0; u < aCount; ++u) @@ -2800,20 +2874,20 @@ nsMessenger::DetachAllAttachments(PRUint32 aCount, // get the listener for running the url nsDelAttachListener * listener = new nsDelAttachListener; - if (!listener) return NS_ERROR_OUT_OF_MEMORY; + if (!listener) + return NS_ERROR_OUT_OF_MEMORY; nsCOMPtr listenerSupports; // auto-delete of the listener with error listener->QueryInterface(NS_GET_IID(nsISupports), getter_AddRefs(listenerSupports)); + if (saveFileUris) + listener->mDetachedFileUris = *saveFileUris; // create the attachments for use by the listener nsAttachmentState * attach = new nsAttachmentState; - if (!attach) return NS_ERROR_OUT_OF_MEMORY; + if (!attach) + return NS_ERROR_OUT_OF_MEMORY; rv = attach->Init(aCount, aContentTypeArray, aUrlArray, aDisplayNameArray, aMessageUriArray); - if (NS_FAILED(rv)) - { - delete attach; - return rv; - } - rv = attach->PrepareForAttachmentDelete(); + if (NS_SUCCEEDED(rv)) + rv = attach->PrepareForAttachmentDelete(); if (NS_FAILED(rv)) { delete attach; @@ -2822,7 +2896,7 @@ nsMessenger::DetachAllAttachments(PRUint32 aCount, // initialize our listener with the attachments and details. The listener takes ownership // of 'attach' immediately irrespective of the return value (error or not). - return listener->StartProcessing(this, mMsgWindow, attach, aSaveFirst); + return listener->StartProcessing(this, mMsgWindow, attach, saveFileUris != nsnull); } nsresult diff --git a/mailnews/base/src/nsMessenger.h b/mailnews/base/src/nsMessenger.h index b4933c36d724..66b9006444f0 100644 --- a/mailnews/base/src/nsMessenger.h +++ b/mailnews/base/src/nsMessenger.h @@ -66,6 +66,18 @@ public: const char* messageUri, const char* contentType, void *closure); nsresult PromptIfFileExists(nsFileSpec &fileSpec); + nsresult DetachAttachments(PRUint32 aCount, + const char ** aContentTypeArray, + const char ** aUrlArray, + const char ** aDisplayNameArray, + const char ** aMessageUriArray, + nsCStringArray *saveFileUris); + nsresult SaveAllAttachments(PRUint32 count, + const char **contentTypeArray, + const char **urlArray, + const char **displayNameArray, + const char **messageUriArray, + PRBool detaching); protected: nsresult DoDelete(nsIRDFCompositeDataSource* db, nsISupportsArray *srcArray, diff --git a/mailnews/local/src/nsLocalMailFolder.cpp b/mailnews/local/src/nsLocalMailFolder.cpp index cb2653b2c836..d386e175db76 100644 --- a/mailnews/local/src/nsLocalMailFolder.cpp +++ b/mailnews/local/src/nsLocalMailFolder.cpp @@ -1791,6 +1791,7 @@ nsMsgLocalMailFolder::CopyMessages(nsIMsgFolder* srcFolder, nsISupportsArray* return rv; } // for srcFolder that are on different server than the dstFolder. +// "this" is the parent of the new dest folder. nsresult nsMsgLocalMailFolder::CopyFolderAcrossServer(nsIMsgFolder* srcFolder, nsIMsgWindow *msgWindow, nsIMsgCopyServiceListener *listener ) diff --git a/mailnews/mime/src/mimei.h b/mailnews/mime/src/mimei.h index 6ed77a2afc44..50ff77b91aef 100644 --- a/mailnews/mime/src/mimei.h +++ b/mailnews/mime/src/mimei.h @@ -421,7 +421,9 @@ public: dexlateion did in fact occur. */ nsCStringArray partsToStrip; /* if we're stripping parts, what parts to strip */ + nsCStringArray detachToFiles; /* if we're detaching parts, where each part was detached to */ PRBool strippingPart; + nsCString detachedFilePath; /* if we've detached this part, filepath of detached part */ }; diff --git a/mailnews/mime/src/mimemult.cpp b/mailnews/mime/src/mimemult.cpp index fd9caecb0006..f6a22472b350 100644 --- a/mailnews/mime/src/mimemult.cpp +++ b/mailnews/mime/src/mimemult.cpp @@ -203,12 +203,15 @@ MimeMultipart_parse_line (char *line, PRInt32 length, MimeObject *obj) newPart.Append('.'); newPart.AppendInt(container->nchildren + 1); obj->options->state->strippingPart = PR_FALSE; + // obj->options->state->detachedFilePath.Truncate(0); // check if this is a sub-part of a part we're stripping. for (PRInt32 partIndex = 0; partIndex < obj->options->state->partsToStrip.Count(); partIndex++) { if (newPart.Find(*obj->options->state->partsToStrip.CStringAt(partIndex)) == 0) { obj->options->state->strippingPart = PR_TRUE; + if (partIndex < obj->options->state->detachToFiles.Count()) + obj->options->state->detachedFilePath = *obj->options->state->detachToFiles.CStringAt(partIndex); break; } } @@ -245,17 +248,45 @@ MimeMultipart_parse_line (char *line, PRInt32 length, MimeObject *obj) { if (obj->options->state->strippingPart) { - nsCAutoString header("Content-Type: text/x-moz-deleted; name=\"Deleted: "); + PRBool detachingPart = obj->options->state->detachedFilePath.Length() > 0; + nsCAutoString fileName; fileName.Adopt(MimeHeaders_get_name(mult->hdrs, obj->options)); - header.Append(fileName); - status = MimeWriteAString(obj, header); - if (status < 0) - return status; - status = MimeWriteAString(obj, NS_LITERAL_CSTRING("\""MSG_LINEBREAK"Content-Transfer-Encoding: 8bit"MSG_LINEBREAK)); - MimeWriteAString(obj, NS_LITERAL_CSTRING("Content-Disposition: inline; filename=\"Deleted:")); - MimeWriteAString(obj, fileName); - MimeWriteAString(obj, NS_LITERAL_CSTRING("\""MSG_LINEBREAK"X-Mozilla-Altered: AttachmentDeleted; date=\"")); + if (detachingPart) + { + char *contentType = MimeHeaders_get(mult->hdrs, "Content-Type", PR_FALSE, PR_FALSE); + MimeWriteAString(obj, NS_LITERAL_CSTRING("Content-Type: ")); + MimeWriteAString(obj, nsDependentCString(contentType)); + PR_Free(contentType); + char *contentEncoding = MimeHeaders_get(mult->hdrs, "Content-Transfer-Encoding", PR_FALSE, PR_FALSE); + if (contentEncoding) + { + MimeWriteAString(obj, NS_LITERAL_CSTRING(MSG_LINEBREAK)); + MimeWriteAString(obj, NS_LITERAL_CSTRING("Content-Transfer-Encoding: ")); + MimeWriteAString(obj, nsDependentCString(contentEncoding)); + PR_Free(contentEncoding); + } + MimeWriteAString(obj, NS_LITERAL_CSTRING(MSG_LINEBREAK)); + MimeWriteAString(obj, NS_LITERAL_CSTRING("Content-Disposition: attachment; filename=\"")); + MimeWriteAString(obj, fileName); + MimeWriteAString(obj, NS_LITERAL_CSTRING("\""MSG_LINEBREAK)); + MimeWriteAString(obj, NS_LITERAL_CSTRING("X-Mozilla-External-Attachment-URL: ")); + MimeWriteAString(obj, obj->options->state->detachedFilePath); + MimeWriteAString(obj, NS_LITERAL_CSTRING(MSG_LINEBREAK)); + MimeWriteAString(obj, NS_LITERAL_CSTRING("X-Mozilla-Altered: AttachmentDetached; date=\"")); + } + else + { + nsCAutoString header("Content-Type: text/x-moz-deleted; name=\"Deleted: "); + header.Append(fileName); + status = MimeWriteAString(obj, header); + if (status < 0) + return status; + status = MimeWriteAString(obj, NS_LITERAL_CSTRING("\""MSG_LINEBREAK"Content-Transfer-Encoding: 8bit"MSG_LINEBREAK)); + MimeWriteAString(obj, NS_LITERAL_CSTRING("Content-Disposition: inline; filename=\"Deleted:")); + MimeWriteAString(obj, fileName); + MimeWriteAString(obj, NS_LITERAL_CSTRING("\""MSG_LINEBREAK"X-Mozilla-Altered: AttachmentDeleted; date=\"")); + } nsCString result; char timeBuffer[128]; PRExplodedTime now; diff --git a/mailnews/mime/src/mimeobj.cpp b/mailnews/mime/src/mimeobj.cpp index 55609db14432..5089cde9fe63 100644 --- a/mailnews/mime/src/mimeobj.cpp +++ b/mailnews/mime/src/mimeobj.cpp @@ -195,20 +195,29 @@ MimeObject_parse_begin (MimeObject *obj) /* If we haven't set up the state object yet, then this should be the outermost object... */ if (obj->options && !obj->options->state) - { - NS_ASSERTION(!obj->headers, "headers should be null"); /* should be the outermost object. */ + { + NS_ASSERTION(!obj->headers, "headers should be null"); /* should be the outermost object. */ - obj->options->state = new MimeParseStateObject; - if (!obj->options->state) return MIME_OUT_OF_MEMORY; - obj->options->state->root = obj; - obj->options->state->separator_suppressed_p = PR_TRUE; /* no first sep */ - const char *delParts = PL_strcasestr(obj->options->url, "&del="); - if (delParts) - { - delParts += 5; // advance past "&del=" - obj->options->state->partsToStrip.ParseString(delParts, ","); - } - } + obj->options->state = new MimeParseStateObject; + if (!obj->options->state) return MIME_OUT_OF_MEMORY; + obj->options->state->root = obj; + obj->options->state->separator_suppressed_p = PR_TRUE; /* no first sep */ + const char *delParts = PL_strcasestr(obj->options->url, "&del="); + const char *detachLocations = PL_strcasestr(obj->options->url, "&detachTo="); + if (delParts) + { + const char *delEnd = PL_strcasestr(delParts + 1, "&"); + if (!delEnd) + delEnd = delParts + strlen(delParts) - 1; + nsCAutoString partsToDel(Substring(delParts + 5, delEnd)); + obj->options->state->partsToStrip.ParseString(partsToDel.get(), ","); + } + if (detachLocations) + { + detachLocations += 10; // advance past "&detachTo=" + obj->options->state->detachToFiles.ParseString(detachLocations, ","); + } + } /* Decide whether this object should be output or not... */ if (!obj->options || !obj->options->output_fn