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  * vim:set ts=2 sts=2 sw=2 et cin:
00003  *
00004  * ***** BEGIN LICENSE BLOCK *****
00005  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00006  *
00007  * The contents of this file are subject to the Mozilla Public License Version
00008  * 1.1 (the "License"); you may not use this file except in compliance with
00009  * the License. You may obtain a copy of the License at
00010  * http://www.mozilla.org/MPL/
00011  *
00012  * Software distributed under the License is distributed on an "AS IS" basis,
00013  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00014  * for the specific language governing rights and limitations under the
00015  * License.
00016  *
00017  * The Original Code is the Mozilla browser.
00018  *
00019  * The Initial Developer of the Original Code is
00020  * Netscape Communications, Inc.
00021  * Portions created by the Initial Developer are Copyright (C) 1999
00022  * the Initial Developer. All Rights Reserved.
00023  *
00024  * Contributor(s):
00025  *   Scott MacGregor <mscott@netscape.com>
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 "nsOSHelperAppService.h"
00042 #include "nsISupports.h"
00043 #include "nsString.h"
00044 #include "nsXPIDLString.h"
00045 #include "nsIURL.h"
00046 #include "nsIMIMEInfo.h"
00047 #include "nsMIMEInfoWin.h"
00048 #include "nsMimeTypes.h"
00049 #include "nsILocalFileWin.h"
00050 #include "nsIProcess.h"
00051 #include "plstr.h"
00052 #include "nsAutoPtr.h"
00053 #include "nsNativeCharsetUtils.h"
00054 #include "nsIWindowsRegKey.h"
00055 
00056 // we need windows.h to read out registry information...
00057 #include <windows.h>
00058 
00059 // shellapi.h is needed to build with WIN32_LEAN_AND_MEAN
00060 #include <shellapi.h>
00061 #include <shlobj.h>
00062 
00063 #define LOG(args) PR_LOG(mLog, PR_LOG_DEBUG, args)
00064 
00065 // helper methods: forward declarations...
00066 static nsresult GetExtensionFrom4xRegistryInfo(const nsACString& aMimeType, 
00067                                                nsString& aFileExtension);
00068 static nsresult GetExtensionFromWindowsMimeDatabase(const nsACString& aMimeType,
00069                                                     nsString& aFileExtension);
00070 
00071 // static member
00072 PRBool nsOSHelperAppService::sIsNT = PR_FALSE;
00073 
00074 nsOSHelperAppService::nsOSHelperAppService() : nsExternalHelperAppService()
00075 {
00076   OSVERSIONINFO osversion;
00077   ::ZeroMemory(&osversion, sizeof(OSVERSIONINFO));
00078   osversion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
00079 
00080   // We'd better make sure that sIsNT is init'd only once, but we can
00081   // get away without doing that explicitly because nsOSHelperAppService
00082   // is a singleton.
00083   if (!GetVersionEx(&osversion)) {
00084     // If the call failed, better be safe and assume *W functions don't work
00085     sIsNT = PR_FALSE;
00086   }
00087   else {
00088     sIsNT = (osversion.dwPlatformId == VER_PLATFORM_WIN32_NT);
00089   }
00090 }
00091 
00092 nsOSHelperAppService::~nsOSHelperAppService()
00093 {}
00094 
00095 // The windows registry provides a mime database key which lists a set of mime types and corresponding "Extension" values. 
00096 // we can use this to look up our mime type to see if there is a preferred extension for the mime type.
00097 static nsresult GetExtensionFromWindowsMimeDatabase(const nsACString& aMimeType,
00098                                                     nsString& aFileExtension)
00099 {
00100   nsAutoString mimeDatabaseKey;
00101   mimeDatabaseKey.AssignLiteral("MIME\\Database\\Content Type\\");
00102 
00103   AppendASCIItoUTF16(aMimeType, mimeDatabaseKey);
00104 
00105   nsCOMPtr<nsIWindowsRegKey> regKey = 
00106     do_CreateInstance("@mozilla.org/windows-registry-key;1");
00107   if (!regKey) 
00108     return NS_ERROR_NOT_AVAILABLE;
00109 
00110   nsresult rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
00111                              mimeDatabaseKey,
00112                              nsIWindowsRegKey::ACCESS_QUERY_VALUE);
00113   
00114   if (NS_SUCCEEDED(rv))
00115      regKey->ReadStringValue(NS_LITERAL_STRING("Extension"), aFileExtension);
00116 
00117   return NS_OK;
00118 }
00119 
00120 // We have a serious problem!! I have this content type and the windows registry only gives me
00121 // helper apps based on extension. Right now, we really don't have a good place to go for 
00122 // trying to figure out the extension for a particular mime type....One short term hack is to look
00123 // this information in 4.x (it's stored in the windows regsitry). 
00124 static nsresult GetExtensionFrom4xRegistryInfo(const nsACString& aMimeType,
00125                                                nsString& aFileExtension)
00126 {
00127   nsCOMPtr<nsIWindowsRegKey> regKey = 
00128     do_CreateInstance("@mozilla.org/windows-registry-key;1");
00129   if (!regKey) 
00130     return NS_ERROR_NOT_AVAILABLE;
00131 
00132   nsresult rv = regKey->
00133     Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
00134          NS_LITERAL_STRING("Software\\Netscape\\Netscape Navigator\\Suffixes"),
00135          nsIWindowsRegKey::ACCESS_QUERY_VALUE);
00136   if (NS_FAILED(rv))
00137     return NS_ERROR_NOT_AVAILABLE;
00138    
00139   rv = regKey->ReadStringValue(NS_ConvertASCIItoUTF16(aMimeType),
00140                                aFileExtension);
00141   if (NS_FAILED(rv))
00142     return NS_OK;
00143 
00144   aFileExtension.Insert(PRUnichar('.'), 0);
00145       
00146   // this may be a comma separated list of extensions...just take the 
00147   // first one for now...
00148 
00149   PRInt32 pos = aFileExtension.FindChar(PRUnichar(','));
00150   if (pos > 0) {
00151     // we have a comma separated list of types...
00152     // truncate everything after the first comma (including the comma)
00153     aFileExtension.Truncate(pos); 
00154   }
00155    
00156   return NS_OK;
00157 }
00158 
00159 NS_IMETHODIMP nsOSHelperAppService::ExternalProtocolHandlerExists(const char * aProtocolScheme, PRBool * aHandlerExists)
00160 {
00161   // look up the protocol scheme in the windows registry....if we find a match then we have a handler for it...
00162   *aHandlerExists = PR_FALSE;
00163   if (aProtocolScheme && *aProtocolScheme)
00164   {
00165      HKEY hKey;
00166      LONG err = ::RegOpenKeyEx(HKEY_CLASSES_ROOT, aProtocolScheme, 0,
00167                                KEY_QUERY_VALUE, &hKey);
00168      if (err == ERROR_SUCCESS)
00169      {
00170        err = ::RegQueryValueEx(hKey, "URL Protocol", NULL, NULL, NULL, NULL);
00171        *aHandlerExists = (err == ERROR_SUCCESS);
00172        // close the key
00173        ::RegCloseKey(hKey);
00174      }
00175   }
00176 
00177   return NS_OK;
00178 }
00179 
00180 typedef ULONG SFGAOF;
00181 typedef HRESULT (STDMETHODCALLTYPE *MySHParseDisplayName)
00182                  (PCWSTR pszName,
00183                   IBindCtx *pbc,
00184                   LPITEMIDLIST *ppidl,
00185                   SFGAOF sfgaoIn,
00186                   SFGAOF *psfgaoOut);
00187 
00188 nsresult nsOSHelperAppService::LoadUriInternal(nsIURI * aURL)
00189 {
00190   nsresult rv = NS_OK;
00191 
00192   // 1. Find the default app for this protocol
00193   // 2. Set up the command line
00194   // 3. Launch the app.
00195 
00196   // For now, we'll just cheat essentially, check for the command line
00197   // then just call ShellExecute()!
00198 
00199   if (aURL)
00200   {
00201     // extract the url spec from the url
00202     nsCAutoString urlSpec;
00203     aURL->GetAsciiSpec(urlSpec);
00204 
00205     // Some versions of windows (Win2k before SP3, Win XP before SP1)
00206     // crash in ShellExecute on long URLs (bug 161357).
00207     // IE 5 and 6 support URLS of 2083 chars in length, 2K is safe
00208     const PRUint32 maxSafeURL(2048);
00209     if (urlSpec.Length() > maxSafeURL)
00210       return NS_ERROR_FAILURE;
00211 
00212     LPITEMIDLIST pidl;
00213     SFGAOF sfgao;
00214 
00215     // bug 394974
00216     HMODULE hDll = ::LoadLibrary("shell32.dll");
00217     MySHParseDisplayName pMySHParseDisplayName = NULL;
00218     if (pMySHParseDisplayName = 
00219           (MySHParseDisplayName)::GetProcAddress(hDll, "SHParseDisplayName")) { // Version 6.0 and higher
00220       if (SUCCEEDED(pMySHParseDisplayName(NS_ConvertASCIItoUTF16(urlSpec).get(), NULL, &pidl, 0, &sfgao))) {
00221         static const char cmdVerb[] = "open";
00222         SHELLEXECUTEINFO sinfo;
00223         memset(&sinfo, 0, sizeof(SHELLEXECUTEINFO));
00224         sinfo.cbSize   = sizeof(SHELLEXECUTEINFO);
00225         sinfo.fMask    = SEE_MASK_FLAG_DDEWAIT|SEE_MASK_FLAG_NO_UI|SEE_MASK_INVOKEIDLIST;
00226         sinfo.hwnd     = GetDesktopWindow();
00227         sinfo.lpVerb   = (LPCSTR)&cmdVerb;
00228         sinfo.nShow    = SW_SHOWNORMAL;
00229         sinfo.lpIDList = pidl;
00230 
00231         BOOL bRes = ShellExecuteEx(&sinfo);
00232 
00233         CoTaskMemFree(pidl);
00234 
00235         if (!bRes || ((int)sinfo.hInstApp) < 32)
00236           rv = NS_ERROR_FAILURE;
00237       }
00238     } else {
00239       // Version of shell32.dll < 6.0, WinXP SP2 is not installed
00240       LONG r = (LONG) ::ShellExecute(NULL, "open", urlSpec.get(), NULL, NULL, 
00241                                      SW_SHOWNORMAL);
00242       if (r < 32) 
00243         rv = NS_ERROR_FAILURE;
00244     }
00245 
00246     if (hDll) 
00247       ::FreeLibrary(hDll);
00248   }
00249 
00250   return rv;
00251 }
00252 
00253 NS_IMETHODIMP nsOSHelperAppService::GetApplicationDescription(const nsACString& aScheme, nsAString& _retval)
00254 {
00255   nsCOMPtr<nsIWindowsRegKey> regKey = 
00256     do_CreateInstance("@mozilla.org/windows-registry-key;1");
00257   if (!regKey) 
00258     return NS_ERROR_NOT_AVAILABLE;
00259 
00260   NS_ConvertASCIItoUTF16 buf(aScheme);
00261 
00262   nsCOMPtr<nsIFile> app;
00263   GetDefaultAppInfo(buf, _retval, getter_AddRefs(app));
00264 
00265   if (!_retval.Equals(buf))
00266     return NS_OK;
00267 
00268   // Fall back to full path
00269   buf.AppendLiteral("\\shell\\open\\command");
00270   nsresult rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
00271                              buf,
00272                              nsIWindowsRegKey::ACCESS_QUERY_VALUE);
00273   if (NS_FAILED(rv))
00274     return NS_ERROR_NOT_AVAILABLE;   
00275    
00276   rv = regKey->ReadStringValue(EmptyString(), _retval); 
00277 
00278   return NS_SUCCEEDED(rv) ? NS_OK : NS_ERROR_NOT_AVAILABLE;
00279 }
00280 
00281 // GetMIMEInfoFromRegistry: This function obtains the values of some of the nsIMIMEInfo
00282 // attributes for the mimeType/extension associated with the input registry key.  The default
00283 // entry for that key is the name of a registry key under HKEY_CLASSES_ROOT.  The default
00284 // value for *that* key is the descriptive name of the type.  The EditFlags value is a binary
00285 // value; the low order bit of the third byte of which indicates that the user does not need
00286 // to be prompted.
00287 //
00288 // This function sets only the Description attribute of the input nsIMIMEInfo.
00289 /* static */
00290 nsresult nsOSHelperAppService::GetMIMEInfoFromRegistry(const nsAFlatString& fileType, nsIMIMEInfo *pInfo)
00291 {
00292   nsresult rv = NS_OK;
00293 
00294   NS_ENSURE_ARG(pInfo);
00295   nsCOMPtr<nsIWindowsRegKey> regKey = 
00296     do_CreateInstance("@mozilla.org/windows-registry-key;1");
00297   if (!regKey) 
00298     return NS_ERROR_NOT_AVAILABLE;
00299 
00300   rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
00301                     fileType, nsIWindowsRegKey::ACCESS_QUERY_VALUE);
00302   if (NS_FAILED(rv))
00303     return NS_ERROR_FAILURE;
00304  
00305   // OK, the default value here is the description of the type.
00306   nsAutoString description;
00307   rv = regKey->ReadStringValue(EmptyString(), description);
00308   if (NS_SUCCEEDED(rv))
00309     pInfo->SetDescription(description);
00310 
00311   return NS_OK;
00312 }
00313 
00315 // method overrides used to gather information from the windows registry for
00316 // various mime types. 
00318 
00320 /* static */ PRBool
00321 nsOSHelperAppService::typeFromExtEquals(const PRUnichar* aExt, const char *aType)
00322 {
00323   if (!aType)
00324     return PR_FALSE;
00325   nsAutoString fileExtToUse;
00326   if (aExt[0] != PRUnichar('.'))
00327     fileExtToUse = PRUnichar('.');
00328 
00329   fileExtToUse.Append(aExt);
00330 
00331   PRBool eq = PR_FALSE;
00332   nsCOMPtr<nsIWindowsRegKey> regKey = 
00333     do_CreateInstance("@mozilla.org/windows-registry-key;1");
00334   if (!regKey) 
00335     return eq;
00336 
00337   nsresult rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
00338                              fileExtToUse,
00339                              nsIWindowsRegKey::ACCESS_QUERY_VALUE);
00340   if (NS_FAILED(rv))
00341       return eq;
00342    
00343   nsAutoString type;
00344   rv = regKey->ReadStringValue(NS_LITERAL_STRING("Content Type"), type);
00345   if (NS_SUCCEEDED(rv))
00346      eq = type.EqualsASCII(aType);
00347 
00348   return eq;
00349 }
00350 
00351 static void RemoveParameters(nsString& aPath)
00352 {
00353   // Command Strings stored in the Windows registry with parameters look like 
00354   // this:
00355   //
00356   // 1) "C:\Program Files\Company Name\product.exe" -foo -bar  (long version)
00357   //                      -- OR --
00358   // 2) C:\PROGRA~1\COMPAN~2\product.exe -foo -bar             (short version)
00359   //
00360   // For 1), the path is the first "" quoted string. (quotes are used to 
00361   //         prevent parameter parsers from choking)
00362   // For 2), the path is the string up until the first space (spaces are 
00363   //         illegal in short DOS-style paths)
00364   //
00365   if (aPath.First() == PRUnichar('"')) {
00366     aPath = Substring(aPath, 1, aPath.Length() - 1);
00367     PRInt32 nextQuote = aPath.FindChar(PRUnichar('"'));
00368     if (nextQuote != kNotFound)
00369       aPath.Truncate(nextQuote);
00370   }
00371   else {
00372     PRInt32 firstSpace = aPath.FindChar(PRUnichar(' '));
00373     if (firstSpace != kNotFound) 
00374       aPath.Truncate(firstSpace);
00375   }
00376 }
00377 
00378 //
00379 // The "real" name of a given helper app (as specified by the path to the 
00380 // executable file held in various registry keys) is stored n the VERSIONINFO
00381 // block in the file's resources. We need to find the path to the executable
00382 // and then retrieve the "FileDescription" field value from the file. 
00383 //
00384 // For a given extension, we find the file handler like so:
00385 //
00386 // HKCR
00387 //     \.ext\                           <type key>     <-- default value
00388 //     <type key>\   
00389 //                \shell\open\command\  <path+params>  <-- default value
00390 //
00391 // We need to do some parsing on the <path+params> to strip off params and
00392 // deal with some Windows quirks (like the fact that many Shell "applications"
00393 // are actually DLLs invoked via rundll32.exe) 
00394 //
00395 nsresult
00396 nsOSHelperAppService::GetDefaultAppInfo(const nsAString& aTypeName,
00397                                         nsAString& aDefaultDescription, 
00398                                         nsIFile** aDefaultApplication)
00399 {
00400   // If all else fails, use the file type key name, which will be 
00401   // something like "pngfile" for .pngs, "WMVFile" for .wmvs, etc. 
00402   aDefaultDescription = aTypeName;
00403   *aDefaultApplication = nsnull;
00404 
00405   nsAutoString handlerKeyName(aTypeName);
00406   handlerKeyName.AppendLiteral("\\shell\\open\\command");
00407   nsCOMPtr<nsIWindowsRegKey> regKey = 
00408     do_CreateInstance("@mozilla.org/windows-registry-key;1");
00409   if (!regKey) 
00410     return NS_OK;
00411 
00412   nsresult rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
00413                              handlerKeyName, 
00414                              nsIWindowsRegKey::ACCESS_QUERY_VALUE);
00415   if (NS_FAILED(rv))
00416     return NS_OK;
00417    
00418   // OK, the default value here is the description of the type.
00419   nsAutoString handlerCommand;
00420   rv = regKey->ReadStringValue(EmptyString(), handlerCommand);
00421   if (NS_FAILED(rv))
00422     return NS_OK;
00423 
00424   nsAutoString handlerFilePath;
00425   // First look to see if we're invoking a Windows shell service, such as 
00426   // the Picture & Fax Viewer, which are invoked through rundll32.exe, and
00427   // so we need to extract the DLL path because that's where the version
00428   // info is held - not in rundll32.exe
00429   //
00430   // The format of rundll32.exe calls is:
00431   //
00432   // rundll32.exe c:\path\to.dll,Function %args
00433   //
00434   // What we want is the DLL - since that's where the real application name
00435   // is stored, e.g. zipfldr.dll, shimgvw.dll, etc. 
00436   // 
00437   // Working from the end of the registry value, the path begins at the last
00438   // comma in the string (stripping off Function and args) to the position
00439   // just after the first space (the space after rundll32.exe).
00440   // 
00441   NS_NAMED_LITERAL_STRING(rundllSegment, "rundll32.exe ");
00442   if (StringBeginsWith(handlerCommand, rundllSegment)) {
00443     PRInt32 lastCommaPos = handlerCommand.RFindChar(',');
00444     PRUint32 rundllSegmentLength = rundllSegment.Length();
00445     PRUint32 len;
00446     if (lastCommaPos != kNotFound)
00447       len = lastCommaPos - rundllSegmentLength;
00448     else
00449       len = handlerCommand.Length() - rundllSegmentLength;
00450     handlerFilePath = Substring(handlerCommand, rundllSegmentLength, len);
00451   }
00452   else
00453     handlerFilePath = handlerCommand;
00454 
00455   // Trim any command parameters so that we have a native path we can 
00456   // initialize a local file with...
00457   RemoveParameters(handlerFilePath);
00458 
00459   // Similarly replace embedded environment variables... (this must be done
00460   // AFTER |RemoveParameters| since it may introduce spaces into the path
00461   // string)
00462 
00463   if (sIsNT) {
00464     DWORD required = ::ExpandEnvironmentStringsW(handlerFilePath.get(),
00465                                                  L"", 0);
00466     if (!required) 
00467       return NS_ERROR_FAILURE;
00468 
00469     nsAutoArrayPtr<WCHAR> destination(new WCHAR[required]); 
00470     if (!destination)
00471       return NS_ERROR_OUT_OF_MEMORY;
00472     if (!::ExpandEnvironmentStringsW(handlerFilePath.get(), destination,
00473                                      required))
00474       return NS_ERROR_FAILURE;
00475 
00476     handlerFilePath = destination;
00477   }
00478   else {
00479     nsCAutoString nativeHandlerFilePath; 
00480     NS_CopyUnicodeToNative(handlerFilePath, nativeHandlerFilePath);
00481     DWORD required = ::ExpandEnvironmentStringsA(nativeHandlerFilePath.get(),
00482                                                  "", 0);
00483     if (!required) 
00484       return NS_ERROR_FAILURE;
00485 
00486     nsAutoArrayPtr<char> destination(new char[required]); 
00487     if (!destination)
00488       return NS_ERROR_OUT_OF_MEMORY;
00489     if (!::ExpandEnvironmentStringsA(nativeHandlerFilePath.get(), 
00490                                      destination, required))
00491       return NS_ERROR_FAILURE;
00492 
00493     NS_CopyNativeToUnicode(nsDependentCString(destination), handlerFilePath);
00494   }
00495 
00496   nsCOMPtr<nsILocalFile> lf;
00497   NS_NewLocalFile(handlerFilePath, PR_TRUE, getter_AddRefs(lf));
00498   if (!lf)
00499     return NS_ERROR_OUT_OF_MEMORY;
00500 
00501   nsILocalFileWin* lfw = nsnull;
00502   CallQueryInterface(lf, &lfw);
00503   if (lfw) {
00504     // The "FileDescription" field contains the actual name of the application.
00505     lfw->GetVersionInfoField("FileDescription", aDefaultDescription);
00506     // QI addref'ed for us.
00507     *aDefaultApplication = lfw;
00508   }
00509 
00510   return NS_OK;
00511 }
00512 
00513 already_AddRefed<nsMIMEInfoWin> nsOSHelperAppService::GetByExtension(const nsAFlatString& aFileExt, const char *aTypeHint)
00514 {
00515   if (aFileExt.IsEmpty())
00516     return nsnull;
00517 
00518   // windows registry assumes your file extension is going to include the '.'.
00519   // so make sure it's there...
00520   nsAutoString fileExtToUse;
00521   if (aFileExt.First() != PRUnichar('.'))
00522     fileExtToUse = PRUnichar('.');
00523 
00524   fileExtToUse.Append(aFileExt);
00525 
00526   // o.t. try to get an entry from the windows registry.
00527   nsCOMPtr<nsIWindowsRegKey> regKey = 
00528     do_CreateInstance("@mozilla.org/windows-registry-key;1");
00529   if (!regKey) 
00530     return nsnull; 
00531 
00532   nsresult rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CLASSES_ROOT,
00533                              fileExtToUse,
00534                              nsIWindowsRegKey::ACCESS_QUERY_VALUE);
00535   if (NS_FAILED(rv))
00536     return nsnull; 
00537 
00538   nsCAutoString typeToUse;
00539   if (aTypeHint && *aTypeHint) {
00540     typeToUse.Assign(aTypeHint);
00541   }
00542   else {
00543     nsAutoString temp;
00544     if (NS_FAILED(regKey->ReadStringValue(NS_LITERAL_STRING("Content Type"), 
00545                   temp))) {
00546       return nsnull; 
00547     }
00548     // Content-Type is always in ASCII
00549     LossyAppendUTF16toASCII(temp, typeToUse);
00550   }
00551 
00552   nsMIMEInfoWin* mimeInfo = new nsMIMEInfoWin(typeToUse);
00553   if (!mimeInfo)
00554     return nsnull; // out of memory
00555 
00556   NS_ADDREF(mimeInfo);
00557   // don't append the '.'
00558   mimeInfo->AppendExtension(NS_ConvertUTF16toUTF8(Substring(fileExtToUse, 1)));
00559 
00560   mimeInfo->SetPreferredAction(nsIMIMEInfo::useSystemDefault);
00561 
00562   nsAutoString description;
00563   PRBool found = NS_SUCCEEDED(regKey->ReadStringValue(EmptyString(), 
00564                                                       description));
00565 
00566   nsAutoString defaultDescription;
00567   nsCOMPtr<nsIFile> defaultApplication;
00568   GetDefaultAppInfo(description, defaultDescription,
00569                     getter_AddRefs(defaultApplication));
00570 
00571   mimeInfo->SetDefaultDescription(defaultDescription);
00572   mimeInfo->SetDefaultApplicationHandler(defaultApplication);
00573 
00574   // Get other nsIMIMEInfo fields from registry, if possible.
00575   if (found)
00576   {
00577       GetMIMEInfoFromRegistry(description, mimeInfo);
00578   }
00579   else {
00580     NS_IF_RELEASE(mimeInfo); // we failed to really find an entry in the registry
00581   }
00582 
00583   return mimeInfo;
00584 }
00585 
00586 already_AddRefed<nsIMIMEInfo> nsOSHelperAppService::GetMIMEInfoFromOS(const nsACString& aMIMEType, const nsACString& aFileExt, PRBool *aFound)
00587 {
00588   *aFound = PR_TRUE;
00589 
00590   const nsCString& flatType = PromiseFlatCString(aMIMEType);
00591   const nsCString& flatExt = PromiseFlatCString(aFileExt);
00592 
00593   nsAutoString fileExtension;
00594   /* XXX The Equals is a gross hack to wallpaper over the most common Win32
00595    * extension issues caused by the fix for bug 116938.  See bug
00596    * 120327, comment 271 for why this is needed.  Not even sure we
00597    * want to remove this once we have fixed all this stuff to work
00598    * right; any info we get from the OS on this type is pretty much
00599    * useless....
00600    * We'll do extension-based lookup for this type later in this function.
00601    */
00602   if (!aMIMEType.LowerCaseEqualsLiteral(APPLICATION_OCTET_STREAM)) {
00603     // (1) try to use the windows mime database to see if there is a mapping to a file extension
00604     // (2) try to see if we have some left over 4.x registry info we can peek at...
00605     GetExtensionFromWindowsMimeDatabase(aMIMEType, fileExtension);
00606     LOG(("Windows mime database: extension '%s'\n", fileExtension.get()));
00607     if (fileExtension.IsEmpty()) {
00608       GetExtensionFrom4xRegistryInfo(aMIMEType, fileExtension);
00609       LOG(("4.x Registry: extension '%s'\n", fileExtension.get()));
00610     }
00611   }
00612   // If we found an extension for the type, do the lookup
00613   nsMIMEInfoWin* mi = nsnull;
00614   if (!fileExtension.IsEmpty())
00615     mi = GetByExtension(fileExtension, flatType.get()).get();
00616   LOG(("Extension lookup on '%s' found: 0x%p\n", fileExtension.get(), mi));
00617 
00618   PRBool hasDefault = PR_FALSE;
00619   if (mi) {
00620     mi->GetHasDefaultHandler(&hasDefault);
00621     // OK. We might have the case that |aFileExt| is a valid extension for the
00622     // mimetype we were given. In that case, we do want to append aFileExt
00623     // to the mimeinfo that we have. (E.g.: We are asked for video/mpeg and
00624     // .mpg, but the primary extension for video/mpeg is .mpeg. But because
00625     // .mpg is an extension for video/mpeg content, we want to append it)
00626     if (!aFileExt.IsEmpty() && typeFromExtEquals(NS_ConvertUTF8toUTF16(flatExt).get(), flatType.get())) {
00627       LOG(("Appending extension '%s' to mimeinfo, because its mimetype is '%s'\n",
00628            flatExt.get(), flatType.get()));
00629       PRBool extExist = PR_FALSE;
00630       mi->ExtensionExists(aFileExt, &extExist);
00631       if (!extExist)
00632         mi->AppendExtension(aFileExt);
00633     }
00634   }
00635   if (!mi || !hasDefault) {
00636     nsRefPtr<nsMIMEInfoWin> miByExt =
00637       GetByExtension(NS_ConvertUTF8toUTF16(aFileExt), flatType.get());
00638     LOG(("Ext. lookup for '%s' found 0x%p\n", flatExt.get(), miByExt.get()));
00639     if (!miByExt && mi)
00640       return mi;
00641     if (miByExt && !mi) {
00642       miByExt.swap(mi);
00643       return mi;
00644     }
00645     if (!miByExt && !mi) {
00646       *aFound = PR_FALSE;
00647       mi = new nsMIMEInfoWin(flatType);
00648       if (mi) {
00649         NS_ADDREF(mi);
00650         if (!aFileExt.IsEmpty())
00651           mi->AppendExtension(aFileExt);
00652       }
00653       
00654       return mi;
00655     }
00656 
00657     // if we get here, mi has no default app. copy from extension lookup.
00658     nsCOMPtr<nsIFile> defaultApp;
00659     nsAutoString desc;
00660     miByExt->GetDefaultDescription(desc);
00661 
00662     mi->SetDefaultDescription(desc);
00663   }
00664   return mi;
00665 }