uriloader/exthandler/unix/nsOSHelperAppService.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     2  *
     3  * This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include <sys/types.h>
     8 #include <sys/stat.h>
    10 #if defined(MOZ_ENABLE_CONTENTACTION)
    11 #include <contentaction/contentaction.h>
    12 #include <QString>
    13 #endif
    15 #include "nsOSHelperAppService.h"
    16 #include "nsMIMEInfoUnix.h"
    17 #ifdef MOZ_WIDGET_GTK
    18 #include "nsGNOMERegistry.h"
    19 #endif
    20 #include "nsISupports.h"
    21 #include "nsString.h"
    22 #include "nsReadableUtils.h"
    23 #include "nsUnicharUtils.h"
    24 #include "nsXPIDLString.h"
    25 #include "nsIURL.h"
    26 #include "nsIFileStreams.h"
    27 #include "nsILineInputStream.h"
    28 #include "nsIFile.h"
    29 #include "nsIProcess.h"
    30 #include "nsNetCID.h"
    31 #include "nsXPCOM.h"
    32 #include "nsISupportsPrimitives.h"
    33 #include "nsCRT.h"
    34 #include "nsDirectoryServiceDefs.h"
    35 #include "nsDirectoryServiceUtils.h"
    36 #include "prenv.h"      // for PR_GetEnv()
    37 #include "nsAutoPtr.h"
    38 #include "mozilla/Preferences.h"
    40 using namespace mozilla;
    42 #define LOG(args) PR_LOG(mLog, PR_LOG_DEBUG, args)
    43 #define LOG_ENABLED() PR_LOG_TEST(mLog, PR_LOG_DEBUG)
    45 static nsresult
    46 FindSemicolon(nsAString::const_iterator& aSemicolon_iter,
    47               const nsAString::const_iterator& aEnd_iter);
    48 static nsresult
    49 ParseMIMEType(const nsAString::const_iterator& aStart_iter,
    50               nsAString::const_iterator& aMajorTypeStart,
    51               nsAString::const_iterator& aMajorTypeEnd,
    52               nsAString::const_iterator& aMinorTypeStart,
    53               nsAString::const_iterator& aMinorTypeEnd,
    54               const nsAString::const_iterator& aEnd_iter);
    56 inline bool
    57 IsNetscapeFormat(const nsACString& aBuffer);
    59 nsOSHelperAppService::nsOSHelperAppService() : nsExternalHelperAppService()
    60 {
    61   mode_t mask = umask(0777);
    62   umask(mask);
    63   mPermissions = 0666 & ~mask;
    64 }
    66 nsOSHelperAppService::~nsOSHelperAppService()
    67 {}
    69 /*
    70  * Take a command with all the mailcap escapes in it and unescape it
    71  * Ideally this needs the mime type, mime type options, and location of the
    72  * temporary file, but this last can't be got from here
    73  */
    74 // static
    75 nsresult
    76 nsOSHelperAppService::UnescapeCommand(const nsAString& aEscapedCommand,
    77                                       const nsAString& aMajorType,
    78                                       const nsAString& aMinorType,
    79                                       nsACString& aUnEscapedCommand) {
    80   LOG(("-- UnescapeCommand"));
    81   LOG(("Command to escape: '%s'\n",
    82        NS_LossyConvertUTF16toASCII(aEscapedCommand).get()));
    83   //  XXX This function will need to get the mime type and various stuff like that being passed in to work properly
    85   LOG(("UnescapeCommand really needs some work -- it should actually do some unescaping\n"));
    87   CopyUTF16toUTF8(aEscapedCommand, aUnEscapedCommand);
    88   LOG(("Escaped command: '%s'\n",
    89        PromiseFlatCString(aUnEscapedCommand).get()));
    90   return NS_OK;
    91 }
    93 /* Put aSemicolon_iter at the first non-escaped semicolon after
    94  * aStart_iter but before aEnd_iter
    95  */
    97 static nsresult
    98 FindSemicolon(nsAString::const_iterator& aSemicolon_iter,
    99               const nsAString::const_iterator& aEnd_iter) {
   100   bool semicolonFound = false;
   101   while (aSemicolon_iter != aEnd_iter && !semicolonFound) {
   102     switch(*aSemicolon_iter) {
   103     case '\\':
   104       aSemicolon_iter.advance(2);
   105       break;
   106     case ';':
   107       semicolonFound = true;
   108       break;
   109     default:
   110       ++aSemicolon_iter;
   111       break;
   112     }
   113   }
   114   return NS_OK;
   115 }
   117 static nsresult
   118 ParseMIMEType(const nsAString::const_iterator& aStart_iter,
   119               nsAString::const_iterator& aMajorTypeStart,
   120               nsAString::const_iterator& aMajorTypeEnd,
   121               nsAString::const_iterator& aMinorTypeStart,
   122               nsAString::const_iterator& aMinorTypeEnd,
   123               const nsAString::const_iterator& aEnd_iter) {
   124   nsAString::const_iterator iter(aStart_iter);
   126   // skip leading whitespace
   127   while (iter != aEnd_iter && nsCRT::IsAsciiSpace(*iter)) {
   128     ++iter;
   129   }
   131   if (iter == aEnd_iter) {
   132     return NS_ERROR_INVALID_ARG;
   133   }
   135   aMajorTypeStart = iter;
   137   // find major/minor separator ('/')
   138   while (iter != aEnd_iter && *iter != '/') {
   139     ++iter;
   140   }
   142   if (iter == aEnd_iter) {
   143     return NS_ERROR_INVALID_ARG;
   144   }
   146   aMajorTypeEnd = iter;
   148   // skip '/'
   149   ++iter;
   151   if (iter == aEnd_iter) {
   152     return NS_ERROR_INVALID_ARG;
   153   }
   155   aMinorTypeStart = iter;
   157   // find end of minor type, delimited by whitespace or ';'
   158   while (iter != aEnd_iter && !nsCRT::IsAsciiSpace(*iter) && *iter != ';') {
   159     ++iter;
   160   }
   162   aMinorTypeEnd = iter;
   164   return NS_OK;
   165 }
   167 // static
   168 nsresult
   169 nsOSHelperAppService::GetFileLocation(const char* aPrefName,
   170                                       const char* aEnvVarName,
   171                                       nsAString& aFileLocation) {
   172   LOG(("-- GetFileLocation.  Pref: '%s'  EnvVar: '%s'\n",
   173        aPrefName,
   174        aEnvVarName));
   175   NS_PRECONDITION(aPrefName, "Null pref name passed; don't do that!");
   177   aFileLocation.Truncate();
   178   /* The lookup order is:
   179      1) user pref
   180      2) env var
   181      3) pref
   182   */
   183   NS_ENSURE_TRUE(Preferences::GetRootBranch(), NS_ERROR_FAILURE);
   185   /*
   186     If we have an env var we should check whether the pref is a user
   187     pref.  If we do not, we don't care.
   188   */
   189   if (Preferences::HasUserValue(aPrefName) &&
   190       NS_SUCCEEDED(Preferences::GetString(aPrefName, &aFileLocation))) {
   191     return NS_OK;
   192   }
   194   if (aEnvVarName && *aEnvVarName) {
   195     char* prefValue = PR_GetEnv(aEnvVarName);
   196     if (prefValue && *prefValue) {
   197       // the pref is in the system charset and it's a filepath... The
   198       // natural way to do the charset conversion is by just initing
   199       // an nsIFile with the native path and asking it for the Unicode
   200       // version.
   201       nsresult rv;
   202       nsCOMPtr<nsIFile> file(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
   203       NS_ENSURE_SUCCESS(rv, rv);
   205       rv = file->InitWithNativePath(nsDependentCString(prefValue));
   206       NS_ENSURE_SUCCESS(rv, rv);
   208       rv = file->GetPath(aFileLocation);
   209       NS_ENSURE_SUCCESS(rv, rv);
   211       return NS_OK;
   212     }
   213   }
   215   return Preferences::GetString(aPrefName, &aFileLocation);
   216 }
   219 /* Get the mime.types file names from prefs and look up info in them
   220    based on extension */
   221 // static
   222 nsresult
   223 nsOSHelperAppService::LookUpTypeAndDescription(const nsAString& aFileExtension,
   224                                                nsAString& aMajorType,
   225                                                nsAString& aMinorType,
   226                                                nsAString& aDescription,
   227                                                bool aUserData) {
   228   LOG(("-- LookUpTypeAndDescription for extension '%s'\n",
   229        NS_LossyConvertUTF16toASCII(aFileExtension).get()));
   230   nsresult rv = NS_OK;
   231   nsAutoString mimeFileName;
   233   const char* filenamePref = aUserData ?
   234     "helpers.private_mime_types_file" : "helpers.global_mime_types_file";
   236   rv = GetFileLocation(filenamePref, nullptr, mimeFileName);
   237   if (NS_SUCCEEDED(rv) && !mimeFileName.IsEmpty()) {
   238     rv = GetTypeAndDescriptionFromMimetypesFile(mimeFileName,
   239                                                 aFileExtension,
   240                                                 aMajorType,
   241                                                 aMinorType,
   242                                                 aDescription);
   243   } else {
   244     rv = NS_ERROR_NOT_AVAILABLE;
   245   }
   247   return rv;
   248 }
   250 inline bool
   251 IsNetscapeFormat(const nsACString& aBuffer) {
   252   return StringBeginsWith(aBuffer, NS_LITERAL_CSTRING("#--Netscape Communications Corporation MIME Information")) ||
   253          StringBeginsWith(aBuffer, NS_LITERAL_CSTRING("#--MCOM MIME Information"));
   254 }
   256 /*
   257  * Create a file stream and line input stream for the filename.
   258  * Leaves the first line of the file in aBuffer and sets the format to
   259  *  true for netscape files and false for normail ones
   260  */
   261 // static
   262 nsresult
   263 nsOSHelperAppService::CreateInputStream(const nsAString& aFilename,
   264                                         nsIFileInputStream ** aFileInputStream,
   265                                         nsILineInputStream ** aLineInputStream,
   266                                         nsACString& aBuffer,
   267                                         bool * aNetscapeFormat,
   268                                         bool * aMore) {
   269   LOG(("-- CreateInputStream"));
   270   nsresult rv = NS_OK;
   272   nsCOMPtr<nsIFile> file(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
   273   if (NS_FAILED(rv))
   274     return rv;
   275   rv = file->InitWithPath(aFilename);
   276   if (NS_FAILED(rv))
   277     return rv;
   279   nsCOMPtr<nsIFileInputStream> fileStream(do_CreateInstance(NS_LOCALFILEINPUTSTREAM_CONTRACTID, &rv));
   280   if (NS_FAILED(rv))
   281     return rv;
   282   rv = fileStream->Init(file, -1, -1, false);
   283   if (NS_FAILED(rv))
   284     return rv;
   286   nsCOMPtr<nsILineInputStream> lineStream(do_QueryInterface(fileStream, &rv));
   288   if (NS_FAILED(rv)) {
   289     LOG(("Interface trouble in stream land!"));
   290     return rv;
   291   }
   293   rv = lineStream->ReadLine(aBuffer, aMore);
   294   if (NS_FAILED(rv)) {
   295     fileStream->Close();
   296     return rv;
   297   }
   299   *aNetscapeFormat = IsNetscapeFormat(aBuffer);
   301   *aFileInputStream = fileStream;
   302   NS_ADDREF(*aFileInputStream);
   303   *aLineInputStream = lineStream;
   304   NS_ADDREF(*aLineInputStream);
   306   return NS_OK;
   307 }
   309 /* Open the file, read the first line, decide what type of file it is,
   310    then get info based on extension */
   311 // static
   312 nsresult
   313 nsOSHelperAppService::GetTypeAndDescriptionFromMimetypesFile(const nsAString& aFilename,
   314                                                              const nsAString& aFileExtension,
   315                                                              nsAString& aMajorType,
   316                                                              nsAString& aMinorType,
   317                                                              nsAString& aDescription) {
   318   LOG(("-- GetTypeAndDescriptionFromMimetypesFile\n"));
   319   LOG(("Getting type and description from types file '%s'\n",
   320        NS_LossyConvertUTF16toASCII(aFilename).get()));
   321   LOG(("Using extension '%s'\n",
   322        NS_LossyConvertUTF16toASCII(aFileExtension).get()));
   323   nsresult rv = NS_OK;
   324   nsCOMPtr<nsIFileInputStream> mimeFile;
   325   nsCOMPtr<nsILineInputStream> mimeTypes;
   326   bool netscapeFormat;
   327   nsAutoString buf;
   328   nsAutoCString cBuf;
   329   bool more = false;
   330   rv = CreateInputStream(aFilename, getter_AddRefs(mimeFile), getter_AddRefs(mimeTypes),
   331                          cBuf, &netscapeFormat, &more);
   333   if (NS_FAILED(rv)) {
   334     return rv;
   335   }
   337   nsAutoString extensions;
   338   nsString entry;
   339   entry.SetCapacity(100);
   340   nsAString::const_iterator majorTypeStart, majorTypeEnd,
   341                             minorTypeStart, minorTypeEnd,
   342                             descriptionStart, descriptionEnd;
   344   do {
   345     CopyASCIItoUTF16(cBuf, buf);
   346     // read through, building up an entry.  If we finish an entry, check for
   347     // a match and return out of the loop if we match
   349     // skip comments and empty lines
   350     if (!buf.IsEmpty() && buf.First() != '#') {
   351       entry.Append(buf);
   352       if (entry.Last() == '\\') {
   353         entry.Truncate(entry.Length() - 1);
   354         entry.Append(char16_t(' '));  // in case there is no trailing whitespace on this line
   355       } else {  // we have a full entry
   356         LOG(("Current entry: '%s'\n",
   357              NS_LossyConvertUTF16toASCII(entry).get()));
   358         if (netscapeFormat) {
   359           rv = ParseNetscapeMIMETypesEntry(entry,
   360                                            majorTypeStart, majorTypeEnd,
   361                                            minorTypeStart, minorTypeEnd,
   362                                            extensions,
   363                                            descriptionStart, descriptionEnd);
   364           if (NS_FAILED(rv)) {
   365             // We sometimes get things like RealPlayer appending
   366             // "normal" entries to "Netscape" .mime.types files.  Try
   367             // to handle that.  Bug 106381.
   368             LOG(("Bogus entry; trying 'normal' mode\n"));
   369             rv = ParseNormalMIMETypesEntry(entry,
   370                                            majorTypeStart, majorTypeEnd,
   371                                            minorTypeStart, minorTypeEnd,
   372                                            extensions,
   373                                            descriptionStart, descriptionEnd);
   374           }
   375         } else {
   376           rv = ParseNormalMIMETypesEntry(entry,
   377                                          majorTypeStart, majorTypeEnd,
   378                                          minorTypeStart, minorTypeEnd,
   379                                          extensions,
   380                                          descriptionStart, descriptionEnd);
   381           if (NS_FAILED(rv)) {
   382             // We sometimes get things like StarOffice prepending
   383             // "normal" entries to "Netscape" .mime.types files.  Try
   384             // to handle that.  Bug 136670.
   385             LOG(("Bogus entry; trying 'Netscape' mode\n"));
   386             rv = ParseNetscapeMIMETypesEntry(entry,
   387                                              majorTypeStart, majorTypeEnd,
   388                                              minorTypeStart, minorTypeEnd,
   389                                              extensions,
   390                                              descriptionStart, descriptionEnd);
   391           }
   392         }
   394         if (NS_SUCCEEDED(rv)) { // entry parses
   395           nsAString::const_iterator start, end;
   396           extensions.BeginReading(start);
   397           extensions.EndReading(end);
   398           nsAString::const_iterator iter(start);
   400           while (start != end) {
   401             FindCharInReadable(',', iter, end);
   402             if (Substring(start, iter).Equals(aFileExtension,
   403                                               nsCaseInsensitiveStringComparator())) {
   404               // it's a match.  Assign the type and description and run
   405               aMajorType.Assign(Substring(majorTypeStart, majorTypeEnd));
   406               aMinorType.Assign(Substring(minorTypeStart, minorTypeEnd));
   407               aDescription.Assign(Substring(descriptionStart, descriptionEnd));
   408               mimeFile->Close();
   409               return NS_OK;
   410             }
   411             if (iter != end) {
   412               ++iter;
   413             }
   414             start = iter;
   415           }
   416         } else {
   417           LOG(("Failed to parse entry: %s\n", NS_LossyConvertUTF16toASCII(entry).get()));
   418         }
   419         // truncate the entry for the next iteration
   420         entry.Truncate();
   421       }
   422     }
   423     if (!more) {
   424       rv = NS_ERROR_NOT_AVAILABLE;
   425       break;
   426     }
   427     // read the next line
   428     rv = mimeTypes->ReadLine(cBuf, &more);
   429   } while (NS_SUCCEEDED(rv));
   431   mimeFile->Close();
   432   return rv;
   433 }
   435 /* Get the mime.types file names from prefs and look up info in them
   436    based on mimetype  */
   437 // static
   438 nsresult
   439 nsOSHelperAppService::LookUpExtensionsAndDescription(const nsAString& aMajorType,
   440                                                      const nsAString& aMinorType,
   441                                                      nsAString& aFileExtensions,
   442                                                      nsAString& aDescription) {
   443   LOG(("-- LookUpExtensionsAndDescription for type '%s/%s'\n",
   444        NS_LossyConvertUTF16toASCII(aMajorType).get(),
   445        NS_LossyConvertUTF16toASCII(aMinorType).get()));
   446   nsresult rv = NS_OK;
   447   nsAutoString mimeFileName;
   449   rv = GetFileLocation("helpers.private_mime_types_file", nullptr, mimeFileName);
   450   if (NS_SUCCEEDED(rv) && !mimeFileName.IsEmpty()) {
   451     rv = GetExtensionsAndDescriptionFromMimetypesFile(mimeFileName,
   452                                                       aMajorType,
   453                                                       aMinorType,
   454                                                       aFileExtensions,
   455                                                       aDescription);
   456   } else {
   457     rv = NS_ERROR_NOT_AVAILABLE;
   458   }
   459   if (NS_FAILED(rv) || aFileExtensions.IsEmpty()) {
   460     rv = GetFileLocation("helpers.global_mime_types_file",
   461                          nullptr, mimeFileName);
   462     if (NS_SUCCEEDED(rv) && !mimeFileName.IsEmpty()) {
   463       rv = GetExtensionsAndDescriptionFromMimetypesFile(mimeFileName,
   464                                                         aMajorType,
   465                                                         aMinorType,
   466                                                         aFileExtensions,
   467                                                         aDescription);
   468     } else {
   469       rv = NS_ERROR_NOT_AVAILABLE;
   470     }
   471   }
   472   return rv;
   473 }
   475 /* Open the file, read the first line, decide what type of file it is,
   476    then get info based on extension */
   477 // static
   478 nsresult
   479 nsOSHelperAppService::GetExtensionsAndDescriptionFromMimetypesFile(const nsAString& aFilename,
   480                                                                    const nsAString& aMajorType,
   481                                                                    const nsAString& aMinorType,
   482                                                                    nsAString& aFileExtensions,
   483                                                                    nsAString& aDescription) {
   484   LOG(("-- GetExtensionsAndDescriptionFromMimetypesFile\n"));
   485   LOG(("Getting extensions and description from types file '%s'\n",
   486        NS_LossyConvertUTF16toASCII(aFilename).get()));
   487   LOG(("Using type '%s/%s'\n",
   488        NS_LossyConvertUTF16toASCII(aMajorType).get(),
   489        NS_LossyConvertUTF16toASCII(aMinorType).get()));
   491   nsresult rv = NS_OK;
   492   nsCOMPtr<nsIFileInputStream> mimeFile;
   493   nsCOMPtr<nsILineInputStream> mimeTypes;
   494   bool netscapeFormat;
   495   nsAutoCString cBuf;
   496   nsAutoString buf;
   497   bool more = false;
   498   rv = CreateInputStream(aFilename, getter_AddRefs(mimeFile), getter_AddRefs(mimeTypes),
   499                          cBuf, &netscapeFormat, &more);
   501   if (NS_FAILED(rv)) {
   502     return rv;
   503   }
   505   nsAutoString extensions;
   506   nsString entry;
   507   entry.SetCapacity(100);
   508   nsAString::const_iterator majorTypeStart, majorTypeEnd,
   509                             minorTypeStart, minorTypeEnd,
   510                             descriptionStart, descriptionEnd;
   512   do {
   513     CopyASCIItoUTF16(cBuf, buf);
   514     // read through, building up an entry.  If we finish an entry, check for
   515     // a match and return out of the loop if we match
   517     // skip comments and empty lines
   518     if (!buf.IsEmpty() && buf.First() != '#') {
   519       entry.Append(buf);
   520       if (entry.Last() == '\\') {
   521         entry.Truncate(entry.Length() - 1);
   522         entry.Append(char16_t(' '));  // in case there is no trailing whitespace on this line
   523       } else {  // we have a full entry
   524         LOG(("Current entry: '%s'\n",
   525              NS_LossyConvertUTF16toASCII(entry).get()));
   526         if (netscapeFormat) {
   527           rv = ParseNetscapeMIMETypesEntry(entry,
   528                                            majorTypeStart, majorTypeEnd,
   529                                            minorTypeStart, minorTypeEnd,
   530                                            extensions,
   531                                            descriptionStart, descriptionEnd);
   533           if (NS_FAILED(rv)) {
   534             // We sometimes get things like RealPlayer appending
   535             // "normal" entries to "Netscape" .mime.types files.  Try
   536             // to handle that.  Bug 106381.
   537             LOG(("Bogus entry; trying 'normal' mode\n"));
   538             rv = ParseNormalMIMETypesEntry(entry,
   539                                            majorTypeStart, majorTypeEnd,
   540                                            minorTypeStart, minorTypeEnd,
   541                                            extensions,
   542                                            descriptionStart, descriptionEnd);
   543           }
   544         } else {
   545           rv = ParseNormalMIMETypesEntry(entry,
   546                                          majorTypeStart, majorTypeEnd,
   547                                          minorTypeStart,
   548                                          minorTypeEnd, extensions,
   549                                          descriptionStart, descriptionEnd);
   551           if (NS_FAILED(rv)) {
   552             // We sometimes get things like StarOffice prepending
   553             // "normal" entries to "Netscape" .mime.types files.  Try
   554             // to handle that.  Bug 136670.
   555             LOG(("Bogus entry; trying 'Netscape' mode\n"));
   556             rv = ParseNetscapeMIMETypesEntry(entry,
   557                                              majorTypeStart, majorTypeEnd,
   558                                              minorTypeStart, minorTypeEnd,
   559                                              extensions,
   560                                              descriptionStart, descriptionEnd);
   561           }
   562         }
   564         if (NS_SUCCEEDED(rv) &&
   565             Substring(majorTypeStart,
   566                       majorTypeEnd).Equals(aMajorType,
   567                                            nsCaseInsensitiveStringComparator())&&
   568             Substring(minorTypeStart,
   569                       minorTypeEnd).Equals(aMinorType,
   570                                            nsCaseInsensitiveStringComparator())) {
   571           // it's a match
   572           aFileExtensions.Assign(extensions);
   573           aDescription.Assign(Substring(descriptionStart, descriptionEnd));
   574           mimeFile->Close();
   575           return NS_OK;
   576         } else if (NS_FAILED(rv)) {
   577           LOG(("Failed to parse entry: %s\n", NS_LossyConvertUTF16toASCII(entry).get()));
   578         }
   580         entry.Truncate();
   581       }
   582     }
   583     if (!more) {
   584       rv = NS_ERROR_NOT_AVAILABLE;
   585       break;
   586     }
   587     // read the next line
   588     rv = mimeTypes->ReadLine(cBuf, &more);
   589   } while (NS_SUCCEEDED(rv));
   591   mimeFile->Close();
   592   return rv;
   593 }
   595 /*
   596  * This parses a Netscape format mime.types entry.  There are two
   597  * possible formats:
   598  *
   599  * type=foo/bar; options exts="baz" description="Some type"
   600  *
   601  * and
   602  *
   603  * type=foo/bar; options description="Some type" exts="baz"
   604  */
   605 // static
   606 nsresult
   607 nsOSHelperAppService::ParseNetscapeMIMETypesEntry(const nsAString& aEntry,
   608                                                   nsAString::const_iterator& aMajorTypeStart,
   609                                                   nsAString::const_iterator& aMajorTypeEnd,
   610                                                   nsAString::const_iterator& aMinorTypeStart,
   611                                                   nsAString::const_iterator& aMinorTypeEnd,
   612                                                   nsAString& aExtensions,
   613                                                   nsAString::const_iterator& aDescriptionStart,
   614                                                   nsAString::const_iterator& aDescriptionEnd) {
   615   LOG(("-- ParseNetscapeMIMETypesEntry\n"));
   616   NS_ASSERTION(!aEntry.IsEmpty(), "Empty Netscape MIME types entry being parsed.");
   618   nsAString::const_iterator start_iter, end_iter, match_start, match_end;
   620   aEntry.BeginReading(start_iter);
   621   aEntry.EndReading(end_iter);
   623   // skip trailing whitespace
   624   do {
   625     --end_iter;
   626   } while (end_iter != start_iter &&
   627            nsCRT::IsAsciiSpace(*end_iter));
   628   // if we're pointing to a quote, don't advance -- we don't want to
   629   // include the quote....
   630   if (*end_iter != '"')
   631     ++end_iter;
   632   match_start = start_iter;
   633   match_end = end_iter;
   635   // Get the major and minor types
   636   // First the major type
   637   if (! FindInReadable(NS_LITERAL_STRING("type="), match_start, match_end)) {
   638     return NS_ERROR_FAILURE;
   639   }
   641   match_start = match_end;
   643   while (match_end != end_iter &&
   644          *match_end != '/') {
   645     ++match_end;
   646   }
   647   if (match_end == end_iter) {
   648     return NS_ERROR_FAILURE;
   649   }
   651   aMajorTypeStart = match_start;
   652   aMajorTypeEnd = match_end;
   654   // now the minor type
   655   if (++match_end == end_iter) {
   656     return NS_ERROR_FAILURE;
   657   }
   659   match_start = match_end;
   661   while (match_end != end_iter &&
   662          !nsCRT::IsAsciiSpace(*match_end) &&
   663          *match_end != ';') {
   664     ++match_end;
   665   }
   666   if (match_end == end_iter) {
   667     return NS_ERROR_FAILURE;
   668   }
   670   aMinorTypeStart = match_start;
   671   aMinorTypeEnd = match_end;
   673   // ignore everything up to the end of the mime type from here on
   674   start_iter = match_end;
   676   // get the extensions
   677   match_start = match_end;
   678   match_end = end_iter;
   679   if (FindInReadable(NS_LITERAL_STRING("exts="), match_start, match_end)) {
   680     nsAString::const_iterator extStart, extEnd;
   682     if (match_end == end_iter ||
   683         (*match_end == '"' && ++match_end == end_iter)) {
   684       return NS_ERROR_FAILURE;
   685     }
   687     extStart = match_end;
   688     match_start = extStart;
   689     match_end = end_iter;
   690     if (FindInReadable(NS_LITERAL_STRING("desc=\""), match_start, match_end)) {
   691       // exts= before desc=, so we have to find the actual end of the extensions
   692       extEnd = match_start;
   693       if (extEnd == extStart) {
   694         return NS_ERROR_FAILURE;
   695       }
   697       do {
   698         --extEnd;
   699       } while (extEnd != extStart &&
   700                nsCRT::IsAsciiSpace(*extEnd));
   702       if (extEnd != extStart && *extEnd == '"') {
   703         --extEnd;
   704       }
   705     } else {
   706       // desc= before exts=, so we can use end_iter as the end of the extensions
   707       extEnd = end_iter;
   708     }
   709     aExtensions = Substring(extStart, extEnd);
   710   } else {
   711     // no extensions
   712     aExtensions.Truncate();
   713   }
   715   // get the description
   716   match_start = start_iter;
   717   match_end = end_iter;
   718   if (FindInReadable(NS_LITERAL_STRING("desc=\""), match_start, match_end)) {
   719     aDescriptionStart = match_end;
   720     match_start = aDescriptionStart;
   721     match_end = end_iter;
   722     if (FindInReadable(NS_LITERAL_STRING("exts="), match_start, match_end)) {
   723       // exts= after desc=, so have to find actual end of description
   724       aDescriptionEnd = match_start;
   725       if (aDescriptionEnd == aDescriptionStart) {
   726         return NS_ERROR_FAILURE;
   727       }
   729       do {
   730         --aDescriptionEnd;
   731       } while (aDescriptionEnd != aDescriptionStart &&
   732                nsCRT::IsAsciiSpace(*aDescriptionEnd));
   734       if (aDescriptionStart != aDescriptionStart && *aDescriptionEnd == '"') {
   735         --aDescriptionEnd;
   736       }
   737     } else {
   738       // desc= after exts=, so use end_iter for the description end
   739       aDescriptionEnd = end_iter;
   740     }
   741   } else {
   742     // no description
   743     aDescriptionStart = start_iter;
   744     aDescriptionEnd = start_iter;
   745   }
   747   return NS_OK;
   748 }
   750 /*
   751  * This parses a normal format mime.types entry.  The format is:
   752  *
   753  * major/minor    ext1 ext2 ext3
   754  */
   755 // static
   756 nsresult
   757 nsOSHelperAppService::ParseNormalMIMETypesEntry(const nsAString& aEntry,
   758                                                 nsAString::const_iterator& aMajorTypeStart,
   759                                                 nsAString::const_iterator& aMajorTypeEnd,
   760                                                 nsAString::const_iterator& aMinorTypeStart,
   761                                                 nsAString::const_iterator& aMinorTypeEnd,
   762                                                 nsAString& aExtensions,
   763                                                 nsAString::const_iterator& aDescriptionStart,
   764                                                 nsAString::const_iterator& aDescriptionEnd) {
   765   LOG(("-- ParseNormalMIMETypesEntry\n"));
   766   NS_ASSERTION(!aEntry.IsEmpty(), "Empty Normal MIME types entry being parsed.");
   768   nsAString::const_iterator start_iter, end_iter, iter;
   770   aEntry.BeginReading(start_iter);
   771   aEntry.EndReading(end_iter);
   773   // no description
   774   aDescriptionStart = start_iter;
   775   aDescriptionEnd = start_iter;
   777   // skip leading whitespace
   778   while (start_iter != end_iter && nsCRT::IsAsciiSpace(*start_iter)) {
   779     ++start_iter;
   780   }
   781   if (start_iter == end_iter) {
   782     return NS_ERROR_FAILURE;
   783   }
   784   // skip trailing whitespace
   785   do {
   786     --end_iter;
   787   } while (end_iter != start_iter && nsCRT::IsAsciiSpace(*end_iter));
   789   ++end_iter; // point to first whitespace char (or to end of string)
   790   iter = start_iter;
   792   // get the major type
   793   if (! FindCharInReadable('/', iter, end_iter))
   794     return NS_ERROR_FAILURE;
   796   nsAString::const_iterator equals_sign_iter(start_iter);
   797   if (FindCharInReadable('=', equals_sign_iter, iter))
   798     return NS_ERROR_FAILURE; // see bug 136670
   800   aMajorTypeStart = start_iter;
   801   aMajorTypeEnd = iter;
   803   // get the minor type
   804   if (++iter == end_iter) {
   805     return NS_ERROR_FAILURE;
   806   }
   807   start_iter = iter;
   809   while (iter != end_iter && !nsCRT::IsAsciiSpace(*iter)) {
   810     ++iter;
   811   }
   812   aMinorTypeStart = start_iter;
   813   aMinorTypeEnd = iter;
   815   // get the extensions
   816   aExtensions.Truncate();
   817   while (iter != end_iter) {
   818     while (iter != end_iter && nsCRT::IsAsciiSpace(*iter)) {
   819       ++iter;
   820     }
   822     start_iter = iter;
   823     while (iter != end_iter && !nsCRT::IsAsciiSpace(*iter)) {
   824       ++iter;
   825     }
   826     aExtensions.Append(Substring(start_iter, iter));
   827     if (iter != end_iter) { // not the last extension
   828       aExtensions.Append(char16_t(','));
   829     }
   830   }
   832   return NS_OK;
   833 }
   835 // static
   836 nsresult
   837 nsOSHelperAppService::LookUpHandlerAndDescription(const nsAString& aMajorType,
   838                                                   const nsAString& aMinorType,
   839                                                   nsAString& aHandler,
   840                                                   nsAString& aDescription,
   841                                                   nsAString& aMozillaFlags) {
   843   // The mailcap lookup is two-pass to handle the case of mailcap files
   844   // that have something like:
   845   //
   846   // text/*; emacs %s
   847   // text/rtf; soffice %s
   848   //
   849   // in that order.  We want to pick up "soffice" for text/rtf in such cases
   850   nsresult rv = DoLookUpHandlerAndDescription(aMajorType,
   851                                               aMinorType,
   852                                               aHandler,
   853                                               aDescription,
   854                                               aMozillaFlags,
   855                                               true);
   856   if (NS_FAILED(rv)) {
   857     rv = DoLookUpHandlerAndDescription(aMajorType,
   858                                        aMinorType,
   859                                        aHandler,
   860                                        aDescription,
   861                                        aMozillaFlags,
   862                                        false);
   863   }
   865   // maybe we have an entry for "aMajorType/*"?
   866   if (NS_FAILED(rv)) {
   867     rv = DoLookUpHandlerAndDescription(aMajorType,
   868                                        NS_LITERAL_STRING("*"),
   869                                        aHandler,
   870                                        aDescription,
   871                                        aMozillaFlags,
   872                                        true);
   873   }
   875   if (NS_FAILED(rv)) {
   876     rv = DoLookUpHandlerAndDescription(aMajorType,
   877                                        NS_LITERAL_STRING("*"),
   878                                        aHandler,
   879                                        aDescription,
   880                                        aMozillaFlags,
   881                                        false);
   882   }
   884   return rv;
   885 }
   887 // static
   888 nsresult
   889 nsOSHelperAppService::DoLookUpHandlerAndDescription(const nsAString& aMajorType,
   890                                                     const nsAString& aMinorType,
   891                                                     nsAString& aHandler,
   892                                                     nsAString& aDescription,
   893                                                     nsAString& aMozillaFlags,
   894                                                     bool aUserData) {
   895   LOG(("-- LookUpHandlerAndDescription for type '%s/%s'\n",
   896        NS_LossyConvertUTF16toASCII(aMajorType).get(),
   897        NS_LossyConvertUTF16toASCII(aMinorType).get()));
   898   nsresult rv = NS_OK;
   899   nsAutoString mailcapFileName;
   901   const char * filenamePref = aUserData ?
   902     "helpers.private_mailcap_file" : "helpers.global_mailcap_file";
   903   const char * filenameEnvVar = aUserData ?
   904     "PERSONAL_MAILCAP" : "MAILCAP";
   906   rv = GetFileLocation(filenamePref, filenameEnvVar, mailcapFileName);
   907   if (NS_SUCCEEDED(rv) && !mailcapFileName.IsEmpty()) {
   908     rv = GetHandlerAndDescriptionFromMailcapFile(mailcapFileName,
   909                                                  aMajorType,
   910                                                  aMinorType,
   911                                                  aHandler,
   912                                                  aDescription,
   913                                                  aMozillaFlags);
   914   } else {
   915     rv = NS_ERROR_NOT_AVAILABLE;
   916   }
   918   return rv;
   919 }
   921 // static
   922 nsresult
   923 nsOSHelperAppService::GetHandlerAndDescriptionFromMailcapFile(const nsAString& aFilename,
   924                                                               const nsAString& aMajorType,
   925                                                               const nsAString& aMinorType,
   926                                                               nsAString& aHandler,
   927                                                               nsAString& aDescription,
   928                                                               nsAString& aMozillaFlags) {
   930   LOG(("-- GetHandlerAndDescriptionFromMailcapFile\n"));
   931   LOG(("Getting handler and description from mailcap file '%s'\n",
   932        NS_LossyConvertUTF16toASCII(aFilename).get()));
   933   LOG(("Using type '%s/%s'\n",
   934        NS_LossyConvertUTF16toASCII(aMajorType).get(),
   935        NS_LossyConvertUTF16toASCII(aMinorType).get()));
   937   nsresult rv = NS_OK;
   938   bool more = false;
   940   nsCOMPtr<nsIFile> file(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
   941   if (NS_FAILED(rv))
   942     return rv;
   943   rv = file->InitWithPath(aFilename);
   944   if (NS_FAILED(rv))
   945     return rv;
   947   nsCOMPtr<nsIFileInputStream> mailcapFile(do_CreateInstance(NS_LOCALFILEINPUTSTREAM_CONTRACTID, &rv));
   948   if (NS_FAILED(rv))
   949     return rv;
   950   rv = mailcapFile->Init(file, -1, -1, false);
   951   if (NS_FAILED(rv))
   952     return rv;
   954   nsCOMPtr<nsILineInputStream> mailcap (do_QueryInterface(mailcapFile, &rv));
   956   if (NS_FAILED(rv)) {
   957     LOG(("Interface trouble in stream land!"));
   958     return rv;
   959   }
   961   nsString entry, buffer;
   962   nsAutoCString cBuffer;
   963   entry.SetCapacity(128);
   964   cBuffer.SetCapacity(80);
   965   rv = mailcap->ReadLine(cBuffer, &more);
   966   if (NS_FAILED(rv)) {
   967     mailcapFile->Close();
   968     return rv;
   969   }
   971   do {  // return on end-of-file in the loop
   973     CopyASCIItoUTF16(cBuffer, buffer);
   974     if (!buffer.IsEmpty() && buffer.First() != '#') {
   975       entry.Append(buffer);
   976       if (entry.Last() == '\\') {  // entry continues on next line
   977         entry.Truncate(entry.Length()-1);
   978         entry.Append(char16_t(' ')); // in case there is no trailing whitespace on this line
   979       } else {  // we have a full entry in entry.  Check it for the type
   980         LOG(("Current entry: '%s'\n",
   981              NS_LossyConvertUTF16toASCII(entry).get()));
   983         nsAString::const_iterator semicolon_iter,
   984                                   start_iter, end_iter,
   985                                   majorTypeStart, majorTypeEnd,
   986                                   minorTypeStart, minorTypeEnd;
   987         entry.BeginReading(start_iter);
   988         entry.EndReading(end_iter);
   989         semicolon_iter = start_iter;
   990         FindSemicolon(semicolon_iter, end_iter);
   991         if (semicolon_iter != end_iter) { // we have something resembling a valid entry
   992           rv = ParseMIMEType(start_iter, majorTypeStart, majorTypeEnd,
   993                              minorTypeStart, minorTypeEnd, semicolon_iter);
   994           if (NS_SUCCEEDED(rv) &&
   995               Substring(majorTypeStart,
   996                         majorTypeEnd).Equals(aMajorType,
   997                                              nsCaseInsensitiveStringComparator()) &&
   998               Substring(minorTypeStart,
   999                         minorTypeEnd).Equals(aMinorType,
  1000                                              nsCaseInsensitiveStringComparator())) { // we have a match
  1001             bool match = true;
  1002             ++semicolon_iter;             // point at the first char past the semicolon
  1003             start_iter = semicolon_iter;  // handler string starts here
  1004             FindSemicolon(semicolon_iter, end_iter);
  1005             while (start_iter != semicolon_iter &&
  1006                    nsCRT::IsAsciiSpace(*start_iter)) {
  1007               ++start_iter;
  1010             LOG(("The real handler is: '%s'\n",
  1011                  NS_LossyConvertUTF16toASCII(Substring(start_iter,
  1012                                                       semicolon_iter)).get()));
  1014             // XXX ugly hack.  Just grab the executable name
  1015             nsAString::const_iterator end_handler_iter = semicolon_iter;
  1016             nsAString::const_iterator end_executable_iter = start_iter;
  1017             while (end_executable_iter != end_handler_iter &&
  1018                    !nsCRT::IsAsciiSpace(*end_executable_iter)) {
  1019               ++end_executable_iter;
  1021             // XXX End ugly hack
  1023             aHandler = Substring(start_iter, end_executable_iter);
  1025             nsAString::const_iterator start_option_iter, end_optionname_iter, equal_sign_iter;
  1026             bool equalSignFound;
  1027             while (match &&
  1028                    semicolon_iter != end_iter &&
  1029                    ++semicolon_iter != end_iter) { // there are options left and we still match
  1030               start_option_iter = semicolon_iter;
  1031               // skip over leading whitespace
  1032               while (start_option_iter != end_iter &&
  1033                      nsCRT::IsAsciiSpace(*start_option_iter)) {
  1034                 ++start_option_iter;
  1036               if (start_option_iter == end_iter) { // nothing actually here
  1037                 break;
  1039               semicolon_iter = start_option_iter;
  1040               FindSemicolon(semicolon_iter, end_iter);
  1041               equal_sign_iter = start_option_iter;
  1042               equalSignFound = false;
  1043               while (equal_sign_iter != semicolon_iter && !equalSignFound) {
  1044                 switch(*equal_sign_iter) {
  1045                 case '\\':
  1046                   equal_sign_iter.advance(2);
  1047                   break;
  1048                 case '=':
  1049                   equalSignFound = true;
  1050                   break;
  1051                 default:
  1052                   ++equal_sign_iter;
  1053                   break;
  1056               end_optionname_iter = start_option_iter;
  1057               // find end of option name
  1058               while (end_optionname_iter != equal_sign_iter &&
  1059                      !nsCRT::IsAsciiSpace(*end_optionname_iter)) {
  1060                 ++end_optionname_iter;
  1062               nsDependentSubstring optionName(start_option_iter, end_optionname_iter);
  1063               if (equalSignFound) {
  1064                 // This is an option that has a name and value
  1065                 if (optionName.EqualsLiteral("description")) {
  1066                   aDescription = Substring(++equal_sign_iter, semicolon_iter);
  1067                 } else if (optionName.EqualsLiteral("x-mozilla-flags")) {
  1068                   aMozillaFlags = Substring(++equal_sign_iter, semicolon_iter);
  1069                 } else if (optionName.EqualsLiteral("test")) {
  1070                   nsAutoCString testCommand;
  1071                   rv = UnescapeCommand(Substring(++equal_sign_iter, semicolon_iter),
  1072                                        aMajorType,
  1073                                        aMinorType,
  1074                                        testCommand);
  1075                   if (NS_FAILED(rv))
  1076                     continue;
  1077                   nsCOMPtr<nsIProcess> process = do_CreateInstance(NS_PROCESS_CONTRACTID, &rv);
  1078                   if (NS_FAILED(rv))
  1079                     continue;
  1080                   nsCOMPtr<nsIFile> file(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
  1081                   if (NS_FAILED(rv))
  1082                     continue;
  1083                   rv = file->InitWithNativePath(NS_LITERAL_CSTRING("/bin/sh"));
  1084                   if (NS_FAILED(rv))
  1085                     continue;
  1086                   rv = process->Init(file);
  1087                   if (NS_FAILED(rv))
  1088                     continue;
  1089                   const char *args[] = { "-c", testCommand.get() };
  1090                   LOG(("Running Test: %s\n", testCommand.get()));
  1091                   rv = process->Run(true, args, 2);
  1092                   if (NS_FAILED(rv))
  1093                     continue;
  1094                   int32_t exitValue;
  1095                   rv = process->GetExitValue(&exitValue);
  1096                   if (NS_FAILED(rv))
  1097                     continue;
  1098                   LOG(("Exit code: %d\n", exitValue));
  1099                   if (exitValue) {
  1100                     match = false;
  1103               } else {
  1104                 // This is an option that just has a name but no value (eg "copiousoutput")
  1105                 if (optionName.EqualsLiteral("needsterminal")) {
  1106                   match = false;
  1112             if (match) { // we did not fail any test clauses; all is good
  1113               // get out of here
  1114               mailcapFile->Close();
  1115               return NS_OK;
  1116             } else { // pretend that this match never happened
  1117               aDescription.Truncate();
  1118               aMozillaFlags.Truncate();
  1119               aHandler.Truncate();
  1123         // zero out the entry for the next cycle
  1124         entry.Truncate();
  1127     if (!more) {
  1128       rv = NS_ERROR_NOT_AVAILABLE;
  1129       break;
  1131     rv = mailcap->ReadLine(cBuffer, &more);
  1132   } while (NS_SUCCEEDED(rv));
  1133   mailcapFile->Close();
  1134   return rv;
  1137 nsresult nsOSHelperAppService::OSProtocolHandlerExists(const char * aProtocolScheme, bool * aHandlerExists)
  1139   LOG(("-- nsOSHelperAppService::OSProtocolHandlerExists for '%s'\n",
  1140        aProtocolScheme));
  1141   *aHandlerExists = false;
  1143 #if defined(MOZ_ENABLE_CONTENTACTION)
  1144   // libcontentaction requires character ':' after scheme
  1145   ContentAction::Action action =
  1146     ContentAction::Action::defaultActionForScheme(QString(aProtocolScheme) + ':');
  1148   if (action.isValid())
  1149     *aHandlerExists = true;
  1150 #endif
  1152 #ifdef MOZ_WIDGET_GTK
  1153   // Check the GConf registry for a protocol handler
  1154   *aHandlerExists = nsGNOMERegistry::HandlerExists(aProtocolScheme);
  1155 #endif
  1157   return NS_OK;
  1160 NS_IMETHODIMP nsOSHelperAppService::GetApplicationDescription(const nsACString& aScheme, nsAString& _retval)
  1162 #ifdef MOZ_WIDGET_GTK
  1163   nsGNOMERegistry::GetAppDescForScheme(aScheme, _retval);
  1164   return _retval.IsEmpty() ? NS_ERROR_NOT_AVAILABLE : NS_OK;
  1165 #else
  1166   return NS_ERROR_NOT_AVAILABLE;
  1167 #endif
  1170 nsresult nsOSHelperAppService::GetFileTokenForPath(const char16_t * platformAppPath, nsIFile ** aFile)
  1172   LOG(("-- nsOSHelperAppService::GetFileTokenForPath: '%s'\n",
  1173        NS_LossyConvertUTF16toASCII(platformAppPath).get()));
  1174   if (! *platformAppPath) { // empty filename--return error
  1175     NS_WARNING("Empty filename passed in.");
  1176     return NS_ERROR_INVALID_ARG;
  1179   // first check if the base class implementation finds anything
  1180   nsresult rv = nsExternalHelperAppService::GetFileTokenForPath(platformAppPath, aFile);
  1181   if (NS_SUCCEEDED(rv))
  1182     return rv;
  1183   // If the reason for failure was that the file doesn't exist, return too
  1184   // (because it means the path was absolute, and so that we shouldn't search in
  1185   // the path)
  1186   if (rv == NS_ERROR_FILE_NOT_FOUND)
  1187     return rv;
  1189   // If we get here, we really should have a relative path.
  1190   NS_ASSERTION(*platformAppPath != char16_t('/'), "Unexpected absolute path");
  1192   nsCOMPtr<nsIFile> localFile (do_CreateInstance(NS_LOCAL_FILE_CONTRACTID));
  1194   if (!localFile) return NS_ERROR_NOT_INITIALIZED;
  1196   bool exists = false;
  1197   // ugly hack.  Walk the PATH variable...
  1198   char* unixpath = PR_GetEnv("PATH");
  1199   nsAutoCString path(unixpath);
  1201   const char* start_iter = path.BeginReading(start_iter);
  1202   const char* colon_iter = start_iter;
  1203   const char* end_iter = path.EndReading(end_iter);
  1205   while (start_iter != end_iter && !exists) {
  1206     while (colon_iter != end_iter && *colon_iter != ':') {
  1207       ++colon_iter;
  1209     localFile->InitWithNativePath(Substring(start_iter, colon_iter));
  1210     rv = localFile->AppendRelativePath(nsDependentString(platformAppPath));
  1211     // Failing AppendRelativePath is a bad thing - it should basically always
  1212     // succeed given a relative path. Show a warning if it does fail.
  1213     // To prevent infinite loops when it does fail, return at this point.
  1214     NS_ENSURE_SUCCESS(rv, rv);
  1215     localFile->Exists(&exists);
  1216     if (!exists) {
  1217       if (colon_iter == end_iter) {
  1218         break;
  1220       ++colon_iter;
  1221       start_iter = colon_iter;
  1225   if (exists) {
  1226     rv = NS_OK;
  1227   } else {
  1228     rv = NS_ERROR_NOT_AVAILABLE;
  1231   *aFile = localFile;
  1232   NS_IF_ADDREF(*aFile);
  1234   return rv;
  1237 already_AddRefed<nsMIMEInfoBase>
  1238 nsOSHelperAppService::GetFromExtension(const nsCString& aFileExt) {
  1239   // if the extension is empty, return immediately
  1240   if (aFileExt.IsEmpty())
  1241     return nullptr;
  1243   LOG(("Here we do an extension lookup for '%s'\n", aFileExt.get()));
  1245   nsAutoString majorType, minorType,
  1246                mime_types_description, mailcap_description,
  1247                handler, mozillaFlags;
  1249   nsresult rv = LookUpTypeAndDescription(NS_ConvertUTF8toUTF16(aFileExt),
  1250                                          majorType,
  1251                                          minorType,
  1252                                          mime_types_description,
  1253                                          true);
  1255   if (NS_FAILED(rv) || majorType.IsEmpty()) {
  1257 #ifdef MOZ_WIDGET_GTK
  1258     LOG(("Looking in GNOME registry\n"));
  1259     nsRefPtr<nsMIMEInfoBase> gnomeInfo =
  1260       nsGNOMERegistry::GetFromExtension(aFileExt);
  1261     if (gnomeInfo) {
  1262       LOG(("Got MIMEInfo from GNOME registry\n"));
  1263       return gnomeInfo.forget();
  1265 #endif
  1267     rv = LookUpTypeAndDescription(NS_ConvertUTF8toUTF16(aFileExt),
  1268                                   majorType,
  1269                                   minorType,
  1270                                   mime_types_description,
  1271                                   false);
  1274   if (NS_FAILED(rv))
  1275     return nullptr;
  1277   NS_LossyConvertUTF16toASCII asciiMajorType(majorType);
  1278   NS_LossyConvertUTF16toASCII asciiMinorType(minorType);
  1280   LOG(("Type/Description results:  majorType='%s', minorType='%s', description='%s'\n",
  1281           asciiMajorType.get(),
  1282           asciiMinorType.get(),
  1283           NS_LossyConvertUTF16toASCII(mime_types_description).get()));
  1285   if (majorType.IsEmpty() && minorType.IsEmpty()) {
  1286     // we didn't get a type mapping, so we can't do anything useful
  1287     return nullptr;
  1290   nsAutoCString mimeType(asciiMajorType + NS_LITERAL_CSTRING("/") + asciiMinorType);
  1291   nsRefPtr<nsMIMEInfoUnix> mimeInfo = new nsMIMEInfoUnix(mimeType);
  1293   mimeInfo->AppendExtension(aFileExt);
  1294   rv = LookUpHandlerAndDescription(majorType, minorType,
  1295                                    handler, mailcap_description,
  1296                                    mozillaFlags);
  1297   LOG(("Handler/Description results:  handler='%s', description='%s', mozillaFlags='%s'\n",
  1298           NS_LossyConvertUTF16toASCII(handler).get(),
  1299           NS_LossyConvertUTF16toASCII(mailcap_description).get(),
  1300           NS_LossyConvertUTF16toASCII(mozillaFlags).get()));
  1301   mailcap_description.Trim(" \t\"");
  1302   mozillaFlags.Trim(" \t");
  1303   if (!mime_types_description.IsEmpty()) {
  1304     mimeInfo->SetDescription(mime_types_description);
  1305   } else {
  1306     mimeInfo->SetDescription(mailcap_description);
  1309   if (NS_SUCCEEDED(rv) && handler.IsEmpty()) {
  1310     rv = NS_ERROR_NOT_AVAILABLE;
  1313   if (NS_SUCCEEDED(rv)) {
  1314     nsCOMPtr<nsIFile> handlerFile;
  1315     rv = GetFileTokenForPath(handler.get(), getter_AddRefs(handlerFile));
  1317     if (NS_SUCCEEDED(rv)) {
  1318       mimeInfo->SetDefaultApplication(handlerFile);
  1319       mimeInfo->SetPreferredAction(nsIMIMEInfo::useSystemDefault);
  1320       mimeInfo->SetDefaultDescription(handler);
  1324   if (NS_FAILED(rv)) {
  1325     mimeInfo->SetPreferredAction(nsIMIMEInfo::saveToDisk);
  1328   return mimeInfo.forget();
  1331 already_AddRefed<nsMIMEInfoBase>
  1332 nsOSHelperAppService::GetFromType(const nsCString& aMIMEType) {
  1333   // if the type is empty, return immediately
  1334   if (aMIMEType.IsEmpty())
  1335     return nullptr;
  1337   LOG(("Here we do a mimetype lookup for '%s'\n", aMIMEType.get()));
  1339   // extract the major and minor types
  1340   NS_ConvertASCIItoUTF16 mimeType(aMIMEType);
  1341   nsAString::const_iterator start_iter, end_iter,
  1342                             majorTypeStart, majorTypeEnd,
  1343                             minorTypeStart, minorTypeEnd;
  1345   mimeType.BeginReading(start_iter);
  1346   mimeType.EndReading(end_iter);
  1348   // XXX FIXME: add typeOptions parsing in here
  1349   nsresult rv = ParseMIMEType(start_iter, majorTypeStart, majorTypeEnd,
  1350                               minorTypeStart, minorTypeEnd, end_iter);
  1352   if (NS_FAILED(rv)) {
  1353     return nullptr;
  1356   nsDependentSubstring majorType(majorTypeStart, majorTypeEnd);
  1357   nsDependentSubstring minorType(minorTypeStart, minorTypeEnd);
  1359   // First check the user's private mailcap file
  1360   nsAutoString mailcap_description, handler, mozillaFlags;
  1361   DoLookUpHandlerAndDescription(majorType,
  1362                                 minorType,
  1363                                 handler,
  1364                                 mailcap_description,
  1365                                 mozillaFlags,
  1366                                 true);
  1368   LOG(("Private Handler/Description results:  handler='%s', description='%s'\n",
  1369           NS_LossyConvertUTF16toASCII(handler).get(),
  1370           NS_LossyConvertUTF16toASCII(mailcap_description).get()));
  1372 #ifdef MOZ_WIDGET_GTK
  1373   nsRefPtr<nsMIMEInfoBase> gnomeInfo;
  1374   if (handler.IsEmpty()) {
  1375     // No useful data yet.  Check the GNOME registry.  Unfortunately, newer
  1376     // GNOME versions no longer have type-to-extension mappings, so we might
  1377     // get back a MIMEInfo without any extensions set.  In that case we'll have
  1378     // to look in our mime.types files for the extensions.
  1379     LOG(("Looking in GNOME registry\n"));
  1380     gnomeInfo = nsGNOMERegistry::GetFromType(aMIMEType);
  1381     if (gnomeInfo && gnomeInfo->HasExtensions()) {
  1382       LOG(("Got MIMEInfo from GNOME registry, and it has extensions set\n"));
  1383       return gnomeInfo.forget();
  1386 #endif
  1388   // Now look up our extensions
  1389   nsAutoString extensions, mime_types_description;
  1390   LookUpExtensionsAndDescription(majorType,
  1391                                  minorType,
  1392                                  extensions,
  1393                                  mime_types_description);
  1395 #ifdef MOZ_WIDGET_GTK
  1396   if (gnomeInfo) {
  1397     LOG(("Got MIMEInfo from GNOME registry without extensions; setting them "
  1398          "to %s\n", NS_LossyConvertUTF16toASCII(extensions).get()));
  1400     NS_ASSERTION(!gnomeInfo->HasExtensions(), "How'd that happen?");
  1401     gnomeInfo->SetFileExtensions(NS_ConvertUTF16toUTF8(extensions));
  1402     return gnomeInfo.forget();
  1404 #endif
  1406   if (handler.IsEmpty()) {
  1407     DoLookUpHandlerAndDescription(majorType,
  1408                                   minorType,
  1409                                   handler,
  1410                                   mailcap_description,
  1411                                   mozillaFlags,
  1412                                   false);
  1415   if (handler.IsEmpty()) {
  1416     DoLookUpHandlerAndDescription(majorType,
  1417                                   NS_LITERAL_STRING("*"),
  1418                                   handler,
  1419                                   mailcap_description,
  1420                                   mozillaFlags,
  1421                                   true);
  1424   if (handler.IsEmpty()) {
  1425     DoLookUpHandlerAndDescription(majorType,
  1426                                   NS_LITERAL_STRING("*"),
  1427                                   handler,
  1428                                   mailcap_description,
  1429                                   mozillaFlags,
  1430                                   false);
  1433   LOG(("Handler/Description results:  handler='%s', description='%s', mozillaFlags='%s'\n",
  1434           NS_LossyConvertUTF16toASCII(handler).get(),
  1435           NS_LossyConvertUTF16toASCII(mailcap_description).get(),
  1436           NS_LossyConvertUTF16toASCII(mozillaFlags).get()));
  1438   mailcap_description.Trim(" \t\"");
  1439   mozillaFlags.Trim(" \t");
  1441   if (handler.IsEmpty() && extensions.IsEmpty() &&
  1442       mailcap_description.IsEmpty() && mime_types_description.IsEmpty()) {
  1443     // No real useful info
  1444     return nullptr;
  1447   nsRefPtr<nsMIMEInfoUnix> mimeInfo = new nsMIMEInfoUnix(aMIMEType);
  1449   mimeInfo->SetFileExtensions(NS_ConvertUTF16toUTF8(extensions));
  1450   if (! mime_types_description.IsEmpty()) {
  1451     mimeInfo->SetDescription(mime_types_description);
  1452   } else {
  1453     mimeInfo->SetDescription(mailcap_description);
  1456   rv = NS_ERROR_NOT_AVAILABLE;
  1457   nsCOMPtr<nsIFile> handlerFile;
  1458   if (!handler.IsEmpty()) {
  1459     rv = GetFileTokenForPath(handler.get(), getter_AddRefs(handlerFile));
  1462   if (NS_SUCCEEDED(rv)) {
  1463     mimeInfo->SetDefaultApplication(handlerFile);
  1464     mimeInfo->SetPreferredAction(nsIMIMEInfo::useSystemDefault);
  1465     mimeInfo->SetDefaultDescription(handler);
  1466   } else {
  1467     mimeInfo->SetPreferredAction(nsIMIMEInfo::saveToDisk);
  1470   return mimeInfo.forget();
  1474 already_AddRefed<nsIMIMEInfo>
  1475 nsOSHelperAppService::GetMIMEInfoFromOS(const nsACString& aType,
  1476                                         const nsACString& aFileExt,
  1477                                         bool       *aFound) {
  1478   *aFound = true;
  1479   nsRefPtr<nsMIMEInfoBase> retval = GetFromType(PromiseFlatCString(aType));
  1480   bool hasDefault = false;
  1481   if (retval)
  1482     retval->GetHasDefaultHandler(&hasDefault);
  1483   if (!retval || !hasDefault) {
  1484     nsRefPtr<nsMIMEInfoBase> miByExt = GetFromExtension(PromiseFlatCString(aFileExt));
  1485     // If we had no extension match, but a type match, use that
  1486     if (!miByExt && retval)
  1487       return retval.forget();
  1488     // If we had an extension match but no type match, set the mimetype and use
  1489     // it
  1490     if (!retval && miByExt) {
  1491       if (!aType.IsEmpty())
  1492         miByExt->SetMIMEType(aType);
  1493       miByExt.swap(retval);
  1495       return retval.forget();
  1497     // If we got nothing, make a new mimeinfo
  1498     if (!retval) {
  1499       *aFound = false;
  1500       retval = new nsMIMEInfoUnix(aType);
  1501       if (retval) {
  1502         if (!aFileExt.IsEmpty())
  1503           retval->AppendExtension(aFileExt);
  1506       return retval.forget();
  1509     // Copy the attributes of retval (mimeinfo from type) onto miByExt, to
  1510     // return it
  1511     // but reset to just collected mDefaultAppDescription (from ext)
  1512     nsAutoString byExtDefault;
  1513     miByExt->GetDefaultDescription(byExtDefault);
  1514     retval->SetDefaultDescription(byExtDefault);
  1515     retval->CopyBasicDataTo(miByExt);
  1517     miByExt.swap(retval);
  1519   return retval.forget();
  1522 NS_IMETHODIMP
  1523 nsOSHelperAppService::GetProtocolHandlerInfoFromOS(const nsACString &aScheme,
  1524                                                    bool *found,
  1525                                                    nsIHandlerInfo **_retval)
  1527   NS_ASSERTION(!aScheme.IsEmpty(), "No scheme was specified!");
  1529   // We must check that a registered handler exists so that gnome_url_show
  1530   // doesn't fallback to gnomevfs.
  1531   // See nsGNOMERegistry::LoadURL and bug 389632.
  1532   nsresult rv = OSProtocolHandlerExists(nsPromiseFlatCString(aScheme).get(),
  1533                                         found);
  1534   if (NS_FAILED(rv))
  1535     return rv;
  1537   nsMIMEInfoUnix *handlerInfo =
  1538     new nsMIMEInfoUnix(aScheme, nsMIMEInfoBase::eProtocolInfo);
  1539   NS_ENSURE_TRUE(handlerInfo, NS_ERROR_OUT_OF_MEMORY);
  1540   NS_ADDREF(*_retval = handlerInfo);
  1542   if (!*found) {
  1543     // Code that calls this requires an object regardless if the OS has
  1544     // something for us, so we return the empty object.
  1545     return NS_OK;
  1548   nsAutoString desc;
  1549   GetApplicationDescription(aScheme, desc);
  1550   handlerInfo->SetDefaultDescription(desc);
  1552   return NS_OK;

mercurial