michael@0: /* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * 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 michael@0: #include michael@0: michael@0: #if defined(MOZ_ENABLE_CONTENTACTION) michael@0: #include michael@0: #include michael@0: #endif michael@0: michael@0: #include "nsOSHelperAppService.h" michael@0: #include "nsMIMEInfoUnix.h" michael@0: #ifdef MOZ_WIDGET_GTK michael@0: #include "nsGNOMERegistry.h" michael@0: #endif michael@0: #include "nsISupports.h" michael@0: #include "nsString.h" michael@0: #include "nsReadableUtils.h" michael@0: #include "nsUnicharUtils.h" michael@0: #include "nsXPIDLString.h" michael@0: #include "nsIURL.h" michael@0: #include "nsIFileStreams.h" michael@0: #include "nsILineInputStream.h" michael@0: #include "nsIFile.h" michael@0: #include "nsIProcess.h" michael@0: #include "nsNetCID.h" michael@0: #include "nsXPCOM.h" michael@0: #include "nsISupportsPrimitives.h" michael@0: #include "nsCRT.h" michael@0: #include "nsDirectoryServiceDefs.h" michael@0: #include "nsDirectoryServiceUtils.h" michael@0: #include "prenv.h" // for PR_GetEnv() michael@0: #include "nsAutoPtr.h" michael@0: #include "mozilla/Preferences.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: #define LOG(args) PR_LOG(mLog, PR_LOG_DEBUG, args) michael@0: #define LOG_ENABLED() PR_LOG_TEST(mLog, PR_LOG_DEBUG) michael@0: michael@0: static nsresult michael@0: FindSemicolon(nsAString::const_iterator& aSemicolon_iter, michael@0: const nsAString::const_iterator& aEnd_iter); michael@0: static nsresult michael@0: ParseMIMEType(const nsAString::const_iterator& aStart_iter, michael@0: nsAString::const_iterator& aMajorTypeStart, michael@0: nsAString::const_iterator& aMajorTypeEnd, michael@0: nsAString::const_iterator& aMinorTypeStart, michael@0: nsAString::const_iterator& aMinorTypeEnd, michael@0: const nsAString::const_iterator& aEnd_iter); michael@0: michael@0: inline bool michael@0: IsNetscapeFormat(const nsACString& aBuffer); michael@0: michael@0: nsOSHelperAppService::nsOSHelperAppService() : nsExternalHelperAppService() michael@0: { michael@0: mode_t mask = umask(0777); michael@0: umask(mask); michael@0: mPermissions = 0666 & ~mask; michael@0: } michael@0: michael@0: nsOSHelperAppService::~nsOSHelperAppService() michael@0: {} michael@0: michael@0: /* michael@0: * Take a command with all the mailcap escapes in it and unescape it michael@0: * Ideally this needs the mime type, mime type options, and location of the michael@0: * temporary file, but this last can't be got from here michael@0: */ michael@0: // static michael@0: nsresult michael@0: nsOSHelperAppService::UnescapeCommand(const nsAString& aEscapedCommand, michael@0: const nsAString& aMajorType, michael@0: const nsAString& aMinorType, michael@0: nsACString& aUnEscapedCommand) { michael@0: LOG(("-- UnescapeCommand")); michael@0: LOG(("Command to escape: '%s'\n", michael@0: NS_LossyConvertUTF16toASCII(aEscapedCommand).get())); michael@0: // XXX This function will need to get the mime type and various stuff like that being passed in to work properly michael@0: michael@0: LOG(("UnescapeCommand really needs some work -- it should actually do some unescaping\n")); michael@0: michael@0: CopyUTF16toUTF8(aEscapedCommand, aUnEscapedCommand); michael@0: LOG(("Escaped command: '%s'\n", michael@0: PromiseFlatCString(aUnEscapedCommand).get())); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* Put aSemicolon_iter at the first non-escaped semicolon after michael@0: * aStart_iter but before aEnd_iter michael@0: */ michael@0: michael@0: static nsresult michael@0: FindSemicolon(nsAString::const_iterator& aSemicolon_iter, michael@0: const nsAString::const_iterator& aEnd_iter) { michael@0: bool semicolonFound = false; michael@0: while (aSemicolon_iter != aEnd_iter && !semicolonFound) { michael@0: switch(*aSemicolon_iter) { michael@0: case '\\': michael@0: aSemicolon_iter.advance(2); michael@0: break; michael@0: case ';': michael@0: semicolonFound = true; michael@0: break; michael@0: default: michael@0: ++aSemicolon_iter; michael@0: break; michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: static nsresult michael@0: ParseMIMEType(const nsAString::const_iterator& aStart_iter, michael@0: nsAString::const_iterator& aMajorTypeStart, michael@0: nsAString::const_iterator& aMajorTypeEnd, michael@0: nsAString::const_iterator& aMinorTypeStart, michael@0: nsAString::const_iterator& aMinorTypeEnd, michael@0: const nsAString::const_iterator& aEnd_iter) { michael@0: nsAString::const_iterator iter(aStart_iter); michael@0: michael@0: // skip leading whitespace michael@0: while (iter != aEnd_iter && nsCRT::IsAsciiSpace(*iter)) { michael@0: ++iter; michael@0: } michael@0: michael@0: if (iter == aEnd_iter) { michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: aMajorTypeStart = iter; michael@0: michael@0: // find major/minor separator ('/') michael@0: while (iter != aEnd_iter && *iter != '/') { michael@0: ++iter; michael@0: } michael@0: michael@0: if (iter == aEnd_iter) { michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: aMajorTypeEnd = iter; michael@0: michael@0: // skip '/' michael@0: ++iter; michael@0: michael@0: if (iter == aEnd_iter) { michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: aMinorTypeStart = iter; michael@0: michael@0: // find end of minor type, delimited by whitespace or ';' michael@0: while (iter != aEnd_iter && !nsCRT::IsAsciiSpace(*iter) && *iter != ';') { michael@0: ++iter; michael@0: } michael@0: michael@0: aMinorTypeEnd = iter; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // static michael@0: nsresult michael@0: nsOSHelperAppService::GetFileLocation(const char* aPrefName, michael@0: const char* aEnvVarName, michael@0: nsAString& aFileLocation) { michael@0: LOG(("-- GetFileLocation. Pref: '%s' EnvVar: '%s'\n", michael@0: aPrefName, michael@0: aEnvVarName)); michael@0: NS_PRECONDITION(aPrefName, "Null pref name passed; don't do that!"); michael@0: michael@0: aFileLocation.Truncate(); michael@0: /* The lookup order is: michael@0: 1) user pref michael@0: 2) env var michael@0: 3) pref michael@0: */ michael@0: NS_ENSURE_TRUE(Preferences::GetRootBranch(), NS_ERROR_FAILURE); michael@0: michael@0: /* michael@0: If we have an env var we should check whether the pref is a user michael@0: pref. If we do not, we don't care. michael@0: */ michael@0: if (Preferences::HasUserValue(aPrefName) && michael@0: NS_SUCCEEDED(Preferences::GetString(aPrefName, &aFileLocation))) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (aEnvVarName && *aEnvVarName) { michael@0: char* prefValue = PR_GetEnv(aEnvVarName); michael@0: if (prefValue && *prefValue) { michael@0: // the pref is in the system charset and it's a filepath... The michael@0: // natural way to do the charset conversion is by just initing michael@0: // an nsIFile with the native path and asking it for the Unicode michael@0: // version. michael@0: nsresult rv; michael@0: nsCOMPtr file(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = file->InitWithNativePath(nsDependentCString(prefValue)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = file->GetPath(aFileLocation); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: return Preferences::GetString(aPrefName, &aFileLocation); michael@0: } michael@0: michael@0: michael@0: /* Get the mime.types file names from prefs and look up info in them michael@0: based on extension */ michael@0: // static michael@0: nsresult michael@0: nsOSHelperAppService::LookUpTypeAndDescription(const nsAString& aFileExtension, michael@0: nsAString& aMajorType, michael@0: nsAString& aMinorType, michael@0: nsAString& aDescription, michael@0: bool aUserData) { michael@0: LOG(("-- LookUpTypeAndDescription for extension '%s'\n", michael@0: NS_LossyConvertUTF16toASCII(aFileExtension).get())); michael@0: nsresult rv = NS_OK; michael@0: nsAutoString mimeFileName; michael@0: michael@0: const char* filenamePref = aUserData ? michael@0: "helpers.private_mime_types_file" : "helpers.global_mime_types_file"; michael@0: michael@0: rv = GetFileLocation(filenamePref, nullptr, mimeFileName); michael@0: if (NS_SUCCEEDED(rv) && !mimeFileName.IsEmpty()) { michael@0: rv = GetTypeAndDescriptionFromMimetypesFile(mimeFileName, michael@0: aFileExtension, michael@0: aMajorType, michael@0: aMinorType, michael@0: aDescription); michael@0: } else { michael@0: rv = NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: inline bool michael@0: IsNetscapeFormat(const nsACString& aBuffer) { michael@0: return StringBeginsWith(aBuffer, NS_LITERAL_CSTRING("#--Netscape Communications Corporation MIME Information")) || michael@0: StringBeginsWith(aBuffer, NS_LITERAL_CSTRING("#--MCOM MIME Information")); michael@0: } michael@0: michael@0: /* michael@0: * Create a file stream and line input stream for the filename. michael@0: * Leaves the first line of the file in aBuffer and sets the format to michael@0: * true for netscape files and false for normail ones michael@0: */ michael@0: // static michael@0: nsresult michael@0: nsOSHelperAppService::CreateInputStream(const nsAString& aFilename, michael@0: nsIFileInputStream ** aFileInputStream, michael@0: nsILineInputStream ** aLineInputStream, michael@0: nsACString& aBuffer, michael@0: bool * aNetscapeFormat, michael@0: bool * aMore) { michael@0: LOG(("-- CreateInputStream")); michael@0: nsresult rv = NS_OK; michael@0: michael@0: nsCOMPtr file(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: rv = file->InitWithPath(aFilename); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: nsCOMPtr fileStream(do_CreateInstance(NS_LOCALFILEINPUTSTREAM_CONTRACTID, &rv)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: rv = fileStream->Init(file, -1, -1, false); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: nsCOMPtr lineStream(do_QueryInterface(fileStream, &rv)); michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: LOG(("Interface trouble in stream land!")); michael@0: return rv; michael@0: } michael@0: michael@0: rv = lineStream->ReadLine(aBuffer, aMore); michael@0: if (NS_FAILED(rv)) { michael@0: fileStream->Close(); michael@0: return rv; michael@0: } michael@0: michael@0: *aNetscapeFormat = IsNetscapeFormat(aBuffer); michael@0: michael@0: *aFileInputStream = fileStream; michael@0: NS_ADDREF(*aFileInputStream); michael@0: *aLineInputStream = lineStream; michael@0: NS_ADDREF(*aLineInputStream); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* Open the file, read the first line, decide what type of file it is, michael@0: then get info based on extension */ michael@0: // static michael@0: nsresult michael@0: nsOSHelperAppService::GetTypeAndDescriptionFromMimetypesFile(const nsAString& aFilename, michael@0: const nsAString& aFileExtension, michael@0: nsAString& aMajorType, michael@0: nsAString& aMinorType, michael@0: nsAString& aDescription) { michael@0: LOG(("-- GetTypeAndDescriptionFromMimetypesFile\n")); michael@0: LOG(("Getting type and description from types file '%s'\n", michael@0: NS_LossyConvertUTF16toASCII(aFilename).get())); michael@0: LOG(("Using extension '%s'\n", michael@0: NS_LossyConvertUTF16toASCII(aFileExtension).get())); michael@0: nsresult rv = NS_OK; michael@0: nsCOMPtr mimeFile; michael@0: nsCOMPtr mimeTypes; michael@0: bool netscapeFormat; michael@0: nsAutoString buf; michael@0: nsAutoCString cBuf; michael@0: bool more = false; michael@0: rv = CreateInputStream(aFilename, getter_AddRefs(mimeFile), getter_AddRefs(mimeTypes), michael@0: cBuf, &netscapeFormat, &more); michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: nsAutoString extensions; michael@0: nsString entry; michael@0: entry.SetCapacity(100); michael@0: nsAString::const_iterator majorTypeStart, majorTypeEnd, michael@0: minorTypeStart, minorTypeEnd, michael@0: descriptionStart, descriptionEnd; michael@0: michael@0: do { michael@0: CopyASCIItoUTF16(cBuf, buf); michael@0: // read through, building up an entry. If we finish an entry, check for michael@0: // a match and return out of the loop if we match michael@0: michael@0: // skip comments and empty lines michael@0: if (!buf.IsEmpty() && buf.First() != '#') { michael@0: entry.Append(buf); michael@0: if (entry.Last() == '\\') { michael@0: entry.Truncate(entry.Length() - 1); michael@0: entry.Append(char16_t(' ')); // in case there is no trailing whitespace on this line michael@0: } else { // we have a full entry michael@0: LOG(("Current entry: '%s'\n", michael@0: NS_LossyConvertUTF16toASCII(entry).get())); michael@0: if (netscapeFormat) { michael@0: rv = ParseNetscapeMIMETypesEntry(entry, michael@0: majorTypeStart, majorTypeEnd, michael@0: minorTypeStart, minorTypeEnd, michael@0: extensions, michael@0: descriptionStart, descriptionEnd); michael@0: if (NS_FAILED(rv)) { michael@0: // We sometimes get things like RealPlayer appending michael@0: // "normal" entries to "Netscape" .mime.types files. Try michael@0: // to handle that. Bug 106381. michael@0: LOG(("Bogus entry; trying 'normal' mode\n")); michael@0: rv = ParseNormalMIMETypesEntry(entry, michael@0: majorTypeStart, majorTypeEnd, michael@0: minorTypeStart, minorTypeEnd, michael@0: extensions, michael@0: descriptionStart, descriptionEnd); michael@0: } michael@0: } else { michael@0: rv = ParseNormalMIMETypesEntry(entry, michael@0: majorTypeStart, majorTypeEnd, michael@0: minorTypeStart, minorTypeEnd, michael@0: extensions, michael@0: descriptionStart, descriptionEnd); michael@0: if (NS_FAILED(rv)) { michael@0: // We sometimes get things like StarOffice prepending michael@0: // "normal" entries to "Netscape" .mime.types files. Try michael@0: // to handle that. Bug 136670. michael@0: LOG(("Bogus entry; trying 'Netscape' mode\n")); michael@0: rv = ParseNetscapeMIMETypesEntry(entry, michael@0: majorTypeStart, majorTypeEnd, michael@0: minorTypeStart, minorTypeEnd, michael@0: extensions, michael@0: descriptionStart, descriptionEnd); michael@0: } michael@0: } michael@0: michael@0: if (NS_SUCCEEDED(rv)) { // entry parses michael@0: nsAString::const_iterator start, end; michael@0: extensions.BeginReading(start); michael@0: extensions.EndReading(end); michael@0: nsAString::const_iterator iter(start); michael@0: michael@0: while (start != end) { michael@0: FindCharInReadable(',', iter, end); michael@0: if (Substring(start, iter).Equals(aFileExtension, michael@0: nsCaseInsensitiveStringComparator())) { michael@0: // it's a match. Assign the type and description and run michael@0: aMajorType.Assign(Substring(majorTypeStart, majorTypeEnd)); michael@0: aMinorType.Assign(Substring(minorTypeStart, minorTypeEnd)); michael@0: aDescription.Assign(Substring(descriptionStart, descriptionEnd)); michael@0: mimeFile->Close(); michael@0: return NS_OK; michael@0: } michael@0: if (iter != end) { michael@0: ++iter; michael@0: } michael@0: start = iter; michael@0: } michael@0: } else { michael@0: LOG(("Failed to parse entry: %s\n", NS_LossyConvertUTF16toASCII(entry).get())); michael@0: } michael@0: // truncate the entry for the next iteration michael@0: entry.Truncate(); michael@0: } michael@0: } michael@0: if (!more) { michael@0: rv = NS_ERROR_NOT_AVAILABLE; michael@0: break; michael@0: } michael@0: // read the next line michael@0: rv = mimeTypes->ReadLine(cBuf, &more); michael@0: } while (NS_SUCCEEDED(rv)); michael@0: michael@0: mimeFile->Close(); michael@0: return rv; michael@0: } michael@0: michael@0: /* Get the mime.types file names from prefs and look up info in them michael@0: based on mimetype */ michael@0: // static michael@0: nsresult michael@0: nsOSHelperAppService::LookUpExtensionsAndDescription(const nsAString& aMajorType, michael@0: const nsAString& aMinorType, michael@0: nsAString& aFileExtensions, michael@0: nsAString& aDescription) { michael@0: LOG(("-- LookUpExtensionsAndDescription for type '%s/%s'\n", michael@0: NS_LossyConvertUTF16toASCII(aMajorType).get(), michael@0: NS_LossyConvertUTF16toASCII(aMinorType).get())); michael@0: nsresult rv = NS_OK; michael@0: nsAutoString mimeFileName; michael@0: michael@0: rv = GetFileLocation("helpers.private_mime_types_file", nullptr, mimeFileName); michael@0: if (NS_SUCCEEDED(rv) && !mimeFileName.IsEmpty()) { michael@0: rv = GetExtensionsAndDescriptionFromMimetypesFile(mimeFileName, michael@0: aMajorType, michael@0: aMinorType, michael@0: aFileExtensions, michael@0: aDescription); michael@0: } else { michael@0: rv = NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: if (NS_FAILED(rv) || aFileExtensions.IsEmpty()) { michael@0: rv = GetFileLocation("helpers.global_mime_types_file", michael@0: nullptr, mimeFileName); michael@0: if (NS_SUCCEEDED(rv) && !mimeFileName.IsEmpty()) { michael@0: rv = GetExtensionsAndDescriptionFromMimetypesFile(mimeFileName, michael@0: aMajorType, michael@0: aMinorType, michael@0: aFileExtensions, michael@0: aDescription); michael@0: } else { michael@0: rv = NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: /* Open the file, read the first line, decide what type of file it is, michael@0: then get info based on extension */ michael@0: // static michael@0: nsresult michael@0: nsOSHelperAppService::GetExtensionsAndDescriptionFromMimetypesFile(const nsAString& aFilename, michael@0: const nsAString& aMajorType, michael@0: const nsAString& aMinorType, michael@0: nsAString& aFileExtensions, michael@0: nsAString& aDescription) { michael@0: LOG(("-- GetExtensionsAndDescriptionFromMimetypesFile\n")); michael@0: LOG(("Getting extensions and description from types file '%s'\n", michael@0: NS_LossyConvertUTF16toASCII(aFilename).get())); michael@0: LOG(("Using type '%s/%s'\n", michael@0: NS_LossyConvertUTF16toASCII(aMajorType).get(), michael@0: NS_LossyConvertUTF16toASCII(aMinorType).get())); michael@0: michael@0: nsresult rv = NS_OK; michael@0: nsCOMPtr mimeFile; michael@0: nsCOMPtr mimeTypes; michael@0: bool netscapeFormat; michael@0: nsAutoCString cBuf; michael@0: nsAutoString buf; michael@0: bool more = false; michael@0: rv = CreateInputStream(aFilename, getter_AddRefs(mimeFile), getter_AddRefs(mimeTypes), michael@0: cBuf, &netscapeFormat, &more); michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: nsAutoString extensions; michael@0: nsString entry; michael@0: entry.SetCapacity(100); michael@0: nsAString::const_iterator majorTypeStart, majorTypeEnd, michael@0: minorTypeStart, minorTypeEnd, michael@0: descriptionStart, descriptionEnd; michael@0: michael@0: do { michael@0: CopyASCIItoUTF16(cBuf, buf); michael@0: // read through, building up an entry. If we finish an entry, check for michael@0: // a match and return out of the loop if we match michael@0: michael@0: // skip comments and empty lines michael@0: if (!buf.IsEmpty() && buf.First() != '#') { michael@0: entry.Append(buf); michael@0: if (entry.Last() == '\\') { michael@0: entry.Truncate(entry.Length() - 1); michael@0: entry.Append(char16_t(' ')); // in case there is no trailing whitespace on this line michael@0: } else { // we have a full entry michael@0: LOG(("Current entry: '%s'\n", michael@0: NS_LossyConvertUTF16toASCII(entry).get())); michael@0: if (netscapeFormat) { michael@0: rv = ParseNetscapeMIMETypesEntry(entry, michael@0: majorTypeStart, majorTypeEnd, michael@0: minorTypeStart, minorTypeEnd, michael@0: extensions, michael@0: descriptionStart, descriptionEnd); michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: // We sometimes get things like RealPlayer appending michael@0: // "normal" entries to "Netscape" .mime.types files. Try michael@0: // to handle that. Bug 106381. michael@0: LOG(("Bogus entry; trying 'normal' mode\n")); michael@0: rv = ParseNormalMIMETypesEntry(entry, michael@0: majorTypeStart, majorTypeEnd, michael@0: minorTypeStart, minorTypeEnd, michael@0: extensions, michael@0: descriptionStart, descriptionEnd); michael@0: } michael@0: } else { michael@0: rv = ParseNormalMIMETypesEntry(entry, michael@0: majorTypeStart, majorTypeEnd, michael@0: minorTypeStart, michael@0: minorTypeEnd, extensions, michael@0: descriptionStart, descriptionEnd); michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: // We sometimes get things like StarOffice prepending michael@0: // "normal" entries to "Netscape" .mime.types files. Try michael@0: // to handle that. Bug 136670. michael@0: LOG(("Bogus entry; trying 'Netscape' mode\n")); michael@0: rv = ParseNetscapeMIMETypesEntry(entry, michael@0: majorTypeStart, majorTypeEnd, michael@0: minorTypeStart, minorTypeEnd, michael@0: extensions, michael@0: descriptionStart, descriptionEnd); michael@0: } michael@0: } michael@0: michael@0: if (NS_SUCCEEDED(rv) && michael@0: Substring(majorTypeStart, michael@0: majorTypeEnd).Equals(aMajorType, michael@0: nsCaseInsensitiveStringComparator())&& michael@0: Substring(minorTypeStart, michael@0: minorTypeEnd).Equals(aMinorType, michael@0: nsCaseInsensitiveStringComparator())) { michael@0: // it's a match michael@0: aFileExtensions.Assign(extensions); michael@0: aDescription.Assign(Substring(descriptionStart, descriptionEnd)); michael@0: mimeFile->Close(); michael@0: return NS_OK; michael@0: } else if (NS_FAILED(rv)) { michael@0: LOG(("Failed to parse entry: %s\n", NS_LossyConvertUTF16toASCII(entry).get())); michael@0: } michael@0: michael@0: entry.Truncate(); michael@0: } michael@0: } michael@0: if (!more) { michael@0: rv = NS_ERROR_NOT_AVAILABLE; michael@0: break; michael@0: } michael@0: // read the next line michael@0: rv = mimeTypes->ReadLine(cBuf, &more); michael@0: } while (NS_SUCCEEDED(rv)); michael@0: michael@0: mimeFile->Close(); michael@0: return rv; michael@0: } michael@0: michael@0: /* michael@0: * This parses a Netscape format mime.types entry. There are two michael@0: * possible formats: michael@0: * michael@0: * type=foo/bar; options exts="baz" description="Some type" michael@0: * michael@0: * and michael@0: * michael@0: * type=foo/bar; options description="Some type" exts="baz" michael@0: */ michael@0: // static michael@0: nsresult michael@0: nsOSHelperAppService::ParseNetscapeMIMETypesEntry(const nsAString& aEntry, michael@0: nsAString::const_iterator& aMajorTypeStart, michael@0: nsAString::const_iterator& aMajorTypeEnd, michael@0: nsAString::const_iterator& aMinorTypeStart, michael@0: nsAString::const_iterator& aMinorTypeEnd, michael@0: nsAString& aExtensions, michael@0: nsAString::const_iterator& aDescriptionStart, michael@0: nsAString::const_iterator& aDescriptionEnd) { michael@0: LOG(("-- ParseNetscapeMIMETypesEntry\n")); michael@0: NS_ASSERTION(!aEntry.IsEmpty(), "Empty Netscape MIME types entry being parsed."); michael@0: michael@0: nsAString::const_iterator start_iter, end_iter, match_start, match_end; michael@0: michael@0: aEntry.BeginReading(start_iter); michael@0: aEntry.EndReading(end_iter); michael@0: michael@0: // skip trailing whitespace michael@0: do { michael@0: --end_iter; michael@0: } while (end_iter != start_iter && michael@0: nsCRT::IsAsciiSpace(*end_iter)); michael@0: // if we're pointing to a quote, don't advance -- we don't want to michael@0: // include the quote.... michael@0: if (*end_iter != '"') michael@0: ++end_iter; michael@0: match_start = start_iter; michael@0: match_end = end_iter; michael@0: michael@0: // Get the major and minor types michael@0: // First the major type michael@0: if (! FindInReadable(NS_LITERAL_STRING("type="), match_start, match_end)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: match_start = match_end; michael@0: michael@0: while (match_end != end_iter && michael@0: *match_end != '/') { michael@0: ++match_end; michael@0: } michael@0: if (match_end == end_iter) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: aMajorTypeStart = match_start; michael@0: aMajorTypeEnd = match_end; michael@0: michael@0: // now the minor type michael@0: if (++match_end == end_iter) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: match_start = match_end; michael@0: michael@0: while (match_end != end_iter && michael@0: !nsCRT::IsAsciiSpace(*match_end) && michael@0: *match_end != ';') { michael@0: ++match_end; michael@0: } michael@0: if (match_end == end_iter) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: aMinorTypeStart = match_start; michael@0: aMinorTypeEnd = match_end; michael@0: michael@0: // ignore everything up to the end of the mime type from here on michael@0: start_iter = match_end; michael@0: michael@0: // get the extensions michael@0: match_start = match_end; michael@0: match_end = end_iter; michael@0: if (FindInReadable(NS_LITERAL_STRING("exts="), match_start, match_end)) { michael@0: nsAString::const_iterator extStart, extEnd; michael@0: michael@0: if (match_end == end_iter || michael@0: (*match_end == '"' && ++match_end == end_iter)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: extStart = match_end; michael@0: match_start = extStart; michael@0: match_end = end_iter; michael@0: if (FindInReadable(NS_LITERAL_STRING("desc=\""), match_start, match_end)) { michael@0: // exts= before desc=, so we have to find the actual end of the extensions michael@0: extEnd = match_start; michael@0: if (extEnd == extStart) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: do { michael@0: --extEnd; michael@0: } while (extEnd != extStart && michael@0: nsCRT::IsAsciiSpace(*extEnd)); michael@0: michael@0: if (extEnd != extStart && *extEnd == '"') { michael@0: --extEnd; michael@0: } michael@0: } else { michael@0: // desc= before exts=, so we can use end_iter as the end of the extensions michael@0: extEnd = end_iter; michael@0: } michael@0: aExtensions = Substring(extStart, extEnd); michael@0: } else { michael@0: // no extensions michael@0: aExtensions.Truncate(); michael@0: } michael@0: michael@0: // get the description michael@0: match_start = start_iter; michael@0: match_end = end_iter; michael@0: if (FindInReadable(NS_LITERAL_STRING("desc=\""), match_start, match_end)) { michael@0: aDescriptionStart = match_end; michael@0: match_start = aDescriptionStart; michael@0: match_end = end_iter; michael@0: if (FindInReadable(NS_LITERAL_STRING("exts="), match_start, match_end)) { michael@0: // exts= after desc=, so have to find actual end of description michael@0: aDescriptionEnd = match_start; michael@0: if (aDescriptionEnd == aDescriptionStart) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: do { michael@0: --aDescriptionEnd; michael@0: } while (aDescriptionEnd != aDescriptionStart && michael@0: nsCRT::IsAsciiSpace(*aDescriptionEnd)); michael@0: michael@0: if (aDescriptionStart != aDescriptionStart && *aDescriptionEnd == '"') { michael@0: --aDescriptionEnd; michael@0: } michael@0: } else { michael@0: // desc= after exts=, so use end_iter for the description end michael@0: aDescriptionEnd = end_iter; michael@0: } michael@0: } else { michael@0: // no description michael@0: aDescriptionStart = start_iter; michael@0: aDescriptionEnd = start_iter; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* michael@0: * This parses a normal format mime.types entry. The format is: michael@0: * michael@0: * major/minor ext1 ext2 ext3 michael@0: */ michael@0: // static michael@0: nsresult michael@0: nsOSHelperAppService::ParseNormalMIMETypesEntry(const nsAString& aEntry, michael@0: nsAString::const_iterator& aMajorTypeStart, michael@0: nsAString::const_iterator& aMajorTypeEnd, michael@0: nsAString::const_iterator& aMinorTypeStart, michael@0: nsAString::const_iterator& aMinorTypeEnd, michael@0: nsAString& aExtensions, michael@0: nsAString::const_iterator& aDescriptionStart, michael@0: nsAString::const_iterator& aDescriptionEnd) { michael@0: LOG(("-- ParseNormalMIMETypesEntry\n")); michael@0: NS_ASSERTION(!aEntry.IsEmpty(), "Empty Normal MIME types entry being parsed."); michael@0: michael@0: nsAString::const_iterator start_iter, end_iter, iter; michael@0: michael@0: aEntry.BeginReading(start_iter); michael@0: aEntry.EndReading(end_iter); michael@0: michael@0: // no description michael@0: aDescriptionStart = start_iter; michael@0: aDescriptionEnd = start_iter; michael@0: michael@0: // skip leading whitespace michael@0: while (start_iter != end_iter && nsCRT::IsAsciiSpace(*start_iter)) { michael@0: ++start_iter; michael@0: } michael@0: if (start_iter == end_iter) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: // skip trailing whitespace michael@0: do { michael@0: --end_iter; michael@0: } while (end_iter != start_iter && nsCRT::IsAsciiSpace(*end_iter)); michael@0: michael@0: ++end_iter; // point to first whitespace char (or to end of string) michael@0: iter = start_iter; michael@0: michael@0: // get the major type michael@0: if (! FindCharInReadable('/', iter, end_iter)) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: nsAString::const_iterator equals_sign_iter(start_iter); michael@0: if (FindCharInReadable('=', equals_sign_iter, iter)) michael@0: return NS_ERROR_FAILURE; // see bug 136670 michael@0: michael@0: aMajorTypeStart = start_iter; michael@0: aMajorTypeEnd = iter; michael@0: michael@0: // get the minor type michael@0: if (++iter == end_iter) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: start_iter = iter; michael@0: michael@0: while (iter != end_iter && !nsCRT::IsAsciiSpace(*iter)) { michael@0: ++iter; michael@0: } michael@0: aMinorTypeStart = start_iter; michael@0: aMinorTypeEnd = iter; michael@0: michael@0: // get the extensions michael@0: aExtensions.Truncate(); michael@0: while (iter != end_iter) { michael@0: while (iter != end_iter && nsCRT::IsAsciiSpace(*iter)) { michael@0: ++iter; michael@0: } michael@0: michael@0: start_iter = iter; michael@0: while (iter != end_iter && !nsCRT::IsAsciiSpace(*iter)) { michael@0: ++iter; michael@0: } michael@0: aExtensions.Append(Substring(start_iter, iter)); michael@0: if (iter != end_iter) { // not the last extension michael@0: aExtensions.Append(char16_t(',')); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // static michael@0: nsresult michael@0: nsOSHelperAppService::LookUpHandlerAndDescription(const nsAString& aMajorType, michael@0: const nsAString& aMinorType, michael@0: nsAString& aHandler, michael@0: nsAString& aDescription, michael@0: nsAString& aMozillaFlags) { michael@0: michael@0: // The mailcap lookup is two-pass to handle the case of mailcap files michael@0: // that have something like: michael@0: // michael@0: // text/*; emacs %s michael@0: // text/rtf; soffice %s michael@0: // michael@0: // in that order. We want to pick up "soffice" for text/rtf in such cases michael@0: nsresult rv = DoLookUpHandlerAndDescription(aMajorType, michael@0: aMinorType, michael@0: aHandler, michael@0: aDescription, michael@0: aMozillaFlags, michael@0: true); michael@0: if (NS_FAILED(rv)) { michael@0: rv = DoLookUpHandlerAndDescription(aMajorType, michael@0: aMinorType, michael@0: aHandler, michael@0: aDescription, michael@0: aMozillaFlags, michael@0: false); michael@0: } michael@0: michael@0: // maybe we have an entry for "aMajorType/*"? michael@0: if (NS_FAILED(rv)) { michael@0: rv = DoLookUpHandlerAndDescription(aMajorType, michael@0: NS_LITERAL_STRING("*"), michael@0: aHandler, michael@0: aDescription, michael@0: aMozillaFlags, michael@0: true); michael@0: } michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: rv = DoLookUpHandlerAndDescription(aMajorType, michael@0: NS_LITERAL_STRING("*"), michael@0: aHandler, michael@0: aDescription, michael@0: aMozillaFlags, michael@0: false); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: // static michael@0: nsresult michael@0: nsOSHelperAppService::DoLookUpHandlerAndDescription(const nsAString& aMajorType, michael@0: const nsAString& aMinorType, michael@0: nsAString& aHandler, michael@0: nsAString& aDescription, michael@0: nsAString& aMozillaFlags, michael@0: bool aUserData) { michael@0: LOG(("-- LookUpHandlerAndDescription for type '%s/%s'\n", michael@0: NS_LossyConvertUTF16toASCII(aMajorType).get(), michael@0: NS_LossyConvertUTF16toASCII(aMinorType).get())); michael@0: nsresult rv = NS_OK; michael@0: nsAutoString mailcapFileName; michael@0: michael@0: const char * filenamePref = aUserData ? michael@0: "helpers.private_mailcap_file" : "helpers.global_mailcap_file"; michael@0: const char * filenameEnvVar = aUserData ? michael@0: "PERSONAL_MAILCAP" : "MAILCAP"; michael@0: michael@0: rv = GetFileLocation(filenamePref, filenameEnvVar, mailcapFileName); michael@0: if (NS_SUCCEEDED(rv) && !mailcapFileName.IsEmpty()) { michael@0: rv = GetHandlerAndDescriptionFromMailcapFile(mailcapFileName, michael@0: aMajorType, michael@0: aMinorType, michael@0: aHandler, michael@0: aDescription, michael@0: aMozillaFlags); michael@0: } else { michael@0: rv = NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: // static michael@0: nsresult michael@0: nsOSHelperAppService::GetHandlerAndDescriptionFromMailcapFile(const nsAString& aFilename, michael@0: const nsAString& aMajorType, michael@0: const nsAString& aMinorType, michael@0: nsAString& aHandler, michael@0: nsAString& aDescription, michael@0: nsAString& aMozillaFlags) { michael@0: michael@0: LOG(("-- GetHandlerAndDescriptionFromMailcapFile\n")); michael@0: LOG(("Getting handler and description from mailcap file '%s'\n", michael@0: NS_LossyConvertUTF16toASCII(aFilename).get())); michael@0: LOG(("Using type '%s/%s'\n", michael@0: NS_LossyConvertUTF16toASCII(aMajorType).get(), michael@0: NS_LossyConvertUTF16toASCII(aMinorType).get())); michael@0: michael@0: nsresult rv = NS_OK; michael@0: bool more = false; michael@0: michael@0: nsCOMPtr file(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: rv = file->InitWithPath(aFilename); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: nsCOMPtr mailcapFile(do_CreateInstance(NS_LOCALFILEINPUTSTREAM_CONTRACTID, &rv)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: rv = mailcapFile->Init(file, -1, -1, false); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: nsCOMPtr mailcap (do_QueryInterface(mailcapFile, &rv)); michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: LOG(("Interface trouble in stream land!")); michael@0: return rv; michael@0: } michael@0: michael@0: nsString entry, buffer; michael@0: nsAutoCString cBuffer; michael@0: entry.SetCapacity(128); michael@0: cBuffer.SetCapacity(80); michael@0: rv = mailcap->ReadLine(cBuffer, &more); michael@0: if (NS_FAILED(rv)) { michael@0: mailcapFile->Close(); michael@0: return rv; michael@0: } michael@0: michael@0: do { // return on end-of-file in the loop michael@0: michael@0: CopyASCIItoUTF16(cBuffer, buffer); michael@0: if (!buffer.IsEmpty() && buffer.First() != '#') { michael@0: entry.Append(buffer); michael@0: if (entry.Last() == '\\') { // entry continues on next line michael@0: entry.Truncate(entry.Length()-1); michael@0: entry.Append(char16_t(' ')); // in case there is no trailing whitespace on this line michael@0: } else { // we have a full entry in entry. Check it for the type michael@0: LOG(("Current entry: '%s'\n", michael@0: NS_LossyConvertUTF16toASCII(entry).get())); michael@0: michael@0: nsAString::const_iterator semicolon_iter, michael@0: start_iter, end_iter, michael@0: majorTypeStart, majorTypeEnd, michael@0: minorTypeStart, minorTypeEnd; michael@0: entry.BeginReading(start_iter); michael@0: entry.EndReading(end_iter); michael@0: semicolon_iter = start_iter; michael@0: FindSemicolon(semicolon_iter, end_iter); michael@0: if (semicolon_iter != end_iter) { // we have something resembling a valid entry michael@0: rv = ParseMIMEType(start_iter, majorTypeStart, majorTypeEnd, michael@0: minorTypeStart, minorTypeEnd, semicolon_iter); michael@0: if (NS_SUCCEEDED(rv) && michael@0: Substring(majorTypeStart, michael@0: majorTypeEnd).Equals(aMajorType, michael@0: nsCaseInsensitiveStringComparator()) && michael@0: Substring(minorTypeStart, michael@0: minorTypeEnd).Equals(aMinorType, michael@0: nsCaseInsensitiveStringComparator())) { // we have a match michael@0: bool match = true; michael@0: ++semicolon_iter; // point at the first char past the semicolon michael@0: start_iter = semicolon_iter; // handler string starts here michael@0: FindSemicolon(semicolon_iter, end_iter); michael@0: while (start_iter != semicolon_iter && michael@0: nsCRT::IsAsciiSpace(*start_iter)) { michael@0: ++start_iter; michael@0: } michael@0: michael@0: LOG(("The real handler is: '%s'\n", michael@0: NS_LossyConvertUTF16toASCII(Substring(start_iter, michael@0: semicolon_iter)).get())); michael@0: michael@0: // XXX ugly hack. Just grab the executable name michael@0: nsAString::const_iterator end_handler_iter = semicolon_iter; michael@0: nsAString::const_iterator end_executable_iter = start_iter; michael@0: while (end_executable_iter != end_handler_iter && michael@0: !nsCRT::IsAsciiSpace(*end_executable_iter)) { michael@0: ++end_executable_iter; michael@0: } michael@0: // XXX End ugly hack michael@0: michael@0: aHandler = Substring(start_iter, end_executable_iter); michael@0: michael@0: nsAString::const_iterator start_option_iter, end_optionname_iter, equal_sign_iter; michael@0: bool equalSignFound; michael@0: while (match && michael@0: semicolon_iter != end_iter && michael@0: ++semicolon_iter != end_iter) { // there are options left and we still match michael@0: start_option_iter = semicolon_iter; michael@0: // skip over leading whitespace michael@0: while (start_option_iter != end_iter && michael@0: nsCRT::IsAsciiSpace(*start_option_iter)) { michael@0: ++start_option_iter; michael@0: } michael@0: if (start_option_iter == end_iter) { // nothing actually here michael@0: break; michael@0: } michael@0: semicolon_iter = start_option_iter; michael@0: FindSemicolon(semicolon_iter, end_iter); michael@0: equal_sign_iter = start_option_iter; michael@0: equalSignFound = false; michael@0: while (equal_sign_iter != semicolon_iter && !equalSignFound) { michael@0: switch(*equal_sign_iter) { michael@0: case '\\': michael@0: equal_sign_iter.advance(2); michael@0: break; michael@0: case '=': michael@0: equalSignFound = true; michael@0: break; michael@0: default: michael@0: ++equal_sign_iter; michael@0: break; michael@0: } michael@0: } michael@0: end_optionname_iter = start_option_iter; michael@0: // find end of option name michael@0: while (end_optionname_iter != equal_sign_iter && michael@0: !nsCRT::IsAsciiSpace(*end_optionname_iter)) { michael@0: ++end_optionname_iter; michael@0: } michael@0: nsDependentSubstring optionName(start_option_iter, end_optionname_iter); michael@0: if (equalSignFound) { michael@0: // This is an option that has a name and value michael@0: if (optionName.EqualsLiteral("description")) { michael@0: aDescription = Substring(++equal_sign_iter, semicolon_iter); michael@0: } else if (optionName.EqualsLiteral("x-mozilla-flags")) { michael@0: aMozillaFlags = Substring(++equal_sign_iter, semicolon_iter); michael@0: } else if (optionName.EqualsLiteral("test")) { michael@0: nsAutoCString testCommand; michael@0: rv = UnescapeCommand(Substring(++equal_sign_iter, semicolon_iter), michael@0: aMajorType, michael@0: aMinorType, michael@0: testCommand); michael@0: if (NS_FAILED(rv)) michael@0: continue; michael@0: nsCOMPtr process = do_CreateInstance(NS_PROCESS_CONTRACTID, &rv); michael@0: if (NS_FAILED(rv)) michael@0: continue; michael@0: nsCOMPtr file(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv)); michael@0: if (NS_FAILED(rv)) michael@0: continue; michael@0: rv = file->InitWithNativePath(NS_LITERAL_CSTRING("/bin/sh")); michael@0: if (NS_FAILED(rv)) michael@0: continue; michael@0: rv = process->Init(file); michael@0: if (NS_FAILED(rv)) michael@0: continue; michael@0: const char *args[] = { "-c", testCommand.get() }; michael@0: LOG(("Running Test: %s\n", testCommand.get())); michael@0: rv = process->Run(true, args, 2); michael@0: if (NS_FAILED(rv)) michael@0: continue; michael@0: int32_t exitValue; michael@0: rv = process->GetExitValue(&exitValue); michael@0: if (NS_FAILED(rv)) michael@0: continue; michael@0: LOG(("Exit code: %d\n", exitValue)); michael@0: if (exitValue) { michael@0: match = false; michael@0: } michael@0: } michael@0: } else { michael@0: // This is an option that just has a name but no value (eg "copiousoutput") michael@0: if (optionName.EqualsLiteral("needsterminal")) { michael@0: match = false; michael@0: } michael@0: } michael@0: } michael@0: michael@0: michael@0: if (match) { // we did not fail any test clauses; all is good michael@0: // get out of here michael@0: mailcapFile->Close(); michael@0: return NS_OK; michael@0: } else { // pretend that this match never happened michael@0: aDescription.Truncate(); michael@0: aMozillaFlags.Truncate(); michael@0: aHandler.Truncate(); michael@0: } michael@0: } michael@0: } michael@0: // zero out the entry for the next cycle michael@0: entry.Truncate(); michael@0: } michael@0: } michael@0: if (!more) { michael@0: rv = NS_ERROR_NOT_AVAILABLE; michael@0: break; michael@0: } michael@0: rv = mailcap->ReadLine(cBuffer, &more); michael@0: } while (NS_SUCCEEDED(rv)); michael@0: mailcapFile->Close(); michael@0: return rv; michael@0: } michael@0: michael@0: nsresult nsOSHelperAppService::OSProtocolHandlerExists(const char * aProtocolScheme, bool * aHandlerExists) michael@0: { michael@0: LOG(("-- nsOSHelperAppService::OSProtocolHandlerExists for '%s'\n", michael@0: aProtocolScheme)); michael@0: *aHandlerExists = false; michael@0: michael@0: #if defined(MOZ_ENABLE_CONTENTACTION) michael@0: // libcontentaction requires character ':' after scheme michael@0: ContentAction::Action action = michael@0: ContentAction::Action::defaultActionForScheme(QString(aProtocolScheme) + ':'); michael@0: michael@0: if (action.isValid()) michael@0: *aHandlerExists = true; michael@0: #endif michael@0: michael@0: #ifdef MOZ_WIDGET_GTK michael@0: // Check the GConf registry for a protocol handler michael@0: *aHandlerExists = nsGNOMERegistry::HandlerExists(aProtocolScheme); michael@0: #endif michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP nsOSHelperAppService::GetApplicationDescription(const nsACString& aScheme, nsAString& _retval) michael@0: { michael@0: #ifdef MOZ_WIDGET_GTK michael@0: nsGNOMERegistry::GetAppDescForScheme(aScheme, _retval); michael@0: return _retval.IsEmpty() ? NS_ERROR_NOT_AVAILABLE : NS_OK; michael@0: #else michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: #endif michael@0: } michael@0: michael@0: nsresult nsOSHelperAppService::GetFileTokenForPath(const char16_t * platformAppPath, nsIFile ** aFile) michael@0: { michael@0: LOG(("-- nsOSHelperAppService::GetFileTokenForPath: '%s'\n", michael@0: NS_LossyConvertUTF16toASCII(platformAppPath).get())); michael@0: if (! *platformAppPath) { // empty filename--return error michael@0: NS_WARNING("Empty filename passed in."); michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: // first check if the base class implementation finds anything michael@0: nsresult rv = nsExternalHelperAppService::GetFileTokenForPath(platformAppPath, aFile); michael@0: if (NS_SUCCEEDED(rv)) michael@0: return rv; michael@0: // If the reason for failure was that the file doesn't exist, return too michael@0: // (because it means the path was absolute, and so that we shouldn't search in michael@0: // the path) michael@0: if (rv == NS_ERROR_FILE_NOT_FOUND) michael@0: return rv; michael@0: michael@0: // If we get here, we really should have a relative path. michael@0: NS_ASSERTION(*platformAppPath != char16_t('/'), "Unexpected absolute path"); michael@0: michael@0: nsCOMPtr localFile (do_CreateInstance(NS_LOCAL_FILE_CONTRACTID)); michael@0: michael@0: if (!localFile) return NS_ERROR_NOT_INITIALIZED; michael@0: michael@0: bool exists = false; michael@0: // ugly hack. Walk the PATH variable... michael@0: char* unixpath = PR_GetEnv("PATH"); michael@0: nsAutoCString path(unixpath); michael@0: michael@0: const char* start_iter = path.BeginReading(start_iter); michael@0: const char* colon_iter = start_iter; michael@0: const char* end_iter = path.EndReading(end_iter); michael@0: michael@0: while (start_iter != end_iter && !exists) { michael@0: while (colon_iter != end_iter && *colon_iter != ':') { michael@0: ++colon_iter; michael@0: } michael@0: localFile->InitWithNativePath(Substring(start_iter, colon_iter)); michael@0: rv = localFile->AppendRelativePath(nsDependentString(platformAppPath)); michael@0: // Failing AppendRelativePath is a bad thing - it should basically always michael@0: // succeed given a relative path. Show a warning if it does fail. michael@0: // To prevent infinite loops when it does fail, return at this point. michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: localFile->Exists(&exists); michael@0: if (!exists) { michael@0: if (colon_iter == end_iter) { michael@0: break; michael@0: } michael@0: ++colon_iter; michael@0: start_iter = colon_iter; michael@0: } michael@0: } michael@0: michael@0: if (exists) { michael@0: rv = NS_OK; michael@0: } else { michael@0: rv = NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: *aFile = localFile; michael@0: NS_IF_ADDREF(*aFile); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsOSHelperAppService::GetFromExtension(const nsCString& aFileExt) { michael@0: // if the extension is empty, return immediately michael@0: if (aFileExt.IsEmpty()) michael@0: return nullptr; michael@0: michael@0: LOG(("Here we do an extension lookup for '%s'\n", aFileExt.get())); michael@0: michael@0: nsAutoString majorType, minorType, michael@0: mime_types_description, mailcap_description, michael@0: handler, mozillaFlags; michael@0: michael@0: nsresult rv = LookUpTypeAndDescription(NS_ConvertUTF8toUTF16(aFileExt), michael@0: majorType, michael@0: minorType, michael@0: mime_types_description, michael@0: true); michael@0: michael@0: if (NS_FAILED(rv) || majorType.IsEmpty()) { michael@0: michael@0: #ifdef MOZ_WIDGET_GTK michael@0: LOG(("Looking in GNOME registry\n")); michael@0: nsRefPtr gnomeInfo = michael@0: nsGNOMERegistry::GetFromExtension(aFileExt); michael@0: if (gnomeInfo) { michael@0: LOG(("Got MIMEInfo from GNOME registry\n")); michael@0: return gnomeInfo.forget(); michael@0: } michael@0: #endif michael@0: michael@0: rv = LookUpTypeAndDescription(NS_ConvertUTF8toUTF16(aFileExt), michael@0: majorType, michael@0: minorType, michael@0: mime_types_description, michael@0: false); michael@0: } michael@0: michael@0: if (NS_FAILED(rv)) michael@0: return nullptr; michael@0: michael@0: NS_LossyConvertUTF16toASCII asciiMajorType(majorType); michael@0: NS_LossyConvertUTF16toASCII asciiMinorType(minorType); michael@0: michael@0: LOG(("Type/Description results: majorType='%s', minorType='%s', description='%s'\n", michael@0: asciiMajorType.get(), michael@0: asciiMinorType.get(), michael@0: NS_LossyConvertUTF16toASCII(mime_types_description).get())); michael@0: michael@0: if (majorType.IsEmpty() && minorType.IsEmpty()) { michael@0: // we didn't get a type mapping, so we can't do anything useful michael@0: return nullptr; michael@0: } michael@0: michael@0: nsAutoCString mimeType(asciiMajorType + NS_LITERAL_CSTRING("/") + asciiMinorType); michael@0: nsRefPtr mimeInfo = new nsMIMEInfoUnix(mimeType); michael@0: michael@0: mimeInfo->AppendExtension(aFileExt); michael@0: rv = LookUpHandlerAndDescription(majorType, minorType, michael@0: handler, mailcap_description, michael@0: mozillaFlags); michael@0: LOG(("Handler/Description results: handler='%s', description='%s', mozillaFlags='%s'\n", michael@0: NS_LossyConvertUTF16toASCII(handler).get(), michael@0: NS_LossyConvertUTF16toASCII(mailcap_description).get(), michael@0: NS_LossyConvertUTF16toASCII(mozillaFlags).get())); michael@0: mailcap_description.Trim(" \t\""); michael@0: mozillaFlags.Trim(" \t"); michael@0: if (!mime_types_description.IsEmpty()) { michael@0: mimeInfo->SetDescription(mime_types_description); michael@0: } else { michael@0: mimeInfo->SetDescription(mailcap_description); michael@0: } michael@0: michael@0: if (NS_SUCCEEDED(rv) && handler.IsEmpty()) { michael@0: rv = NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: if (NS_SUCCEEDED(rv)) { michael@0: nsCOMPtr handlerFile; michael@0: rv = GetFileTokenForPath(handler.get(), getter_AddRefs(handlerFile)); michael@0: michael@0: if (NS_SUCCEEDED(rv)) { michael@0: mimeInfo->SetDefaultApplication(handlerFile); michael@0: mimeInfo->SetPreferredAction(nsIMIMEInfo::useSystemDefault); michael@0: mimeInfo->SetDefaultDescription(handler); michael@0: } michael@0: } michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: mimeInfo->SetPreferredAction(nsIMIMEInfo::saveToDisk); michael@0: } michael@0: michael@0: return mimeInfo.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsOSHelperAppService::GetFromType(const nsCString& aMIMEType) { michael@0: // if the type is empty, return immediately michael@0: if (aMIMEType.IsEmpty()) michael@0: return nullptr; michael@0: michael@0: LOG(("Here we do a mimetype lookup for '%s'\n", aMIMEType.get())); michael@0: michael@0: // extract the major and minor types michael@0: NS_ConvertASCIItoUTF16 mimeType(aMIMEType); michael@0: nsAString::const_iterator start_iter, end_iter, michael@0: majorTypeStart, majorTypeEnd, michael@0: minorTypeStart, minorTypeEnd; michael@0: michael@0: mimeType.BeginReading(start_iter); michael@0: mimeType.EndReading(end_iter); michael@0: michael@0: // XXX FIXME: add typeOptions parsing in here michael@0: nsresult rv = ParseMIMEType(start_iter, majorTypeStart, majorTypeEnd, michael@0: minorTypeStart, minorTypeEnd, end_iter); michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsDependentSubstring majorType(majorTypeStart, majorTypeEnd); michael@0: nsDependentSubstring minorType(minorTypeStart, minorTypeEnd); michael@0: michael@0: // First check the user's private mailcap file michael@0: nsAutoString mailcap_description, handler, mozillaFlags; michael@0: DoLookUpHandlerAndDescription(majorType, michael@0: minorType, michael@0: handler, michael@0: mailcap_description, michael@0: mozillaFlags, michael@0: true); michael@0: michael@0: LOG(("Private Handler/Description results: handler='%s', description='%s'\n", michael@0: NS_LossyConvertUTF16toASCII(handler).get(), michael@0: NS_LossyConvertUTF16toASCII(mailcap_description).get())); michael@0: michael@0: #ifdef MOZ_WIDGET_GTK michael@0: nsRefPtr gnomeInfo; michael@0: if (handler.IsEmpty()) { michael@0: // No useful data yet. Check the GNOME registry. Unfortunately, newer michael@0: // GNOME versions no longer have type-to-extension mappings, so we might michael@0: // get back a MIMEInfo without any extensions set. In that case we'll have michael@0: // to look in our mime.types files for the extensions. michael@0: LOG(("Looking in GNOME registry\n")); michael@0: gnomeInfo = nsGNOMERegistry::GetFromType(aMIMEType); michael@0: if (gnomeInfo && gnomeInfo->HasExtensions()) { michael@0: LOG(("Got MIMEInfo from GNOME registry, and it has extensions set\n")); michael@0: return gnomeInfo.forget(); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: // Now look up our extensions michael@0: nsAutoString extensions, mime_types_description; michael@0: LookUpExtensionsAndDescription(majorType, michael@0: minorType, michael@0: extensions, michael@0: mime_types_description); michael@0: michael@0: #ifdef MOZ_WIDGET_GTK michael@0: if (gnomeInfo) { michael@0: LOG(("Got MIMEInfo from GNOME registry without extensions; setting them " michael@0: "to %s\n", NS_LossyConvertUTF16toASCII(extensions).get())); michael@0: michael@0: NS_ASSERTION(!gnomeInfo->HasExtensions(), "How'd that happen?"); michael@0: gnomeInfo->SetFileExtensions(NS_ConvertUTF16toUTF8(extensions)); michael@0: return gnomeInfo.forget(); michael@0: } michael@0: #endif michael@0: michael@0: if (handler.IsEmpty()) { michael@0: DoLookUpHandlerAndDescription(majorType, michael@0: minorType, michael@0: handler, michael@0: mailcap_description, michael@0: mozillaFlags, michael@0: false); michael@0: } michael@0: michael@0: if (handler.IsEmpty()) { michael@0: DoLookUpHandlerAndDescription(majorType, michael@0: NS_LITERAL_STRING("*"), michael@0: handler, michael@0: mailcap_description, michael@0: mozillaFlags, michael@0: true); michael@0: } michael@0: michael@0: if (handler.IsEmpty()) { michael@0: DoLookUpHandlerAndDescription(majorType, michael@0: NS_LITERAL_STRING("*"), michael@0: handler, michael@0: mailcap_description, michael@0: mozillaFlags, michael@0: false); michael@0: } michael@0: michael@0: LOG(("Handler/Description results: handler='%s', description='%s', mozillaFlags='%s'\n", michael@0: NS_LossyConvertUTF16toASCII(handler).get(), michael@0: NS_LossyConvertUTF16toASCII(mailcap_description).get(), michael@0: NS_LossyConvertUTF16toASCII(mozillaFlags).get())); michael@0: michael@0: mailcap_description.Trim(" \t\""); michael@0: mozillaFlags.Trim(" \t"); michael@0: michael@0: if (handler.IsEmpty() && extensions.IsEmpty() && michael@0: mailcap_description.IsEmpty() && mime_types_description.IsEmpty()) { michael@0: // No real useful info michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr mimeInfo = new nsMIMEInfoUnix(aMIMEType); michael@0: michael@0: mimeInfo->SetFileExtensions(NS_ConvertUTF16toUTF8(extensions)); michael@0: if (! mime_types_description.IsEmpty()) { michael@0: mimeInfo->SetDescription(mime_types_description); michael@0: } else { michael@0: mimeInfo->SetDescription(mailcap_description); michael@0: } michael@0: michael@0: rv = NS_ERROR_NOT_AVAILABLE; michael@0: nsCOMPtr handlerFile; michael@0: if (!handler.IsEmpty()) { michael@0: rv = GetFileTokenForPath(handler.get(), getter_AddRefs(handlerFile)); michael@0: } michael@0: michael@0: if (NS_SUCCEEDED(rv)) { michael@0: mimeInfo->SetDefaultApplication(handlerFile); michael@0: mimeInfo->SetPreferredAction(nsIMIMEInfo::useSystemDefault); michael@0: mimeInfo->SetDefaultDescription(handler); michael@0: } else { michael@0: mimeInfo->SetPreferredAction(nsIMIMEInfo::saveToDisk); michael@0: } michael@0: michael@0: return mimeInfo.forget(); michael@0: } michael@0: michael@0: michael@0: already_AddRefed michael@0: nsOSHelperAppService::GetMIMEInfoFromOS(const nsACString& aType, michael@0: const nsACString& aFileExt, michael@0: bool *aFound) { michael@0: *aFound = true; michael@0: nsRefPtr retval = GetFromType(PromiseFlatCString(aType)); michael@0: bool hasDefault = false; michael@0: if (retval) michael@0: retval->GetHasDefaultHandler(&hasDefault); michael@0: if (!retval || !hasDefault) { michael@0: nsRefPtr miByExt = GetFromExtension(PromiseFlatCString(aFileExt)); michael@0: // If we had no extension match, but a type match, use that michael@0: if (!miByExt && retval) michael@0: return retval.forget(); michael@0: // If we had an extension match but no type match, set the mimetype and use michael@0: // it michael@0: if (!retval && miByExt) { michael@0: if (!aType.IsEmpty()) michael@0: miByExt->SetMIMEType(aType); michael@0: miByExt.swap(retval); michael@0: michael@0: return retval.forget(); michael@0: } michael@0: // If we got nothing, make a new mimeinfo michael@0: if (!retval) { michael@0: *aFound = false; michael@0: retval = new nsMIMEInfoUnix(aType); michael@0: if (retval) { michael@0: if (!aFileExt.IsEmpty()) michael@0: retval->AppendExtension(aFileExt); michael@0: } michael@0: michael@0: return retval.forget(); michael@0: } michael@0: michael@0: // Copy the attributes of retval (mimeinfo from type) onto miByExt, to michael@0: // return it michael@0: // but reset to just collected mDefaultAppDescription (from ext) michael@0: nsAutoString byExtDefault; michael@0: miByExt->GetDefaultDescription(byExtDefault); michael@0: retval->SetDefaultDescription(byExtDefault); michael@0: retval->CopyBasicDataTo(miByExt); michael@0: michael@0: miByExt.swap(retval); michael@0: } michael@0: return retval.forget(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsOSHelperAppService::GetProtocolHandlerInfoFromOS(const nsACString &aScheme, michael@0: bool *found, michael@0: nsIHandlerInfo **_retval) michael@0: { michael@0: NS_ASSERTION(!aScheme.IsEmpty(), "No scheme was specified!"); michael@0: michael@0: // We must check that a registered handler exists so that gnome_url_show michael@0: // doesn't fallback to gnomevfs. michael@0: // See nsGNOMERegistry::LoadURL and bug 389632. michael@0: nsresult rv = OSProtocolHandlerExists(nsPromiseFlatCString(aScheme).get(), michael@0: found); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: nsMIMEInfoUnix *handlerInfo = michael@0: new nsMIMEInfoUnix(aScheme, nsMIMEInfoBase::eProtocolInfo); michael@0: NS_ENSURE_TRUE(handlerInfo, NS_ERROR_OUT_OF_MEMORY); michael@0: NS_ADDREF(*_retval = handlerInfo); michael@0: michael@0: if (!*found) { michael@0: // Code that calls this requires an object regardless if the OS has michael@0: // something for us, so we return the empty object. michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsAutoString desc; michael@0: GetApplicationDescription(aScheme, desc); michael@0: handlerInfo->SetDefaultDescription(desc); michael@0: michael@0: return NS_OK; michael@0: }