michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim:set ts=2 sw=2 sts=2 et: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsMIMEInfoImpl.h" michael@0: #include "nsXPIDLString.h" michael@0: #include "nsReadableUtils.h" michael@0: #include "nsStringEnumerator.h" michael@0: #include "nsIFile.h" michael@0: #include "nsIFileURL.h" michael@0: #include "nsEscape.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsIURILoader.h" michael@0: #include "nsCURILoader.h" michael@0: michael@0: // nsISupports methods michael@0: NS_IMPL_ADDREF(nsMIMEInfoBase) michael@0: NS_IMPL_RELEASE(nsMIMEInfoBase) michael@0: michael@0: NS_INTERFACE_MAP_BEGIN(nsMIMEInfoBase) michael@0: NS_INTERFACE_MAP_ENTRY(nsIHandlerInfo) michael@0: // This is only an nsIMIMEInfo if it's a MIME handler. michael@0: NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIMIMEInfo, mClass == eMIMEInfo) michael@0: NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIHandlerInfo) michael@0: NS_INTERFACE_MAP_END_THREADSAFE michael@0: michael@0: // nsMIMEInfoImpl methods michael@0: michael@0: // Constructors for a MIME handler. michael@0: nsMIMEInfoBase::nsMIMEInfoBase(const char *aMIMEType) : michael@0: mSchemeOrType(aMIMEType), michael@0: mClass(eMIMEInfo), michael@0: mPreferredAction(nsIMIMEInfo::saveToDisk), michael@0: mAlwaysAskBeforeHandling(true) michael@0: { michael@0: } michael@0: michael@0: nsMIMEInfoBase::nsMIMEInfoBase(const nsACString& aMIMEType) : michael@0: mSchemeOrType(aMIMEType), michael@0: mClass(eMIMEInfo), michael@0: mPreferredAction(nsIMIMEInfo::saveToDisk), michael@0: mAlwaysAskBeforeHandling(true) michael@0: { michael@0: } michael@0: michael@0: // Constructor for a handler that lets the caller specify whether this is a michael@0: // MIME handler or a protocol handler. In the long run, these will be distinct michael@0: // classes (f.e. nsMIMEInfo and nsProtocolInfo), but for now we reuse this class michael@0: // for both and distinguish between the two kinds of handlers via the aClass michael@0: // argument to this method, which can be either eMIMEInfo or eProtocolInfo. michael@0: nsMIMEInfoBase::nsMIMEInfoBase(const nsACString& aType, HandlerClass aClass) : michael@0: mSchemeOrType(aType), michael@0: mClass(aClass), michael@0: mPreferredAction(nsIMIMEInfo::saveToDisk), michael@0: mAlwaysAskBeforeHandling(true) michael@0: { michael@0: } michael@0: michael@0: nsMIMEInfoBase::~nsMIMEInfoBase() michael@0: { michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMIMEInfoBase::GetFileExtensions(nsIUTF8StringEnumerator** aResult) michael@0: { michael@0: return NS_NewUTF8StringEnumerator(aResult, &mExtensions, this); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMIMEInfoBase::ExtensionExists(const nsACString& aExtension, bool *_retval) michael@0: { michael@0: NS_ASSERTION(!aExtension.IsEmpty(), "no extension"); michael@0: bool found = false; michael@0: uint32_t extCount = mExtensions.Length(); michael@0: if (extCount < 1) return NS_OK; michael@0: michael@0: for (uint8_t i=0; i < extCount; i++) { michael@0: const nsCString& ext = mExtensions[i]; michael@0: if (ext.Equals(aExtension, nsCaseInsensitiveCStringComparator())) { michael@0: found = true; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: *_retval = found; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMIMEInfoBase::GetPrimaryExtension(nsACString& _retval) michael@0: { michael@0: if (!mExtensions.Length()) michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: michael@0: _retval = mExtensions[0]; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMIMEInfoBase::SetPrimaryExtension(const nsACString& aExtension) michael@0: { michael@0: NS_ASSERTION(!aExtension.IsEmpty(), "no extension"); michael@0: uint32_t extCount = mExtensions.Length(); michael@0: uint8_t i; michael@0: bool found = false; michael@0: for (i=0; i < extCount; i++) { michael@0: const nsCString& ext = mExtensions[i]; michael@0: if (ext.Equals(aExtension, nsCaseInsensitiveCStringComparator())) { michael@0: found = true; michael@0: break; michael@0: } michael@0: } michael@0: if (found) { michael@0: mExtensions.RemoveElementAt(i); michael@0: } michael@0: michael@0: mExtensions.InsertElementAt(0, aExtension); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMIMEInfoBase::AppendExtension(const nsACString& aExtension) michael@0: { michael@0: mExtensions.AppendElement(aExtension); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMIMEInfoBase::GetType(nsACString& aType) michael@0: { michael@0: if (mSchemeOrType.IsEmpty()) michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: michael@0: aType = mSchemeOrType; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMIMEInfoBase::GetMIMEType(nsACString& aMIMEType) michael@0: { michael@0: if (mSchemeOrType.IsEmpty()) michael@0: return NS_ERROR_NOT_INITIALIZED; michael@0: michael@0: aMIMEType = mSchemeOrType; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMIMEInfoBase::GetDescription(nsAString& aDescription) michael@0: { michael@0: aDescription = mDescription; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMIMEInfoBase::SetDescription(const nsAString& aDescription) michael@0: { michael@0: mDescription = aDescription; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMIMEInfoBase::Equals(nsIMIMEInfo *aMIMEInfo, bool *_retval) michael@0: { michael@0: if (!aMIMEInfo) return NS_ERROR_NULL_POINTER; michael@0: michael@0: nsAutoCString type; michael@0: nsresult rv = aMIMEInfo->GetMIMEType(type); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: *_retval = mSchemeOrType.Equals(type); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMIMEInfoBase::SetFileExtensions(const nsACString& aExtensions) michael@0: { michael@0: mExtensions.Clear(); michael@0: nsCString extList( aExtensions ); michael@0: michael@0: int32_t breakLocation = -1; michael@0: while ( (breakLocation= extList.FindChar(',') )!= -1) michael@0: { michael@0: mExtensions.AppendElement(Substring(extList.get(), extList.get() + breakLocation)); michael@0: extList.Cut(0, breakLocation+1 ); michael@0: } michael@0: if ( !extList.IsEmpty() ) michael@0: mExtensions.AppendElement( extList ); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMIMEInfoBase::GetDefaultDescription(nsAString& aDefaultDescription) michael@0: { michael@0: aDefaultDescription = mDefaultAppDescription; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMIMEInfoBase::GetPreferredApplicationHandler(nsIHandlerApp ** aPreferredAppHandler) michael@0: { michael@0: *aPreferredAppHandler = mPreferredApplication; michael@0: NS_IF_ADDREF(*aPreferredAppHandler); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMIMEInfoBase::SetPreferredApplicationHandler(nsIHandlerApp * aPreferredAppHandler) michael@0: { michael@0: mPreferredApplication = aPreferredAppHandler; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMIMEInfoBase::GetPossibleApplicationHandlers(nsIMutableArray ** aPossibleAppHandlers) michael@0: { michael@0: if (!mPossibleApplications) michael@0: mPossibleApplications = do_CreateInstance(NS_ARRAY_CONTRACTID); michael@0: michael@0: if (!mPossibleApplications) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: *aPossibleAppHandlers = mPossibleApplications; michael@0: NS_IF_ADDREF(*aPossibleAppHandlers); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMIMEInfoBase::GetPreferredAction(nsHandlerInfoAction * aPreferredAction) michael@0: { michael@0: *aPreferredAction = mPreferredAction; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMIMEInfoBase::SetPreferredAction(nsHandlerInfoAction aPreferredAction) michael@0: { michael@0: mPreferredAction = aPreferredAction; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMIMEInfoBase::GetAlwaysAskBeforeHandling(bool * aAlwaysAsk) michael@0: { michael@0: *aAlwaysAsk = mAlwaysAskBeforeHandling; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMIMEInfoBase::SetAlwaysAskBeforeHandling(bool aAlwaysAsk) michael@0: { michael@0: mAlwaysAskBeforeHandling = aAlwaysAsk; michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* static */ michael@0: nsresult michael@0: nsMIMEInfoBase::GetLocalFileFromURI(nsIURI *aURI, nsIFile **aFile) michael@0: { michael@0: nsresult rv; michael@0: michael@0: nsCOMPtr fileUrl = do_QueryInterface(aURI, &rv); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: nsCOMPtr file; michael@0: rv = fileUrl->GetFile(getter_AddRefs(file)); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: file.forget(aFile); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMIMEInfoBase::LaunchWithFile(nsIFile* aFile) michael@0: { michael@0: nsresult rv; michael@0: michael@0: // it doesn't make any sense to call this on protocol handlers michael@0: NS_ASSERTION(mClass == eMIMEInfo, michael@0: "nsMIMEInfoBase should have mClass == eMIMEInfo"); michael@0: michael@0: if (mPreferredAction == useSystemDefault) { michael@0: return LaunchDefaultWithFile(aFile); michael@0: } michael@0: michael@0: if (mPreferredAction == useHelperApp) { michael@0: if (!mPreferredApplication) michael@0: return NS_ERROR_FILE_NOT_FOUND; michael@0: michael@0: // at the moment, we only know how to hand files off to local handlers michael@0: nsCOMPtr localHandler = michael@0: do_QueryInterface(mPreferredApplication, &rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr executable; michael@0: rv = localHandler->GetExecutable(getter_AddRefs(executable)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsAutoCString path; michael@0: aFile->GetNativePath(path); michael@0: return LaunchWithIProcess(executable, path); michael@0: } michael@0: michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMIMEInfoBase::LaunchWithURI(nsIURI* aURI, michael@0: nsIInterfaceRequestor* aWindowContext) michael@0: { michael@0: // for now, this is only being called with protocol handlers; that michael@0: // will change once we get to more general registerContentHandler michael@0: // support michael@0: NS_ASSERTION(mClass == eProtocolInfo, michael@0: "nsMIMEInfoBase should be a protocol handler"); michael@0: michael@0: if (mPreferredAction == useSystemDefault) { michael@0: return LoadUriInternal(aURI); michael@0: } michael@0: michael@0: if (mPreferredAction == useHelperApp) { michael@0: if (!mPreferredApplication) michael@0: return NS_ERROR_FILE_NOT_FOUND; michael@0: michael@0: return mPreferredApplication->LaunchWithURI(aURI, aWindowContext); michael@0: } michael@0: michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: void michael@0: nsMIMEInfoBase::CopyBasicDataTo(nsMIMEInfoBase* aOther) michael@0: { michael@0: aOther->mSchemeOrType = mSchemeOrType; michael@0: aOther->mDefaultAppDescription = mDefaultAppDescription; michael@0: aOther->mExtensions = mExtensions; michael@0: } michael@0: michael@0: /* static */ michael@0: already_AddRefed michael@0: nsMIMEInfoBase::InitProcess(nsIFile* aApp, nsresult* aResult) michael@0: { michael@0: NS_ASSERTION(aApp, "Unexpected null pointer, fix caller"); michael@0: michael@0: nsCOMPtr process = do_CreateInstance(NS_PROCESS_CONTRACTID, michael@0: aResult); michael@0: if (NS_FAILED(*aResult)) michael@0: return nullptr; michael@0: michael@0: *aResult = process->Init(aApp); michael@0: if (NS_FAILED(*aResult)) michael@0: return nullptr; michael@0: michael@0: return process.forget(); michael@0: } michael@0: michael@0: /* static */ michael@0: nsresult michael@0: nsMIMEInfoBase::LaunchWithIProcess(nsIFile* aApp, const nsCString& aArg) michael@0: { michael@0: nsresult rv; michael@0: nsCOMPtr process = InitProcess(aApp, &rv); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: const char *string = aArg.get(); michael@0: michael@0: return process->Run(false, &string, 1); michael@0: } michael@0: michael@0: /* static */ michael@0: nsresult michael@0: nsMIMEInfoBase::LaunchWithIProcess(nsIFile* aApp, const nsString& aArg) michael@0: { michael@0: nsresult rv; michael@0: nsCOMPtr process = InitProcess(aApp, &rv); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: const char16_t *string = aArg.get(); michael@0: michael@0: return process->Runw(false, &string, 1); michael@0: } michael@0: michael@0: // nsMIMEInfoImpl implementation michael@0: NS_IMETHODIMP michael@0: nsMIMEInfoImpl::GetDefaultDescription(nsAString& aDefaultDescription) michael@0: { michael@0: if (mDefaultAppDescription.IsEmpty() && mDefaultApplication) { michael@0: // Don't want to cache this, just in case someone resets the app michael@0: // without changing the description.... michael@0: mDefaultApplication->GetLeafName(aDefaultDescription); michael@0: } else { michael@0: aDefaultDescription = mDefaultAppDescription; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMIMEInfoImpl::GetHasDefaultHandler(bool * _retval) michael@0: { michael@0: *_retval = !mDefaultAppDescription.IsEmpty(); michael@0: if (mDefaultApplication) { michael@0: bool exists; michael@0: *_retval = NS_SUCCEEDED(mDefaultApplication->Exists(&exists)) && exists; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsMIMEInfoImpl::LaunchDefaultWithFile(nsIFile* aFile) michael@0: { michael@0: if (!mDefaultApplication) michael@0: return NS_ERROR_FILE_NOT_FOUND; michael@0: michael@0: nsAutoCString nativePath; michael@0: aFile->GetNativePath(nativePath); michael@0: michael@0: return LaunchWithIProcess(mDefaultApplication, nativePath); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsMIMEInfoBase::GetPossibleLocalHandlers(nsIArray **_retval) michael@0: { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: }