From 84bf45c17f8021a4ea0d408049b6271a8e7ba1c1 Mon Sep 17 00:00:00 2001 From: "bzbarsky%mit.edu" Date: Wed, 6 Feb 2002 03:39:04 +0000 Subject: [PATCH] Make parts of multipart streams expose content-disposition. Bug 123043, r=law, sr=darin. git-svn-id: svn://10.0.0.236/trunk@113743 18797224-902f-48f8-a5cc-f745e15eee43 --- .../base/public/nsIMultiPartChannel.idl | 6 ++ .../converters/nsMultiMixedConv.cpp | 24 ++++- .../streamconv/converters/nsMultiMixedConv.h | 1 + .../exthandler/nsExternalHelperAppService.cpp | 99 ++++++++++++------- 4 files changed, 95 insertions(+), 35 deletions(-) diff --git a/mozilla/netwerk/base/public/nsIMultiPartChannel.idl b/mozilla/netwerk/base/public/nsIMultiPartChannel.idl index 6a1d2e7ec38..362820a44de 100644 --- a/mozilla/netwerk/base/public/nsIMultiPartChannel.idl +++ b/mozilla/netwerk/base/public/nsIMultiPartChannel.idl @@ -39,4 +39,10 @@ interface nsIMultiPartChannel : nsISupports */ readonly attribute nsIChannel baseChannel; + /** + * Access to the Content-Disposition header field of this part of + * a multipart message. This allows getting the preferred + * handling method, preferred filename, etc. See RFC 2183. + */ + attribute string contentDisposition; }; diff --git a/mozilla/netwerk/streamconv/converters/nsMultiMixedConv.cpp b/mozilla/netwerk/streamconv/converters/nsMultiMixedConv.cpp index e3095aa8149..26dc738ef25 100644 --- a/mozilla/netwerk/streamconv/converters/nsMultiMixedConv.cpp +++ b/mozilla/netwerk/streamconv/converters/nsMultiMixedConv.cpp @@ -82,6 +82,7 @@ protected: nsCOMPtr mLoadGroup; nsCString mContentType; + nsCString mContentDisposition; PRInt32 mContentLength; PRBool mIsByteRangeRequest; @@ -317,6 +318,19 @@ nsPartChannel::SetContentLength(PRInt32 aContentLength) return NS_OK; } +NS_IMETHODIMP +nsPartChannel::GetContentDisposition(char * *aContentDisposition) +{ + *aContentDisposition = ToNewCString(mContentDisposition); + return NS_OK; +} + +NS_IMETHODIMP +nsPartChannel::SetContentDisposition(const char *aContentDisposition) +{ + mContentDisposition.Assign(aContentDisposition); + return NS_OK; +} // // nsIByteRangeRequest implementation... @@ -730,9 +744,15 @@ nsMultiMixedConv::SendStart(nsIChannel *aChannel) { rv = mPartChannel->SetContentType(mContentType.get()); if (NS_FAILED(rv)) return rv; - mPartChannel->SetContentLength(mContentLength); + rv = mPartChannel->SetContentLength(mContentLength); if (NS_FAILED(rv)) return rv; + nsCOMPtr partChannel(do_QueryInterface(mPartChannel)); + if (partChannel) { + rv = partChannel->SetContentDisposition(mContentDisposition.get()); + if (NS_FAILED(rv)) return rv; + } + nsLoadFlags loadFlags = 0; mPartChannel->GetLoadFlags(&loadFlags); loadFlags |= nsIChannel::LOAD_REPLACE; @@ -878,6 +898,8 @@ nsMultiMixedConv::ParseHeaders(nsIChannel *aChannel, char *&aPtr, nsCRT::free(tmpString); } else if (headerStr.EqualsIgnoreCase("content-length")) { mContentLength = atoi(headerVal.get()); + } else if (headerStr.EqualsIgnoreCase("content-disposition")) { + mContentDisposition = headerVal; } else if (headerStr.EqualsIgnoreCase("set-cookie")) { // setting headers on the HTTP channel // causes HTTP to notify, again if necessary, diff --git a/mozilla/netwerk/streamconv/converters/nsMultiMixedConv.h b/mozilla/netwerk/streamconv/converters/nsMultiMixedConv.h index 965f10ced74..3e2c373a83c 100644 --- a/mozilla/netwerk/streamconv/converters/nsMultiMixedConv.h +++ b/mozilla/netwerk/streamconv/converters/nsMultiMixedConv.h @@ -125,6 +125,7 @@ protected: nsCOMPtr mContext; nsCString mContentType; nsCString mContentCharset; + nsCString mContentDisposition; PRInt32 mContentLength; char *mBuffer; diff --git a/mozilla/uriloader/exthandler/nsExternalHelperAppService.cpp b/mozilla/uriloader/exthandler/nsExternalHelperAppService.cpp index b64c324f90e..a7160cc8884 100644 --- a/mozilla/uriloader/exthandler/nsExternalHelperAppService.cpp +++ b/mozilla/uriloader/exthandler/nsExternalHelperAppService.cpp @@ -60,8 +60,9 @@ #include "nsNetCID.h" #include "nsMimeTypes.h" -// used for http content header disposition information. +// used for header disposition information. #include "nsIHttpChannel.h" +#include "nsIMultiPartChannel.h" #include "nsIAtom.h" #include "nsIObserverService.h" // so we can be an xpcom shutdown observer @@ -771,44 +772,74 @@ NS_IMETHODIMP nsExternalAppHandler::CloseProgressWindow() void nsExternalAppHandler::ExtractSuggestedFileNameFromChannel(nsIChannel* aChannel) { - // if the channel is an http channel and we have a content disposition header set, - // then use the file name suggested there as the preferred file name to SUGGEST to the user. - // we shouldn't actually use that without their permission...o.t. just use our temp file - // Try to get HTTP channel....if we have a content-disposition header then we can - nsCOMPtr httpChannel = do_QueryInterface( aChannel ); + /* + * If the channel is an http or part of a multipart channel and we + * have a content disposition header set, then use the file name + * suggested there as the preferred file name to SUGGEST to the + * user. we shouldn't actually use that without their + * permission...o.t. just use our temp file + */ + nsXPIDLCString disp; + nsresult rv = NS_OK; + // First see whether this is an http channel + nsCOMPtr httpChannel( do_QueryInterface( aChannel ) ); if ( httpChannel ) { - // Get content-disposition response header and extract a file name if there is one... - // content-disposition: has format: disposition-type < ; filename=value > - nsXPIDLCString disp; - nsresult rv = httpChannel->GetResponseHeader( "content-disposition", getter_Copies( disp ) ); - if ( NS_SUCCEEDED( rv ) && disp ) + rv = httpChannel->GetResponseHeader( "content-disposition", getter_Copies( disp ) ); + } + if ( NS_FAILED(rv) || disp.IsEmpty() ) + { + nsCOMPtr multipartChannel( do_QueryInterface( aChannel ) ); + if ( multipartChannel ) { - nsCAutoString dispositionValue; - dispositionValue = disp; - PRInt32 pos = dispositionValue.Find("filename=", PR_TRUE); - if (pos != kNotFound) - { - // extract everything after the filename= part and treat that as the file name... - nsCAutoString dispFileName; - dispositionValue.Right(dispFileName, dispositionValue.Length() - - (pos + nsCRT::strlen("filename="))); - if (!dispFileName.IsEmpty()) // if we got a file name back.. - { - pos = dispFileName.FindChar(';'); - if (pos > 0) - dispFileName.Truncate(pos); + rv = multipartChannel->GetContentDisposition( getter_Copies( disp ) ); + } + } + // content-disposition: has format: + // disposition-type < ; name=value >* < ; filename=value > < ; name=value >* + if ( NS_SUCCEEDED( rv ) && !disp.IsEmpty() ) + { + nsACString::const_iterator start, end; + disp.BeginReading(start); + disp.EndReading(end); + nsACString::const_iterator iter = end; + if (CaseInsensitiveFindInReadable(NS_LITERAL_CSTRING("filename="), + start, + iter)) + { + // The value is either a string with no whitespace or a string + // in double quotes. See RFC 2183 and bug 66181. - // According to RFC 2183, filename can be given as filename=value, - // where value is token or quoted-string. See bug 66181. - dispFileName.StripChar('"'); - - // ONLY if we got here, will we remember the suggested file name... - mSuggestedFileName.AssignWithConversion(dispFileName.get()); + // Search for the ';' if it's not in double quotes, then walk + // back past any whitespace + if (iter != end) { // otherwise our filename is empty... + char endChar = ';'; + if (*iter == '"') { + endChar = '"'; + ++iter; // since we had iter < end, this is not running us past the end of the string } - } // if we found a file name in the header disposition field - } // we had a disp header - } // if we had an http channel + start = iter; + FindCharInReadable(endChar, iter, end); + // Now start points at the beginning of the filename. iter + // points to just past its end if the name was quoted. If we + // looked for a semicolon, we need to step back past + // whitespace. + if (endChar == ';' && iter != start) { + --iter; + while (iter != start && nsCRT::IsAsciiSpace(*iter)) { + iter--; + } + ++iter; + } + + if (iter != start) { // not empty + // ONLY if we got here, will we remember the suggested file name... + // The filename must be ASCII, see RFC 2183, section 2.3 + CopyASCIItoUCS2(Substring(start, iter), mSuggestedFileName); + } + } + } // if we found a file name in the header disposition field + } // we had a disp header } nsresult nsExternalAppHandler::RetargetLoadNotifications(nsIRequest *request)