зеркало из https://github.com/mozilla/pjs.git
back end work for stripping mime attachments from messages, sr=mscott 2920
This commit is contained in:
Родитель
dc8257352c
Коммит
9733147b70
|
@ -47,7 +47,7 @@ interface nsIDOMWindowInternal;
|
|||
interface nsITransactionManager;
|
||||
interface nsIMsgMessageService;
|
||||
|
||||
[scriptable, uuid(74028422-7d28-4624-8fff-f8ec4288332a)]
|
||||
[scriptable, uuid(f0f5b18b-9fb8-44d8-ad41-8d837d89fa0c)]
|
||||
interface nsIMessenger : nsISupports {
|
||||
|
||||
const long eUnknown = 0;
|
||||
|
@ -106,12 +106,18 @@ interface nsIMessenger : nsISupports {
|
|||
void saveAllAttachments(in unsigned long count, [array, size_is(count)] in string contentTypeArray,
|
||||
[array, size_is(count)] in string urlArray, [array, size_is(count)] in string displayNameArray,
|
||||
[array, size_is(count)] in string messageUriArray);
|
||||
void detachAttachment(in string contentTpe, in string url, in string displayName, in string messageUri, in boolean saveFirst);
|
||||
void detachAllAttachments(in unsigned long count, [array, size_is(count)] in string contentTypeArray,
|
||||
[array, size_is(count)] in string urlArray, [array, size_is(count)] in string displayNameArray,
|
||||
[array, size_is(count)] in string messageUriArray, in boolean saveFirst);
|
||||
// saveAttachmentToFolder is used by the drag and drop code to drop an attachment to a destination folder
|
||||
// we need to return the actual file path (including the filename).
|
||||
nsILocalFile saveAttachmentToFolder(in string contentType, in string url, in string displayName, in string messageUri, in nsILocalFile aDestFolder);
|
||||
|
||||
attribute boolean sendingUnsentMsgs;
|
||||
|
||||
readonly attribute string lastDisplayedMessageUri;
|
||||
|
||||
nsIMsgMessageService messageServiceFromURI(in string uri);
|
||||
};
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
// mscott@netscape.com. It's critical that the code in here for displaying
|
||||
// the message headers for a selected message remain as fast as possible. In particular,
|
||||
// right now, we only introduce one reflow per message. i.e. if you click on a message in the thread
|
||||
// pane, we batch up all the changes for displaying the header pane (to, cc, attachements button, etc.)
|
||||
// pane, we batch up all the changes for displaying the header pane (to, cc, attachments button, etc.)
|
||||
// and we make a single pass to display them. It's critical that we maintain this one reflow per message
|
||||
// view in the message header pane.
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -70,6 +70,10 @@ var gOpenLabel;
|
|||
var gOpenLabelAccesskey;
|
||||
var gSaveLabel;
|
||||
var gSaveLabelAccesskey;
|
||||
var gDetachLabel;
|
||||
var gDetachLabelAccesskey;
|
||||
var gDeleteLabel;
|
||||
var gDeleteLabelAccesskey;
|
||||
var gMessengerBundle;
|
||||
var gProfileDirURL;
|
||||
var gIOService;
|
||||
|
@ -935,22 +939,96 @@ createNewAttachmentInfo.prototype.printAttachment = function printAttachment()
|
|||
*/
|
||||
}
|
||||
|
||||
createNewAttachmentInfo.prototype.detachAttachment = function detachAttachment()
|
||||
{
|
||||
messenger.detachAttachment(this.contentType,
|
||||
this.url,
|
||||
encodeURIComponent(this.displayName),
|
||||
this.uri,
|
||||
true); // save
|
||||
}
|
||||
|
||||
createNewAttachmentInfo.prototype.deleteAttachment = function deleteAttachment()
|
||||
{
|
||||
messenger.detachAttachment(this.contentType,
|
||||
this.url,
|
||||
encodeURIComponent(this.displayName),
|
||||
this.uri,
|
||||
false); // don't save
|
||||
}
|
||||
|
||||
createNewAttachmentInfo.prototype.isDeleted = function isDeleted()
|
||||
{
|
||||
return (this.contentType == 'text/x-moz-deleted');
|
||||
}
|
||||
|
||||
function onShowAttachmentContextMenu()
|
||||
{
|
||||
// if no attachments are selected, disable the Open and Save...
|
||||
var attachmentList = document.getElementById('attachmentList');
|
||||
var selectedAttachments = attachmentList.selectedItems;
|
||||
var isSelected = (selectedAttachments.length > 0);
|
||||
|
||||
// if no attachments are selected you can't open an attachment
|
||||
var openMenu = document.getElementById('context-openAttachment');
|
||||
var saveMenu = document.getElementById('context-saveAttachment');
|
||||
if (selectedAttachments.length > 0)
|
||||
if (isSelected)
|
||||
{
|
||||
openMenu.removeAttribute('disabled');
|
||||
saveMenu.removeAttribute('disabled');
|
||||
}
|
||||
else
|
||||
{
|
||||
openMenu.setAttribute('disabled', true);
|
||||
}
|
||||
|
||||
// determine if all *selected* attachments are deleted
|
||||
var isAllDeleted = true;
|
||||
for (index in selectedAttachments)
|
||||
{
|
||||
var listItem = selectedAttachments[index];
|
||||
isAllDeleted = listItem.attachment['isDeleted']();
|
||||
}
|
||||
|
||||
// if no attachments are selected, or all selected attachments are already
|
||||
// deleted, you can't save, detach or delete an attachment
|
||||
var saveMenu = document.getElementById('context-saveAttachment');
|
||||
var detachMenu = document.getElementById('context-detachAttachment');
|
||||
var deleteMenu = document.getElementById('context-deleteAttachment');
|
||||
if ( isSelected && !isAllDeleted )
|
||||
{
|
||||
saveMenu.removeAttribute('disabled');
|
||||
detachMenu.removeAttribute('disabled');
|
||||
deleteMenu.removeAttribute('disabled');
|
||||
}
|
||||
else
|
||||
{
|
||||
saveMenu.setAttribute('disabled', true);
|
||||
detachMenu.setAttribute('disabled', true);
|
||||
deleteMenu.setAttribute('disabled', true);
|
||||
}
|
||||
|
||||
// determine if *all* attachments are deleted
|
||||
isAllDeleted = true;
|
||||
for (index in currentAttachments)
|
||||
{
|
||||
var attachment = currentAttachments[index];
|
||||
isAllDeleted = (attachment.contentType == 'text/x-moz-deleted');
|
||||
}
|
||||
|
||||
// if all selected attachments are already deleted, you can't save, detach or
|
||||
// delete any attachments
|
||||
var saveAllMenu = document.getElementById('context-saveAllAttachments');
|
||||
var detachAllMenu = document.getElementById('context-detachAllAttachments');
|
||||
var deleteAllMenu = document.getElementById('context-deleteAllAttachments');
|
||||
if ( !isAllDeleted )
|
||||
{
|
||||
saveAllMenu.removeAttribute('disabled');
|
||||
detachAllMenu.removeAttribute('disabled');
|
||||
deleteAllMenu.removeAttribute('disabled');
|
||||
}
|
||||
else
|
||||
{
|
||||
saveAllMenu.setAttribute('disabled', true);
|
||||
detachAllMenu.setAttribute('disabled', true);
|
||||
deleteAllMenu.setAttribute('disabled', true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1018,7 +1096,10 @@ function displayAttachmentsForExpandedView()
|
|||
function setApplicationIconForAttachment(attachment, listitem)
|
||||
{
|
||||
// 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=16&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=16&contentType=" + attachment.contentType);
|
||||
}
|
||||
|
||||
function displayAttachmentsForCollapsedView()
|
||||
|
@ -1045,20 +1126,41 @@ function FillAttachmentListPopup(popup)
|
|||
|
||||
if (!gBuildAttachmentPopupForCurrentMsg) return;
|
||||
|
||||
var attachmentIndex = 0;
|
||||
|
||||
// otherwise we need to build the attachment view...
|
||||
// First clear out the old view...
|
||||
ClearAttachmentMenu(popup);
|
||||
|
||||
// add all attachments to the popup and determine if *all* attachments
|
||||
// are deleted
|
||||
var attachmentIndex = 0;
|
||||
var isAllDeleted = true;
|
||||
for (index in currentAttachments)
|
||||
{
|
||||
++attachmentIndex;
|
||||
addAttachmentToPopup(popup, currentAttachments[index], attachmentIndex);
|
||||
var attachment = currentAttachments[index];
|
||||
isAllDeleted = (attachment.contentType == 'text/x-moz-deleted');
|
||||
addAttachmentToPopup(popup, attachment, attachmentIndex);
|
||||
}
|
||||
|
||||
// if all selected attachments are already deleted, you can't save, detach or
|
||||
// delete any attachments
|
||||
var saveAllMenu = document.getElementById('file-saveAllAttachments');
|
||||
var detachAllMenu = document.getElementById('file-detachAllAttachments');
|
||||
var deleteAllMenu = document.getElementById('file-deleteAllAttachments');
|
||||
if ( !isAllDeleted )
|
||||
{
|
||||
saveAllMenu.removeAttribute('disabled');
|
||||
detachAllMenu.removeAttribute('disabled');
|
||||
deleteAllMenu.removeAttribute('disabled');
|
||||
}
|
||||
else
|
||||
{
|
||||
saveAllMenu.setAttribute('disabled', true);
|
||||
detachAllMenu.setAttribute('disabled', true);
|
||||
deleteAllMenu.setAttribute('disabled', true);
|
||||
}
|
||||
|
||||
gBuildAttachmentPopupForCurrentMsg = false;
|
||||
|
||||
}
|
||||
|
||||
// Public method used to clear the file attachment menu
|
||||
|
@ -1066,7 +1168,8 @@ function ClearAttachmentMenu(popup)
|
|||
{
|
||||
if ( popup )
|
||||
{
|
||||
while ( popup.childNodes.length > 2 )
|
||||
//Note: 4 == number of entries in attachmentMenuList definition in msgHdrViewOverlay.xul
|
||||
while ( popup.childNodes.length > 4 )
|
||||
popup.removeChild(popup.childNodes[0]);
|
||||
}
|
||||
}
|
||||
|
@ -1088,11 +1191,12 @@ function addAttachmentToPopup(popup, attachment, attachmentIndex)
|
|||
if (!gMessengerBundle)
|
||||
gMessengerBundle = document.getElementById("bundle_messenger");
|
||||
|
||||
// insert the item just before the separator...the separator is the 2nd to last element in the popup.
|
||||
// insert the item just before the separator...
|
||||
item.setAttribute('class', 'menu-iconic');
|
||||
setApplicationIconForAttachment(attachment,item);
|
||||
var numItemsInPopup = popup.childNodes.length;
|
||||
item = popup.insertBefore(item, popup.childNodes[numItemsInPopup-2]);
|
||||
//Note: 4 == number of entries in attachmentMenuList definition in msgHdrViewOverlay.xul
|
||||
item = popup.insertBefore(item, popup.childNodes[numItemsInPopup-4]);
|
||||
|
||||
var formattedDisplayNameString = gMessengerBundle.getFormattedString("attachmentDisplayNameFormat",
|
||||
[attachmentIndex, attachment.displayName]);
|
||||
|
@ -1116,6 +1220,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);
|
||||
|
@ -1129,12 +1241,32 @@ function addAttachmentToPopup(popup, attachment, attachmentIndex)
|
|||
menuitementry.setAttribute('oncommand', 'this.attachment.saveAttachment()');
|
||||
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
|
||||
{
|
||||
|
@ -1145,23 +1277,42 @@ 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] = encodeURI(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);
|
||||
// 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 save all attachments **\n");
|
||||
dump ("** failed to handle all attachments **\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -60,13 +60,27 @@
|
|||
oncommand="handleAttachmentSelection('openAttachment');"/>
|
||||
<menuitem id="context-saveAttachment" label="&saveAsAttachmentCmd.label;" accesskey="&saveAsAttachmentCmd.accesskey;"
|
||||
oncommand="handleAttachmentSelection('saveAttachment');"/>
|
||||
<menuitem id="context-detachAttachment" label="&detachAttachmentCmd.label;"
|
||||
oncommand="handleAttachmentSelection('detachAttachment');"/>
|
||||
<menuitem id="context-deleteAttachment" label="&deleteAttachmentCmd.label;"
|
||||
oncommand="handleAttachmentSelection('deleteAttachment');"/>
|
||||
<menuseparator/>
|
||||
<menuitem id="context-saveAllAttachments" oncommand="SaveAllAttachments();" label="&saveAllAttachmentsCmd.label;" accesskey="&saveAllAttachmentsCmd.accesskey;"/>
|
||||
<menuitem id="context-saveAllAttachments" oncommand="HandleAllAttachments('save');"
|
||||
label="&saveAllAttachmentsCmd.label;" accesskey="&saveAllAttachmentsCmd.accesskey;"/>
|
||||
<menuitem id="context-detachAllAttachments" oncommand="HandleAllAttachments('detach');"
|
||||
label="&detachAllAttachmentsCmd.label;"/>
|
||||
<menuitem id="context-deleteAllAttachments" oncommand="HandleAllAttachments('delete');"
|
||||
label="&deleteAllAttachmentsCmd.label;"/>
|
||||
</popup>
|
||||
|
||||
<popup id="attachmentMenuList">
|
||||
<menuseparator/>
|
||||
<menuitem label="&saveAllAttachmentsCmd.label;" accesskey="&saveAllAttachmentsCmd.accesskey;" oncommand="SaveAllAttachments();"/>
|
||||
<menuitem id="file-saveAllAttachments" label="&saveAllAttachmentsCmd.label;"
|
||||
accesskey="&saveAllAttachmentsCmd.accesskey;" oncommand="HandleAllAttachments('save');"/>
|
||||
<menuitem id="file-detachAllAttachments" label="&detachAllAttachmentsCmd.label;"
|
||||
accesskey="&detachAllAttachmentsCmd.accesskey;" oncommand="HandleAllAttachments('detach');" />
|
||||
<menuitem id="file-deleteAllAttachments" label="&deleteAllAttachmentsCmd.label;"
|
||||
accesskey="&deleteAllAttachmentsCmd.accesskey;" oncommand="HandleAllAttachments('delete');" />
|
||||
</popup>
|
||||
|
||||
<tooltip id="attachmentListTooltip"
|
||||
|
@ -160,7 +174,7 @@
|
|||
|
||||
<vbox id="expandedAttachmentBox" class="header-part1" keywordrelated="true" originalclass="header-part1" collapsed="true">
|
||||
<label id="attachmentText" value="&attachmentsTree.label;" crop="right"/>
|
||||
<listbox id="attachmentList" rows="3" style="overflow: auto;"
|
||||
<listbox id="attachmentList" rows="3" style="overflow: auto;" seltype="multiple"
|
||||
onclick="attachmentListClick(event);" ondraggesture="nsDragAndDrop.startDrag(event,attachmentAreaDNDObserver);" ondragover="nsDragAndDrop.dragOver(event, attachmentAreaDNDObserver);" context="attachmentListContext"/>
|
||||
</vbox>
|
||||
</hbox>
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -61,8 +61,14 @@
|
|||
<!ENTITY saveAsAttachmentCmd.accesskey "A">
|
||||
<!ENTITY printAttachmentCmd.label ".Print">
|
||||
<!ENTITY printAttachmentCmd.accesskey "P">
|
||||
<!ENTITY detachAttachmentCmd.label "Detach ...">
|
||||
<!ENTITY deleteAttachmentCmd.label "Delete">
|
||||
<!ENTITY saveAllAttachmentsCmd.label "Save All...">
|
||||
<!ENTITY saveAllAttachmentsCmd.accesskey "S">
|
||||
<!ENTITY detachAllAttachmentsCmd.label "Detach All...">
|
||||
<!ENTITY detachAllAttachmentsCmd.accesskey "D">
|
||||
<!ENTITY deleteAllAttachmentsCmd.label "Delete All...">
|
||||
<!ENTITY deleteAllAttachmentsCmd.accesskey "E">
|
||||
|
||||
<!ENTITY copyLinkCmd.label "Copy Link Location">
|
||||
<!ENTITY copyLinkCmd.accesskey "C">
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
* Contributor(s):
|
||||
* philip zhao <philip.zhao@sun.com>
|
||||
* Seth Spitzer <sspitzer@netscape.com>
|
||||
* Brodie Thiesfield <brofield@jellycan.com>
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||
|
@ -52,6 +53,8 @@
|
|||
#include "nsIFileSpec.h"
|
||||
#include "nsILocalFile.h"
|
||||
#include "nsISupportsObsolete.h"
|
||||
#include "nsSpecialSystemDirectory.h"
|
||||
#include "nsQuickSort.h"
|
||||
#if defined(XP_MAC) || defined(XP_MACOSX)
|
||||
#include "nsIAppleFileDecoder.h"
|
||||
#if defined(XP_MACOSX)
|
||||
|
@ -108,6 +111,8 @@
|
|||
#include "nsIMsgStatusFeedback.h"
|
||||
#include "nsMsgRDFUtils.h"
|
||||
|
||||
#include "nsIMsgHdr.h"
|
||||
|
||||
// compose
|
||||
#include "nsMsgCompCID.h"
|
||||
#include "nsMsgI18N.h"
|
||||
|
@ -151,6 +156,8 @@ static NS_DEFINE_CID(kMsgSendLaterCID, NS_MSGSENDLATER_CID);
|
|||
#define MESSENGER_SAVE_DIR_PREF_NAME "messenger.save.dir"
|
||||
#define MAILNEWS_ALLOW_PLUGINS_PREF_NAME "mailnews.message_display.allow.plugins"
|
||||
|
||||
#define MIMETYPE_DELETED "text/x-moz-deleted"
|
||||
|
||||
//
|
||||
// Convert an nsString buffer to plain text...
|
||||
//
|
||||
|
@ -1523,6 +1530,18 @@ NS_IMETHODIMP nsMessenger::SetDocumentCharset(const char *characterSet)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsMessenger::GetLastDisplayedMessageUri(char ** aLastDisplayedMessageUri)
|
||||
{
|
||||
if (!aLastDisplayedMessageUri)
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
*aLastDisplayedMessageUri = NS_STATIC_CAST(char*,
|
||||
nsMemory::Clone(mLastDisplayURI.get(), mLastDisplayURI.Length()+1));
|
||||
if (!*aLastDisplayedMessageUri)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// This is the listener class for the send operation.
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -2149,3 +2168,694 @@ nsMessenger::SetLastSaveDirectory(nsILocalFile *aLocalFile)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Detach/Delete Attachments
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static char * GetAttachmentPartId(const char * aAttachmentUrl)
|
||||
{
|
||||
static const char partIdPrefix[] = "part=";
|
||||
char * partId = strstr(aAttachmentUrl, partIdPrefix);
|
||||
return partId ? (partId + sizeof(partIdPrefix) - 1) : nsnull;
|
||||
}
|
||||
|
||||
static int CompareAttachmentPartId(const char * aAttachUrlLeft, const char * aAttachUrlRight)
|
||||
{
|
||||
// part ids are numbers separated by periods, like "1.2.3.4".
|
||||
// we sort by doing a numerical comparison on each item in turn. e.g. "1.4" < "1.25"
|
||||
// shorter entries come before longer entries. e.g. "1.4" < "1.4.1.2"
|
||||
// return values:
|
||||
// -2 left is a parent of right
|
||||
// -1 left is less than right
|
||||
// 0 left == right
|
||||
// 1 right is greater than left
|
||||
// 2 right is a parent of left
|
||||
|
||||
char * partIdLeft = GetAttachmentPartId(aAttachUrlLeft);
|
||||
char * partIdRight = GetAttachmentPartId(aAttachUrlRight);
|
||||
long idLeft, idRight;
|
||||
do
|
||||
{
|
||||
NS_ABORT_IF_FALSE(partIdLeft && NS_IS_DIGIT(*partIdLeft), "Invalid character in part id string");
|
||||
NS_ABORT_IF_FALSE(partIdRight && NS_IS_DIGIT(*partIdRight), "Invalid character in part id string");
|
||||
|
||||
// if the part numbers are different then the numerically smaller one is first
|
||||
idLeft = strtol(partIdLeft, &partIdLeft, 10);
|
||||
idRight = strtol(partIdRight, &partIdRight, 10);
|
||||
if (idLeft != idRight)
|
||||
return idLeft < idRight ? -1 : 1;
|
||||
|
||||
// if one part id is complete but the other isn't, then the shortest one
|
||||
// is first (parents before children)
|
||||
if (*partIdLeft != *partIdRight)
|
||||
return *partIdRight ? -2 : 2;
|
||||
|
||||
// if both part ids are complete (*partIdLeft == *partIdRight now) then
|
||||
// they are equal
|
||||
if (!*partIdLeft)
|
||||
return 0;
|
||||
|
||||
NS_ABORT_IF_FALSE(*partIdLeft == '.', "Invalid character in part id string");
|
||||
NS_ABORT_IF_FALSE(*partIdRight == '.', "Invalid character in part id string");
|
||||
|
||||
++partIdLeft;
|
||||
++partIdRight;
|
||||
}
|
||||
while (PR_TRUE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ------------------------------------
|
||||
|
||||
// struct on purpose -> show that we don't ever want a vtable
|
||||
struct msgAttachment
|
||||
{
|
||||
msgAttachment()
|
||||
: mContentType(nsnull),
|
||||
mUrl(nsnull),
|
||||
mDisplayName(nsnull),
|
||||
mMessageUri(nsnull)
|
||||
{
|
||||
}
|
||||
|
||||
~msgAttachment()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
CRTFREEIF(mContentType);
|
||||
CRTFREEIF(mUrl);
|
||||
CRTFREEIF(mDisplayName);
|
||||
CRTFREEIF(mMessageUri);
|
||||
}
|
||||
|
||||
PRBool Init(const char * aContentType, const char * aUrl,
|
||||
const char * aDisplayName, const char * aMessageUri)
|
||||
{
|
||||
Clear();
|
||||
mContentType = nsCRT::strdup(aContentType);
|
||||
mUrl = nsCRT::strdup(aUrl);
|
||||
mDisplayName = nsCRT::strdup(aDisplayName);
|
||||
mMessageUri = nsCRT::strdup(aMessageUri);
|
||||
return (mContentType && mUrl && mDisplayName && mMessageUri);
|
||||
}
|
||||
|
||||
// take the pointers from aSource
|
||||
void Adopt(msgAttachment & aSource)
|
||||
{
|
||||
Clear();
|
||||
|
||||
mContentType = aSource.mContentType;
|
||||
mUrl = aSource.mUrl;
|
||||
mDisplayName = aSource.mDisplayName;
|
||||
mMessageUri = aSource.mMessageUri;
|
||||
|
||||
aSource.mContentType = nsnull;
|
||||
aSource.mUrl = nsnull;
|
||||
aSource.mDisplayName = nsnull;
|
||||
aSource.mMessageUri = nsnull;
|
||||
}
|
||||
|
||||
char* mContentType;
|
||||
char* mUrl;
|
||||
char* mDisplayName;
|
||||
char* mMessageUri;
|
||||
|
||||
private:
|
||||
// disable by not implementing
|
||||
msgAttachment(const msgAttachment & rhs);
|
||||
msgAttachment & operator=(const msgAttachment & rhs);
|
||||
};
|
||||
|
||||
// ------------------------------------
|
||||
|
||||
class nsAttachmentState
|
||||
{
|
||||
public:
|
||||
nsAttachmentState();
|
||||
~nsAttachmentState();
|
||||
nsresult Init(PRUint32 aCount,
|
||||
const char **aContentTypeArray,
|
||||
const char **aUrlArray,
|
||||
const char **aDisplayNameArray,
|
||||
const char **aMessageUriArray);
|
||||
nsresult PrepareForAttachmentDelete();
|
||||
|
||||
private:
|
||||
static int SortAttachmentsByPartId(const void * aLeft, const void * aRight, void *);
|
||||
|
||||
public:
|
||||
PRUint32 mCount;
|
||||
PRUint32 mCurIndex;
|
||||
msgAttachment* mAttachmentArray;
|
||||
};
|
||||
|
||||
nsAttachmentState::nsAttachmentState()
|
||||
: mCount(0),
|
||||
mCurIndex(0),
|
||||
mAttachmentArray(nsnull)
|
||||
{
|
||||
}
|
||||
|
||||
nsAttachmentState::~nsAttachmentState()
|
||||
{
|
||||
delete[] mAttachmentArray;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsAttachmentState::Init(PRUint32 aCount, const char ** aContentTypeArray,
|
||||
const char ** aUrlArray, const char ** aDisplayNameArray,
|
||||
const char ** aMessageUriArray)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(aCount > 0, "count is invalid");
|
||||
|
||||
mCount = aCount;
|
||||
mCurIndex = 0;
|
||||
delete[] mAttachmentArray;
|
||||
|
||||
mAttachmentArray = new msgAttachment[aCount];
|
||||
if (!mAttachmentArray)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
for(PRUint32 u = 0; u < aCount; ++u)
|
||||
{
|
||||
if (!mAttachmentArray[u].Init(aContentTypeArray[u], aUrlArray[u],
|
||||
aDisplayNameArray[u], aMessageUriArray[u]))
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsAttachmentState::PrepareForAttachmentDelete()
|
||||
{
|
||||
// this must be called before any processing
|
||||
if (mCurIndex != 0)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
// this prepares the attachment list for use in deletion. In order to prepare, we
|
||||
// sorts the attachments in numerical ascending order on their part id, remove all
|
||||
// duplicates and remove any subparts which will be removed automatically by the
|
||||
// removal of the parent.
|
||||
//
|
||||
// e.g. the attachment list processing (showing only part ids)
|
||||
// before: 1.11, 1.3, 1.2, 1.2.1.3, 1.4.1.2
|
||||
// sorted: 1.2, 1.2.1.3, 1.3, 1.4.1.2, 1.11
|
||||
// after: 1.2, 1.3, 1.4.1.2, 1.11
|
||||
|
||||
// sort
|
||||
NS_QuickSort(mAttachmentArray, mCount, sizeof(msgAttachment), SortAttachmentsByPartId, nsnull);
|
||||
|
||||
// remove duplicates and sub-items
|
||||
int nCompare;
|
||||
for(PRUint32 u = 1; u < mCount;)
|
||||
{
|
||||
nCompare = ::CompareAttachmentPartId(mAttachmentArray[u-1].mUrl, mAttachmentArray[u].mUrl);
|
||||
if (nCompare == 0 || nCompare == -2) // [u-1] is the same as or a parent of [u]
|
||||
{
|
||||
// shuffle the array down (and thus keeping the sorted order)
|
||||
// this will get rid of the current unnecessary element
|
||||
for (PRUint32 i = u + 1; i < mCount; ++i)
|
||||
{
|
||||
mAttachmentArray[i-1].Adopt(mAttachmentArray[i]);
|
||||
}
|
||||
--mCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
++u;
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
int
|
||||
nsAttachmentState::SortAttachmentsByPartId(const void * aLeft, const void * aRight, void *)
|
||||
{
|
||||
msgAttachment & attachLeft = *((msgAttachment*) aLeft);
|
||||
msgAttachment & attachRight = *((msgAttachment*) aRight);
|
||||
return ::CompareAttachmentPartId(attachLeft.mUrl, attachRight.mUrl);
|
||||
}
|
||||
|
||||
// ------------------------------------
|
||||
|
||||
class nsDelAttachListener : public nsIStreamListener,
|
||||
public nsIUrlListener,
|
||||
public nsIMsgCopyServiceListener
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSISTREAMLISTENER
|
||||
NS_DECL_NSIREQUESTOBSERVER
|
||||
NS_DECL_NSIURLLISTENER
|
||||
NS_DECL_NSIMSGCOPYSERVICELISTENER
|
||||
|
||||
public:
|
||||
nsDelAttachListener();
|
||||
virtual ~nsDelAttachListener();
|
||||
nsresult StartProcessing(nsMessenger * aMessenger, nsIMsgWindow * aMsgWindow,
|
||||
nsAttachmentState * aAttach, PRBool aSaveFirst);
|
||||
|
||||
public:
|
||||
nsAttachmentState * mAttach; // list of attachments to process
|
||||
PRBool mSaveFirst; // detach (PR_TRUE) or delete (PR_FALSE)
|
||||
nsCOMPtr<nsIFileSpec> mMsgFileSpec; // temporary file (processed mail)
|
||||
nsCOMPtr<nsIOutputStream> mMsgFileStream; // temporary file (processed mail)
|
||||
nsCOMPtr<nsIMsgMessageService> mMessageService; // original message service
|
||||
nsCOMPtr<nsIMsgDBHdr> mOriginalMessage; // original message header
|
||||
nsCOMPtr<nsIMsgFolder> mMessageFolder; // original message folder
|
||||
nsCOMPtr<nsIMessenger> mMessenger; // our messenger instance
|
||||
nsCOMPtr<nsIMsgWindow> mMsgWindow; // our UI window
|
||||
PRUint32 mNewMessageKey; // new message key
|
||||
|
||||
// temp
|
||||
PRBool mWrittenExtra;
|
||||
};
|
||||
|
||||
//
|
||||
// nsISupports
|
||||
//
|
||||
NS_IMPL_ISUPPORTS3(nsDelAttachListener,nsIStreamListener,nsIUrlListener,nsIMsgCopyServiceListener)
|
||||
|
||||
//
|
||||
// nsIRequestObserver
|
||||
//
|
||||
NS_IMETHODIMP
|
||||
nsDelAttachListener::OnStartRequest(nsIRequest * aRequest, nsISupports * aContext)
|
||||
{
|
||||
// called when we start processing the StreamMessage request.
|
||||
// This is called after OnStartRunningUrl().
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDelAttachListener::OnStopRequest(nsIRequest * aRequest, nsISupports * aContext, nsresult aStatusCode)
|
||||
{
|
||||
// called when we have completed processing the StreamMessage request.
|
||||
// This is called after OnStopRequest(). This means that we have now
|
||||
// received all data of the message and we have completed processing.
|
||||
// We now start to copy the processed message from the temporary file
|
||||
// back into the message store, replacing the original message.
|
||||
|
||||
if (NS_FAILED(aStatusCode))
|
||||
return aStatusCode;
|
||||
|
||||
// called when we complete processing of the StreamMessage request.
|
||||
// This is called before OnStopRunningUrl().
|
||||
nsresult rv;
|
||||
|
||||
// all attachments refer to the same message
|
||||
const char * messageUri = mAttach->mAttachmentArray[0].mMessageUri;
|
||||
|
||||
// copy the file back into the folder. Note: if we set msgToReplace then
|
||||
// CopyFileMessage() fails, do the delete ourselves
|
||||
nsCOMPtr<nsIMsgCopyServiceListener> listenerCopyService;
|
||||
rv = this->QueryInterface( NS_GET_IID(nsIMsgCopyServiceListener), getter_AddRefs(listenerCopyService) );
|
||||
NS_ENSURE_SUCCESS(rv,rv);
|
||||
|
||||
mMsgFileStream = nsnull;
|
||||
mMsgFileSpec->CloseStream();
|
||||
mNewMessageKey = PR_UINT32_MAX;
|
||||
rv = mMessageFolder->CopyFileMessage(
|
||||
mMsgFileSpec, // fileSpec
|
||||
nsnull, // msgToReplace
|
||||
PR_FALSE, // isDraft
|
||||
mMsgWindow, // msgWindow
|
||||
listenerCopyService); // listener
|
||||
NS_ENSURE_SUCCESS(rv,rv);
|
||||
// only if the currently selected message is the one that we are about to delete then we
|
||||
// change the selection to the new message that we just added. Failures in this code are not fatal.
|
||||
// Note that can only do this if we have the new message key, which we don't always get from IMAP.
|
||||
if (mNewMessageKey != PR_UINT32_MAX && mMsgWindow)
|
||||
{
|
||||
nsXPIDLCString displayUri;
|
||||
mMessenger->GetLastDisplayedMessageUri(getter_Copies(displayUri));
|
||||
if (displayUri.Equals(messageUri))
|
||||
{
|
||||
mMessageFolder->GenerateMessageURI(mNewMessageKey, getter_Copies(displayUri));
|
||||
if (displayUri)
|
||||
{
|
||||
mMsgWindow->SelectMessage(displayUri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// delete the original message
|
||||
nsCOMPtr<nsISupportsArray> messageArray;
|
||||
rv = NS_NewISupportsArray(getter_AddRefs(messageArray));
|
||||
NS_ENSURE_SUCCESS(rv,rv);
|
||||
rv = messageArray->AppendElement(mOriginalMessage);
|
||||
NS_ENSURE_SUCCESS(rv,rv);
|
||||
return mMessageFolder->DeleteMessages(
|
||||
messageArray, // messages
|
||||
mMsgWindow, // msgWindow
|
||||
PR_TRUE, // deleteStorage
|
||||
PR_TRUE, // isMove
|
||||
listenerCopyService, // listener
|
||||
PR_FALSE); // allowUndo
|
||||
}
|
||||
|
||||
//
|
||||
// nsIStreamListener
|
||||
//
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDelAttachListener::OnDataAvailable(nsIRequest * aRequest, nsISupports * aSupport,
|
||||
nsIInputStream * aInStream, PRUint32 aSrcOffset,
|
||||
PRUint32 aCount)
|
||||
{
|
||||
if (!mMsgFileStream)
|
||||
return NS_ERROR_NULL_POINTER;
|
||||
// the 'correct' way is something like creating a stream converter that calls a
|
||||
// mime converter which we pass stuff to and it will modify the stream for us.
|
||||
// I haven't got that bit working yet so at the moment we just write the message
|
||||
// out raw to the file.
|
||||
|
||||
PRUint32 uiWritten;
|
||||
return mMsgFileStream->WriteFrom(aInStream, aCount, &uiWritten);
|
||||
}
|
||||
|
||||
//
|
||||
// nsIUrlListener
|
||||
//
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDelAttachListener::OnStartRunningUrl(nsIURI * aUrl)
|
||||
{
|
||||
// called when we start processing the StreamMessage request. This is
|
||||
// called before OnStartRequest().
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDelAttachListener::OnStopRunningUrl(nsIURI * aUrl, nsresult aExitCode)
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//
|
||||
// nsIMsgCopyServiceListener
|
||||
//
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDelAttachListener::OnStartCopy(void)
|
||||
{
|
||||
// never called?
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDelAttachListener::OnProgress(PRUint32 aProgress, PRUint32 aProgressMax)
|
||||
{
|
||||
// never called?
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDelAttachListener::SetMessageKey(PRUint32 aKey)
|
||||
{
|
||||
// called during the copy of the modified message back into the message
|
||||
// store to notify us of the message key of the newly created message.
|
||||
mNewMessageKey = aKey;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDelAttachListener::GetMessageId(nsCString * aMessageId)
|
||||
{
|
||||
// never called?
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDelAttachListener::OnStopCopy(nsresult aStatus)
|
||||
{
|
||||
// never called?
|
||||
return aStatus;
|
||||
}
|
||||
|
||||
//
|
||||
// local methods
|
||||
//
|
||||
|
||||
nsDelAttachListener::nsDelAttachListener()
|
||||
{
|
||||
mAttach = nsnull;
|
||||
mSaveFirst = PR_FALSE;
|
||||
mWrittenExtra = PR_FALSE;
|
||||
mNewMessageKey = PR_UINT32_MAX;
|
||||
}
|
||||
|
||||
nsDelAttachListener::~nsDelAttachListener()
|
||||
{
|
||||
if (mAttach)
|
||||
{
|
||||
delete mAttach;
|
||||
}
|
||||
if (mMsgFileStream)
|
||||
{
|
||||
mMsgFileStream->Close();
|
||||
mMsgFileStream = 0;
|
||||
}
|
||||
if (mMsgFileSpec)
|
||||
{
|
||||
mMsgFileSpec->Flush();
|
||||
mMsgFileSpec->CloseStream();
|
||||
mMsgFileSpec->Delete(PR_FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDelAttachListener::StartProcessing(nsMessenger * aMessenger, nsIMsgWindow * aMsgWindow,
|
||||
nsAttachmentState * aAttach, PRBool aSaveFirst)
|
||||
{
|
||||
aMessenger->QueryInterface(NS_GET_IID(nsIMessenger), getter_AddRefs(mMessenger));
|
||||
mMsgWindow = aMsgWindow;
|
||||
mAttach = aAttach;
|
||||
mSaveFirst = aSaveFirst;
|
||||
|
||||
nsresult rv;
|
||||
|
||||
// all attachments refer to the same message
|
||||
const char * messageUri = mAttach->mAttachmentArray[0].mMessageUri;
|
||||
|
||||
// get the message service, original message and folder for this message
|
||||
rv = GetMessageServiceFromURI(messageUri, getter_AddRefs(mMessageService));
|
||||
NS_ENSURE_SUCCESS(rv,rv);
|
||||
rv = mMessageService->MessageURIToMsgHdr(messageUri, getter_AddRefs(mOriginalMessage));
|
||||
NS_ENSURE_SUCCESS(rv,rv);
|
||||
rv = mOriginalMessage->GetFolder(getter_AddRefs(mMessageFolder));
|
||||
NS_ENSURE_SUCCESS(rv,rv);
|
||||
|
||||
// ensure that we can store and delete messages in this folder, if we
|
||||
// can't then we can't do attachment deleting
|
||||
PRBool canDelete = PR_FALSE;
|
||||
mMessageFolder->GetCanDeleteMessages(&canDelete);
|
||||
PRBool canFile = PR_FALSE;
|
||||
mMessageFolder->GetCanFileMessages(&canFile);
|
||||
if (!canDelete || !canFile)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
// create an output stream on a temporary file. This stream will save the modified
|
||||
// message data to a file which we will later use to replace the existing message.
|
||||
// The file is removed in the destructor.
|
||||
nsFileSpec * msgFileSpec = new nsFileSpec(
|
||||
nsSpecialSystemDirectory(nsSpecialSystemDirectory::OS_TemporaryDirectory) );
|
||||
if (!msgFileSpec) return NS_ERROR_OUT_OF_MEMORY;
|
||||
*msgFileSpec += "nsmail.tmp";
|
||||
msgFileSpec->MakeUnique();
|
||||
rv = NS_NewFileSpecWithSpec(*msgFileSpec, getter_AddRefs(mMsgFileSpec));
|
||||
nsCOMPtr<nsILocalFile> msgFile;
|
||||
if (NS_SUCCEEDED(rv))
|
||||
rv = NS_FileSpecToIFile(msgFileSpec, getter_AddRefs(msgFile));
|
||||
delete msgFileSpec;
|
||||
NS_ENSURE_SUCCESS(rv,rv);
|
||||
nsCOMPtr<nsIOutputStream> fileOutputStream;
|
||||
rv = NS_NewLocalFileOutputStream(getter_AddRefs(fileOutputStream), msgFile, -1, 00600);
|
||||
NS_ENSURE_SUCCESS(rv,rv);
|
||||
rv = NS_NewBufferedOutputStream(getter_AddRefs(mMsgFileStream), fileOutputStream, FOUR_K);
|
||||
NS_ENSURE_SUCCESS(rv,rv);
|
||||
|
||||
// create the additional header for data conversion. This will tell the stream converter
|
||||
// which MIME emitter we want to use, and it will tell the MIME emitter which attachments
|
||||
// should be deleted.
|
||||
const char * partId;
|
||||
const char * nextField;
|
||||
nsCAutoString sHeader("attach&del=");
|
||||
for (PRUint32 u = 0; u < mAttach->mCount; ++u)
|
||||
{
|
||||
if (u > 0)
|
||||
sHeader.Append(",");
|
||||
partId = GetAttachmentPartId(mAttach->mAttachmentArray[u].mUrl);
|
||||
nextField = PL_strchr(partId, '&');
|
||||
sHeader.Append(partId, nextField ? nextField - partId : -1);
|
||||
}
|
||||
|
||||
// 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<nsISupports> listenerSupports;
|
||||
rv = this->QueryInterface(NS_GET_IID(nsISupports), getter_AddRefs(listenerSupports));
|
||||
NS_ENSURE_SUCCESS(rv,rv);
|
||||
nsCOMPtr<nsIUrlListener> listenerUrlListener = do_QueryInterface(listenerSupports, &rv);
|
||||
NS_ENSURE_SUCCESS(rv,rv);
|
||||
rv = mMessageService->StreamMessage(
|
||||
messageUri, // aMessageURI
|
||||
listenerSupports, // aConsumer
|
||||
mMsgWindow, // aMsgWindow
|
||||
listenerUrlListener, // aUrlListener
|
||||
PR_TRUE, // aConvertData
|
||||
sHeader.get(), // aAdditionalHeader
|
||||
nsnull ); // requestUri
|
||||
NS_ENSURE_SUCCESS(rv,rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// ------------------------------------
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsMessenger::DetachAttachment(const char * aContentType, const char * aUrl,
|
||||
const char * aDisplayName, const char * aMessageUri,
|
||||
PRBool aSaveFirst)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aContentType);
|
||||
NS_ENSURE_ARG_POINTER(aUrl);
|
||||
NS_ENSURE_ARG_POINTER(aDisplayName);
|
||||
NS_ENSURE_ARG_POINTER(aMessageUri);
|
||||
|
||||
// convenience function for JS, processing handled by DetachAllAttachments()
|
||||
return DetachAllAttachments(1, &aContentType, &aUrl, &aDisplayName, &aMessageUri, aSaveFirst);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsMessenger::DetachAllAttachments(PRUint32 aCount,
|
||||
const char ** aContentTypeArray,
|
||||
const char ** aUrlArray,
|
||||
const char ** aDisplayNameArray,
|
||||
const char ** aMessageUriArray,
|
||||
PRBool aSaveFirst)
|
||||
{
|
||||
NS_ENSURE_ARG_MIN(aCount, 1);
|
||||
NS_ENSURE_ARG_POINTER(aContentTypeArray);
|
||||
NS_ENSURE_ARG_POINTER(aUrlArray);
|
||||
NS_ENSURE_ARG_POINTER(aDisplayNameArray);
|
||||
NS_ENSURE_ARG_POINTER(aMessageUriArray);
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
// ensure that our arguments are valid
|
||||
// char * partId;
|
||||
for (PRUint32 u = 0; u < aCount; ++u)
|
||||
{
|
||||
// ensure all of the message URI are the same, we cannot process
|
||||
// attachments from different messages
|
||||
if (u > 0 && 0 != nsCRT::strcmp(aMessageUriArray[0], aMessageUriArray[u]))
|
||||
{
|
||||
rv = NS_ERROR_INVALID_ARG;
|
||||
break;
|
||||
}
|
||||
|
||||
// ensure that we don't have deleted messages in this list
|
||||
if (0 == nsCRT::strcmp(aContentTypeArray[u], MIMETYPE_DELETED))
|
||||
{
|
||||
rv = NS_ERROR_INVALID_ARG;
|
||||
break;
|
||||
}
|
||||
|
||||
// for the moment we prevent any attachments other than root level
|
||||
// attachments being deleted (i.e. you can't delete attachments from a
|
||||
// email forwarded as an attachment). We do this by ensuring that the
|
||||
// part id only has a single period in it (e.g. "1.2").
|
||||
//TODO: support non-root level attachment delete
|
||||
// partId = ::GetAttachmentPartId(aUrlArray[u]);
|
||||
// if (!partId || PL_strchr(partId, '.') != PL_strrchr(partId, '.'))
|
||||
// {
|
||||
// rv = NS_ERROR_INVALID_ARG;
|
||||
// break;
|
||||
// }
|
||||
}
|
||||
if (NS_FAILED(rv))
|
||||
{
|
||||
Alert("deleteAttachmentFailure");
|
||||
return rv;
|
||||
}
|
||||
|
||||
//TODO: ensure that nothing else is processing this message uri at the same time
|
||||
|
||||
//TODO: if any of the selected attachments are messages that contain other
|
||||
// attachments we need to warn the user that all sub-attachments of those
|
||||
// messages will also be deleted. Best to display a list of them.
|
||||
|
||||
// get the listener for running the url
|
||||
nsDelAttachListener * listener = new nsDelAttachListener;
|
||||
if (!listener) return NS_ERROR_OUT_OF_MEMORY;
|
||||
nsCOMPtr<nsISupports> listenerSupports; // auto-delete of the listener with error
|
||||
listener->QueryInterface(NS_GET_IID(nsISupports), getter_AddRefs(listenerSupports));
|
||||
|
||||
// create the attachments for use by the listener
|
||||
nsAttachmentState * attach = new nsAttachmentState;
|
||||
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_FAILED(rv))
|
||||
{
|
||||
delete attach;
|
||||
return rv;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsMessenger::PromptIfDeleteAttachments(PRBool aSaveFirst,
|
||||
PRUint32 aCount,
|
||||
const char ** aDisplayNameArray)
|
||||
{
|
||||
nsresult rv = NS_ERROR_FAILURE;
|
||||
|
||||
nsCOMPtr<nsIPrompt> dialog(do_GetInterface(mDocShell));
|
||||
if (!dialog) return rv;
|
||||
|
||||
if (!mStringBundle)
|
||||
{
|
||||
rv = InitStringBundle();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
|
||||
// create the list of attachments we are removing
|
||||
nsXPIDLString displayString;
|
||||
nsXPIDLString attachmentList;
|
||||
for (PRUint32 u = 0; u < aCount; ++u)
|
||||
{
|
||||
rv = ConvertAndSanitizeFileName(aDisplayNameArray[u], getter_Copies(displayString), nsnull);
|
||||
if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
|
||||
attachmentList.Append(displayString);
|
||||
attachmentList.Append(PRUnichar('\n'));
|
||||
}
|
||||
const PRUnichar *formatStrings[] = { attachmentList.get() };
|
||||
|
||||
// format the message and display
|
||||
nsXPIDLString promptMessage;
|
||||
const PRUnichar * propertyName = aSaveFirst ?
|
||||
NS_LITERAL_STRING("detachAttachments").get() : NS_LITERAL_STRING("deleteAttachments").get();
|
||||
rv = mStringBundle->FormatStringFromName(propertyName, formatStrings, 1,getter_Copies(promptMessage));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PRBool dialogResult = PR_FALSE;
|
||||
rv = dialog->Confirm(nsnull, promptMessage, &dialogResult);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return dialogResult ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
|
|
@ -74,6 +74,7 @@ protected:
|
|||
nsISupportsArray *srcArray, nsISupportsArray *arguments);
|
||||
const nsAdoptingString GetString(const nsAFlatString& aStringName);
|
||||
nsresult InitStringBundle();
|
||||
nsresult PromptIfDeleteAttachments(PRBool saveFirst, PRUint32 count, const char **displayNameArray);
|
||||
|
||||
private:
|
||||
nsresult GetLastSaveDirectory(nsILocalFile **aLastSaveAsDir);
|
||||
|
|
|
@ -8014,7 +8014,7 @@ nsresult nsImapMockChannel::OpenCacheEntry()
|
|||
{
|
||||
// check if this is a filter plugin requesting the message. In that case,we're not
|
||||
// fetching a part, and we want the cache key to be just the uri.
|
||||
if (strcmp(anchor, "?header=filter"))
|
||||
if (strcmp(anchor, "?header=filter") && strcmp(anchor, "?header=attach"))
|
||||
mTryingToReadPart = PR_TRUE;
|
||||
else
|
||||
*anchor = '\0';
|
||||
|
|
|
@ -468,7 +468,7 @@ nsresult nsMailboxProtocol::LoadUrl(nsIURI * aURL, nsISupports * aConsumer)
|
|||
|
||||
// check if this is a filter plugin requesting the message.
|
||||
// in that case, set up a text converter
|
||||
convertData = (queryStr.Find("header=filter") != kNotFound);
|
||||
convertData = (queryStr.Find("header=filter") != kNotFound || queryStr.Find("header=attach") != kNotFound);
|
||||
}
|
||||
else if (m_mailboxAction == nsIMailboxUrl::ActionFetchPart)
|
||||
{
|
||||
|
|
|
@ -251,8 +251,9 @@ nsMailboxService::StreamMessage(const char *aMessageURI, nsISupports *aConsumer,
|
|||
const char *aAdditionalHeader,
|
||||
nsIURI **aURL)
|
||||
{
|
||||
// The mailbox protocol object will look for "header=filter" to decide if it wants to convert
|
||||
// the data instead of using aConvertData. It turns out to be way too hard to pass aConvertData
|
||||
// The mailbox protocol object will look for "header=filter" or
|
||||
// "header=attach" to decide if it wants to convert the data instead of
|
||||
// using aConvertData. It turns out to be way too hard to pass aConvertData
|
||||
// all the way over to the mailbox protocol object.
|
||||
nsCAutoString aURIString(aMessageURI);
|
||||
if (aAdditionalHeader)
|
||||
|
|
|
@ -50,7 +50,7 @@ interface nsIURI;
|
|||
|
||||
typedef long nsMimeOutputType;
|
||||
|
||||
[scriptable, uuid(E4ED8894-3F9E-11d3-9896-001083010E9B)]
|
||||
[scriptable, uuid(fdc2956e-d558-43fb-bfdd-fb9511229aa5)]
|
||||
interface nsMimeOutput
|
||||
{
|
||||
const long nsMimeMessageSplitDisplay = 0;
|
||||
|
@ -66,7 +66,8 @@ interface nsMimeOutput
|
|||
const long nsMimeMessageSource = 11;
|
||||
const long nsMimeMessageFilterSniffer = 12;
|
||||
const long nsMimeMessageDecrypt = 13;
|
||||
const long nsMimeUnknown = 14;
|
||||
const long nsMimeMessageAttach = 14;
|
||||
const long nsMimeUnknown = 15;
|
||||
};
|
||||
|
||||
[scriptable, uuid(FA81CAA0-6261-11d3-8311-00805F2A0107)]
|
||||
|
|
|
@ -467,7 +467,9 @@ static void *MimeCMS_init(MimeObject *obj,
|
|||
// processing will be shown in the UI.
|
||||
|
||||
if (!strstr(urlSpec.get(), "?header=filter") &&
|
||||
!strstr(urlSpec.get(), "&header=filter"))
|
||||
!strstr(urlSpec.get(), "&header=filter") &&
|
||||
!strstr(urlSpec.get(), "?header=attach") &&
|
||||
!strstr(urlSpec.get(), "&header=attach"))
|
||||
{
|
||||
msgurl = do_QueryInterface(uri);
|
||||
if (msgurl)
|
||||
|
|
|
@ -447,7 +447,8 @@ mime_find_class (const char *content_type, MimeHeaders *hdrs,
|
|||
attachment types and inline images) and is for paranoid users.
|
||||
*/
|
||||
if (opts && opts->format_out != nsMimeOutput::nsMimeMessageFilterSniffer &&
|
||||
opts->format_out != nsMimeOutput::nsMimeMessageDecrypt)
|
||||
opts->format_out != nsMimeOutput::nsMimeMessageDecrypt
|
||||
&& opts->format_out != nsMimeOutput::nsMimeMessageAttach)
|
||||
if (prefBranch)
|
||||
{
|
||||
prefBranch->GetIntPref("mailnews.display.html_as", &html_as);
|
||||
|
@ -556,7 +557,8 @@ mime_find_class (const char *content_type, MimeHeaders *hdrs,
|
|||
// Preliminary use the normal plain text
|
||||
clazz = (MimeObjectClass *)&mimeInlineTextPlainClass;
|
||||
|
||||
if (opts && opts->format_out != nsMimeOutput::nsMimeMessageFilterSniffer)
|
||||
if (opts && opts->format_out != nsMimeOutput::nsMimeMessageFilterSniffer
|
||||
&& opts->format_out != nsMimeOutput::nsMimeMessageAttach)
|
||||
{
|
||||
PRBool disable_format_flowed = PR_FALSE;
|
||||
if (prefBranch)
|
||||
|
@ -657,7 +659,7 @@ mime_find_class (const char *content_type, MimeHeaders *hdrs,
|
|||
/* Treat all unknown multipart subtypes as "multipart/mixed" */
|
||||
clazz = (MimeObjectClass *)&mimeMultipartMixedClass;
|
||||
|
||||
/* If we are sniffing a message, let's threat alternative parts as mixed */
|
||||
/* If we are sniffing a message, let's treat alternative parts as mixed */
|
||||
if (opts && opts->format_out == nsMimeOutput::nsMimeMessageFilterSniffer)
|
||||
if (clazz == (MimeObjectClass *)&mimeMultipartAlternativeClass)
|
||||
clazz = (MimeObjectClass *)&mimeMultipartMixedClass;
|
||||
|
@ -1801,12 +1803,24 @@ MimeObject_write(MimeObject *obj, const char *output, PRInt32 length,
|
|||
{
|
||||
if (!obj->output_p) return 0;
|
||||
|
||||
// if we're stripping attachments, check if any parent is not being ouput
|
||||
if (obj->options->format_out == nsMimeOutput::nsMimeMessageAttach)
|
||||
{
|
||||
// if true, mime genrates a separator in html - we don't want that.
|
||||
user_visible_p = PR_FALSE;
|
||||
|
||||
for (MimeObject *parent = obj->parent; parent; parent = parent->parent)
|
||||
{
|
||||
if (!parent->output_p)
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (!obj->options->state->first_data_written_p)
|
||||
{
|
||||
int status = MimeObject_output_init(obj, 0);
|
||||
if (status < 0) return status;
|
||||
NS_ASSERTION(obj->options->state->first_data_written_p, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
|
||||
}
|
||||
{
|
||||
int status = MimeObject_output_init(obj, 0);
|
||||
if (status < 0) return status;
|
||||
NS_ASSERTION(obj->options->state->first_data_written_p, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
|
||||
}
|
||||
|
||||
return MimeOptions_write(obj->options, output, length, user_visible_p);
|
||||
}
|
||||
|
|
|
@ -231,6 +231,7 @@
|
|||
*/
|
||||
|
||||
#include "mimehdrs.h"
|
||||
#include "nsVoidArray.h"
|
||||
|
||||
typedef struct MimeObject MimeObject;
|
||||
typedef struct MimeObjectClass MimeObjectClass;
|
||||
|
@ -383,8 +384,14 @@ extern void mime_set_crypto_stamp(MimeObject *obj,
|
|||
PRBool signed_p, PRBool encrypted_p);
|
||||
#endif // ENABLE_SMIME
|
||||
|
||||
struct MimeParseStateObject {
|
||||
class MimeParseStateObject {
|
||||
public:
|
||||
|
||||
MimeParseStateObject()
|
||||
{root = 0; separator_queued_p = PR_FALSE; separator_suppressed_p = PR_FALSE;
|
||||
first_part_written_p = PR_FALSE; post_header_html_run_p = PR_FALSE; first_data_written_p = PR_FALSE;
|
||||
decrypted_p = PR_FALSE; strippingPart = PR_FALSE;
|
||||
}
|
||||
MimeObject *root; /* The outermost parser object. */
|
||||
|
||||
PRBool separator_queued_p; /* Whether a separator should be written out
|
||||
|
@ -407,10 +414,12 @@ struct MimeParseStateObject {
|
|||
PRBool first_data_written_p; /* State used for Mozilla lazy-stream-
|
||||
creation evilness. */
|
||||
|
||||
PRBool decrypted_p; /* If options->dexlate_p is true, then this
|
||||
will be set to indicate whether any
|
||||
dexlateion did in fact occur.
|
||||
*/
|
||||
PRBool decrypted_p; /* If options->dexlate_p is true, then this
|
||||
will be set to indicate whether any
|
||||
dexlateion did in fact occur.
|
||||
*/
|
||||
nsCStringArray partsToStrip; /* if we're stripping parts, what parts to strip */
|
||||
PRBool strippingPart;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -167,7 +167,8 @@ MimeLeaf_parse_buffer (const char *buffer, PRInt32 size, MimeObject *obj)
|
|||
|
||||
if (leaf->decoder_data &&
|
||||
obj->options &&
|
||||
obj->options->format_out != nsMimeOutput::nsMimeMessageDecrypt)
|
||||
obj->options->format_out != nsMimeOutput::nsMimeMessageDecrypt
|
||||
&& obj->options->format_out != nsMimeOutput::nsMimeMessageAttach)
|
||||
return MimeDecoderWrite (leaf->decoder_data, buffer, size);
|
||||
else
|
||||
return ((MimeLeafClass *)obj->clazz)->parse_decoded_buffer (buffer, size,
|
||||
|
|
|
@ -289,7 +289,9 @@ MimeMultCMS_init (MimeObject *obj)
|
|||
// processing will be shown in the UI.
|
||||
|
||||
if (!strstr(urlSpec.get(), "?header=filter") &&
|
||||
!strstr(urlSpec.get(), "&header=filter"))
|
||||
!strstr(urlSpec.get(), "&header=filter")&&
|
||||
!strstr(urlSpec.get(), "?header=attach") &&
|
||||
!strstr(urlSpec.get(), "&header=attach"))
|
||||
{
|
||||
msgurl = do_QueryInterface(uri);
|
||||
if (msgurl)
|
||||
|
|
|
@ -635,7 +635,7 @@ NotifyEmittersOfAttachmentList(MimeDisplayOptions *opt,
|
|||
if ( (opt->format_out == nsMimeOutput::nsMimeMessageQuoting) ||
|
||||
(opt->format_out == nsMimeOutput::nsMimeMessageBodyQuoting) ||
|
||||
(opt->format_out == nsMimeOutput::nsMimeMessageSaveAs) ||
|
||||
(opt->format_out == nsMimeOutput::nsMimeMessagePrintOutput) )
|
||||
(opt->format_out == nsMimeOutput::nsMimeMessagePrintOutput))
|
||||
{
|
||||
mimeEmitterAddAttachmentField(opt, HEADER_CONTENT_DESCRIPTION, tmp->description);
|
||||
mimeEmitterAddAttachmentField(opt, HEADER_CONTENT_TYPE, tmp->real_type);
|
||||
|
@ -1555,28 +1555,31 @@ mime_bridge_create_display_stream(
|
|||
msd->options->write_html_p = PR_TRUE;
|
||||
switch (format_out)
|
||||
{
|
||||
case nsMimeOutput::nsMimeMessageSplitDisplay: // the wrapper HTML output to produce the split header/body display
|
||||
case nsMimeOutput::nsMimeMessageHeaderDisplay: // the split header/body display
|
||||
case nsMimeOutput::nsMimeMessageBodyDisplay: // the split header/body display
|
||||
case nsMimeOutput::nsMimeMessageSplitDisplay: // the wrapper HTML output to produce the split header/body display
|
||||
case nsMimeOutput::nsMimeMessageHeaderDisplay: // the split header/body display
|
||||
case nsMimeOutput::nsMimeMessageBodyDisplay: // the split header/body display
|
||||
msd->options->fancy_headers_p = PR_TRUE;
|
||||
msd->options->output_vcard_buttons_p = PR_TRUE;
|
||||
msd->options->fancy_links_p = PR_TRUE;
|
||||
break;
|
||||
|
||||
case nsMimeOutput::nsMimeMessageSaveAs: // Save As operations
|
||||
case nsMimeOutput::nsMimeMessageQuoting: // all HTML quoted/printed output
|
||||
case nsMimeOutput::nsMimeMessagePrintOutput:
|
||||
case nsMimeOutput::nsMimeMessageSaveAs: // Save As operations
|
||||
case nsMimeOutput::nsMimeMessageQuoting: // all HTML quoted/printed output
|
||||
case nsMimeOutput::nsMimeMessagePrintOutput:
|
||||
msd->options->fancy_headers_p = PR_TRUE;
|
||||
msd->options->fancy_links_p = PR_TRUE;
|
||||
break;
|
||||
|
||||
case nsMimeOutput::nsMimeMessageBodyQuoting: // only HTML body quoted output
|
||||
case nsMimeOutput::nsMimeMessageBodyQuoting: // only HTML body quoted output
|
||||
MIME_HeaderType = MimeHeadersNone;
|
||||
break;
|
||||
|
||||
case nsMimeOutput::nsMimeMessageAttach: // handling attachment storage
|
||||
msd->options->write_html_p = PR_FALSE;
|
||||
break;
|
||||
case nsMimeOutput::nsMimeMessageRaw: // the raw RFC822 data (view source) and attachments
|
||||
case nsMimeOutput::nsMimeMessageDraftOrTemplate: // Loading drafts & templates
|
||||
case nsMimeOutput::nsMimeMessageEditorTemplate: // Loading templates into editor
|
||||
case nsMimeOutput::nsMimeMessageDraftOrTemplate: // Loading drafts & templates
|
||||
case nsMimeOutput::nsMimeMessageEditorTemplate: // Loading templates into editor
|
||||
case nsMimeOutput::nsMimeMessageFilterSniffer: // generating an output that can be scan by a message filter
|
||||
break;
|
||||
|
||||
|
|
|
@ -138,6 +138,11 @@ MimeMultipart_finalize (MimeObject *object)
|
|||
((MimeObjectClass*)&MIME_SUPERCLASS)->finalize(object);
|
||||
}
|
||||
|
||||
int MimeWriteAString(MimeObject *obj, const nsACString &string)
|
||||
{
|
||||
const nsCString &flatString = PromiseFlatCString(string);
|
||||
return MimeObject_write(obj, flatString.get(), flatString.Length(), PR_TRUE);
|
||||
}
|
||||
|
||||
static int
|
||||
MimeMultipart_parse_line (char *line, PRInt32 length, MimeObject *obj)
|
||||
|
@ -157,7 +162,8 @@ MimeMultipart_parse_line (char *line, PRInt32 length, MimeObject *obj)
|
|||
if (obj->output_p &&
|
||||
obj->options &&
|
||||
!obj->options->write_html_p &&
|
||||
obj->options->output_fn)
|
||||
obj->options->output_fn
|
||||
&& obj->options->format_out != nsMimeOutput::nsMimeMessageAttach)
|
||||
return MimeObject_write(obj, line, length, PR_TRUE);
|
||||
|
||||
|
||||
|
@ -190,9 +196,28 @@ MimeMultipart_parse_line (char *line, PRInt32 length, MimeObject *obj)
|
|||
mult->hdrs = MimeHeaders_new();
|
||||
if (!mult->hdrs)
|
||||
return MIME_OUT_OF_MEMORY;
|
||||
if (obj->options->state->partsToStrip.Count() > 0)
|
||||
{
|
||||
nsCAutoString newPart(mime_part_address(obj));
|
||||
MimeContainer *container = (MimeContainer*) obj;
|
||||
newPart.Append('.');
|
||||
newPart.AppendInt(container->nchildren + 1);
|
||||
obj->options->state->strippingPart = PR_FALSE;
|
||||
// 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;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Now return, to ignore the boundary line itself. */
|
||||
if (obj->options->format_out == nsMimeOutput::nsMimeMessageAttach)
|
||||
return MimeObject_write(obj, line, length, PR_TRUE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -201,13 +226,13 @@ MimeMultipart_parse_line (char *line, PRInt32 length, MimeObject *obj)
|
|||
child part, ignore it, etc.) */
|
||||
|
||||
switch (mult->state)
|
||||
{
|
||||
case MimeMultipartPreamble:
|
||||
case MimeMultipartEpilogue:
|
||||
/* Ignore this line. */
|
||||
break;
|
||||
{
|
||||
case MimeMultipartPreamble:
|
||||
case MimeMultipartEpilogue:
|
||||
/* Ignore this line. */
|
||||
break;
|
||||
|
||||
case MimeMultipartHeaders:
|
||||
case MimeMultipartHeaders:
|
||||
/* Parse this line as a header for the sub-part. */
|
||||
{
|
||||
status = MimeHeaders_parse_line(line, length, mult->hdrs);
|
||||
|
@ -218,6 +243,31 @@ MimeMultipart_parse_line (char *line, PRInt32 length, MimeObject *obj)
|
|||
//
|
||||
if (*line == nsCRT::CR || *line == nsCRT::LF)
|
||||
{
|
||||
if (obj->options->state->strippingPart)
|
||||
{
|
||||
nsCAutoString header("Content-Type: text/x-moz-deleted; name=\"Deleted: ");
|
||||
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=\""));
|
||||
nsCString result;
|
||||
char timeBuffer[128];
|
||||
PRExplodedTime now;
|
||||
PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &now);
|
||||
PR_FormatTimeUSEnglish(timeBuffer, sizeof(timeBuffer),
|
||||
"%a %b %d %H:%M:%S %Y",
|
||||
&now);
|
||||
MimeWriteAString(obj, nsDependentCString(timeBuffer));
|
||||
MimeWriteAString(obj, NS_LITERAL_CSTRING("\""MSG_LINEBREAK));
|
||||
MimeWriteAString(obj, NS_LITERAL_CSTRING("The original MIME headers for this attachment are:"MSG_LINEBREAK));
|
||||
MimeHeaders_write_raw_headers(mult->hdrs, obj->options, PR_FALSE);
|
||||
}
|
||||
status = ((MimeMultipartClass *) obj->clazz)->create_child(obj);
|
||||
if (status < 0) return status;
|
||||
PR_ASSERT(mult->state != MimeMultipartHeaders);
|
||||
|
@ -231,6 +281,13 @@ MimeMultipart_parse_line (char *line, PRInt32 length, MimeObject *obj)
|
|||
PRBool isAlternative = PR_FALSE;
|
||||
|
||||
MimeContainer *container = (MimeContainer*) obj;
|
||||
// check if we're stripping the part of this newly created child.
|
||||
if (container->children && container->nchildren > 0)
|
||||
{
|
||||
MimeObject *kid = container->children[container->nchildren-1];
|
||||
if (kid->output_p)
|
||||
kid->output_p = !obj->options->state->strippingPart;
|
||||
}
|
||||
if (container->children && container->nchildren == 1)
|
||||
{
|
||||
PRBool isAlternativeOrRelated = PR_FALSE;
|
||||
|
@ -292,34 +349,35 @@ MimeMultipart_parse_line (char *line, PRInt32 length, MimeObject *obj)
|
|||
break;
|
||||
}
|
||||
|
||||
case MimeMultipartPartFirstLine:
|
||||
/* Hand this line off to the sub-part. */
|
||||
status = (((MimeMultipartClass *) obj->clazz)->parse_child_line(obj,
|
||||
line,
|
||||
length,
|
||||
PR_TRUE));
|
||||
if (status < 0) return status;
|
||||
mult->state = MimeMultipartPartLine;
|
||||
break;
|
||||
case MimeMultipartPartFirstLine:
|
||||
/* Hand this line off to the sub-part. */
|
||||
status = (((MimeMultipartClass *) obj->clazz)->parse_child_line(obj,
|
||||
line, length, PR_TRUE));
|
||||
if (status < 0) return status;
|
||||
mult->state = MimeMultipartPartLine;
|
||||
break;
|
||||
|
||||
case MimeMultipartPartLine:
|
||||
/* Hand this line off to the sub-part. */
|
||||
status = (((MimeMultipartClass *) obj->clazz)->parse_child_line(obj,
|
||||
line,
|
||||
length,
|
||||
PR_FALSE));
|
||||
if (status < 0) return status;
|
||||
break;
|
||||
case MimeMultipartPartLine:
|
||||
/* Hand this line off to the sub-part. */
|
||||
status = (((MimeMultipartClass *) obj->clazz)->parse_child_line(obj,
|
||||
line, length, PR_FALSE));
|
||||
if (status < 0) return status;
|
||||
break;
|
||||
|
||||
case MimeMultipartSkipPartLine:
|
||||
/* we are skipping that part, therefore just ignore the line */
|
||||
break;
|
||||
case MimeMultipartSkipPartLine:
|
||||
/* we are skipping that part, therefore just ignore the line */
|
||||
break;
|
||||
|
||||
default:
|
||||
PR_ASSERT(0);
|
||||
return -1;
|
||||
}
|
||||
default:
|
||||
PR_ASSERT(0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (obj->options->format_out == nsMimeOutput::nsMimeMessageAttach && mult->state != MimeMultipartPartLine)
|
||||
{
|
||||
if (!obj->options->state->strippingPart /* || mult->state != MimeMultipartHeaders */)
|
||||
return MimeObject_write(obj, line, length, PR_FALSE);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -198,11 +198,16 @@ MimeObject_parse_begin (MimeObject *obj)
|
|||
{
|
||||
NS_ASSERTION(!obj->headers, "headers should be null"); /* should be the outermost object. */
|
||||
|
||||
obj->options->state = PR_NEW(MimeParseStateObject);
|
||||
obj->options->state = new MimeParseStateObject;
|
||||
if (!obj->options->state) return MIME_OUT_OF_MEMORY;
|
||||
memset(obj->options->state, 0, sizeof(*obj->options->state));
|
||||
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, ",");
|
||||
}
|
||||
}
|
||||
|
||||
/* Decide whether this object should be output or not... */
|
||||
|
@ -228,7 +233,8 @@ MimeObject_parse_begin (MimeObject *obj)
|
|||
// First, check for an exact match
|
||||
obj->output_p = !strcmp(id, obj->options->part_to_load);
|
||||
if (!obj->output_p && (obj->options->format_out == nsMimeOutput::nsMimeMessageRaw ||
|
||||
obj->options->format_out == nsMimeOutput::nsMimeMessageBodyDisplay))
|
||||
obj->options->format_out == nsMimeOutput::nsMimeMessageBodyDisplay
|
||||
|| obj->options->format_out == nsMimeOutput::nsMimeMessageAttach))
|
||||
{
|
||||
// Then, check for subpart
|
||||
unsigned int partlen = strlen(obj->options->part_to_load);
|
||||
|
|
|
@ -123,10 +123,11 @@ MimeInlineTextPlain_parse_begin (MimeObject *obj)
|
|||
obj->options->format_out == nsMimeOutput::nsMimeMessageBodyQuoting
|
||||
) ); // The output will be inserted in the composer as quotation
|
||||
PRBool plainHTML = quoting || (obj->options &&
|
||||
obj->options->format_out == nsMimeOutput::nsMimeMessageSaveAs);
|
||||
(obj->options->format_out == nsMimeOutput::nsMimeMessageSaveAs));
|
||||
// Just good(tm) HTML. No reliance on CSS.
|
||||
PRBool rawPlainText = obj->options &&
|
||||
obj->options->format_out == nsMimeOutput::nsMimeMessageFilterSniffer;
|
||||
(obj->options->format_out == nsMimeOutput::nsMimeMessageFilterSniffer
|
||||
|| obj->options->format_out == nsMimeOutput::nsMimeMessageAttach);
|
||||
|
||||
status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_begin(obj);
|
||||
if (status < 0) return status;
|
||||
|
@ -262,7 +263,8 @@ MimeInlineTextPlain_parse_eof (MimeObject *obj, PRBool abort_p)
|
|||
) ); // see above
|
||||
|
||||
PRBool rawPlainText = obj->options &&
|
||||
obj->options->format_out == nsMimeOutput::nsMimeMessageFilterSniffer;
|
||||
(obj->options->format_out == nsMimeOutput::nsMimeMessageFilterSniffer
|
||||
|| obj->options->format_out == nsMimeOutput::nsMimeMessageAttach);
|
||||
|
||||
/* Run parent method first, to flush out any buffered data. */
|
||||
status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_eof(obj, abort_p);
|
||||
|
@ -314,7 +316,8 @@ MimeInlineTextPlain_parse_line (char *line, PRInt32 length, MimeObject *obj)
|
|||
// see above
|
||||
|
||||
PRBool rawPlainText = obj->options &&
|
||||
obj->options->format_out == nsMimeOutput::nsMimeMessageFilterSniffer;
|
||||
(obj->options->format_out == nsMimeOutput::nsMimeMessageFilterSniffer
|
||||
|| obj->options->format_out == nsMimeOutput::nsMimeMessageAttach);
|
||||
|
||||
// this routine gets called for every line of data that comes through the
|
||||
// mime converter. It's important to make sure we are efficient with
|
||||
|
|
|
@ -90,7 +90,7 @@ typedef struct MimeHeaders
|
|||
} MimeHeaders;
|
||||
|
||||
class MimeDisplayOptions;
|
||||
typedef struct MimeParseStateObject MimeParseStateObject;
|
||||
class MimeParseStateObject;
|
||||
typedef struct MSG_AttachmentData MSG_AttachmentData;
|
||||
|
||||
/* Given the name of a header, returns the contents of that header as
|
||||
|
|
|
@ -320,7 +320,7 @@ bridge_set_mime_stream_converter_listener(void *bridgeStream, nsIMimeStreamConve
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
// find a query element in a url and return a pointer to it's data
|
||||
// find a query element in a url and return a pointer to its data
|
||||
// (query must be in the form "query=")
|
||||
static const char *
|
||||
FindQueryElementData(const char * aUrl, const char * aQuery)
|
||||
|
@ -462,7 +462,8 @@ nsStreamConverter::DetermineOutputFormat(const char *aUrl, nsMimeOutputType *aNe
|
|||
{ "none", "text/html", nsMimeOutput::nsMimeMessageBodyDisplay },
|
||||
{ "quote", "text/html", nsMimeOutput::nsMimeMessageQuoting },
|
||||
{ "saveas", "text/html", nsMimeOutput::nsMimeMessageSaveAs },
|
||||
{ "src", "text/plain", nsMimeOutput::nsMimeMessageSource }
|
||||
{ "src", "text/plain", nsMimeOutput::nsMimeMessageSource },
|
||||
{ "attach", "raw", nsMimeOutput::nsMimeMessageAttach }
|
||||
};
|
||||
|
||||
// find the requested header in table, ensure that we don't match on a prefix
|
||||
|
@ -573,6 +574,7 @@ NS_IMETHODIMP nsStreamConverter::Init(nsIURI *aURI, nsIStreamListener * aOutList
|
|||
mOutputFormat = "text/html";
|
||||
break;
|
||||
|
||||
case nsMimeOutput::nsMimeMessageAttach:
|
||||
case nsMimeOutput::nsMimeMessageDecrypt:
|
||||
case nsMimeOutput::nsMimeMessageRaw: // the raw RFC822 data and attachments
|
||||
mOutputFormat = "raw";
|
||||
|
|
|
@ -723,7 +723,8 @@ nsresult nsNNTPProtocol::SetupPartExtractorListener(nsIStreamListener * aConsume
|
|||
|
||||
// check if this is a filter plugin requesting the message.
|
||||
// in that case, set up a text converter
|
||||
convertData = (queryStr.Find("header=filter") != kNotFound);
|
||||
convertData = (queryStr.Find("header=filter") != kNotFound
|
||||
|| queryStr.Find("header=attach") != kNotFound);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
Загрузка…
Ссылка в новой задаче