Back to index

lightning-sunbird  0.9+nobinonly
nsOSHelperAppService.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 3; indent-tabs-mode: nil; c-basic-offset: 2 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is the Mozilla browser.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications, Inc.
00020  * Portions created by the Initial Developer are Copyright (C) 1999
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Scott MacGregor <mscott@netscape.com>
00025  *   Boris Zbarsky <bzbarsky@mit.edu>  (Added mailcap and mime.types support)
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either of the GNU General Public License Version 2 or later (the "GPL"),
00029  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 #include <sys/types.h>
00042 #include <sys/stat.h>
00043 
00044 #include "nsOSHelperAppService.h"
00045 #ifdef MOZ_WIDGET_GTK2
00046 #include "nsGNOMERegistry.h"
00047 #endif
00048 #include "nsISupports.h"
00049 #include "nsString.h"
00050 #include "nsReadableUtils.h"
00051 #include "nsUnicharUtils.h"
00052 #include "nsXPIDLString.h"
00053 #include "nsIURL.h"
00054 #include "nsIFileStreams.h"
00055 #include "nsILineInputStream.h"
00056 #include "nsILocalFile.h"
00057 #include "nsIProcess.h"
00058 #include "nsIPrefService.h"
00059 #include "nsIPrefBranch.h"
00060 #include "nsNetCID.h"
00061 #include "nsXPCOM.h"
00062 #include "nsISupportsPrimitives.h"
00063 #include "nsHashtable.h"
00064 #include "nsCRT.h"
00065 #include "nsDirectoryServiceDefs.h"
00066 #include "nsDirectoryServiceUtils.h"
00067 #include "prenv.h"      // for PR_GetEnv()
00068 #include "nsAutoPtr.h"
00069 #include <stdlib.h>         // for system()
00070 
00071 #define LOG(args) PR_LOG(mLog, PR_LOG_DEBUG, args)
00072 #define LOG_ENABLED() PR_LOG_TEST(mLog, PR_LOG_DEBUG)
00073 
00074 static nsresult
00075 FindSemicolon(nsAString::const_iterator& aSemicolon_iter,
00076               const nsAString::const_iterator& aEnd_iter);
00077 static nsresult
00078 ParseMIMEType(const nsAString::const_iterator& aStart_iter,
00079               nsAString::const_iterator& aMajorTypeStart,
00080               nsAString::const_iterator& aMajorTypeEnd,
00081               nsAString::const_iterator& aMinorTypeStart,
00082               nsAString::const_iterator& aMinorTypeEnd,
00083               const nsAString::const_iterator& aEnd_iter);
00084 
00085 inline PRBool
00086 IsNetscapeFormat(const nsACString& aBuffer);
00087 
00088 nsOSHelperAppService::nsOSHelperAppService() : nsExternalHelperAppService()
00089 {
00090 #ifdef MOZ_WIDGET_GTK2
00091   nsGNOMERegistry::Startup();
00092 #endif
00093 
00094   mode_t mask = umask(0777);
00095   umask(mask);
00096   mPermissions = 0666 & ~mask;
00097 }
00098 
00099 nsOSHelperAppService::~nsOSHelperAppService()
00100 {}
00101 
00102 /*
00103  * Take a command with all the mailcap escapes in it and unescape it
00104  * Ideally this needs the mime type, mime type options, and location of the
00105  * temporary file, but this last can't be got from here
00106  */
00107 // static
00108 nsresult
00109 nsOSHelperAppService::UnescapeCommand(const nsAString& aEscapedCommand,
00110                                       const nsAString& aMajorType,
00111                                       const nsAString& aMinorType,
00112                                       nsHashtable& aTypeOptions,
00113                                       nsACString& aUnEscapedCommand) {
00114   LOG(("-- UnescapeCommand"));
00115   LOG(("Command to escape: '%s'\n",
00116        NS_LossyConvertUCS2toASCII(aEscapedCommand).get()));
00117   //  XXX This function will need to get the mime type and various stuff like that being passed in to work properly
00118   
00119   LOG(("UnescapeCommand really needs some work -- it should actually do some unescaping\n"));
00120 
00121   CopyUTF16toUTF8(aEscapedCommand, aUnEscapedCommand);
00122   LOG(("Escaped command: '%s'\n",
00123        PromiseFlatCString(aUnEscapedCommand).get()));
00124   return NS_OK;
00125 }
00126 
00127 /* Put aSemicolon_iter at the first non-escaped semicolon after
00128  * aStart_iter but before aEnd_iter
00129  */
00130 
00131 static nsresult
00132 FindSemicolon(nsAString::const_iterator& aSemicolon_iter,
00133               const nsAString::const_iterator& aEnd_iter) {
00134   PRBool semicolonFound = PR_FALSE;
00135   while (aSemicolon_iter != aEnd_iter && !semicolonFound) {
00136     switch(*aSemicolon_iter) {
00137     case '\\':
00138       aSemicolon_iter.advance(2);
00139       break;
00140     case ';':
00141       semicolonFound = PR_TRUE;
00142       break;
00143     default:
00144       ++aSemicolon_iter;
00145       break;
00146     }
00147   }
00148   return NS_OK;
00149 }
00150 
00151 static nsresult
00152 ParseMIMEType(const nsAString::const_iterator& aStart_iter,
00153               nsAString::const_iterator& aMajorTypeStart,
00154               nsAString::const_iterator& aMajorTypeEnd,
00155               nsAString::const_iterator& aMinorTypeStart,
00156               nsAString::const_iterator& aMinorTypeEnd,
00157               const nsAString::const_iterator& aEnd_iter) {
00158   nsAString::const_iterator iter(aStart_iter);
00159   
00160   // skip leading whitespace
00161   while (iter != aEnd_iter && nsCRT::IsAsciiSpace(*iter)) {
00162     ++iter;
00163   }
00164 
00165   if (iter == aEnd_iter) {
00166     return NS_ERROR_INVALID_ARG;
00167   }
00168   
00169   aMajorTypeStart = iter;
00170 
00171   // find major/minor separator ('/')
00172   while (iter != aEnd_iter && *iter != '/') {
00173     ++iter;
00174   }
00175   
00176   if (iter == aEnd_iter) {
00177     return NS_ERROR_INVALID_ARG;
00178   }
00179 
00180   aMajorTypeEnd = iter;
00181   
00182   // skip '/'
00183   ++iter;
00184 
00185   if (iter == aEnd_iter) {
00186     return NS_ERROR_INVALID_ARG;
00187   }
00188 
00189   aMinorTypeStart = iter;
00190 
00191   // find end of minor type, delimited by whitespace or ';'
00192   while (iter != aEnd_iter && !nsCRT::IsAsciiSpace(*iter) && *iter != ';') {
00193     ++iter;
00194   }
00195 
00196   aMinorTypeEnd = iter;
00197 
00198   return NS_OK;
00199 }
00200 
00201 // static
00202 nsresult
00203 nsOSHelperAppService::GetFileLocation(const char* aPrefName,
00204                                       const char* aEnvVarName,
00205                                       PRUnichar** aFileLocation) {
00206   LOG(("-- GetFileLocation.  Pref: '%s'  EnvVar: '%s'\n",
00207        aPrefName,
00208        aEnvVarName));
00209   NS_PRECONDITION(aPrefName, "Null pref name passed; don't do that!");
00210   
00211   nsresult rv;
00212   *aFileLocation = nsnull;
00213   /* The lookup order is:
00214      1) user pref
00215      2) env var
00216      3) pref
00217   */
00218   nsCOMPtr<nsIPrefService> prefService(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
00219   NS_ENSURE_SUCCESS(rv, rv);
00220   nsCOMPtr<nsIPrefBranch> prefBranch;
00221   rv = prefService->GetBranch(nsnull, getter_AddRefs(prefBranch));
00222   NS_ENSURE_SUCCESS(rv, rv);
00223 
00224   /*
00225     If we have an env var we should check whether the pref is a user
00226     pref.  If we do not, we don't care.
00227   */
00228   nsCOMPtr<nsISupportsString> prefFileName;
00229   PRBool isUserPref = PR_FALSE;
00230   prefBranch->PrefHasUserValue(aPrefName, &isUserPref);
00231   if (isUserPref) {
00232     rv = prefBranch->GetComplexValue(aPrefName,
00233                                      NS_GET_IID(nsISupportsString),
00234                                      getter_AddRefs(prefFileName));
00235     if (NS_SUCCEEDED(rv)) {
00236       return prefFileName->ToString(aFileLocation);
00237     }
00238   }
00239 
00240   if (aEnvVarName && *aEnvVarName) {
00241     char* prefValue = PR_GetEnv(aEnvVarName);
00242     if (prefValue && *prefValue) {
00243       // the pref is in the system charset and it's a filepath... The
00244       // natural way to do the charset conversion is by just initing
00245       // an nsIFile with the native path and asking it for the Unicode
00246       // version.
00247       nsCOMPtr<nsILocalFile> file(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
00248       NS_ENSURE_SUCCESS(rv, rv);
00249 
00250       rv = file->InitWithNativePath(nsDependentCString(prefValue));
00251       NS_ENSURE_SUCCESS(rv, rv);
00252 
00253       nsAutoString unicodePath;
00254       rv = file->GetPath(unicodePath);
00255       NS_ENSURE_SUCCESS(rv, rv);
00256       
00257       *aFileLocation = ToNewUnicode(unicodePath);
00258       if (!*aFileLocation)
00259         return NS_ERROR_OUT_OF_MEMORY;
00260       return NS_OK;
00261     }
00262   }
00263   
00264   rv = prefBranch->GetComplexValue(aPrefName,
00265                                    NS_GET_IID(nsISupportsString),
00266                                    getter_AddRefs(prefFileName));
00267   if (NS_SUCCEEDED(rv)) {
00268     return prefFileName->ToString(aFileLocation);
00269   }
00270   
00271   return rv;
00272 }
00273 
00274 
00275 /* Get the mime.types file names from prefs and look up info in them
00276    based on extension */
00277 // static
00278 nsresult
00279 nsOSHelperAppService::LookUpTypeAndDescription(const nsAString& aFileExtension,
00280                                                nsAString& aMajorType,
00281                                                nsAString& aMinorType,
00282                                                nsAString& aDescription,
00283                                                PRBool aUserData) {
00284   LOG(("-- LookUpTypeAndDescription for extension '%s'\n",
00285        NS_LossyConvertUCS2toASCII(aFileExtension).get()));
00286   nsresult rv = NS_OK;
00287   nsXPIDLString mimeFileName;
00288 
00289   const char* filenamePref = aUserData ?
00290     "helpers.private_mime_types_file" : "helpers.global_mime_types_file";
00291   
00292   rv = GetFileLocation(filenamePref,
00293                        nsnull,
00294                        getter_Copies(mimeFileName));
00295   if (NS_SUCCEEDED(rv) && !mimeFileName.IsEmpty()) {
00296     rv = GetTypeAndDescriptionFromMimetypesFile(mimeFileName,
00297                                                 aFileExtension,
00298                                                 aMajorType,
00299                                                 aMinorType,
00300                                                 aDescription);
00301   } else {
00302     rv = NS_ERROR_NOT_AVAILABLE;
00303   }
00304 
00305   return rv;
00306 }
00307 
00308 inline PRBool
00309 IsNetscapeFormat(const nsACString& aBuffer) {
00310   return StringBeginsWith(aBuffer, NS_LITERAL_CSTRING("#--Netscape Communications Corporation MIME Information")) ||
00311          StringBeginsWith(aBuffer, NS_LITERAL_CSTRING("#--MCOM MIME Information"));
00312 }
00313 
00314 /*
00315  * Create a file stream and line input stream for the filename.
00316  * Leaves the first line of the file in aBuffer and sets the format to
00317  *  PR_TRUE for netscape files and false for normail ones
00318  */
00319 // static
00320 nsresult
00321 nsOSHelperAppService::CreateInputStream(const nsAString& aFilename,
00322                                         nsIFileInputStream ** aFileInputStream,
00323                                         nsILineInputStream ** aLineInputStream,
00324                                         nsACString& aBuffer,
00325                                         PRBool * aNetscapeFormat,
00326                                         PRBool * aMore) {
00327   LOG(("-- CreateInputStream"));
00328   nsresult rv = NS_OK;
00329 
00330   nsCOMPtr<nsILocalFile> file(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
00331   if (NS_FAILED(rv))
00332     return rv;
00333   rv = file->InitWithPath(aFilename);
00334   if (NS_FAILED(rv))
00335     return rv;
00336 
00337   nsCOMPtr<nsIFileInputStream> fileStream(do_CreateInstance(NS_LOCALFILEINPUTSTREAM_CONTRACTID, &rv));
00338   if (NS_FAILED(rv))
00339     return rv;
00340   rv = fileStream->Init(file, -1, -1, PR_FALSE);
00341   if (NS_FAILED(rv))
00342     return rv;
00343 
00344   nsCOMPtr<nsILineInputStream> lineStream(do_QueryInterface(fileStream, &rv));
00345 
00346   if (NS_FAILED(rv)) {
00347     LOG(("Interface trouble in stream land!"));
00348     return rv;
00349   }
00350 
00351   rv = lineStream->ReadLine(aBuffer, aMore);
00352   if (NS_FAILED(rv)) {
00353     fileStream->Close();
00354     return rv;
00355   }
00356 
00357   *aNetscapeFormat = IsNetscapeFormat(aBuffer);
00358 
00359   *aFileInputStream = fileStream;
00360   NS_ADDREF(*aFileInputStream);
00361   *aLineInputStream = lineStream;
00362   NS_ADDREF(*aLineInputStream);
00363 
00364   return NS_OK;
00365 }
00366 
00367 /* Open the file, read the first line, decide what type of file it is,
00368    then get info based on extension */
00369 // static
00370 nsresult
00371 nsOSHelperAppService::GetTypeAndDescriptionFromMimetypesFile(const nsAString& aFilename,
00372                                                              const nsAString& aFileExtension,
00373                                                              nsAString& aMajorType,
00374                                                              nsAString& aMinorType,
00375                                                              nsAString& aDescription) {
00376   LOG(("-- GetTypeAndDescriptionFromMimetypesFile\n"));
00377   LOG(("Getting type and description from types file '%s'\n",
00378        NS_LossyConvertUCS2toASCII(aFilename).get()));
00379   LOG(("Using extension '%s'\n",
00380        NS_LossyConvertUCS2toASCII(aFileExtension).get()));
00381   nsresult rv = NS_OK;
00382   nsCOMPtr<nsIFileInputStream> mimeFile;
00383   nsCOMPtr<nsILineInputStream> mimeTypes;
00384   PRBool netscapeFormat;
00385   nsAutoString buf;
00386   nsCAutoString cBuf;
00387   PRBool more = PR_FALSE;
00388   rv = CreateInputStream(aFilename, getter_AddRefs(mimeFile), getter_AddRefs(mimeTypes),
00389                          cBuf, &netscapeFormat, &more);
00390 
00391   if (NS_FAILED(rv)) {
00392     return rv;
00393   }
00394   
00395   nsAutoString extensions;
00396   nsString entry;
00397   entry.SetCapacity(100);
00398   nsAString::const_iterator majorTypeStart, majorTypeEnd,
00399                             minorTypeStart, minorTypeEnd,
00400                             descriptionStart, descriptionEnd;
00401 
00402   do {
00403     CopyASCIItoUTF16(cBuf, buf);
00404     // read through, building up an entry.  If we finish an entry, check for
00405     // a match and return out of the loop if we match
00406 
00407     // skip comments and empty lines
00408     if (!buf.IsEmpty() && buf.First() != '#') {
00409       entry.Append(buf);
00410       if (entry.Last() == '\\') {
00411         entry.Truncate(entry.Length() - 1);
00412         entry.Append(PRUnichar(' '));  // in case there is no trailing whitespace on this line
00413       } else {  // we have a full entry
00414         LOG(("Current entry: '%s'\n",
00415              NS_LossyConvertUCS2toASCII(entry).get()));
00416         if (netscapeFormat) {
00417           rv = ParseNetscapeMIMETypesEntry(entry,
00418                                            majorTypeStart, majorTypeEnd,
00419                                            minorTypeStart, minorTypeEnd,
00420                                            extensions,
00421                                            descriptionStart, descriptionEnd);
00422           if (NS_FAILED(rv)) {
00423             // We sometimes get things like RealPlayer appending
00424             // "normal" entries to "Netscape" .mime.types files.  Try
00425             // to handle that.  Bug 106381.
00426             LOG(("Bogus entry; trying 'normal' mode\n"));
00427             rv = ParseNormalMIMETypesEntry(entry,
00428                                            majorTypeStart, majorTypeEnd,
00429                                            minorTypeStart, minorTypeEnd,
00430                                            extensions,
00431                                            descriptionStart, descriptionEnd);
00432           }
00433         } else {
00434           rv = ParseNormalMIMETypesEntry(entry,
00435                                          majorTypeStart, majorTypeEnd,
00436                                          minorTypeStart, minorTypeEnd,
00437                                          extensions,
00438                                          descriptionStart, descriptionEnd);
00439           if (NS_FAILED(rv)) {
00440             // We sometimes get things like StarOffice prepending
00441             // "normal" entries to "Netscape" .mime.types files.  Try
00442             // to handle that.  Bug 136670.
00443             LOG(("Bogus entry; trying 'Netscape' mode\n"));
00444             rv = ParseNetscapeMIMETypesEntry(entry,
00445                                              majorTypeStart, majorTypeEnd,
00446                                              minorTypeStart, minorTypeEnd,
00447                                              extensions,
00448                                              descriptionStart, descriptionEnd);
00449           }
00450         }
00451 
00452         if (NS_SUCCEEDED(rv)) { // entry parses
00453           nsAString::const_iterator start, end;
00454           extensions.BeginReading(start);
00455           extensions.EndReading(end);
00456           nsAString::const_iterator iter(start);
00457 
00458           while (start != end) {
00459             FindCharInReadable(',', iter, end);
00460             if (Substring(start, iter).Equals(aFileExtension,
00461                                               nsCaseInsensitiveStringComparator())) {
00462               // it's a match.  Assign the type and description and run
00463               aMajorType.Assign(Substring(majorTypeStart, majorTypeEnd));
00464               aMinorType.Assign(Substring(minorTypeStart, minorTypeEnd));
00465               aDescription.Assign(Substring(descriptionStart, descriptionEnd));
00466               mimeFile->Close();
00467               return NS_OK;
00468             }
00469             if (iter != end) {
00470               ++iter;
00471             }
00472             start = iter;
00473           }
00474         } else {
00475           LOG(("Failed to parse entry: %s\n", NS_LossyConvertUCS2toASCII(entry).get()));
00476         }
00477         // truncate the entry for the next iteration
00478         entry.Truncate();
00479       }
00480     }
00481     if (!more) {
00482       rv = NS_ERROR_NOT_AVAILABLE;
00483       break;
00484     }
00485     // read the next line
00486     rv = mimeTypes->ReadLine(cBuf, &more);
00487   } while (NS_SUCCEEDED(rv));
00488 
00489   mimeFile->Close();
00490   return rv;
00491 }
00492 
00493 /* Get the mime.types file names from prefs and look up info in them
00494    based on mimetype  */
00495 // static
00496 nsresult
00497 nsOSHelperAppService::LookUpExtensionsAndDescription(const nsAString& aMajorType,
00498                                                      const nsAString& aMinorType,
00499                                                      nsAString& aFileExtensions,
00500                                                      nsAString& aDescription) {
00501   LOG(("-- LookUpExtensionsAndDescription for type '%s/%s'\n",
00502        NS_LossyConvertUCS2toASCII(aMajorType).get(),
00503        NS_LossyConvertUCS2toASCII(aMinorType).get()));
00504   nsresult rv = NS_OK;
00505   nsXPIDLString mimeFileName;
00506 
00507   rv = GetFileLocation("helpers.private_mime_types_file",
00508                        nsnull,
00509                        getter_Copies(mimeFileName));
00510   if (NS_SUCCEEDED(rv) && !mimeFileName.IsEmpty()) {
00511     rv = GetExtensionsAndDescriptionFromMimetypesFile(mimeFileName,
00512                                                       aMajorType,
00513                                                       aMinorType,
00514                                                       aFileExtensions,
00515                                                       aDescription);
00516   } else {
00517     rv = NS_ERROR_NOT_AVAILABLE;
00518   }
00519   if (NS_FAILED(rv) || aFileExtensions.IsEmpty()) {
00520     rv = GetFileLocation("helpers.global_mime_types_file",
00521                          nsnull,
00522                          getter_Copies(mimeFileName));
00523     if (NS_SUCCEEDED(rv) && !mimeFileName.IsEmpty()) {
00524       rv = GetExtensionsAndDescriptionFromMimetypesFile(mimeFileName,
00525                                                         aMajorType,
00526                                                         aMinorType,
00527                                                         aFileExtensions,
00528                                                         aDescription);
00529     } else {
00530       rv = NS_ERROR_NOT_AVAILABLE;
00531     }
00532   }
00533   return rv;
00534 }
00535 
00536 /* Open the file, read the first line, decide what type of file it is,
00537    then get info based on extension */
00538 // static
00539 nsresult
00540 nsOSHelperAppService::GetExtensionsAndDescriptionFromMimetypesFile(const nsAString& aFilename,
00541                                                                    const nsAString& aMajorType,
00542                                                                    const nsAString& aMinorType,
00543                                                                    nsAString& aFileExtensions,
00544                                                                    nsAString& aDescription) {
00545   LOG(("-- GetExtensionsAndDescriptionFromMimetypesFile\n"));
00546   LOG(("Getting extensions and description from types file '%s'\n",
00547        NS_LossyConvertUCS2toASCII(aFilename).get()));
00548   LOG(("Using type '%s/%s'\n",
00549        NS_LossyConvertUCS2toASCII(aMajorType).get(),
00550        NS_LossyConvertUCS2toASCII(aMinorType).get()));
00551 
00552   nsresult rv = NS_OK;
00553   nsCOMPtr<nsIFileInputStream> mimeFile;
00554   nsCOMPtr<nsILineInputStream> mimeTypes;
00555   PRBool netscapeFormat;
00556   nsCAutoString cBuf;
00557   nsAutoString buf;
00558   PRBool more = PR_FALSE;
00559   rv = CreateInputStream(aFilename, getter_AddRefs(mimeFile), getter_AddRefs(mimeTypes),
00560                          cBuf, &netscapeFormat, &more);
00561 
00562   if (NS_FAILED(rv)) {
00563     return rv;
00564   }
00565   
00566   nsAutoString extensions;
00567   nsString entry;
00568   entry.SetCapacity(100);
00569   nsAString::const_iterator majorTypeStart, majorTypeEnd,
00570                             minorTypeStart, minorTypeEnd,
00571                             descriptionStart, descriptionEnd;
00572   
00573   do {
00574     CopyASCIItoUTF16(cBuf, buf);
00575     // read through, building up an entry.  If we finish an entry, check for
00576     // a match and return out of the loop if we match
00577 
00578     // skip comments and empty lines
00579     if (!buf.IsEmpty() && buf.First() != '#') {
00580       entry.Append(buf);
00581       if (entry.Last() == '\\') {
00582         entry.Truncate(entry.Length() - 1);
00583         entry.Append(PRUnichar(' '));  // in case there is no trailing whitespace on this line
00584       } else {  // we have a full entry
00585         LOG(("Current entry: '%s'\n",
00586              NS_LossyConvertUCS2toASCII(entry).get()));
00587         if (netscapeFormat) {
00588           rv = ParseNetscapeMIMETypesEntry(entry,
00589                                            majorTypeStart, majorTypeEnd,
00590                                            minorTypeStart, minorTypeEnd,
00591                                            extensions,
00592                                            descriptionStart, descriptionEnd);
00593           
00594           if (NS_FAILED(rv)) {
00595             // We sometimes get things like RealPlayer appending
00596             // "normal" entries to "Netscape" .mime.types files.  Try
00597             // to handle that.  Bug 106381.
00598             LOG(("Bogus entry; trying 'normal' mode\n"));
00599             rv = ParseNormalMIMETypesEntry(entry,
00600                                            majorTypeStart, majorTypeEnd,
00601                                            minorTypeStart, minorTypeEnd,
00602                                            extensions,
00603                                            descriptionStart, descriptionEnd);
00604           }
00605         } else {
00606           rv = ParseNormalMIMETypesEntry(entry,
00607                                          majorTypeStart, majorTypeEnd,
00608                                          minorTypeStart,
00609                                          minorTypeEnd, extensions,
00610                                          descriptionStart, descriptionEnd);
00611           
00612           if (NS_FAILED(rv)) {
00613             // We sometimes get things like StarOffice prepending
00614             // "normal" entries to "Netscape" .mime.types files.  Try
00615             // to handle that.  Bug 136670.
00616             LOG(("Bogus entry; trying 'Netscape' mode\n"));
00617             rv = ParseNetscapeMIMETypesEntry(entry,
00618                                              majorTypeStart, majorTypeEnd,
00619                                              minorTypeStart, minorTypeEnd,
00620                                              extensions,
00621                                              descriptionStart, descriptionEnd);
00622           }
00623         }
00624         
00625         if (NS_SUCCEEDED(rv) &&
00626             Substring(majorTypeStart,
00627                       majorTypeEnd).Equals(aMajorType,
00628                                            nsCaseInsensitiveStringComparator())&&
00629             Substring(minorTypeStart,
00630                       minorTypeEnd).Equals(aMinorType,
00631                                            nsCaseInsensitiveStringComparator())) {
00632           // it's a match
00633           aFileExtensions.Assign(extensions);
00634           aDescription.Assign(Substring(descriptionStart, descriptionEnd));
00635           mimeFile->Close();
00636           return NS_OK;
00637         } else if (NS_FAILED(rv)) {
00638           LOG(("Failed to parse entry: %s\n", NS_LossyConvertUCS2toASCII(entry).get()));
00639         }
00640         
00641         entry.Truncate();
00642       }
00643     }
00644     if (!more) {
00645       rv = NS_ERROR_NOT_AVAILABLE;
00646       break;
00647     }
00648     // read the next line
00649     rv = mimeTypes->ReadLine(cBuf, &more);
00650   } while (NS_SUCCEEDED(rv));
00651 
00652   mimeFile->Close();
00653   return rv;
00654 }
00655 
00656 /*
00657  * This parses a Netscape format mime.types entry.  There are two
00658  * possible formats:
00659  *
00660  * type=foo/bar; options exts="baz" description="Some type"
00661  *
00662  * and
00663  *  
00664  * type=foo/bar; options description="Some type" exts="baz"
00665  */
00666 // static
00667 nsresult
00668 nsOSHelperAppService::ParseNetscapeMIMETypesEntry(const nsAString& aEntry,
00669                                                   nsAString::const_iterator& aMajorTypeStart,
00670                                                   nsAString::const_iterator& aMajorTypeEnd,
00671                                                   nsAString::const_iterator& aMinorTypeStart,
00672                                                   nsAString::const_iterator& aMinorTypeEnd,
00673                                                   nsAString& aExtensions,
00674                                                   nsAString::const_iterator& aDescriptionStart,
00675                                                   nsAString::const_iterator& aDescriptionEnd) {
00676   LOG(("-- ParseNetscapeMIMETypesEntry\n"));
00677   NS_ASSERTION(!aEntry.IsEmpty(), "Empty Netscape MIME types entry being parsed.");
00678   
00679   nsAString::const_iterator start_iter, end_iter, match_start, match_end;
00680 
00681   aEntry.BeginReading(start_iter);
00682   aEntry.EndReading(end_iter);
00683   
00684   // skip trailing whitespace
00685   do {
00686     --end_iter;
00687   } while (end_iter != start_iter &&
00688            nsCRT::IsAsciiSpace(*end_iter));
00689   // if we're pointing to a quote, don't advance -- we don't want to
00690   // include the quote....
00691   if (*end_iter != '"')
00692     ++end_iter;
00693   match_start = start_iter;
00694   match_end = end_iter;
00695   
00696   // Get the major and minor types
00697   // First the major type
00698   if (! FindInReadable(NS_LITERAL_STRING("type="), match_start, match_end)) {
00699     return NS_ERROR_FAILURE;
00700   }
00701   
00702   match_start = match_end;
00703   
00704   while (match_end != end_iter &&
00705          *match_end != '/') {
00706     ++match_end;
00707   }
00708   if (match_end == end_iter) {
00709     return NS_ERROR_FAILURE;
00710   }
00711   
00712   aMajorTypeStart = match_start;
00713   aMajorTypeEnd = match_end;
00714 
00715   // now the minor type
00716   if (++match_end == end_iter) {
00717     return NS_ERROR_FAILURE;
00718   }
00719   
00720   match_start = match_end;
00721   
00722   while (match_end != end_iter &&
00723          !nsCRT::IsAsciiSpace(*match_end) &&
00724          *match_end != ';') {
00725     ++match_end;
00726   }
00727   if (match_end == end_iter) {
00728     return NS_ERROR_FAILURE;
00729   }
00730   
00731   aMinorTypeStart = match_start;
00732   aMinorTypeEnd = match_end;
00733   
00734   // ignore everything up to the end of the mime type from here on
00735   start_iter = match_end;
00736   
00737   // get the extensions
00738   match_start = match_end;
00739   match_end = end_iter;
00740   if (FindInReadable(NS_LITERAL_STRING("exts="), match_start, match_end)) {
00741     nsAString::const_iterator extStart, extEnd;
00742 
00743     if (match_end == end_iter ||
00744         (*match_end == '"' && ++match_end == end_iter)) {
00745       return NS_ERROR_FAILURE;
00746     }
00747   
00748     extStart = match_end;
00749     match_start = extStart;
00750     match_end = end_iter;
00751     if (FindInReadable(NS_LITERAL_STRING("desc=\""), match_start, match_end)) {
00752       // exts= before desc=, so we have to find the actual end of the extensions
00753       extEnd = match_start;
00754       if (extEnd == extStart) {
00755         return NS_ERROR_FAILURE;
00756       }
00757     
00758       do {
00759         --extEnd;
00760       } while (extEnd != extStart &&
00761                nsCRT::IsAsciiSpace(*extEnd));
00762       
00763       if (extEnd != extStart && *extEnd == '"') {
00764         --extEnd;
00765       }
00766     } else {
00767       // desc= before exts=, so we can use end_iter as the end of the extensions
00768       extEnd = end_iter;
00769     }
00770     aExtensions = Substring(extStart, extEnd);
00771   } else {
00772     // no extensions
00773     aExtensions.Truncate();
00774   }
00775 
00776   // get the description
00777   match_start = start_iter;
00778   match_end = end_iter;
00779   if (FindInReadable(NS_LITERAL_STRING("desc=\""), match_start, match_end)) {
00780     aDescriptionStart = match_end;
00781     match_start = aDescriptionStart;
00782     match_end = end_iter;
00783     if (FindInReadable(NS_LITERAL_STRING("exts="), match_start, match_end)) {
00784       // exts= after desc=, so have to find actual end of description
00785       aDescriptionEnd = match_start;
00786       if (aDescriptionEnd == aDescriptionStart) {
00787         return NS_ERROR_FAILURE;
00788       }
00789       
00790       do {
00791         --aDescriptionEnd;
00792       } while (aDescriptionEnd != aDescriptionStart &&
00793                nsCRT::IsAsciiSpace(*aDescriptionEnd));
00794       
00795       if (aDescriptionStart != aDescriptionStart && *aDescriptionEnd == '"') {
00796         --aDescriptionEnd;
00797       }
00798     } else {
00799       // desc= after exts=, so use end_iter for the description end
00800       aDescriptionEnd = end_iter;
00801     }
00802   } else {
00803     // no description
00804     aDescriptionStart = start_iter;
00805     aDescriptionEnd = start_iter;
00806   }
00807 
00808   return NS_OK;
00809 }
00810 
00811 /*
00812  * This parses a normal format mime.types entry.  The format is:
00813  *
00814  * major/minor    ext1 ext2 ext3
00815  */
00816 // static
00817 nsresult
00818 nsOSHelperAppService::ParseNormalMIMETypesEntry(const nsAString& aEntry,
00819                                                 nsAString::const_iterator& aMajorTypeStart,
00820                                                 nsAString::const_iterator& aMajorTypeEnd,
00821                                                 nsAString::const_iterator& aMinorTypeStart,
00822                                                 nsAString::const_iterator& aMinorTypeEnd,
00823                                                 nsAString& aExtensions,
00824                                                 nsAString::const_iterator& aDescriptionStart,
00825                                                 nsAString::const_iterator& aDescriptionEnd) {
00826   LOG(("-- ParseNormalMIMETypesEntry\n"));
00827   NS_ASSERTION(!aEntry.IsEmpty(), "Empty Normal MIME types entry being parsed.");
00828 
00829   nsAString::const_iterator start_iter, end_iter, iter;
00830   
00831   aEntry.BeginReading(start_iter);
00832   aEntry.EndReading(end_iter);
00833 
00834   // no description
00835   aDescriptionStart = start_iter;
00836   aDescriptionEnd = start_iter;
00837 
00838   // skip leading whitespace
00839   while (start_iter != end_iter && nsCRT::IsAsciiSpace(*start_iter)) {
00840     ++start_iter;
00841   }
00842   if (start_iter == end_iter) {
00843     return NS_ERROR_FAILURE;
00844   }
00845   // skip trailing whitespace
00846   do {
00847     --end_iter;
00848   } while (end_iter != start_iter && nsCRT::IsAsciiSpace(*end_iter));
00849            
00850   ++end_iter; // point to first whitespace char (or to end of string)
00851   iter = start_iter;
00852 
00853   // get the major type
00854   if (! FindCharInReadable('/', iter, end_iter))
00855     return NS_ERROR_FAILURE;
00856 
00857   nsAString::const_iterator equals_sign_iter(start_iter);
00858   if (FindCharInReadable('=', equals_sign_iter, iter))
00859     return NS_ERROR_FAILURE; // see bug 136670
00860   
00861   aMajorTypeStart = start_iter;
00862   aMajorTypeEnd = iter;
00863   
00864   // get the minor type
00865   if (++iter == end_iter) {
00866     return NS_ERROR_FAILURE;
00867   }
00868   start_iter = iter;
00869 
00870   while (iter != end_iter && !nsCRT::IsAsciiSpace(*iter)) { 
00871     ++iter;
00872   }
00873   aMinorTypeStart = start_iter;
00874   aMinorTypeEnd = iter;
00875 
00876   // get the extensions
00877   aExtensions.Truncate();
00878   while (iter != end_iter) {
00879     while (iter != end_iter && nsCRT::IsAsciiSpace(*iter)) {
00880       ++iter;
00881     }
00882 
00883     start_iter = iter;
00884     while (iter != end_iter && !nsCRT::IsAsciiSpace(*iter)) {
00885       ++iter;
00886     }
00887     aExtensions.Append(Substring(start_iter, iter));
00888     if (iter != end_iter) { // not the last extension
00889       aExtensions.Append(PRUnichar(','));
00890     }
00891   }
00892 
00893   return NS_OK;
00894 }
00895 
00896 // static
00897 nsresult
00898 nsOSHelperAppService::LookUpHandlerAndDescription(const nsAString& aMajorType,
00899                                                   const nsAString& aMinorType,
00900                                                   nsHashtable& aTypeOptions,
00901                                                   nsAString& aHandler,
00902                                                   nsAString& aDescription,
00903                                                   nsAString& aMozillaFlags) {
00904   
00905   // The mailcap lookup is two-pass to handle the case of mailcap files
00906   // that have something like:
00907   //
00908   // text/*; emacs %s
00909   // text/rtf; soffice %s
00910   //
00911   // in that order.  We want to pick up "soffice" for text/rtf in such cases
00912   nsresult rv = DoLookUpHandlerAndDescription(aMajorType,
00913                                               aMinorType,
00914                                               aTypeOptions,
00915                                               aHandler,
00916                                               aDescription,
00917                                               aMozillaFlags,
00918                                               PR_TRUE);
00919   if (NS_FAILED(rv)) {
00920     rv = DoLookUpHandlerAndDescription(aMajorType,
00921                                        aMinorType,
00922                                        aTypeOptions,
00923                                        aHandler,
00924                                        aDescription,
00925                                        aMozillaFlags,
00926                                        PR_FALSE);
00927   }
00928 
00929   // maybe we have an entry for "aMajorType/*"?
00930   if (NS_FAILED(rv)) {
00931     rv = DoLookUpHandlerAndDescription(aMajorType,
00932                                        NS_LITERAL_STRING("*"),
00933                                        aTypeOptions,
00934                                        aHandler,
00935                                        aDescription,
00936                                        aMozillaFlags,
00937                                        PR_TRUE);
00938   }
00939 
00940   if (NS_FAILED(rv)) {
00941     rv = DoLookUpHandlerAndDescription(aMajorType,
00942                                        NS_LITERAL_STRING("*"),
00943                                        aTypeOptions,
00944                                        aHandler,
00945                                        aDescription,
00946                                        aMozillaFlags,
00947                                        PR_FALSE);
00948   }
00949 
00950   return rv;
00951 }
00952 
00953 // static
00954 nsresult
00955 nsOSHelperAppService::DoLookUpHandlerAndDescription(const nsAString& aMajorType,
00956                                                     const nsAString& aMinorType,
00957                                                     nsHashtable& aTypeOptions,
00958                                                     nsAString& aHandler,
00959                                                     nsAString& aDescription,
00960                                                     nsAString& aMozillaFlags,
00961                                                     PRBool aUserData) {
00962   LOG(("-- LookUpHandlerAndDescription for type '%s/%s'\n",
00963        NS_LossyConvertUCS2toASCII(aMajorType).get(),
00964        NS_LossyConvertUCS2toASCII(aMinorType).get()));
00965   nsresult rv = NS_OK;
00966   nsXPIDLString mailcapFileName;
00967 
00968   const char * filenamePref = aUserData ?
00969     "helpers.private_mailcap_file" : "helpers.global_mailcap_file";
00970   const char * filenameEnvVar = aUserData ?
00971     "PERSONAL_MAILCAP" : "MAILCAP";
00972   
00973   rv = GetFileLocation(filenamePref,
00974                        filenameEnvVar,
00975                        getter_Copies(mailcapFileName));
00976   if (NS_SUCCEEDED(rv) && !mailcapFileName.IsEmpty()) {
00977     rv = GetHandlerAndDescriptionFromMailcapFile(mailcapFileName,
00978                                                  aMajorType,
00979                                                  aMinorType,
00980                                                  aTypeOptions,
00981                                                  aHandler,
00982                                                  aDescription,
00983                                                  aMozillaFlags);
00984   } else {
00985     rv = NS_ERROR_NOT_AVAILABLE;
00986   }
00987 
00988   return rv;
00989 }
00990 
00991 // static
00992 nsresult
00993 nsOSHelperAppService::GetHandlerAndDescriptionFromMailcapFile(const nsAString& aFilename,
00994                                                               const nsAString& aMajorType,
00995                                                               const nsAString& aMinorType,
00996                                                               nsHashtable& aTypeOptions,
00997                                                               nsAString& aHandler,
00998                                                               nsAString& aDescription,
00999                                                               nsAString& aMozillaFlags) {
01000 
01001   LOG(("-- GetHandlerAndDescriptionFromMailcapFile\n"));
01002   LOG(("Getting handler and description from mailcap file '%s'\n",
01003        NS_LossyConvertUCS2toASCII(aFilename).get()));
01004   LOG(("Using type '%s/%s'\n",
01005        NS_LossyConvertUCS2toASCII(aMajorType).get(),
01006        NS_LossyConvertUCS2toASCII(aMinorType).get()));
01007 
01008   nsresult rv = NS_OK;
01009   PRBool more = PR_FALSE;
01010   
01011   nsCOMPtr<nsILocalFile> file(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
01012   if (NS_FAILED(rv))
01013     return rv;
01014   rv = file->InitWithPath(aFilename);
01015   if (NS_FAILED(rv))
01016     return rv;
01017 
01018   nsCOMPtr<nsIFileInputStream> mailcapFile(do_CreateInstance(NS_LOCALFILEINPUTSTREAM_CONTRACTID, &rv));
01019   if (NS_FAILED(rv))
01020     return rv;
01021   rv = mailcapFile->Init(file, -1, -1, PR_FALSE);
01022   if (NS_FAILED(rv))
01023     return rv;
01024 
01025   nsCOMPtr<nsILineInputStream> mailcap (do_QueryInterface(mailcapFile, &rv));
01026 
01027   if (NS_FAILED(rv)) {
01028     LOG(("Interface trouble in stream land!"));
01029     return rv;
01030   }
01031 
01032   nsString entry, buffer;
01033   nsCAutoString cBuffer;
01034   entry.SetCapacity(128);
01035   cBuffer.SetCapacity(80);
01036   rv = mailcap->ReadLine(cBuffer, &more);
01037   if (NS_FAILED(rv)) {
01038     mailcapFile->Close();
01039     return rv;
01040   }
01041 
01042   do {  // return on end-of-file in the loop
01043 
01044     CopyASCIItoUTF16(cBuffer, buffer);
01045     if (!buffer.IsEmpty() && buffer.First() != '#') {
01046       entry.Append(buffer);
01047       if (entry.Last() == '\\') {  // entry continues on next line
01048         entry.Truncate(entry.Length()-1);
01049         entry.Append(PRUnichar(' ')); // in case there is no trailing whitespace on this line
01050       } else {  // we have a full entry in entry.  Check it for the type
01051         LOG(("Current entry: '%s'\n",
01052              NS_LossyConvertUCS2toASCII(entry).get()));
01053 
01054         nsAString::const_iterator semicolon_iter,
01055                                   start_iter, end_iter,
01056                                   majorTypeStart, majorTypeEnd,
01057                                   minorTypeStart, minorTypeEnd;
01058         entry.BeginReading(start_iter);
01059         entry.EndReading(end_iter);
01060         semicolon_iter = start_iter;
01061         FindSemicolon(semicolon_iter, end_iter);
01062         if (semicolon_iter != end_iter) { // we have something resembling a valid entry
01063           rv = ParseMIMEType(start_iter, majorTypeStart, majorTypeEnd,
01064                              minorTypeStart, minorTypeEnd, semicolon_iter);
01065           if (NS_SUCCEEDED(rv) &&
01066               Substring(majorTypeStart,
01067                         majorTypeEnd).Equals(aMajorType,
01068                                              nsCaseInsensitiveStringComparator()) &&
01069               Substring(minorTypeStart,
01070                         minorTypeEnd).Equals(aMinorType,
01071                                              nsCaseInsensitiveStringComparator())) { // we have a match
01072             PRBool match = PR_TRUE;
01073             ++semicolon_iter;             // point at the first char past the semicolon
01074             start_iter = semicolon_iter;  // handler string starts here
01075             FindSemicolon(semicolon_iter, end_iter);
01076             while (start_iter != semicolon_iter &&
01077                    nsCRT::IsAsciiSpace(*start_iter)) {
01078               ++start_iter;
01079             }
01080 
01081             LOG(("The real handler is: '%s'\n",
01082                  NS_LossyConvertUCS2toASCII(Substring(start_iter,
01083                                                       semicolon_iter)).get()));
01084               
01085             // XXX ugly hack.  Just grab the executable name
01086             nsAString::const_iterator end_handler_iter = semicolon_iter;
01087             nsAString::const_iterator end_executable_iter = start_iter;
01088             while (end_executable_iter != end_handler_iter &&
01089                    !nsCRT::IsAsciiSpace(*end_executable_iter)) {
01090               ++end_executable_iter;
01091             }
01092             // XXX End ugly hack
01093             
01094             aHandler = Substring(start_iter, end_executable_iter);
01095             
01096             nsAString::const_iterator start_option_iter, end_optionname_iter, equal_sign_iter;
01097             PRBool equalSignFound;
01098             while (match &&
01099                    semicolon_iter != end_iter &&
01100                    ++semicolon_iter != end_iter) { // there are options left and we still match
01101               start_option_iter = semicolon_iter;
01102               // skip over leading whitespace
01103               while (start_option_iter != end_iter &&
01104                      nsCRT::IsAsciiSpace(*start_option_iter)) {
01105                 ++start_option_iter;
01106               }
01107               if (start_option_iter == end_iter) { // nothing actually here
01108                 break;
01109               }
01110               semicolon_iter = start_option_iter;
01111               FindSemicolon(semicolon_iter, end_iter);
01112               equal_sign_iter = start_option_iter;
01113               equalSignFound = PR_FALSE;
01114               while (equal_sign_iter != semicolon_iter && !equalSignFound) {
01115                 switch(*equal_sign_iter) {
01116                 case '\\':
01117                   equal_sign_iter.advance(2);
01118                   break;
01119                 case '=':
01120                   equalSignFound = PR_TRUE;
01121                   break;
01122                 default:
01123                   ++equal_sign_iter;
01124                   break;
01125                 }
01126               }
01127               end_optionname_iter = start_option_iter;
01128               // find end of option name
01129               while (end_optionname_iter != equal_sign_iter &&
01130                      !nsCRT::IsAsciiSpace(*end_optionname_iter)) {
01131                 ++end_optionname_iter;
01132               }                     
01133               nsDependentSubstring optionName(start_option_iter, end_optionname_iter);
01134               if (equalSignFound) {
01135                 // This is an option that has a name and value
01136                 if (optionName.EqualsLiteral("description")) {
01137                   aDescription = Substring(++equal_sign_iter, semicolon_iter);
01138                 } else if (optionName.EqualsLiteral("x-mozilla-flags")) {
01139                   aMozillaFlags = Substring(++equal_sign_iter, semicolon_iter);
01140                 } else if (optionName.EqualsLiteral("test")) {
01141                   nsCAutoString testCommand;
01142                   rv = UnescapeCommand(Substring(++equal_sign_iter, semicolon_iter),
01143                                        aMajorType,
01144                                        aMinorType,
01145                                        aTypeOptions,
01146                                        testCommand);
01147                   LOG(("Running Test: %s\n", testCommand.get()));
01148                   // XXX this should not use system(), since that can block the UI thread!
01149                   if (NS_SUCCEEDED(rv) && system(testCommand.get()) != 0) {
01150                     match = PR_FALSE;
01151                   }
01152                 }
01153               } else {
01154                 // This is an option that just has a name but no value (eg "copiousoutput")
01155               }
01156             }
01157             
01158 
01159             if (match) { // we did not fail any test clauses; all is good
01160               // get out of here
01161               mailcapFile->Close();
01162               return NS_OK;
01163             } else { // pretend that this match never happened
01164               aDescription.Truncate();
01165               aMozillaFlags.Truncate();
01166               aHandler.Truncate();
01167             }
01168           }
01169         }
01170         // zero out the entry for the next cycle
01171         entry.Truncate();
01172       }    
01173     }
01174     if (!more) {
01175       rv = NS_ERROR_NOT_AVAILABLE;
01176       break;
01177     }
01178     rv = mailcap->ReadLine(cBuffer, &more);
01179   } while (NS_SUCCEEDED(rv));
01180   mailcapFile->Close();
01181   return rv;
01182 }
01183 
01184 /* Looks up the handler for a specific scheme from prefs and returns the
01185  * file representing it in aApp. Note: This function doesn't guarantee the
01186  * existance of *aApp.
01187  */
01188 nsresult
01189 nsOSHelperAppService::GetHandlerAppFromPrefs(const char* aScheme, /*out*/ nsIFile** aApp)
01190 {
01191   nsresult rv;
01192   nsCOMPtr<nsIPrefService> srv(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
01193   if (NS_FAILED(rv)) // we have no pref service... that's bad
01194     return rv;
01195 
01196   nsCOMPtr<nsIPrefBranch> branch;
01197   srv->GetBranch("network.protocol-handler.app.", getter_AddRefs(branch));
01198   if (!branch) // No protocol handlers set up -> can't load url
01199     return NS_ERROR_NOT_AVAILABLE;
01200 
01201   nsXPIDLCString appPath;
01202   rv = branch->GetCharPref(aScheme, getter_Copies(appPath));
01203   if (NS_FAILED(rv))
01204     return rv;
01205 
01206   LOG(("   found app %s\n", appPath.get()));
01207 
01208   // First, try to treat |appPath| as absolute path, if it starts with '/'
01209   NS_ConvertUTF8toUTF16 utf16AppPath(appPath);
01210   if (appPath.First() == '/') {
01211     nsILocalFile* file;
01212     rv = NS_NewLocalFile(utf16AppPath, PR_TRUE, &file);
01213     *aApp = file;
01214     // If this worked, we are finished
01215     if (NS_SUCCEEDED(rv))
01216       return NS_OK;
01217   }
01218 
01219   // Second, check for a file in the mozilla app directory
01220   rv = NS_GetSpecialDirectory(NS_OS_CURRENT_PROCESS_DIR, aApp);
01221   if (NS_SUCCEEDED(rv)) {
01222     rv = (*aApp)->Append(utf16AppPath);
01223     if (NS_SUCCEEDED(rv)) {
01224       PRBool exists = PR_FALSE;
01225       rv = (*aApp)->Exists(&exists);
01226       if (NS_SUCCEEDED(rv) && exists)
01227         return NS_OK;
01228     }
01229     NS_RELEASE(*aApp);
01230   }
01231 
01232   // Thirdly, search the path
01233   return GetFileTokenForPath(utf16AppPath.get(), aApp);
01234 }
01235 
01236 NS_IMETHODIMP nsOSHelperAppService::ExternalProtocolHandlerExists(const char * aProtocolScheme, PRBool * aHandlerExists)
01237 {
01238   LOG(("-- nsOSHelperAppService::ExternalProtocolHandlerExists for '%s'\n",
01239        aProtocolScheme));
01240   *aHandlerExists = PR_FALSE;
01241 
01242   nsCOMPtr<nsIFile> app;
01243   nsresult rv = GetHandlerAppFromPrefs(aProtocolScheme, getter_AddRefs(app));
01244   if (NS_SUCCEEDED(rv)) {
01245     PRBool isExecutable = PR_FALSE, exists = PR_FALSE;
01246     nsresult rv1 = app->Exists(&exists);
01247     nsresult rv2 = app->IsExecutable(&isExecutable);
01248     *aHandlerExists = (NS_SUCCEEDED(rv1) && exists && NS_SUCCEEDED(rv2) && isExecutable);
01249     LOG(("   handler exists: %s\n", *aHandlerExists ? "yes" : "no"));
01250   }
01251 
01252 #ifdef MOZ_WIDGET_GTK2
01253   // Check the GConf registry for a protocol handler
01254   if (!*aHandlerExists)
01255     *aHandlerExists = nsGNOMERegistry::HandlerExists(aProtocolScheme);
01256 #endif
01257 
01258   return NS_OK;
01259 }
01260 
01261 nsresult nsOSHelperAppService::LoadUriInternal(nsIURI * aURI)
01262 {
01263   // Gets a string pref network.protocol-handler.app.<scheme>
01264   // and executes it
01265   LOG(("-- nsOSHelperAppService::LoadUrl\n"));
01266 
01267   nsCAutoString scheme;
01268   nsresult rv = aURI->GetScheme(scheme);
01269   if (NS_FAILED(rv)) // need a scheme
01270     return rv;
01271 
01272   nsCOMPtr<nsIFile> appFile;
01273   rv = GetHandlerAppFromPrefs(scheme.get(), getter_AddRefs(appFile));
01274   if (NS_SUCCEEDED(rv)) {
01275     // Let's not support passing arguments for now
01276     nsCOMPtr<nsIProcess> proc(do_CreateInstance("@mozilla.org/process/util;1", &rv));
01277     if (NS_FAILED(rv))
01278       return rv;
01279 
01280     rv = proc->Init(appFile);
01281     if (NS_FAILED(rv))
01282       return rv;
01283 
01284     nsCAutoString spec;
01285     rv = aURI->GetAsciiSpec(spec);
01286     if (NS_FAILED(rv))
01287       return rv;
01288 
01289     const char* args[] = { spec.get() };
01290     PRUint32 tmp;
01291     return proc->Run(/*blocking*/PR_FALSE, args, NS_ARRAY_LENGTH(args), &tmp);
01292   }
01293 
01294 #ifdef MOZ_WIDGET_GTK2
01295   return nsGNOMERegistry::LoadURL(aURI);
01296 #else
01297   return rv;
01298 #endif
01299 }
01300 
01301 NS_IMETHODIMP nsOSHelperAppService::GetApplicationDescription(const nsACString& aScheme, nsAString& _retval)
01302 {
01303   nsCOMPtr<nsIFile> appFile;
01304   nsresult rv = GetHandlerAppFromPrefs(PromiseFlatCString(aScheme).get(),
01305                                        getter_AddRefs(appFile));
01306   if (NS_SUCCEEDED(rv))
01307     return appFile->GetLeafName(_retval);
01308 
01309 #ifdef MOZ_WIDGET_GTK2
01310   nsGNOMERegistry::GetAppDescForScheme(aScheme, _retval);
01311   return _retval.IsEmpty() ? NS_ERROR_NOT_AVAILABLE : NS_OK;
01312 #else
01313   return NS_ERROR_NOT_AVAILABLE;
01314 #endif
01315 }
01316 
01317 nsresult nsOSHelperAppService::GetFileTokenForPath(const PRUnichar * platformAppPath, nsIFile ** aFile)
01318 {
01319   LOG(("-- nsOSHelperAppService::GetFileTokenForPath: '%s'\n",
01320        NS_LossyConvertUCS2toASCII(platformAppPath).get()));
01321   if (! *platformAppPath) { // empty filename--return error
01322     NS_WARNING("Empty filename passed in.");
01323     return NS_ERROR_INVALID_ARG;
01324   }
01325  
01326   // first check if the base class implementation finds anything
01327   nsresult rv = nsExternalHelperAppService::GetFileTokenForPath(platformAppPath, aFile);
01328   if (NS_SUCCEEDED(rv))
01329     return rv;
01330   // If the reason for failure was that the file doesn't exist, return too
01331   // (because it means the path was absolute, and so that we shouldn't search in
01332   // the path)
01333   if (rv == NS_ERROR_FILE_NOT_FOUND)
01334     return rv;
01335 
01336   // If we get here, we really should have a relative path.
01337   NS_ASSERTION(*platformAppPath != PRUnichar('/'), "Unexpected absolute path");
01338 
01339   nsCOMPtr<nsILocalFile> localFile (do_CreateInstance(NS_LOCAL_FILE_CONTRACTID));
01340 
01341   if (!localFile) return NS_ERROR_NOT_INITIALIZED;
01342  
01343   PRBool exists = PR_FALSE;
01344   // ugly hack.  Walk the PATH variable...
01345   char* unixpath = PR_GetEnv("PATH");
01346   nsCAutoString path(unixpath);
01347 
01348   const char* start_iter = path.BeginReading(start_iter);
01349   const char* colon_iter = start_iter;
01350   const char* end_iter = path.EndReading(end_iter);
01351 
01352   while (start_iter != end_iter && !exists) {
01353     while (colon_iter != end_iter && *colon_iter != ':') {
01354       ++colon_iter;
01355     }
01356     localFile->InitWithNativePath(Substring(start_iter, colon_iter));
01357     rv = localFile->AppendRelativePath(nsDependentString(platformAppPath));
01358     // Failing AppendRelativePath is a bad thing - it should basically always
01359     // succeed given a relative path. Show a warning if it does fail.
01360     // To prevent infinite loops when it does fail, return at this point.
01361     NS_ENSURE_SUCCESS(rv, rv);
01362     localFile->Exists(&exists);
01363     if (!exists) {
01364       if (colon_iter == end_iter) {
01365         break;
01366       }
01367       ++colon_iter;
01368       start_iter = colon_iter;
01369     }
01370   }
01371 
01372   if (exists) {
01373     rv = NS_OK;
01374   } else {
01375     rv = NS_ERROR_NOT_AVAILABLE;
01376   }
01377   
01378   *aFile = localFile;
01379   NS_IF_ADDREF(*aFile);
01380 
01381   return rv;
01382 }
01383 
01384 already_AddRefed<nsMIMEInfoBase>
01385 nsOSHelperAppService::GetFromExtension(const nsCString& aFileExt) {
01386   // if the extension is empty, return immediately
01387   if (aFileExt.IsEmpty())
01388     return nsnull;
01389   
01390   LOG(("Here we do an extension lookup for '%s'\n", aFileExt.get()));
01391 
01392   nsAutoString majorType, minorType,
01393                mime_types_description, mailcap_description,
01394                handler, mozillaFlags;
01395   
01396   nsresult rv = LookUpTypeAndDescription(NS_ConvertUTF8toUCS2(aFileExt),
01397                                          majorType,
01398                                          minorType,
01399                                          mime_types_description,
01400                                          PR_TRUE);
01401 
01402   if (NS_FAILED(rv) || majorType.IsEmpty()) {
01403     
01404 #ifdef MOZ_WIDGET_GTK2
01405     LOG(("Looking in GNOME registry\n"));
01406     nsMIMEInfoBase *gnomeInfo = nsGNOMERegistry::GetFromExtension(aFileExt.get()).get();
01407     if (gnomeInfo) {
01408       LOG(("Got MIMEInfo from GNOME registry\n"));
01409       return gnomeInfo;
01410     }
01411 #endif
01412 
01413     rv = LookUpTypeAndDescription(NS_ConvertUTF8toUCS2(aFileExt),
01414                                   majorType,
01415                                   minorType,
01416                                   mime_types_description,
01417                                   PR_FALSE);
01418   }
01419   
01420   if (NS_FAILED(rv))
01421     return nsnull;
01422 
01423   NS_LossyConvertUTF16toASCII asciiMajorType(majorType);
01424   NS_LossyConvertUTF16toASCII asciiMinorType(minorType);
01425 
01426   LOG(("Type/Description results:  majorType='%s', minorType='%s', description='%s'\n",
01427           asciiMajorType.get(),
01428           asciiMinorType.get(),
01429           NS_LossyConvertUCS2toASCII(mime_types_description).get()));
01430 
01431   if (majorType.IsEmpty() && minorType.IsEmpty()) {
01432     // we didn't get a type mapping, so we can't do anything useful
01433     return nsnull;
01434   }
01435 
01436   nsCAutoString mimeType(asciiMajorType + NS_LITERAL_CSTRING("/") + asciiMinorType);
01437   nsMIMEInfoImpl* mimeInfo = new nsMIMEInfoImpl(mimeType);
01438   if (!mimeInfo)
01439     return nsnull;
01440   NS_ADDREF(mimeInfo);
01441   
01442   mimeInfo->AppendExtension(aFileExt);
01443   nsHashtable typeOptions; // empty hash table
01444   rv = LookUpHandlerAndDescription(majorType, minorType, typeOptions,
01445                                    handler, mailcap_description,
01446                                    mozillaFlags);
01447   LOG(("Handler/Description results:  handler='%s', description='%s', mozillaFlags='%s'\n",
01448           NS_LossyConvertUCS2toASCII(handler).get(),
01449           NS_LossyConvertUCS2toASCII(mailcap_description).get(),
01450           NS_LossyConvertUCS2toASCII(mozillaFlags).get()));
01451   mailcap_description.Trim(" \t\"");
01452   mozillaFlags.Trim(" \t");
01453   if (!mime_types_description.IsEmpty()) {
01454     mimeInfo->SetDescription(mime_types_description);
01455   } else {
01456     mimeInfo->SetDescription(mailcap_description);
01457   }
01458 
01459   if (NS_SUCCEEDED(rv) && handler.IsEmpty()) {
01460     rv = NS_ERROR_NOT_AVAILABLE;
01461   }
01462   
01463   if (NS_SUCCEEDED(rv)) {
01464     nsCOMPtr<nsIFile> handlerFile;
01465     rv = GetFileTokenForPath(handler.get(), getter_AddRefs(handlerFile));
01466     
01467     if (NS_SUCCEEDED(rv)) {
01468       mimeInfo->SetDefaultApplication(handlerFile);
01469       mimeInfo->SetPreferredAction(nsIMIMEInfo::useSystemDefault);
01470       mimeInfo->SetDefaultDescription(handler);
01471     }
01472   }
01473 
01474   if (NS_FAILED(rv)) {
01475     mimeInfo->SetPreferredAction(nsIMIMEInfo::saveToDisk);
01476   }
01477 
01478   return mimeInfo;
01479 }
01480 
01481 already_AddRefed<nsMIMEInfoBase>
01482 nsOSHelperAppService::GetFromType(const nsCString& aMIMEType) {
01483   // if the type is empty, return immediately
01484   if (aMIMEType.IsEmpty())
01485     return nsnull;
01486   
01487   LOG(("Here we do a mimetype lookup for '%s'\n", aMIMEType.get()));
01488 
01489   // extract the major and minor types
01490   NS_ConvertASCIItoUTF16 mimeType(aMIMEType);
01491   nsAString::const_iterator start_iter, end_iter,
01492                             majorTypeStart, majorTypeEnd,
01493                             minorTypeStart, minorTypeEnd;
01494 
01495   mimeType.BeginReading(start_iter);
01496   mimeType.EndReading(end_iter);
01497 
01498   // XXX FIXME: add typeOptions parsing in here
01499   nsHashtable typeOptions;  
01500   nsresult rv = ParseMIMEType(start_iter, majorTypeStart, majorTypeEnd,
01501                               minorTypeStart, minorTypeEnd, end_iter);
01502 
01503   if (NS_FAILED(rv)) {
01504     return nsnull;
01505   }
01506 
01507   nsDependentSubstring majorType(majorTypeStart, majorTypeEnd);
01508   nsDependentSubstring minorType(minorTypeStart, minorTypeEnd);
01509 
01510   // First check the user's private mailcap file
01511   nsAutoString mailcap_description, handler, mozillaFlags;
01512   DoLookUpHandlerAndDescription(majorType,
01513                                 minorType,
01514                                 typeOptions,
01515                                 handler,
01516                                 mailcap_description,
01517                                 mozillaFlags,
01518                                 PR_TRUE);
01519   
01520   LOG(("Private Handler/Description results:  handler='%s', description='%s'\n",
01521           NS_LossyConvertUTF16toASCII(handler).get(),
01522           NS_LossyConvertUTF16toASCII(mailcap_description).get()));
01523 
01524 #ifdef MOZ_WIDGET_GTK2
01525   nsMIMEInfoBase *gnomeInfo = nsnull;
01526   if (handler.IsEmpty()) {
01527     // No useful data yet.  Check the GNOME registry.  Unfortunately, newer
01528     // GNOME versions no longer have type-to-extension mappings, so we might
01529     // get back a MIMEInfo without any extensions set.  In that case we'll have
01530     // to look in our mime.types files for the extensions.    
01531     LOG(("Looking in GNOME registry\n"));
01532     gnomeInfo = nsGNOMERegistry::GetFromType(aMIMEType.get()).get();
01533     if (gnomeInfo && gnomeInfo->HasExtensions()) {
01534       LOG(("Got MIMEInfo from GNOME registry, and it has extensions set\n"));
01535       return gnomeInfo;
01536     }
01537   }
01538 #endif
01539 
01540   // Now look up our extensions
01541   nsAutoString extensions, mime_types_description;
01542   LookUpExtensionsAndDescription(majorType,
01543                                  minorType,
01544                                  extensions,
01545                                  mime_types_description);
01546 
01547 #ifdef MOZ_WIDGET_GTK2
01548   if (gnomeInfo) {
01549     LOG(("Got MIMEInfo from GNOME registry without extensions; setting them "
01550          "to %s\n", NS_LossyConvertUTF16toASCII(extensions).get()));
01551 
01552     NS_ASSERTION(!gnomeInfo->HasExtensions(), "How'd that happen?");
01553     gnomeInfo->SetFileExtensions(NS_ConvertUTF16toUTF8(extensions));
01554     return gnomeInfo;
01555   }
01556 #endif
01557 
01558   if (handler.IsEmpty()) {
01559     DoLookUpHandlerAndDescription(majorType,
01560                                   minorType,
01561                                   typeOptions,
01562                                   handler,
01563                                   mailcap_description,
01564                                   mozillaFlags,
01565                                   PR_FALSE);
01566   }
01567 
01568   if (handler.IsEmpty()) {
01569     DoLookUpHandlerAndDescription(majorType,
01570                                   NS_LITERAL_STRING("*"),
01571                                   typeOptions,
01572                                   handler,
01573                                   mailcap_description,
01574                                   mozillaFlags,
01575                                   PR_TRUE);
01576   }
01577 
01578   if (handler.IsEmpty()) {
01579     DoLookUpHandlerAndDescription(majorType,
01580                                   NS_LITERAL_STRING("*"),
01581                                   typeOptions,
01582                                   handler,
01583                                   mailcap_description,
01584                                   mozillaFlags,
01585                                   PR_FALSE);
01586   }  
01587   
01588   LOG(("Handler/Description results:  handler='%s', description='%s', mozillaFlags='%s'\n",
01589           NS_LossyConvertUCS2toASCII(handler).get(),
01590           NS_LossyConvertUCS2toASCII(mailcap_description).get(),
01591           NS_LossyConvertUCS2toASCII(mozillaFlags).get()));
01592   
01593   mailcap_description.Trim(" \t\"");
01594   mozillaFlags.Trim(" \t");
01595 
01596   if (handler.IsEmpty() && extensions.IsEmpty() &&
01597       mailcap_description.IsEmpty() && mime_types_description.IsEmpty()) {
01598     // No real useful info
01599     return nsnull;
01600   }
01601   
01602   nsMIMEInfoImpl* mimeInfo = new nsMIMEInfoImpl(aMIMEType);
01603   if (!mimeInfo)
01604     return nsnull;
01605   NS_ADDREF(mimeInfo);
01606 
01607   mimeInfo->SetFileExtensions(NS_ConvertUCS2toUTF8(extensions));
01608   if (! mime_types_description.IsEmpty()) {
01609     mimeInfo->SetDescription(mime_types_description);
01610   } else {
01611     mimeInfo->SetDescription(mailcap_description);
01612   }
01613 
01614   rv = NS_ERROR_NOT_AVAILABLE;
01615   nsCOMPtr<nsIFile> handlerFile;
01616   if (!handler.IsEmpty()) {
01617     rv = GetFileTokenForPath(handler.get(), getter_AddRefs(handlerFile));
01618   }
01619   
01620   if (NS_SUCCEEDED(rv)) {
01621     mimeInfo->SetDefaultApplication(handlerFile);
01622     mimeInfo->SetPreferredAction(nsIMIMEInfo::useSystemDefault);
01623     mimeInfo->SetDefaultDescription(handler);
01624   } else {
01625     mimeInfo->SetPreferredAction(nsIMIMEInfo::saveToDisk);
01626   }
01627 
01628   return mimeInfo;
01629 }
01630 
01631 
01632 already_AddRefed<nsIMIMEInfo>
01633 nsOSHelperAppService::GetMIMEInfoFromOS(const nsACString& aType,
01634                                         const nsACString& aFileExt,
01635                                         PRBool     *aFound) {
01636   *aFound = PR_TRUE;
01637   nsMIMEInfoBase* retval = GetFromType(PromiseFlatCString(aType)).get();
01638   PRBool hasDefault = PR_FALSE;
01639   if (retval)
01640     retval->GetHasDefaultHandler(&hasDefault);
01641   if (!retval || !hasDefault) {
01642     nsRefPtr<nsMIMEInfoBase> miByExt = GetFromExtension(PromiseFlatCString(aFileExt));
01643     // If we had no extension match, but a type match, use that
01644     if (!miByExt && retval)
01645       return retval;
01646     // If we had an extension match but no type match, set the mimetype and use
01647     // it
01648     if (!retval && miByExt) {
01649       if (!aType.IsEmpty())
01650         miByExt->SetMIMEType(aType);
01651       miByExt.swap(retval);
01652 
01653       return retval;
01654     }
01655     // If we got nothing, make a new mimeinfo
01656     if (!retval) {
01657       *aFound = PR_FALSE;
01658       retval = new nsMIMEInfoImpl(aType);
01659       if (retval) {
01660         NS_ADDREF(retval);
01661         if (!aFileExt.IsEmpty())
01662           retval->AppendExtension(aFileExt);
01663       }
01664       
01665       return retval;
01666     }
01667 
01668     // Copy the attributes of retval onto miByExt, to return it
01669     retval->CopyBasicDataTo(miByExt);
01670 
01671     miByExt.swap(retval);
01672   }
01673   return retval;
01674 }
01675 
01676 void
01677 nsOSHelperAppService::FixFilePermissions(nsILocalFile* aFile)
01678 {
01679   aFile->SetPermissions(mPermissions); 
01680 }
01681