1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/uriloader/exthandler/unix/nsOSHelperAppService.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1553 @@ 1.4 +/* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 2 -*- 1.5 + * 1.6 + * This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +#include <sys/types.h> 1.11 +#include <sys/stat.h> 1.12 + 1.13 +#if defined(MOZ_ENABLE_CONTENTACTION) 1.14 +#include <contentaction/contentaction.h> 1.15 +#include <QString> 1.16 +#endif 1.17 + 1.18 +#include "nsOSHelperAppService.h" 1.19 +#include "nsMIMEInfoUnix.h" 1.20 +#ifdef MOZ_WIDGET_GTK 1.21 +#include "nsGNOMERegistry.h" 1.22 +#endif 1.23 +#include "nsISupports.h" 1.24 +#include "nsString.h" 1.25 +#include "nsReadableUtils.h" 1.26 +#include "nsUnicharUtils.h" 1.27 +#include "nsXPIDLString.h" 1.28 +#include "nsIURL.h" 1.29 +#include "nsIFileStreams.h" 1.30 +#include "nsILineInputStream.h" 1.31 +#include "nsIFile.h" 1.32 +#include "nsIProcess.h" 1.33 +#include "nsNetCID.h" 1.34 +#include "nsXPCOM.h" 1.35 +#include "nsISupportsPrimitives.h" 1.36 +#include "nsCRT.h" 1.37 +#include "nsDirectoryServiceDefs.h" 1.38 +#include "nsDirectoryServiceUtils.h" 1.39 +#include "prenv.h" // for PR_GetEnv() 1.40 +#include "nsAutoPtr.h" 1.41 +#include "mozilla/Preferences.h" 1.42 + 1.43 +using namespace mozilla; 1.44 + 1.45 +#define LOG(args) PR_LOG(mLog, PR_LOG_DEBUG, args) 1.46 +#define LOG_ENABLED() PR_LOG_TEST(mLog, PR_LOG_DEBUG) 1.47 + 1.48 +static nsresult 1.49 +FindSemicolon(nsAString::const_iterator& aSemicolon_iter, 1.50 + const nsAString::const_iterator& aEnd_iter); 1.51 +static nsresult 1.52 +ParseMIMEType(const nsAString::const_iterator& aStart_iter, 1.53 + nsAString::const_iterator& aMajorTypeStart, 1.54 + nsAString::const_iterator& aMajorTypeEnd, 1.55 + nsAString::const_iterator& aMinorTypeStart, 1.56 + nsAString::const_iterator& aMinorTypeEnd, 1.57 + const nsAString::const_iterator& aEnd_iter); 1.58 + 1.59 +inline bool 1.60 +IsNetscapeFormat(const nsACString& aBuffer); 1.61 + 1.62 +nsOSHelperAppService::nsOSHelperAppService() : nsExternalHelperAppService() 1.63 +{ 1.64 + mode_t mask = umask(0777); 1.65 + umask(mask); 1.66 + mPermissions = 0666 & ~mask; 1.67 +} 1.68 + 1.69 +nsOSHelperAppService::~nsOSHelperAppService() 1.70 +{} 1.71 + 1.72 +/* 1.73 + * Take a command with all the mailcap escapes in it and unescape it 1.74 + * Ideally this needs the mime type, mime type options, and location of the 1.75 + * temporary file, but this last can't be got from here 1.76 + */ 1.77 +// static 1.78 +nsresult 1.79 +nsOSHelperAppService::UnescapeCommand(const nsAString& aEscapedCommand, 1.80 + const nsAString& aMajorType, 1.81 + const nsAString& aMinorType, 1.82 + nsACString& aUnEscapedCommand) { 1.83 + LOG(("-- UnescapeCommand")); 1.84 + LOG(("Command to escape: '%s'\n", 1.85 + NS_LossyConvertUTF16toASCII(aEscapedCommand).get())); 1.86 + // XXX This function will need to get the mime type and various stuff like that being passed in to work properly 1.87 + 1.88 + LOG(("UnescapeCommand really needs some work -- it should actually do some unescaping\n")); 1.89 + 1.90 + CopyUTF16toUTF8(aEscapedCommand, aUnEscapedCommand); 1.91 + LOG(("Escaped command: '%s'\n", 1.92 + PromiseFlatCString(aUnEscapedCommand).get())); 1.93 + return NS_OK; 1.94 +} 1.95 + 1.96 +/* Put aSemicolon_iter at the first non-escaped semicolon after 1.97 + * aStart_iter but before aEnd_iter 1.98 + */ 1.99 + 1.100 +static nsresult 1.101 +FindSemicolon(nsAString::const_iterator& aSemicolon_iter, 1.102 + const nsAString::const_iterator& aEnd_iter) { 1.103 + bool semicolonFound = false; 1.104 + while (aSemicolon_iter != aEnd_iter && !semicolonFound) { 1.105 + switch(*aSemicolon_iter) { 1.106 + case '\\': 1.107 + aSemicolon_iter.advance(2); 1.108 + break; 1.109 + case ';': 1.110 + semicolonFound = true; 1.111 + break; 1.112 + default: 1.113 + ++aSemicolon_iter; 1.114 + break; 1.115 + } 1.116 + } 1.117 + return NS_OK; 1.118 +} 1.119 + 1.120 +static nsresult 1.121 +ParseMIMEType(const nsAString::const_iterator& aStart_iter, 1.122 + nsAString::const_iterator& aMajorTypeStart, 1.123 + nsAString::const_iterator& aMajorTypeEnd, 1.124 + nsAString::const_iterator& aMinorTypeStart, 1.125 + nsAString::const_iterator& aMinorTypeEnd, 1.126 + const nsAString::const_iterator& aEnd_iter) { 1.127 + nsAString::const_iterator iter(aStart_iter); 1.128 + 1.129 + // skip leading whitespace 1.130 + while (iter != aEnd_iter && nsCRT::IsAsciiSpace(*iter)) { 1.131 + ++iter; 1.132 + } 1.133 + 1.134 + if (iter == aEnd_iter) { 1.135 + return NS_ERROR_INVALID_ARG; 1.136 + } 1.137 + 1.138 + aMajorTypeStart = iter; 1.139 + 1.140 + // find major/minor separator ('/') 1.141 + while (iter != aEnd_iter && *iter != '/') { 1.142 + ++iter; 1.143 + } 1.144 + 1.145 + if (iter == aEnd_iter) { 1.146 + return NS_ERROR_INVALID_ARG; 1.147 + } 1.148 + 1.149 + aMajorTypeEnd = iter; 1.150 + 1.151 + // skip '/' 1.152 + ++iter; 1.153 + 1.154 + if (iter == aEnd_iter) { 1.155 + return NS_ERROR_INVALID_ARG; 1.156 + } 1.157 + 1.158 + aMinorTypeStart = iter; 1.159 + 1.160 + // find end of minor type, delimited by whitespace or ';' 1.161 + while (iter != aEnd_iter && !nsCRT::IsAsciiSpace(*iter) && *iter != ';') { 1.162 + ++iter; 1.163 + } 1.164 + 1.165 + aMinorTypeEnd = iter; 1.166 + 1.167 + return NS_OK; 1.168 +} 1.169 + 1.170 +// static 1.171 +nsresult 1.172 +nsOSHelperAppService::GetFileLocation(const char* aPrefName, 1.173 + const char* aEnvVarName, 1.174 + nsAString& aFileLocation) { 1.175 + LOG(("-- GetFileLocation. Pref: '%s' EnvVar: '%s'\n", 1.176 + aPrefName, 1.177 + aEnvVarName)); 1.178 + NS_PRECONDITION(aPrefName, "Null pref name passed; don't do that!"); 1.179 + 1.180 + aFileLocation.Truncate(); 1.181 + /* The lookup order is: 1.182 + 1) user pref 1.183 + 2) env var 1.184 + 3) pref 1.185 + */ 1.186 + NS_ENSURE_TRUE(Preferences::GetRootBranch(), NS_ERROR_FAILURE); 1.187 + 1.188 + /* 1.189 + If we have an env var we should check whether the pref is a user 1.190 + pref. If we do not, we don't care. 1.191 + */ 1.192 + if (Preferences::HasUserValue(aPrefName) && 1.193 + NS_SUCCEEDED(Preferences::GetString(aPrefName, &aFileLocation))) { 1.194 + return NS_OK; 1.195 + } 1.196 + 1.197 + if (aEnvVarName && *aEnvVarName) { 1.198 + char* prefValue = PR_GetEnv(aEnvVarName); 1.199 + if (prefValue && *prefValue) { 1.200 + // the pref is in the system charset and it's a filepath... The 1.201 + // natural way to do the charset conversion is by just initing 1.202 + // an nsIFile with the native path and asking it for the Unicode 1.203 + // version. 1.204 + nsresult rv; 1.205 + nsCOMPtr<nsIFile> file(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv)); 1.206 + NS_ENSURE_SUCCESS(rv, rv); 1.207 + 1.208 + rv = file->InitWithNativePath(nsDependentCString(prefValue)); 1.209 + NS_ENSURE_SUCCESS(rv, rv); 1.210 + 1.211 + rv = file->GetPath(aFileLocation); 1.212 + NS_ENSURE_SUCCESS(rv, rv); 1.213 + 1.214 + return NS_OK; 1.215 + } 1.216 + } 1.217 + 1.218 + return Preferences::GetString(aPrefName, &aFileLocation); 1.219 +} 1.220 + 1.221 + 1.222 +/* Get the mime.types file names from prefs and look up info in them 1.223 + based on extension */ 1.224 +// static 1.225 +nsresult 1.226 +nsOSHelperAppService::LookUpTypeAndDescription(const nsAString& aFileExtension, 1.227 + nsAString& aMajorType, 1.228 + nsAString& aMinorType, 1.229 + nsAString& aDescription, 1.230 + bool aUserData) { 1.231 + LOG(("-- LookUpTypeAndDescription for extension '%s'\n", 1.232 + NS_LossyConvertUTF16toASCII(aFileExtension).get())); 1.233 + nsresult rv = NS_OK; 1.234 + nsAutoString mimeFileName; 1.235 + 1.236 + const char* filenamePref = aUserData ? 1.237 + "helpers.private_mime_types_file" : "helpers.global_mime_types_file"; 1.238 + 1.239 + rv = GetFileLocation(filenamePref, nullptr, mimeFileName); 1.240 + if (NS_SUCCEEDED(rv) && !mimeFileName.IsEmpty()) { 1.241 + rv = GetTypeAndDescriptionFromMimetypesFile(mimeFileName, 1.242 + aFileExtension, 1.243 + aMajorType, 1.244 + aMinorType, 1.245 + aDescription); 1.246 + } else { 1.247 + rv = NS_ERROR_NOT_AVAILABLE; 1.248 + } 1.249 + 1.250 + return rv; 1.251 +} 1.252 + 1.253 +inline bool 1.254 +IsNetscapeFormat(const nsACString& aBuffer) { 1.255 + return StringBeginsWith(aBuffer, NS_LITERAL_CSTRING("#--Netscape Communications Corporation MIME Information")) || 1.256 + StringBeginsWith(aBuffer, NS_LITERAL_CSTRING("#--MCOM MIME Information")); 1.257 +} 1.258 + 1.259 +/* 1.260 + * Create a file stream and line input stream for the filename. 1.261 + * Leaves the first line of the file in aBuffer and sets the format to 1.262 + * true for netscape files and false for normail ones 1.263 + */ 1.264 +// static 1.265 +nsresult 1.266 +nsOSHelperAppService::CreateInputStream(const nsAString& aFilename, 1.267 + nsIFileInputStream ** aFileInputStream, 1.268 + nsILineInputStream ** aLineInputStream, 1.269 + nsACString& aBuffer, 1.270 + bool * aNetscapeFormat, 1.271 + bool * aMore) { 1.272 + LOG(("-- CreateInputStream")); 1.273 + nsresult rv = NS_OK; 1.274 + 1.275 + nsCOMPtr<nsIFile> file(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv)); 1.276 + if (NS_FAILED(rv)) 1.277 + return rv; 1.278 + rv = file->InitWithPath(aFilename); 1.279 + if (NS_FAILED(rv)) 1.280 + return rv; 1.281 + 1.282 + nsCOMPtr<nsIFileInputStream> fileStream(do_CreateInstance(NS_LOCALFILEINPUTSTREAM_CONTRACTID, &rv)); 1.283 + if (NS_FAILED(rv)) 1.284 + return rv; 1.285 + rv = fileStream->Init(file, -1, -1, false); 1.286 + if (NS_FAILED(rv)) 1.287 + return rv; 1.288 + 1.289 + nsCOMPtr<nsILineInputStream> lineStream(do_QueryInterface(fileStream, &rv)); 1.290 + 1.291 + if (NS_FAILED(rv)) { 1.292 + LOG(("Interface trouble in stream land!")); 1.293 + return rv; 1.294 + } 1.295 + 1.296 + rv = lineStream->ReadLine(aBuffer, aMore); 1.297 + if (NS_FAILED(rv)) { 1.298 + fileStream->Close(); 1.299 + return rv; 1.300 + } 1.301 + 1.302 + *aNetscapeFormat = IsNetscapeFormat(aBuffer); 1.303 + 1.304 + *aFileInputStream = fileStream; 1.305 + NS_ADDREF(*aFileInputStream); 1.306 + *aLineInputStream = lineStream; 1.307 + NS_ADDREF(*aLineInputStream); 1.308 + 1.309 + return NS_OK; 1.310 +} 1.311 + 1.312 +/* Open the file, read the first line, decide what type of file it is, 1.313 + then get info based on extension */ 1.314 +// static 1.315 +nsresult 1.316 +nsOSHelperAppService::GetTypeAndDescriptionFromMimetypesFile(const nsAString& aFilename, 1.317 + const nsAString& aFileExtension, 1.318 + nsAString& aMajorType, 1.319 + nsAString& aMinorType, 1.320 + nsAString& aDescription) { 1.321 + LOG(("-- GetTypeAndDescriptionFromMimetypesFile\n")); 1.322 + LOG(("Getting type and description from types file '%s'\n", 1.323 + NS_LossyConvertUTF16toASCII(aFilename).get())); 1.324 + LOG(("Using extension '%s'\n", 1.325 + NS_LossyConvertUTF16toASCII(aFileExtension).get())); 1.326 + nsresult rv = NS_OK; 1.327 + nsCOMPtr<nsIFileInputStream> mimeFile; 1.328 + nsCOMPtr<nsILineInputStream> mimeTypes; 1.329 + bool netscapeFormat; 1.330 + nsAutoString buf; 1.331 + nsAutoCString cBuf; 1.332 + bool more = false; 1.333 + rv = CreateInputStream(aFilename, getter_AddRefs(mimeFile), getter_AddRefs(mimeTypes), 1.334 + cBuf, &netscapeFormat, &more); 1.335 + 1.336 + if (NS_FAILED(rv)) { 1.337 + return rv; 1.338 + } 1.339 + 1.340 + nsAutoString extensions; 1.341 + nsString entry; 1.342 + entry.SetCapacity(100); 1.343 + nsAString::const_iterator majorTypeStart, majorTypeEnd, 1.344 + minorTypeStart, minorTypeEnd, 1.345 + descriptionStart, descriptionEnd; 1.346 + 1.347 + do { 1.348 + CopyASCIItoUTF16(cBuf, buf); 1.349 + // read through, building up an entry. If we finish an entry, check for 1.350 + // a match and return out of the loop if we match 1.351 + 1.352 + // skip comments and empty lines 1.353 + if (!buf.IsEmpty() && buf.First() != '#') { 1.354 + entry.Append(buf); 1.355 + if (entry.Last() == '\\') { 1.356 + entry.Truncate(entry.Length() - 1); 1.357 + entry.Append(char16_t(' ')); // in case there is no trailing whitespace on this line 1.358 + } else { // we have a full entry 1.359 + LOG(("Current entry: '%s'\n", 1.360 + NS_LossyConvertUTF16toASCII(entry).get())); 1.361 + if (netscapeFormat) { 1.362 + rv = ParseNetscapeMIMETypesEntry(entry, 1.363 + majorTypeStart, majorTypeEnd, 1.364 + minorTypeStart, minorTypeEnd, 1.365 + extensions, 1.366 + descriptionStart, descriptionEnd); 1.367 + if (NS_FAILED(rv)) { 1.368 + // We sometimes get things like RealPlayer appending 1.369 + // "normal" entries to "Netscape" .mime.types files. Try 1.370 + // to handle that. Bug 106381. 1.371 + LOG(("Bogus entry; trying 'normal' mode\n")); 1.372 + rv = ParseNormalMIMETypesEntry(entry, 1.373 + majorTypeStart, majorTypeEnd, 1.374 + minorTypeStart, minorTypeEnd, 1.375 + extensions, 1.376 + descriptionStart, descriptionEnd); 1.377 + } 1.378 + } else { 1.379 + rv = ParseNormalMIMETypesEntry(entry, 1.380 + majorTypeStart, majorTypeEnd, 1.381 + minorTypeStart, minorTypeEnd, 1.382 + extensions, 1.383 + descriptionStart, descriptionEnd); 1.384 + if (NS_FAILED(rv)) { 1.385 + // We sometimes get things like StarOffice prepending 1.386 + // "normal" entries to "Netscape" .mime.types files. Try 1.387 + // to handle that. Bug 136670. 1.388 + LOG(("Bogus entry; trying 'Netscape' mode\n")); 1.389 + rv = ParseNetscapeMIMETypesEntry(entry, 1.390 + majorTypeStart, majorTypeEnd, 1.391 + minorTypeStart, minorTypeEnd, 1.392 + extensions, 1.393 + descriptionStart, descriptionEnd); 1.394 + } 1.395 + } 1.396 + 1.397 + if (NS_SUCCEEDED(rv)) { // entry parses 1.398 + nsAString::const_iterator start, end; 1.399 + extensions.BeginReading(start); 1.400 + extensions.EndReading(end); 1.401 + nsAString::const_iterator iter(start); 1.402 + 1.403 + while (start != end) { 1.404 + FindCharInReadable(',', iter, end); 1.405 + if (Substring(start, iter).Equals(aFileExtension, 1.406 + nsCaseInsensitiveStringComparator())) { 1.407 + // it's a match. Assign the type and description and run 1.408 + aMajorType.Assign(Substring(majorTypeStart, majorTypeEnd)); 1.409 + aMinorType.Assign(Substring(minorTypeStart, minorTypeEnd)); 1.410 + aDescription.Assign(Substring(descriptionStart, descriptionEnd)); 1.411 + mimeFile->Close(); 1.412 + return NS_OK; 1.413 + } 1.414 + if (iter != end) { 1.415 + ++iter; 1.416 + } 1.417 + start = iter; 1.418 + } 1.419 + } else { 1.420 + LOG(("Failed to parse entry: %s\n", NS_LossyConvertUTF16toASCII(entry).get())); 1.421 + } 1.422 + // truncate the entry for the next iteration 1.423 + entry.Truncate(); 1.424 + } 1.425 + } 1.426 + if (!more) { 1.427 + rv = NS_ERROR_NOT_AVAILABLE; 1.428 + break; 1.429 + } 1.430 + // read the next line 1.431 + rv = mimeTypes->ReadLine(cBuf, &more); 1.432 + } while (NS_SUCCEEDED(rv)); 1.433 + 1.434 + mimeFile->Close(); 1.435 + return rv; 1.436 +} 1.437 + 1.438 +/* Get the mime.types file names from prefs and look up info in them 1.439 + based on mimetype */ 1.440 +// static 1.441 +nsresult 1.442 +nsOSHelperAppService::LookUpExtensionsAndDescription(const nsAString& aMajorType, 1.443 + const nsAString& aMinorType, 1.444 + nsAString& aFileExtensions, 1.445 + nsAString& aDescription) { 1.446 + LOG(("-- LookUpExtensionsAndDescription for type '%s/%s'\n", 1.447 + NS_LossyConvertUTF16toASCII(aMajorType).get(), 1.448 + NS_LossyConvertUTF16toASCII(aMinorType).get())); 1.449 + nsresult rv = NS_OK; 1.450 + nsAutoString mimeFileName; 1.451 + 1.452 + rv = GetFileLocation("helpers.private_mime_types_file", nullptr, mimeFileName); 1.453 + if (NS_SUCCEEDED(rv) && !mimeFileName.IsEmpty()) { 1.454 + rv = GetExtensionsAndDescriptionFromMimetypesFile(mimeFileName, 1.455 + aMajorType, 1.456 + aMinorType, 1.457 + aFileExtensions, 1.458 + aDescription); 1.459 + } else { 1.460 + rv = NS_ERROR_NOT_AVAILABLE; 1.461 + } 1.462 + if (NS_FAILED(rv) || aFileExtensions.IsEmpty()) { 1.463 + rv = GetFileLocation("helpers.global_mime_types_file", 1.464 + nullptr, mimeFileName); 1.465 + if (NS_SUCCEEDED(rv) && !mimeFileName.IsEmpty()) { 1.466 + rv = GetExtensionsAndDescriptionFromMimetypesFile(mimeFileName, 1.467 + aMajorType, 1.468 + aMinorType, 1.469 + aFileExtensions, 1.470 + aDescription); 1.471 + } else { 1.472 + rv = NS_ERROR_NOT_AVAILABLE; 1.473 + } 1.474 + } 1.475 + return rv; 1.476 +} 1.477 + 1.478 +/* Open the file, read the first line, decide what type of file it is, 1.479 + then get info based on extension */ 1.480 +// static 1.481 +nsresult 1.482 +nsOSHelperAppService::GetExtensionsAndDescriptionFromMimetypesFile(const nsAString& aFilename, 1.483 + const nsAString& aMajorType, 1.484 + const nsAString& aMinorType, 1.485 + nsAString& aFileExtensions, 1.486 + nsAString& aDescription) { 1.487 + LOG(("-- GetExtensionsAndDescriptionFromMimetypesFile\n")); 1.488 + LOG(("Getting extensions and description from types file '%s'\n", 1.489 + NS_LossyConvertUTF16toASCII(aFilename).get())); 1.490 + LOG(("Using type '%s/%s'\n", 1.491 + NS_LossyConvertUTF16toASCII(aMajorType).get(), 1.492 + NS_LossyConvertUTF16toASCII(aMinorType).get())); 1.493 + 1.494 + nsresult rv = NS_OK; 1.495 + nsCOMPtr<nsIFileInputStream> mimeFile; 1.496 + nsCOMPtr<nsILineInputStream> mimeTypes; 1.497 + bool netscapeFormat; 1.498 + nsAutoCString cBuf; 1.499 + nsAutoString buf; 1.500 + bool more = false; 1.501 + rv = CreateInputStream(aFilename, getter_AddRefs(mimeFile), getter_AddRefs(mimeTypes), 1.502 + cBuf, &netscapeFormat, &more); 1.503 + 1.504 + if (NS_FAILED(rv)) { 1.505 + return rv; 1.506 + } 1.507 + 1.508 + nsAutoString extensions; 1.509 + nsString entry; 1.510 + entry.SetCapacity(100); 1.511 + nsAString::const_iterator majorTypeStart, majorTypeEnd, 1.512 + minorTypeStart, minorTypeEnd, 1.513 + descriptionStart, descriptionEnd; 1.514 + 1.515 + do { 1.516 + CopyASCIItoUTF16(cBuf, buf); 1.517 + // read through, building up an entry. If we finish an entry, check for 1.518 + // a match and return out of the loop if we match 1.519 + 1.520 + // skip comments and empty lines 1.521 + if (!buf.IsEmpty() && buf.First() != '#') { 1.522 + entry.Append(buf); 1.523 + if (entry.Last() == '\\') { 1.524 + entry.Truncate(entry.Length() - 1); 1.525 + entry.Append(char16_t(' ')); // in case there is no trailing whitespace on this line 1.526 + } else { // we have a full entry 1.527 + LOG(("Current entry: '%s'\n", 1.528 + NS_LossyConvertUTF16toASCII(entry).get())); 1.529 + if (netscapeFormat) { 1.530 + rv = ParseNetscapeMIMETypesEntry(entry, 1.531 + majorTypeStart, majorTypeEnd, 1.532 + minorTypeStart, minorTypeEnd, 1.533 + extensions, 1.534 + descriptionStart, descriptionEnd); 1.535 + 1.536 + if (NS_FAILED(rv)) { 1.537 + // We sometimes get things like RealPlayer appending 1.538 + // "normal" entries to "Netscape" .mime.types files. Try 1.539 + // to handle that. Bug 106381. 1.540 + LOG(("Bogus entry; trying 'normal' mode\n")); 1.541 + rv = ParseNormalMIMETypesEntry(entry, 1.542 + majorTypeStart, majorTypeEnd, 1.543 + minorTypeStart, minorTypeEnd, 1.544 + extensions, 1.545 + descriptionStart, descriptionEnd); 1.546 + } 1.547 + } else { 1.548 + rv = ParseNormalMIMETypesEntry(entry, 1.549 + majorTypeStart, majorTypeEnd, 1.550 + minorTypeStart, 1.551 + minorTypeEnd, extensions, 1.552 + descriptionStart, descriptionEnd); 1.553 + 1.554 + if (NS_FAILED(rv)) { 1.555 + // We sometimes get things like StarOffice prepending 1.556 + // "normal" entries to "Netscape" .mime.types files. Try 1.557 + // to handle that. Bug 136670. 1.558 + LOG(("Bogus entry; trying 'Netscape' mode\n")); 1.559 + rv = ParseNetscapeMIMETypesEntry(entry, 1.560 + majorTypeStart, majorTypeEnd, 1.561 + minorTypeStart, minorTypeEnd, 1.562 + extensions, 1.563 + descriptionStart, descriptionEnd); 1.564 + } 1.565 + } 1.566 + 1.567 + if (NS_SUCCEEDED(rv) && 1.568 + Substring(majorTypeStart, 1.569 + majorTypeEnd).Equals(aMajorType, 1.570 + nsCaseInsensitiveStringComparator())&& 1.571 + Substring(minorTypeStart, 1.572 + minorTypeEnd).Equals(aMinorType, 1.573 + nsCaseInsensitiveStringComparator())) { 1.574 + // it's a match 1.575 + aFileExtensions.Assign(extensions); 1.576 + aDescription.Assign(Substring(descriptionStart, descriptionEnd)); 1.577 + mimeFile->Close(); 1.578 + return NS_OK; 1.579 + } else if (NS_FAILED(rv)) { 1.580 + LOG(("Failed to parse entry: %s\n", NS_LossyConvertUTF16toASCII(entry).get())); 1.581 + } 1.582 + 1.583 + entry.Truncate(); 1.584 + } 1.585 + } 1.586 + if (!more) { 1.587 + rv = NS_ERROR_NOT_AVAILABLE; 1.588 + break; 1.589 + } 1.590 + // read the next line 1.591 + rv = mimeTypes->ReadLine(cBuf, &more); 1.592 + } while (NS_SUCCEEDED(rv)); 1.593 + 1.594 + mimeFile->Close(); 1.595 + return rv; 1.596 +} 1.597 + 1.598 +/* 1.599 + * This parses a Netscape format mime.types entry. There are two 1.600 + * possible formats: 1.601 + * 1.602 + * type=foo/bar; options exts="baz" description="Some type" 1.603 + * 1.604 + * and 1.605 + * 1.606 + * type=foo/bar; options description="Some type" exts="baz" 1.607 + */ 1.608 +// static 1.609 +nsresult 1.610 +nsOSHelperAppService::ParseNetscapeMIMETypesEntry(const nsAString& aEntry, 1.611 + nsAString::const_iterator& aMajorTypeStart, 1.612 + nsAString::const_iterator& aMajorTypeEnd, 1.613 + nsAString::const_iterator& aMinorTypeStart, 1.614 + nsAString::const_iterator& aMinorTypeEnd, 1.615 + nsAString& aExtensions, 1.616 + nsAString::const_iterator& aDescriptionStart, 1.617 + nsAString::const_iterator& aDescriptionEnd) { 1.618 + LOG(("-- ParseNetscapeMIMETypesEntry\n")); 1.619 + NS_ASSERTION(!aEntry.IsEmpty(), "Empty Netscape MIME types entry being parsed."); 1.620 + 1.621 + nsAString::const_iterator start_iter, end_iter, match_start, match_end; 1.622 + 1.623 + aEntry.BeginReading(start_iter); 1.624 + aEntry.EndReading(end_iter); 1.625 + 1.626 + // skip trailing whitespace 1.627 + do { 1.628 + --end_iter; 1.629 + } while (end_iter != start_iter && 1.630 + nsCRT::IsAsciiSpace(*end_iter)); 1.631 + // if we're pointing to a quote, don't advance -- we don't want to 1.632 + // include the quote.... 1.633 + if (*end_iter != '"') 1.634 + ++end_iter; 1.635 + match_start = start_iter; 1.636 + match_end = end_iter; 1.637 + 1.638 + // Get the major and minor types 1.639 + // First the major type 1.640 + if (! FindInReadable(NS_LITERAL_STRING("type="), match_start, match_end)) { 1.641 + return NS_ERROR_FAILURE; 1.642 + } 1.643 + 1.644 + match_start = match_end; 1.645 + 1.646 + while (match_end != end_iter && 1.647 + *match_end != '/') { 1.648 + ++match_end; 1.649 + } 1.650 + if (match_end == end_iter) { 1.651 + return NS_ERROR_FAILURE; 1.652 + } 1.653 + 1.654 + aMajorTypeStart = match_start; 1.655 + aMajorTypeEnd = match_end; 1.656 + 1.657 + // now the minor type 1.658 + if (++match_end == end_iter) { 1.659 + return NS_ERROR_FAILURE; 1.660 + } 1.661 + 1.662 + match_start = match_end; 1.663 + 1.664 + while (match_end != end_iter && 1.665 + !nsCRT::IsAsciiSpace(*match_end) && 1.666 + *match_end != ';') { 1.667 + ++match_end; 1.668 + } 1.669 + if (match_end == end_iter) { 1.670 + return NS_ERROR_FAILURE; 1.671 + } 1.672 + 1.673 + aMinorTypeStart = match_start; 1.674 + aMinorTypeEnd = match_end; 1.675 + 1.676 + // ignore everything up to the end of the mime type from here on 1.677 + start_iter = match_end; 1.678 + 1.679 + // get the extensions 1.680 + match_start = match_end; 1.681 + match_end = end_iter; 1.682 + if (FindInReadable(NS_LITERAL_STRING("exts="), match_start, match_end)) { 1.683 + nsAString::const_iterator extStart, extEnd; 1.684 + 1.685 + if (match_end == end_iter || 1.686 + (*match_end == '"' && ++match_end == end_iter)) { 1.687 + return NS_ERROR_FAILURE; 1.688 + } 1.689 + 1.690 + extStart = match_end; 1.691 + match_start = extStart; 1.692 + match_end = end_iter; 1.693 + if (FindInReadable(NS_LITERAL_STRING("desc=\""), match_start, match_end)) { 1.694 + // exts= before desc=, so we have to find the actual end of the extensions 1.695 + extEnd = match_start; 1.696 + if (extEnd == extStart) { 1.697 + return NS_ERROR_FAILURE; 1.698 + } 1.699 + 1.700 + do { 1.701 + --extEnd; 1.702 + } while (extEnd != extStart && 1.703 + nsCRT::IsAsciiSpace(*extEnd)); 1.704 + 1.705 + if (extEnd != extStart && *extEnd == '"') { 1.706 + --extEnd; 1.707 + } 1.708 + } else { 1.709 + // desc= before exts=, so we can use end_iter as the end of the extensions 1.710 + extEnd = end_iter; 1.711 + } 1.712 + aExtensions = Substring(extStart, extEnd); 1.713 + } else { 1.714 + // no extensions 1.715 + aExtensions.Truncate(); 1.716 + } 1.717 + 1.718 + // get the description 1.719 + match_start = start_iter; 1.720 + match_end = end_iter; 1.721 + if (FindInReadable(NS_LITERAL_STRING("desc=\""), match_start, match_end)) { 1.722 + aDescriptionStart = match_end; 1.723 + match_start = aDescriptionStart; 1.724 + match_end = end_iter; 1.725 + if (FindInReadable(NS_LITERAL_STRING("exts="), match_start, match_end)) { 1.726 + // exts= after desc=, so have to find actual end of description 1.727 + aDescriptionEnd = match_start; 1.728 + if (aDescriptionEnd == aDescriptionStart) { 1.729 + return NS_ERROR_FAILURE; 1.730 + } 1.731 + 1.732 + do { 1.733 + --aDescriptionEnd; 1.734 + } while (aDescriptionEnd != aDescriptionStart && 1.735 + nsCRT::IsAsciiSpace(*aDescriptionEnd)); 1.736 + 1.737 + if (aDescriptionStart != aDescriptionStart && *aDescriptionEnd == '"') { 1.738 + --aDescriptionEnd; 1.739 + } 1.740 + } else { 1.741 + // desc= after exts=, so use end_iter for the description end 1.742 + aDescriptionEnd = end_iter; 1.743 + } 1.744 + } else { 1.745 + // no description 1.746 + aDescriptionStart = start_iter; 1.747 + aDescriptionEnd = start_iter; 1.748 + } 1.749 + 1.750 + return NS_OK; 1.751 +} 1.752 + 1.753 +/* 1.754 + * This parses a normal format mime.types entry. The format is: 1.755 + * 1.756 + * major/minor ext1 ext2 ext3 1.757 + */ 1.758 +// static 1.759 +nsresult 1.760 +nsOSHelperAppService::ParseNormalMIMETypesEntry(const nsAString& aEntry, 1.761 + nsAString::const_iterator& aMajorTypeStart, 1.762 + nsAString::const_iterator& aMajorTypeEnd, 1.763 + nsAString::const_iterator& aMinorTypeStart, 1.764 + nsAString::const_iterator& aMinorTypeEnd, 1.765 + nsAString& aExtensions, 1.766 + nsAString::const_iterator& aDescriptionStart, 1.767 + nsAString::const_iterator& aDescriptionEnd) { 1.768 + LOG(("-- ParseNormalMIMETypesEntry\n")); 1.769 + NS_ASSERTION(!aEntry.IsEmpty(), "Empty Normal MIME types entry being parsed."); 1.770 + 1.771 + nsAString::const_iterator start_iter, end_iter, iter; 1.772 + 1.773 + aEntry.BeginReading(start_iter); 1.774 + aEntry.EndReading(end_iter); 1.775 + 1.776 + // no description 1.777 + aDescriptionStart = start_iter; 1.778 + aDescriptionEnd = start_iter; 1.779 + 1.780 + // skip leading whitespace 1.781 + while (start_iter != end_iter && nsCRT::IsAsciiSpace(*start_iter)) { 1.782 + ++start_iter; 1.783 + } 1.784 + if (start_iter == end_iter) { 1.785 + return NS_ERROR_FAILURE; 1.786 + } 1.787 + // skip trailing whitespace 1.788 + do { 1.789 + --end_iter; 1.790 + } while (end_iter != start_iter && nsCRT::IsAsciiSpace(*end_iter)); 1.791 + 1.792 + ++end_iter; // point to first whitespace char (or to end of string) 1.793 + iter = start_iter; 1.794 + 1.795 + // get the major type 1.796 + if (! FindCharInReadable('/', iter, end_iter)) 1.797 + return NS_ERROR_FAILURE; 1.798 + 1.799 + nsAString::const_iterator equals_sign_iter(start_iter); 1.800 + if (FindCharInReadable('=', equals_sign_iter, iter)) 1.801 + return NS_ERROR_FAILURE; // see bug 136670 1.802 + 1.803 + aMajorTypeStart = start_iter; 1.804 + aMajorTypeEnd = iter; 1.805 + 1.806 + // get the minor type 1.807 + if (++iter == end_iter) { 1.808 + return NS_ERROR_FAILURE; 1.809 + } 1.810 + start_iter = iter; 1.811 + 1.812 + while (iter != end_iter && !nsCRT::IsAsciiSpace(*iter)) { 1.813 + ++iter; 1.814 + } 1.815 + aMinorTypeStart = start_iter; 1.816 + aMinorTypeEnd = iter; 1.817 + 1.818 + // get the extensions 1.819 + aExtensions.Truncate(); 1.820 + while (iter != end_iter) { 1.821 + while (iter != end_iter && nsCRT::IsAsciiSpace(*iter)) { 1.822 + ++iter; 1.823 + } 1.824 + 1.825 + start_iter = iter; 1.826 + while (iter != end_iter && !nsCRT::IsAsciiSpace(*iter)) { 1.827 + ++iter; 1.828 + } 1.829 + aExtensions.Append(Substring(start_iter, iter)); 1.830 + if (iter != end_iter) { // not the last extension 1.831 + aExtensions.Append(char16_t(',')); 1.832 + } 1.833 + } 1.834 + 1.835 + return NS_OK; 1.836 +} 1.837 + 1.838 +// static 1.839 +nsresult 1.840 +nsOSHelperAppService::LookUpHandlerAndDescription(const nsAString& aMajorType, 1.841 + const nsAString& aMinorType, 1.842 + nsAString& aHandler, 1.843 + nsAString& aDescription, 1.844 + nsAString& aMozillaFlags) { 1.845 + 1.846 + // The mailcap lookup is two-pass to handle the case of mailcap files 1.847 + // that have something like: 1.848 + // 1.849 + // text/*; emacs %s 1.850 + // text/rtf; soffice %s 1.851 + // 1.852 + // in that order. We want to pick up "soffice" for text/rtf in such cases 1.853 + nsresult rv = DoLookUpHandlerAndDescription(aMajorType, 1.854 + aMinorType, 1.855 + aHandler, 1.856 + aDescription, 1.857 + aMozillaFlags, 1.858 + true); 1.859 + if (NS_FAILED(rv)) { 1.860 + rv = DoLookUpHandlerAndDescription(aMajorType, 1.861 + aMinorType, 1.862 + aHandler, 1.863 + aDescription, 1.864 + aMozillaFlags, 1.865 + false); 1.866 + } 1.867 + 1.868 + // maybe we have an entry for "aMajorType/*"? 1.869 + if (NS_FAILED(rv)) { 1.870 + rv = DoLookUpHandlerAndDescription(aMajorType, 1.871 + NS_LITERAL_STRING("*"), 1.872 + aHandler, 1.873 + aDescription, 1.874 + aMozillaFlags, 1.875 + true); 1.876 + } 1.877 + 1.878 + if (NS_FAILED(rv)) { 1.879 + rv = DoLookUpHandlerAndDescription(aMajorType, 1.880 + NS_LITERAL_STRING("*"), 1.881 + aHandler, 1.882 + aDescription, 1.883 + aMozillaFlags, 1.884 + false); 1.885 + } 1.886 + 1.887 + return rv; 1.888 +} 1.889 + 1.890 +// static 1.891 +nsresult 1.892 +nsOSHelperAppService::DoLookUpHandlerAndDescription(const nsAString& aMajorType, 1.893 + const nsAString& aMinorType, 1.894 + nsAString& aHandler, 1.895 + nsAString& aDescription, 1.896 + nsAString& aMozillaFlags, 1.897 + bool aUserData) { 1.898 + LOG(("-- LookUpHandlerAndDescription for type '%s/%s'\n", 1.899 + NS_LossyConvertUTF16toASCII(aMajorType).get(), 1.900 + NS_LossyConvertUTF16toASCII(aMinorType).get())); 1.901 + nsresult rv = NS_OK; 1.902 + nsAutoString mailcapFileName; 1.903 + 1.904 + const char * filenamePref = aUserData ? 1.905 + "helpers.private_mailcap_file" : "helpers.global_mailcap_file"; 1.906 + const char * filenameEnvVar = aUserData ? 1.907 + "PERSONAL_MAILCAP" : "MAILCAP"; 1.908 + 1.909 + rv = GetFileLocation(filenamePref, filenameEnvVar, mailcapFileName); 1.910 + if (NS_SUCCEEDED(rv) && !mailcapFileName.IsEmpty()) { 1.911 + rv = GetHandlerAndDescriptionFromMailcapFile(mailcapFileName, 1.912 + aMajorType, 1.913 + aMinorType, 1.914 + aHandler, 1.915 + aDescription, 1.916 + aMozillaFlags); 1.917 + } else { 1.918 + rv = NS_ERROR_NOT_AVAILABLE; 1.919 + } 1.920 + 1.921 + return rv; 1.922 +} 1.923 + 1.924 +// static 1.925 +nsresult 1.926 +nsOSHelperAppService::GetHandlerAndDescriptionFromMailcapFile(const nsAString& aFilename, 1.927 + const nsAString& aMajorType, 1.928 + const nsAString& aMinorType, 1.929 + nsAString& aHandler, 1.930 + nsAString& aDescription, 1.931 + nsAString& aMozillaFlags) { 1.932 + 1.933 + LOG(("-- GetHandlerAndDescriptionFromMailcapFile\n")); 1.934 + LOG(("Getting handler and description from mailcap file '%s'\n", 1.935 + NS_LossyConvertUTF16toASCII(aFilename).get())); 1.936 + LOG(("Using type '%s/%s'\n", 1.937 + NS_LossyConvertUTF16toASCII(aMajorType).get(), 1.938 + NS_LossyConvertUTF16toASCII(aMinorType).get())); 1.939 + 1.940 + nsresult rv = NS_OK; 1.941 + bool more = false; 1.942 + 1.943 + nsCOMPtr<nsIFile> file(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv)); 1.944 + if (NS_FAILED(rv)) 1.945 + return rv; 1.946 + rv = file->InitWithPath(aFilename); 1.947 + if (NS_FAILED(rv)) 1.948 + return rv; 1.949 + 1.950 + nsCOMPtr<nsIFileInputStream> mailcapFile(do_CreateInstance(NS_LOCALFILEINPUTSTREAM_CONTRACTID, &rv)); 1.951 + if (NS_FAILED(rv)) 1.952 + return rv; 1.953 + rv = mailcapFile->Init(file, -1, -1, false); 1.954 + if (NS_FAILED(rv)) 1.955 + return rv; 1.956 + 1.957 + nsCOMPtr<nsILineInputStream> mailcap (do_QueryInterface(mailcapFile, &rv)); 1.958 + 1.959 + if (NS_FAILED(rv)) { 1.960 + LOG(("Interface trouble in stream land!")); 1.961 + return rv; 1.962 + } 1.963 + 1.964 + nsString entry, buffer; 1.965 + nsAutoCString cBuffer; 1.966 + entry.SetCapacity(128); 1.967 + cBuffer.SetCapacity(80); 1.968 + rv = mailcap->ReadLine(cBuffer, &more); 1.969 + if (NS_FAILED(rv)) { 1.970 + mailcapFile->Close(); 1.971 + return rv; 1.972 + } 1.973 + 1.974 + do { // return on end-of-file in the loop 1.975 + 1.976 + CopyASCIItoUTF16(cBuffer, buffer); 1.977 + if (!buffer.IsEmpty() && buffer.First() != '#') { 1.978 + entry.Append(buffer); 1.979 + if (entry.Last() == '\\') { // entry continues on next line 1.980 + entry.Truncate(entry.Length()-1); 1.981 + entry.Append(char16_t(' ')); // in case there is no trailing whitespace on this line 1.982 + } else { // we have a full entry in entry. Check it for the type 1.983 + LOG(("Current entry: '%s'\n", 1.984 + NS_LossyConvertUTF16toASCII(entry).get())); 1.985 + 1.986 + nsAString::const_iterator semicolon_iter, 1.987 + start_iter, end_iter, 1.988 + majorTypeStart, majorTypeEnd, 1.989 + minorTypeStart, minorTypeEnd; 1.990 + entry.BeginReading(start_iter); 1.991 + entry.EndReading(end_iter); 1.992 + semicolon_iter = start_iter; 1.993 + FindSemicolon(semicolon_iter, end_iter); 1.994 + if (semicolon_iter != end_iter) { // we have something resembling a valid entry 1.995 + rv = ParseMIMEType(start_iter, majorTypeStart, majorTypeEnd, 1.996 + minorTypeStart, minorTypeEnd, semicolon_iter); 1.997 + if (NS_SUCCEEDED(rv) && 1.998 + Substring(majorTypeStart, 1.999 + majorTypeEnd).Equals(aMajorType, 1.1000 + nsCaseInsensitiveStringComparator()) && 1.1001 + Substring(minorTypeStart, 1.1002 + minorTypeEnd).Equals(aMinorType, 1.1003 + nsCaseInsensitiveStringComparator())) { // we have a match 1.1004 + bool match = true; 1.1005 + ++semicolon_iter; // point at the first char past the semicolon 1.1006 + start_iter = semicolon_iter; // handler string starts here 1.1007 + FindSemicolon(semicolon_iter, end_iter); 1.1008 + while (start_iter != semicolon_iter && 1.1009 + nsCRT::IsAsciiSpace(*start_iter)) { 1.1010 + ++start_iter; 1.1011 + } 1.1012 + 1.1013 + LOG(("The real handler is: '%s'\n", 1.1014 + NS_LossyConvertUTF16toASCII(Substring(start_iter, 1.1015 + semicolon_iter)).get())); 1.1016 + 1.1017 + // XXX ugly hack. Just grab the executable name 1.1018 + nsAString::const_iterator end_handler_iter = semicolon_iter; 1.1019 + nsAString::const_iterator end_executable_iter = start_iter; 1.1020 + while (end_executable_iter != end_handler_iter && 1.1021 + !nsCRT::IsAsciiSpace(*end_executable_iter)) { 1.1022 + ++end_executable_iter; 1.1023 + } 1.1024 + // XXX End ugly hack 1.1025 + 1.1026 + aHandler = Substring(start_iter, end_executable_iter); 1.1027 + 1.1028 + nsAString::const_iterator start_option_iter, end_optionname_iter, equal_sign_iter; 1.1029 + bool equalSignFound; 1.1030 + while (match && 1.1031 + semicolon_iter != end_iter && 1.1032 + ++semicolon_iter != end_iter) { // there are options left and we still match 1.1033 + start_option_iter = semicolon_iter; 1.1034 + // skip over leading whitespace 1.1035 + while (start_option_iter != end_iter && 1.1036 + nsCRT::IsAsciiSpace(*start_option_iter)) { 1.1037 + ++start_option_iter; 1.1038 + } 1.1039 + if (start_option_iter == end_iter) { // nothing actually here 1.1040 + break; 1.1041 + } 1.1042 + semicolon_iter = start_option_iter; 1.1043 + FindSemicolon(semicolon_iter, end_iter); 1.1044 + equal_sign_iter = start_option_iter; 1.1045 + equalSignFound = false; 1.1046 + while (equal_sign_iter != semicolon_iter && !equalSignFound) { 1.1047 + switch(*equal_sign_iter) { 1.1048 + case '\\': 1.1049 + equal_sign_iter.advance(2); 1.1050 + break; 1.1051 + case '=': 1.1052 + equalSignFound = true; 1.1053 + break; 1.1054 + default: 1.1055 + ++equal_sign_iter; 1.1056 + break; 1.1057 + } 1.1058 + } 1.1059 + end_optionname_iter = start_option_iter; 1.1060 + // find end of option name 1.1061 + while (end_optionname_iter != equal_sign_iter && 1.1062 + !nsCRT::IsAsciiSpace(*end_optionname_iter)) { 1.1063 + ++end_optionname_iter; 1.1064 + } 1.1065 + nsDependentSubstring optionName(start_option_iter, end_optionname_iter); 1.1066 + if (equalSignFound) { 1.1067 + // This is an option that has a name and value 1.1068 + if (optionName.EqualsLiteral("description")) { 1.1069 + aDescription = Substring(++equal_sign_iter, semicolon_iter); 1.1070 + } else if (optionName.EqualsLiteral("x-mozilla-flags")) { 1.1071 + aMozillaFlags = Substring(++equal_sign_iter, semicolon_iter); 1.1072 + } else if (optionName.EqualsLiteral("test")) { 1.1073 + nsAutoCString testCommand; 1.1074 + rv = UnescapeCommand(Substring(++equal_sign_iter, semicolon_iter), 1.1075 + aMajorType, 1.1076 + aMinorType, 1.1077 + testCommand); 1.1078 + if (NS_FAILED(rv)) 1.1079 + continue; 1.1080 + nsCOMPtr<nsIProcess> process = do_CreateInstance(NS_PROCESS_CONTRACTID, &rv); 1.1081 + if (NS_FAILED(rv)) 1.1082 + continue; 1.1083 + nsCOMPtr<nsIFile> file(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv)); 1.1084 + if (NS_FAILED(rv)) 1.1085 + continue; 1.1086 + rv = file->InitWithNativePath(NS_LITERAL_CSTRING("/bin/sh")); 1.1087 + if (NS_FAILED(rv)) 1.1088 + continue; 1.1089 + rv = process->Init(file); 1.1090 + if (NS_FAILED(rv)) 1.1091 + continue; 1.1092 + const char *args[] = { "-c", testCommand.get() }; 1.1093 + LOG(("Running Test: %s\n", testCommand.get())); 1.1094 + rv = process->Run(true, args, 2); 1.1095 + if (NS_FAILED(rv)) 1.1096 + continue; 1.1097 + int32_t exitValue; 1.1098 + rv = process->GetExitValue(&exitValue); 1.1099 + if (NS_FAILED(rv)) 1.1100 + continue; 1.1101 + LOG(("Exit code: %d\n", exitValue)); 1.1102 + if (exitValue) { 1.1103 + match = false; 1.1104 + } 1.1105 + } 1.1106 + } else { 1.1107 + // This is an option that just has a name but no value (eg "copiousoutput") 1.1108 + if (optionName.EqualsLiteral("needsterminal")) { 1.1109 + match = false; 1.1110 + } 1.1111 + } 1.1112 + } 1.1113 + 1.1114 + 1.1115 + if (match) { // we did not fail any test clauses; all is good 1.1116 + // get out of here 1.1117 + mailcapFile->Close(); 1.1118 + return NS_OK; 1.1119 + } else { // pretend that this match never happened 1.1120 + aDescription.Truncate(); 1.1121 + aMozillaFlags.Truncate(); 1.1122 + aHandler.Truncate(); 1.1123 + } 1.1124 + } 1.1125 + } 1.1126 + // zero out the entry for the next cycle 1.1127 + entry.Truncate(); 1.1128 + } 1.1129 + } 1.1130 + if (!more) { 1.1131 + rv = NS_ERROR_NOT_AVAILABLE; 1.1132 + break; 1.1133 + } 1.1134 + rv = mailcap->ReadLine(cBuffer, &more); 1.1135 + } while (NS_SUCCEEDED(rv)); 1.1136 + mailcapFile->Close(); 1.1137 + return rv; 1.1138 +} 1.1139 + 1.1140 +nsresult nsOSHelperAppService::OSProtocolHandlerExists(const char * aProtocolScheme, bool * aHandlerExists) 1.1141 +{ 1.1142 + LOG(("-- nsOSHelperAppService::OSProtocolHandlerExists for '%s'\n", 1.1143 + aProtocolScheme)); 1.1144 + *aHandlerExists = false; 1.1145 + 1.1146 +#if defined(MOZ_ENABLE_CONTENTACTION) 1.1147 + // libcontentaction requires character ':' after scheme 1.1148 + ContentAction::Action action = 1.1149 + ContentAction::Action::defaultActionForScheme(QString(aProtocolScheme) + ':'); 1.1150 + 1.1151 + if (action.isValid()) 1.1152 + *aHandlerExists = true; 1.1153 +#endif 1.1154 + 1.1155 +#ifdef MOZ_WIDGET_GTK 1.1156 + // Check the GConf registry for a protocol handler 1.1157 + *aHandlerExists = nsGNOMERegistry::HandlerExists(aProtocolScheme); 1.1158 +#endif 1.1159 + 1.1160 + return NS_OK; 1.1161 +} 1.1162 + 1.1163 +NS_IMETHODIMP nsOSHelperAppService::GetApplicationDescription(const nsACString& aScheme, nsAString& _retval) 1.1164 +{ 1.1165 +#ifdef MOZ_WIDGET_GTK 1.1166 + nsGNOMERegistry::GetAppDescForScheme(aScheme, _retval); 1.1167 + return _retval.IsEmpty() ? NS_ERROR_NOT_AVAILABLE : NS_OK; 1.1168 +#else 1.1169 + return NS_ERROR_NOT_AVAILABLE; 1.1170 +#endif 1.1171 +} 1.1172 + 1.1173 +nsresult nsOSHelperAppService::GetFileTokenForPath(const char16_t * platformAppPath, nsIFile ** aFile) 1.1174 +{ 1.1175 + LOG(("-- nsOSHelperAppService::GetFileTokenForPath: '%s'\n", 1.1176 + NS_LossyConvertUTF16toASCII(platformAppPath).get())); 1.1177 + if (! *platformAppPath) { // empty filename--return error 1.1178 + NS_WARNING("Empty filename passed in."); 1.1179 + return NS_ERROR_INVALID_ARG; 1.1180 + } 1.1181 + 1.1182 + // first check if the base class implementation finds anything 1.1183 + nsresult rv = nsExternalHelperAppService::GetFileTokenForPath(platformAppPath, aFile); 1.1184 + if (NS_SUCCEEDED(rv)) 1.1185 + return rv; 1.1186 + // If the reason for failure was that the file doesn't exist, return too 1.1187 + // (because it means the path was absolute, and so that we shouldn't search in 1.1188 + // the path) 1.1189 + if (rv == NS_ERROR_FILE_NOT_FOUND) 1.1190 + return rv; 1.1191 + 1.1192 + // If we get here, we really should have a relative path. 1.1193 + NS_ASSERTION(*platformAppPath != char16_t('/'), "Unexpected absolute path"); 1.1194 + 1.1195 + nsCOMPtr<nsIFile> localFile (do_CreateInstance(NS_LOCAL_FILE_CONTRACTID)); 1.1196 + 1.1197 + if (!localFile) return NS_ERROR_NOT_INITIALIZED; 1.1198 + 1.1199 + bool exists = false; 1.1200 + // ugly hack. Walk the PATH variable... 1.1201 + char* unixpath = PR_GetEnv("PATH"); 1.1202 + nsAutoCString path(unixpath); 1.1203 + 1.1204 + const char* start_iter = path.BeginReading(start_iter); 1.1205 + const char* colon_iter = start_iter; 1.1206 + const char* end_iter = path.EndReading(end_iter); 1.1207 + 1.1208 + while (start_iter != end_iter && !exists) { 1.1209 + while (colon_iter != end_iter && *colon_iter != ':') { 1.1210 + ++colon_iter; 1.1211 + } 1.1212 + localFile->InitWithNativePath(Substring(start_iter, colon_iter)); 1.1213 + rv = localFile->AppendRelativePath(nsDependentString(platformAppPath)); 1.1214 + // Failing AppendRelativePath is a bad thing - it should basically always 1.1215 + // succeed given a relative path. Show a warning if it does fail. 1.1216 + // To prevent infinite loops when it does fail, return at this point. 1.1217 + NS_ENSURE_SUCCESS(rv, rv); 1.1218 + localFile->Exists(&exists); 1.1219 + if (!exists) { 1.1220 + if (colon_iter == end_iter) { 1.1221 + break; 1.1222 + } 1.1223 + ++colon_iter; 1.1224 + start_iter = colon_iter; 1.1225 + } 1.1226 + } 1.1227 + 1.1228 + if (exists) { 1.1229 + rv = NS_OK; 1.1230 + } else { 1.1231 + rv = NS_ERROR_NOT_AVAILABLE; 1.1232 + } 1.1233 + 1.1234 + *aFile = localFile; 1.1235 + NS_IF_ADDREF(*aFile); 1.1236 + 1.1237 + return rv; 1.1238 +} 1.1239 + 1.1240 +already_AddRefed<nsMIMEInfoBase> 1.1241 +nsOSHelperAppService::GetFromExtension(const nsCString& aFileExt) { 1.1242 + // if the extension is empty, return immediately 1.1243 + if (aFileExt.IsEmpty()) 1.1244 + return nullptr; 1.1245 + 1.1246 + LOG(("Here we do an extension lookup for '%s'\n", aFileExt.get())); 1.1247 + 1.1248 + nsAutoString majorType, minorType, 1.1249 + mime_types_description, mailcap_description, 1.1250 + handler, mozillaFlags; 1.1251 + 1.1252 + nsresult rv = LookUpTypeAndDescription(NS_ConvertUTF8toUTF16(aFileExt), 1.1253 + majorType, 1.1254 + minorType, 1.1255 + mime_types_description, 1.1256 + true); 1.1257 + 1.1258 + if (NS_FAILED(rv) || majorType.IsEmpty()) { 1.1259 + 1.1260 +#ifdef MOZ_WIDGET_GTK 1.1261 + LOG(("Looking in GNOME registry\n")); 1.1262 + nsRefPtr<nsMIMEInfoBase> gnomeInfo = 1.1263 + nsGNOMERegistry::GetFromExtension(aFileExt); 1.1264 + if (gnomeInfo) { 1.1265 + LOG(("Got MIMEInfo from GNOME registry\n")); 1.1266 + return gnomeInfo.forget(); 1.1267 + } 1.1268 +#endif 1.1269 + 1.1270 + rv = LookUpTypeAndDescription(NS_ConvertUTF8toUTF16(aFileExt), 1.1271 + majorType, 1.1272 + minorType, 1.1273 + mime_types_description, 1.1274 + false); 1.1275 + } 1.1276 + 1.1277 + if (NS_FAILED(rv)) 1.1278 + return nullptr; 1.1279 + 1.1280 + NS_LossyConvertUTF16toASCII asciiMajorType(majorType); 1.1281 + NS_LossyConvertUTF16toASCII asciiMinorType(minorType); 1.1282 + 1.1283 + LOG(("Type/Description results: majorType='%s', minorType='%s', description='%s'\n", 1.1284 + asciiMajorType.get(), 1.1285 + asciiMinorType.get(), 1.1286 + NS_LossyConvertUTF16toASCII(mime_types_description).get())); 1.1287 + 1.1288 + if (majorType.IsEmpty() && minorType.IsEmpty()) { 1.1289 + // we didn't get a type mapping, so we can't do anything useful 1.1290 + return nullptr; 1.1291 + } 1.1292 + 1.1293 + nsAutoCString mimeType(asciiMajorType + NS_LITERAL_CSTRING("/") + asciiMinorType); 1.1294 + nsRefPtr<nsMIMEInfoUnix> mimeInfo = new nsMIMEInfoUnix(mimeType); 1.1295 + 1.1296 + mimeInfo->AppendExtension(aFileExt); 1.1297 + rv = LookUpHandlerAndDescription(majorType, minorType, 1.1298 + handler, mailcap_description, 1.1299 + mozillaFlags); 1.1300 + LOG(("Handler/Description results: handler='%s', description='%s', mozillaFlags='%s'\n", 1.1301 + NS_LossyConvertUTF16toASCII(handler).get(), 1.1302 + NS_LossyConvertUTF16toASCII(mailcap_description).get(), 1.1303 + NS_LossyConvertUTF16toASCII(mozillaFlags).get())); 1.1304 + mailcap_description.Trim(" \t\""); 1.1305 + mozillaFlags.Trim(" \t"); 1.1306 + if (!mime_types_description.IsEmpty()) { 1.1307 + mimeInfo->SetDescription(mime_types_description); 1.1308 + } else { 1.1309 + mimeInfo->SetDescription(mailcap_description); 1.1310 + } 1.1311 + 1.1312 + if (NS_SUCCEEDED(rv) && handler.IsEmpty()) { 1.1313 + rv = NS_ERROR_NOT_AVAILABLE; 1.1314 + } 1.1315 + 1.1316 + if (NS_SUCCEEDED(rv)) { 1.1317 + nsCOMPtr<nsIFile> handlerFile; 1.1318 + rv = GetFileTokenForPath(handler.get(), getter_AddRefs(handlerFile)); 1.1319 + 1.1320 + if (NS_SUCCEEDED(rv)) { 1.1321 + mimeInfo->SetDefaultApplication(handlerFile); 1.1322 + mimeInfo->SetPreferredAction(nsIMIMEInfo::useSystemDefault); 1.1323 + mimeInfo->SetDefaultDescription(handler); 1.1324 + } 1.1325 + } 1.1326 + 1.1327 + if (NS_FAILED(rv)) { 1.1328 + mimeInfo->SetPreferredAction(nsIMIMEInfo::saveToDisk); 1.1329 + } 1.1330 + 1.1331 + return mimeInfo.forget(); 1.1332 +} 1.1333 + 1.1334 +already_AddRefed<nsMIMEInfoBase> 1.1335 +nsOSHelperAppService::GetFromType(const nsCString& aMIMEType) { 1.1336 + // if the type is empty, return immediately 1.1337 + if (aMIMEType.IsEmpty()) 1.1338 + return nullptr; 1.1339 + 1.1340 + LOG(("Here we do a mimetype lookup for '%s'\n", aMIMEType.get())); 1.1341 + 1.1342 + // extract the major and minor types 1.1343 + NS_ConvertASCIItoUTF16 mimeType(aMIMEType); 1.1344 + nsAString::const_iterator start_iter, end_iter, 1.1345 + majorTypeStart, majorTypeEnd, 1.1346 + minorTypeStart, minorTypeEnd; 1.1347 + 1.1348 + mimeType.BeginReading(start_iter); 1.1349 + mimeType.EndReading(end_iter); 1.1350 + 1.1351 + // XXX FIXME: add typeOptions parsing in here 1.1352 + nsresult rv = ParseMIMEType(start_iter, majorTypeStart, majorTypeEnd, 1.1353 + minorTypeStart, minorTypeEnd, end_iter); 1.1354 + 1.1355 + if (NS_FAILED(rv)) { 1.1356 + return nullptr; 1.1357 + } 1.1358 + 1.1359 + nsDependentSubstring majorType(majorTypeStart, majorTypeEnd); 1.1360 + nsDependentSubstring minorType(minorTypeStart, minorTypeEnd); 1.1361 + 1.1362 + // First check the user's private mailcap file 1.1363 + nsAutoString mailcap_description, handler, mozillaFlags; 1.1364 + DoLookUpHandlerAndDescription(majorType, 1.1365 + minorType, 1.1366 + handler, 1.1367 + mailcap_description, 1.1368 + mozillaFlags, 1.1369 + true); 1.1370 + 1.1371 + LOG(("Private Handler/Description results: handler='%s', description='%s'\n", 1.1372 + NS_LossyConvertUTF16toASCII(handler).get(), 1.1373 + NS_LossyConvertUTF16toASCII(mailcap_description).get())); 1.1374 + 1.1375 +#ifdef MOZ_WIDGET_GTK 1.1376 + nsRefPtr<nsMIMEInfoBase> gnomeInfo; 1.1377 + if (handler.IsEmpty()) { 1.1378 + // No useful data yet. Check the GNOME registry. Unfortunately, newer 1.1379 + // GNOME versions no longer have type-to-extension mappings, so we might 1.1380 + // get back a MIMEInfo without any extensions set. In that case we'll have 1.1381 + // to look in our mime.types files for the extensions. 1.1382 + LOG(("Looking in GNOME registry\n")); 1.1383 + gnomeInfo = nsGNOMERegistry::GetFromType(aMIMEType); 1.1384 + if (gnomeInfo && gnomeInfo->HasExtensions()) { 1.1385 + LOG(("Got MIMEInfo from GNOME registry, and it has extensions set\n")); 1.1386 + return gnomeInfo.forget(); 1.1387 + } 1.1388 + } 1.1389 +#endif 1.1390 + 1.1391 + // Now look up our extensions 1.1392 + nsAutoString extensions, mime_types_description; 1.1393 + LookUpExtensionsAndDescription(majorType, 1.1394 + minorType, 1.1395 + extensions, 1.1396 + mime_types_description); 1.1397 + 1.1398 +#ifdef MOZ_WIDGET_GTK 1.1399 + if (gnomeInfo) { 1.1400 + LOG(("Got MIMEInfo from GNOME registry without extensions; setting them " 1.1401 + "to %s\n", NS_LossyConvertUTF16toASCII(extensions).get())); 1.1402 + 1.1403 + NS_ASSERTION(!gnomeInfo->HasExtensions(), "How'd that happen?"); 1.1404 + gnomeInfo->SetFileExtensions(NS_ConvertUTF16toUTF8(extensions)); 1.1405 + return gnomeInfo.forget(); 1.1406 + } 1.1407 +#endif 1.1408 + 1.1409 + if (handler.IsEmpty()) { 1.1410 + DoLookUpHandlerAndDescription(majorType, 1.1411 + minorType, 1.1412 + handler, 1.1413 + mailcap_description, 1.1414 + mozillaFlags, 1.1415 + false); 1.1416 + } 1.1417 + 1.1418 + if (handler.IsEmpty()) { 1.1419 + DoLookUpHandlerAndDescription(majorType, 1.1420 + NS_LITERAL_STRING("*"), 1.1421 + handler, 1.1422 + mailcap_description, 1.1423 + mozillaFlags, 1.1424 + true); 1.1425 + } 1.1426 + 1.1427 + if (handler.IsEmpty()) { 1.1428 + DoLookUpHandlerAndDescription(majorType, 1.1429 + NS_LITERAL_STRING("*"), 1.1430 + handler, 1.1431 + mailcap_description, 1.1432 + mozillaFlags, 1.1433 + false); 1.1434 + } 1.1435 + 1.1436 + LOG(("Handler/Description results: handler='%s', description='%s', mozillaFlags='%s'\n", 1.1437 + NS_LossyConvertUTF16toASCII(handler).get(), 1.1438 + NS_LossyConvertUTF16toASCII(mailcap_description).get(), 1.1439 + NS_LossyConvertUTF16toASCII(mozillaFlags).get())); 1.1440 + 1.1441 + mailcap_description.Trim(" \t\""); 1.1442 + mozillaFlags.Trim(" \t"); 1.1443 + 1.1444 + if (handler.IsEmpty() && extensions.IsEmpty() && 1.1445 + mailcap_description.IsEmpty() && mime_types_description.IsEmpty()) { 1.1446 + // No real useful info 1.1447 + return nullptr; 1.1448 + } 1.1449 + 1.1450 + nsRefPtr<nsMIMEInfoUnix> mimeInfo = new nsMIMEInfoUnix(aMIMEType); 1.1451 + 1.1452 + mimeInfo->SetFileExtensions(NS_ConvertUTF16toUTF8(extensions)); 1.1453 + if (! mime_types_description.IsEmpty()) { 1.1454 + mimeInfo->SetDescription(mime_types_description); 1.1455 + } else { 1.1456 + mimeInfo->SetDescription(mailcap_description); 1.1457 + } 1.1458 + 1.1459 + rv = NS_ERROR_NOT_AVAILABLE; 1.1460 + nsCOMPtr<nsIFile> handlerFile; 1.1461 + if (!handler.IsEmpty()) { 1.1462 + rv = GetFileTokenForPath(handler.get(), getter_AddRefs(handlerFile)); 1.1463 + } 1.1464 + 1.1465 + if (NS_SUCCEEDED(rv)) { 1.1466 + mimeInfo->SetDefaultApplication(handlerFile); 1.1467 + mimeInfo->SetPreferredAction(nsIMIMEInfo::useSystemDefault); 1.1468 + mimeInfo->SetDefaultDescription(handler); 1.1469 + } else { 1.1470 + mimeInfo->SetPreferredAction(nsIMIMEInfo::saveToDisk); 1.1471 + } 1.1472 + 1.1473 + return mimeInfo.forget(); 1.1474 +} 1.1475 + 1.1476 + 1.1477 +already_AddRefed<nsIMIMEInfo> 1.1478 +nsOSHelperAppService::GetMIMEInfoFromOS(const nsACString& aType, 1.1479 + const nsACString& aFileExt, 1.1480 + bool *aFound) { 1.1481 + *aFound = true; 1.1482 + nsRefPtr<nsMIMEInfoBase> retval = GetFromType(PromiseFlatCString(aType)); 1.1483 + bool hasDefault = false; 1.1484 + if (retval) 1.1485 + retval->GetHasDefaultHandler(&hasDefault); 1.1486 + if (!retval || !hasDefault) { 1.1487 + nsRefPtr<nsMIMEInfoBase> miByExt = GetFromExtension(PromiseFlatCString(aFileExt)); 1.1488 + // If we had no extension match, but a type match, use that 1.1489 + if (!miByExt && retval) 1.1490 + return retval.forget(); 1.1491 + // If we had an extension match but no type match, set the mimetype and use 1.1492 + // it 1.1493 + if (!retval && miByExt) { 1.1494 + if (!aType.IsEmpty()) 1.1495 + miByExt->SetMIMEType(aType); 1.1496 + miByExt.swap(retval); 1.1497 + 1.1498 + return retval.forget(); 1.1499 + } 1.1500 + // If we got nothing, make a new mimeinfo 1.1501 + if (!retval) { 1.1502 + *aFound = false; 1.1503 + retval = new nsMIMEInfoUnix(aType); 1.1504 + if (retval) { 1.1505 + if (!aFileExt.IsEmpty()) 1.1506 + retval->AppendExtension(aFileExt); 1.1507 + } 1.1508 + 1.1509 + return retval.forget(); 1.1510 + } 1.1511 + 1.1512 + // Copy the attributes of retval (mimeinfo from type) onto miByExt, to 1.1513 + // return it 1.1514 + // but reset to just collected mDefaultAppDescription (from ext) 1.1515 + nsAutoString byExtDefault; 1.1516 + miByExt->GetDefaultDescription(byExtDefault); 1.1517 + retval->SetDefaultDescription(byExtDefault); 1.1518 + retval->CopyBasicDataTo(miByExt); 1.1519 + 1.1520 + miByExt.swap(retval); 1.1521 + } 1.1522 + return retval.forget(); 1.1523 +} 1.1524 + 1.1525 +NS_IMETHODIMP 1.1526 +nsOSHelperAppService::GetProtocolHandlerInfoFromOS(const nsACString &aScheme, 1.1527 + bool *found, 1.1528 + nsIHandlerInfo **_retval) 1.1529 +{ 1.1530 + NS_ASSERTION(!aScheme.IsEmpty(), "No scheme was specified!"); 1.1531 + 1.1532 + // We must check that a registered handler exists so that gnome_url_show 1.1533 + // doesn't fallback to gnomevfs. 1.1534 + // See nsGNOMERegistry::LoadURL and bug 389632. 1.1535 + nsresult rv = OSProtocolHandlerExists(nsPromiseFlatCString(aScheme).get(), 1.1536 + found); 1.1537 + if (NS_FAILED(rv)) 1.1538 + return rv; 1.1539 + 1.1540 + nsMIMEInfoUnix *handlerInfo = 1.1541 + new nsMIMEInfoUnix(aScheme, nsMIMEInfoBase::eProtocolInfo); 1.1542 + NS_ENSURE_TRUE(handlerInfo, NS_ERROR_OUT_OF_MEMORY); 1.1543 + NS_ADDREF(*_retval = handlerInfo); 1.1544 + 1.1545 + if (!*found) { 1.1546 + // Code that calls this requires an object regardless if the OS has 1.1547 + // something for us, so we return the empty object. 1.1548 + return NS_OK; 1.1549 + } 1.1550 + 1.1551 + nsAutoString desc; 1.1552 + GetApplicationDescription(aScheme, desc); 1.1553 + handlerInfo->SetDefaultDescription(desc); 1.1554 + 1.1555 + return NS_OK; 1.1556 +}