back end work for stripping mime attachments from messages, sr=mscott 2920

This commit is contained in:
bienvenu%nventure.com 2005-02-09 01:40:24 +00:00
Родитель dc8257352c
Коммит 9733147b70
23 изменённых файлов: 1101 добавлений и 101 удалений

Просмотреть файл

@ -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
{