Back to index

lightning-sunbird  0.9+nobinonly
nsMailWinIntegration.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Thunderbird Windows Integration.
00016  *
00017  * The Initial Developer of the Original Code is
00018  *   Scott MacGregor <mscott@mozilla.org>.
00019  * Portions created by the Initial Developer are Copyright (C) 2006
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include "nsMailWinIntegration.h"
00039 #include "nsIServiceManager.h"
00040 #include "nsICategoryManager.h"
00041 #include "nsCRT.h"
00042 #include "nsIStringBundle.h"
00043 #include "nsNativeCharsetUtils.h"
00044 #include "nsIPrefService.h"
00045 #ifndef __MINGW32__
00046 #include "nsIMapiSupport.h"
00047 #endif
00048 #include "shlobj.h"
00049 #include "windows.h"
00050 #include "shellapi.h"
00051 #include "nsILocalFile.h"
00052 
00053 #include <mbstring.h>
00054 
00055 #ifndef MAX_BUF
00056 #define MAX_BUF 4096
00057 #endif
00058 
00059 #define REG_FAILED(val) \
00060   (val != ERROR_SUCCESS)
00061 
00062 NS_IMPL_ISUPPORTS1(nsWindowsShellService, nsIShellService)
00063 
00064 static nsresult
00065 OpenUserKeyForReading(HKEY aStartKey, const char* aKeyName, HKEY* aKey)
00066 {
00067   DWORD result = ::RegOpenKeyEx(aStartKey, aKeyName, 0, KEY_READ, aKey);
00068 
00069   switch (result) 
00070   {
00071     case ERROR_SUCCESS:
00072       break;
00073     case ERROR_ACCESS_DENIED:
00074       return NS_ERROR_FILE_ACCESS_DENIED;
00075     case ERROR_FILE_NOT_FOUND:
00076       if (aStartKey == HKEY_LOCAL_MACHINE) 
00077       {
00078         // prevent infinite recursion on the second pass through here if 
00079         // ::RegOpenKeyEx fails in the all-users case. 
00080         return NS_ERROR_NOT_AVAILABLE;
00081       }
00082       return OpenUserKeyForReading(HKEY_LOCAL_MACHINE, aKeyName, aKey);
00083   }
00084   return NS_OK;
00085 }
00086 
00087 // Sets the default mail registry keys for Windows versions prior to Vista.
00088 // Try to open / create the key in HKLM and if that fails try to do the same
00089 // in HKCU. Though this is not strictly the behavior I would expect it is the
00090 // same behavior that Firefox and IE has when setting the default browser previous to Vista.
00091 static nsresult OpenKeyForWriting(HKEY aStartKey, const char* aKeyName, HKEY* aKey, PRBool aHKLMOnly)
00092 {
00093   DWORD dwDisp = 0;
00094   DWORD rv = ::RegCreateKeyEx(aStartKey, aKeyName, 0, NULL, 0,
00095                               KEY_READ | KEY_WRITE, NULL, aKey, &dwDisp);
00096 
00097   switch (rv) 
00098   {
00099     case ERROR_SUCCESS:
00100       break;
00101     case ERROR_ACCESS_DENIED:
00102       if (aHKLMOnly || aStartKey == HKEY_CURRENT_USER)
00103         return NS_ERROR_FILE_ACCESS_DENIED;
00104       // fallback to HKCU immediately on access denied since we won't be able
00105       // to create the key.
00106       return OpenKeyForWriting(HKEY_CURRENT_USER, aKeyName, aKey, aHKLMOnly);
00107     case ERROR_FILE_NOT_FOUND:
00108       rv = ::RegCreateKey(aStartKey, aKeyName, aKey);
00109       if (rv != ERROR_SUCCESS)
00110       {
00111         if (aHKLMOnly || aStartKey == HKEY_CURRENT_USER) 
00112         {
00113           // prevent infinite recursion on the second pass through here if 
00114           // ::RegCreateKey fails in the current user case.
00115           return NS_ERROR_FILE_ACCESS_DENIED;
00116         }
00117         return OpenKeyForWriting(HKEY_CURRENT_USER, aKeyName, aKey, aHKLMOnly);
00118       }
00119   }
00120 
00121   return NS_OK;
00122 }
00123 
00125 // Default Mail Registry Settings
00127 
00128 typedef enum { NO_SUBSTITUTION    = 0x00,
00129                APP_PATH_SUBSTITUTION  = 0x01,
00130                APPNAME_SUBSTITUTION = 0x02,
00131                UNINST_PATH_SUBSTITUTION  = 0x04,
00132                MAPIDLL_PATH_SUBSTITUTION = 0x08,
00133                HKLM_ONLY = 0x10,
00134                USE_FOR_DEFAULT_TEST = 0x20} SettingFlags;
00135 
00136 #define CLS "SOFTWARE\\Classes\\"
00137 #define MAILCLIENTS "SOFTWARE\\Clients\\Mail\\"
00138 #define NEWSCLIENTS "SOFTWARE\\Clients\\News\\"
00139 #define MOZ_CLIENT_MAIL_KEY "Software\\Clients\\Mail"
00140 #define MOZ_CLIENT_NEWS_KEY "Software\\Clients\\News"
00141 #define DI "\\DefaultIcon"
00142 #define II "\\InstallInfo" 
00143 
00144 // APP_REG_NAME_MAIL and APP_REG_NAME_NEWS should be kept in synch with
00145 // AppRegNameMail and AppRegNameNews in the installer file: defines.nsi.in
00146 #define APP_REG_NAME_MAIL L"Thunderbird"
00147 #define APP_REG_NAME_NEWS L"Thunderbird (News)"
00148 #define CLS_EML "ThunderbirdEML"
00149 #define CLS_MAILTOURL "Thunderbird.Url.mailto"
00150 #define CLS_NEWSURL "Thunderbird.Url.news"
00151 #define CLS_FEEDURL "Thunderbird.Url.feed"
00152 #define SOP "\\shell\\open\\command"
00153 
00154 // For the InstallInfo HideIconsCommand, ShowIconsCommand, and ReinstallCommand
00155 // registry keys. This must be kept in sync with the uninstaller.
00156 #define UNINSTALL_EXE "\\uninstall\\helper.exe"
00157 #define EXE "thunderbird.exe"
00158 
00159 #define VAL_ICON "%APPPATH%,0"
00160 #define VAL_OPEN "\"%APPPATH%\" \"%1\""
00161 
00162 #define MAKE_KEY_NAME1(PREFIX, MID) \
00163   PREFIX MID
00164 
00165 #define MAKE_KEY_NAME2(PREFIX, MID, SUFFIX) \
00166   PREFIX MID SUFFIX
00167 
00168 static SETTING gMailSettings[] = {
00169   // File Extension Aliases
00170   { MAKE_KEY_NAME1(CLS, ".eml"),    "", CLS_EML, NO_SUBSTITUTION },
00171 
00172   // File Extension Class
00173   { MAKE_KEY_NAME2(CLS, CLS_EML, DI),  "",  VAL_ICON, APP_PATH_SUBSTITUTION },
00174   { MAKE_KEY_NAME2(CLS, CLS_EML, SOP), "",  VAL_OPEN, APP_PATH_SUBSTITUTION},
00175 
00176   // Protocol Handler Class - for Vista and above
00177   { MAKE_KEY_NAME2(CLS, CLS_MAILTOURL, DI),  "", VAL_ICON, APP_PATH_SUBSTITUTION },
00178   { MAKE_KEY_NAME2(CLS, CLS_MAILTOURL, SOP), "", "\"%APPPATH%\" -osint -compose \"%1\"", APP_PATH_SUBSTITUTION },
00179   
00180   // Protocol Handlers
00181   { MAKE_KEY_NAME2(CLS, "mailto", DI),  "", VAL_ICON, APP_PATH_SUBSTITUTION},
00182   { MAKE_KEY_NAME2(CLS, "mailto", SOP), "", "\"%APPPATH%\" -osint -compose \"%1\"", APP_PATH_SUBSTITUTION | USE_FOR_DEFAULT_TEST}, 
00183 
00184   // Mail Client Keys
00185   { MAKE_KEY_NAME1(MAILCLIENTS, "%APPNAME%"),  
00186     "DLLPath", 
00187     "%MAPIDLLPATH%", 
00188     MAPIDLL_PATH_SUBSTITUTION | HKLM_ONLY | APPNAME_SUBSTITUTION },
00189   { MAKE_KEY_NAME2(MAILCLIENTS, "%APPNAME%", II),
00190     "HideIconsCommand",
00191     "\"%UNINSTPATH%\" /HideShortcuts",
00192     UNINST_PATH_SUBSTITUTION | APPNAME_SUBSTITUTION | HKLM_ONLY },
00193   { MAKE_KEY_NAME2(MAILCLIENTS, "%APPNAME%", II),
00194     "ReinstallCommand",
00195     "\"%UNINSTPATH%\" /SetAsDefaultAppGlobal",
00196     UNINST_PATH_SUBSTITUTION | APPNAME_SUBSTITUTION | HKLM_ONLY },
00197   { MAKE_KEY_NAME2(MAILCLIENTS, "%APPNAME%", II),
00198     "ShowIconsCommand",
00199     "\"%UNINSTPATH%\" /ShowShortcuts",
00200     UNINST_PATH_SUBSTITUTION | APPNAME_SUBSTITUTION | HKLM_ONLY },
00201   { MAKE_KEY_NAME2(MAILCLIENTS, "%APPNAME%", DI),  
00202     "", 
00203     "%APPPATH%,0", 
00204     APP_PATH_SUBSTITUTION | APPNAME_SUBSTITUTION | HKLM_ONLY },
00205   { MAKE_KEY_NAME2(MAILCLIENTS, "%APPNAME%", SOP), 
00206     "", 
00207     "\"%APPPATH%\" -mail",   
00208     APP_PATH_SUBSTITUTION | APPNAME_SUBSTITUTION | HKLM_ONLY },
00209   { MAKE_KEY_NAME1(MAILCLIENTS, "%APPNAME%\\shell\\properties\\command"),
00210     "", 
00211     "\"%APPPATH%\" -options",   
00212     APP_PATH_SUBSTITUTION | APPNAME_SUBSTITUTION | HKLM_ONLY },
00213 };
00214 
00215 static SETTING gNewsSettings[] = {
00216    // Protocol Handler Class - for Vista and above
00217   { MAKE_KEY_NAME2(CLS, CLS_NEWSURL, DI),  "", VAL_ICON, APP_PATH_SUBSTITUTION },
00218   { MAKE_KEY_NAME2(CLS, CLS_NEWSURL, SOP), "", "\"%APPPATH%\" -osint -mail \"%1\"", APP_PATH_SUBSTITUTION },
00219 
00220   // Protocol Handlers
00221   { MAKE_KEY_NAME2(CLS, "news", DI),  "", VAL_ICON, APP_PATH_SUBSTITUTION},
00222   { MAKE_KEY_NAME2(CLS, "news", SOP), "", "\"%APPPATH%\" -osint -mail \"%1\"", APP_PATH_SUBSTITUTION | USE_FOR_DEFAULT_TEST},
00223   { MAKE_KEY_NAME2(CLS, "nntp", DI),  "", VAL_ICON, APP_PATH_SUBSTITUTION},
00224   { MAKE_KEY_NAME2(CLS, "nntp", SOP), "", "\"%APPPATH%\" -osint -mail \"%1\"", APP_PATH_SUBSTITUTION | USE_FOR_DEFAULT_TEST}, 
00225   { MAKE_KEY_NAME2(CLS, "snews", DI),  "", VAL_ICON, APP_PATH_SUBSTITUTION},
00226   { MAKE_KEY_NAME2(CLS, "snews", SOP), "", "\"%APPPATH%\" -osint -mail \"%1\"", APP_PATH_SUBSTITUTION}, 
00227 
00228   // News Client Keys
00229   { MAKE_KEY_NAME1(NEWSCLIENTS, "%APPNAME%"),  
00230     "DLLPath", 
00231     "%MAPIDLLPATH%", 
00232     MAPIDLL_PATH_SUBSTITUTION | APPNAME_SUBSTITUTION | HKLM_ONLY },
00233   { MAKE_KEY_NAME2(NEWSCLIENTS, "%APPNAME%", DI),  
00234     "", 
00235     "%APPPATH%,0", 
00236     APP_PATH_SUBSTITUTION | APPNAME_SUBSTITUTION | HKLM_ONLY },
00237   { MAKE_KEY_NAME2(NEWSCLIENTS, "%APPNAME%", SOP), 
00238     "", 
00239     "\"%APPPATH%\" -mail",   
00240     APP_PATH_SUBSTITUTION | APPNAME_SUBSTITUTION | HKLM_ONLY },
00241 };
00242 
00243 static SETTING gFeedSettings[] = {
00244    // Protocol Handler Class - for Vista and above
00245   { MAKE_KEY_NAME2(CLS, CLS_FEEDURL, DI),  "", VAL_ICON, APP_PATH_SUBSTITUTION },
00246   { MAKE_KEY_NAME2(CLS, CLS_FEEDURL, SOP), "", VAL_OPEN, APP_PATH_SUBSTITUTION },
00247   
00248   // Protocol Handlers
00249   { MAKE_KEY_NAME2(CLS, "feed", DI),  "", VAL_ICON, APP_PATH_SUBSTITUTION},
00250   { MAKE_KEY_NAME2(CLS, "feed", SOP), "", "\"%APPPATH%\" -osint -mail \"%1\"", APP_PATH_SUBSTITUTION | USE_FOR_DEFAULT_TEST},
00251 };
00252 
00253 nsresult nsWindowsShellService::Init()
00254 {
00255   nsresult rv;
00256 
00257   nsCOMPtr<nsIStringBundleService> bundleService(do_GetService("@mozilla.org/intl/stringbundle;1", &rv));
00258   NS_ENSURE_SUCCESS(rv, rv);
00259   
00260   nsCOMPtr<nsIStringBundle> bundle, brandBundle;
00261   rv = bundleService->CreateBundle("chrome://branding/locale/brand.properties", getter_AddRefs(brandBundle));
00262   NS_ENSURE_SUCCESS(rv, rv);
00263 
00264   brandBundle->GetStringFromName(NS_LITERAL_STRING("brandFullName").get(),
00265                                  getter_Copies(mBrandFullName));
00266   brandBundle->GetStringFromName(NS_LITERAL_STRING("brandShortName").get(),
00267                                  getter_Copies(mBrandShortName));
00268 
00269   char appPath[MAX_BUF];
00270   if (!::GetModuleFileName(0, appPath, MAX_BUF))
00271     return NS_ERROR_FAILURE;
00272 
00273   mAppLongPath = appPath;
00274 
00275   nsCOMPtr<nsILocalFile> lf;
00276   rv = NS_NewNativeLocalFile(mAppLongPath, PR_TRUE,
00277                                       getter_AddRefs(lf));
00278   NS_ENSURE_SUCCESS(rv, rv);
00279 
00280   nsCOMPtr<nsIFile> appDir;
00281   rv = lf->GetParent(getter_AddRefs(appDir));
00282   NS_ENSURE_SUCCESS(rv, rv);
00283 
00284   appDir->GetNativePath(mUninstallPath);
00285   mUninstallPath.Append(UNINSTALL_EXE);
00286 
00287   // Support short path to the exe so if it is already set the user is not
00288   // prompted to set the default mail client again.
00289   if (!::GetShortPathName(appPath, appPath, MAX_BUF))
00290     return NS_ERROR_FAILURE;
00291 
00292   ToUpperCase(mAppShortPath = appPath);
00293 
00294   rv = NS_NewNativeLocalFile(mAppLongPath, PR_TRUE, getter_AddRefs(lf));
00295   NS_ENSURE_SUCCESS(rv, rv);
00296 
00297   rv = lf->SetNativeLeafName(nsDependentCString("mozMapi32.dll"));
00298   NS_ENSURE_SUCCESS(rv, rv);
00299 
00300   return lf->GetNativePath(mMapiDLLPath);
00301 }
00302 
00303 nsWindowsShellService::nsWindowsShellService()
00304 :mCheckedThisSession(PR_FALSE)
00305 {
00306 }
00307 
00308 NS_IMETHODIMP
00309 nsWindowsShellService::IsDefaultClient(PRBool aStartupCheck, PRUint16 aApps, PRBool *aIsDefaultClient)
00310 {
00311   if (IsDefaultClientVista(aStartupCheck, aApps, aIsDefaultClient))
00312     return NS_OK;
00313 
00314   *aIsDefaultClient = PR_TRUE;
00315 
00316   // for each type, 
00317   if (aApps & nsIShellService::MAIL)
00318     *aIsDefaultClient &= TestForDefault(gMailSettings, sizeof(gMailSettings)/sizeof(SETTING));
00319   if (aApps & nsIShellService::NEWS)
00320     *aIsDefaultClient &= TestForDefault(gNewsSettings, sizeof(gNewsSettings)/sizeof(SETTING));
00321   if (aApps & nsIShellService::RSS)
00322     *aIsDefaultClient &= TestForDefault(gFeedSettings, sizeof(gFeedSettings)/sizeof(SETTING));
00323 
00324   // If this is the first mail window, maintain internal state that we've
00325   // checked this session (so that subsequent window opens don't show the 
00326   // default client dialog).
00327   if (aStartupCheck)
00328     mCheckedThisSession = PR_TRUE;
00329 
00330   return NS_OK;
00331 }
00332 
00333 DWORD
00334 nsWindowsShellService::DeleteRegKeyDefaultValue(HKEY baseKey, const char *keyName)
00335 {
00336   HKEY key;
00337   DWORD rc = ::RegOpenKeyEx(baseKey, keyName, 0, KEY_WRITE, &key);
00338   if (rc == ERROR_SUCCESS) {
00339     rc = ::RegDeleteValue(key, "");
00340     ::RegCloseKey(key);
00341   }
00342   return rc;
00343 }
00344 
00345 NS_IMETHODIMP
00346 nsWindowsShellService::SetDefaultClient(PRBool aForAllUsers, PRUint16 aApps)
00347 {
00348   // Delete the protocol and file handlers under HKCU if they exist. This way
00349   // the HKCU registry is cleaned up when HKLM is writeable or if it isn't
00350   // the values will then be added under HKCU.
00351   if (aApps & nsIShellService::MAIL)
00352   {
00353     (void)DeleteRegKey(HKEY_CURRENT_USER, "Software\\Classes\\ThunderbirdEML");
00354     (void)DeleteRegKeyDefaultValue(HKEY_CURRENT_USER, "Software\\Classes\\.eml");
00355     (void)DeleteRegKey(HKEY_CURRENT_USER, "Software\\Classes\\mailto\\shell\\open");
00356     (void)DeleteRegKey(HKEY_CURRENT_USER, "Software\\Classes\\mailto\\DefaultIcon");
00357   }
00358 
00359   if (aApps & nsIShellService::NEWS)
00360   {
00361     (void)DeleteRegKey(HKEY_CURRENT_USER, "Software\\Classes\\news\\shell\\open");
00362     (void)DeleteRegKey(HKEY_CURRENT_USER, "Software\\Classes\\news\\DefaultIcon");
00363     (void)DeleteRegKey(HKEY_CURRENT_USER, "Software\\Classes\\snews\\shell\\open");
00364     (void)DeleteRegKey(HKEY_CURRENT_USER, "Software\\Classes\\snews\\DefaultIcon");
00365     (void)DeleteRegKey(HKEY_CURRENT_USER, "Software\\Classes\\nntp\\shell\\open");
00366     (void)DeleteRegKey(HKEY_CURRENT_USER, "Software\\Classes\\nntp\\DefaultIcon");
00367   }
00368 
00369   if (aApps & nsIShellService::RSS)
00370   {
00371     (void)DeleteRegKey(HKEY_CURRENT_USER, "Software\\Classes\\feed\\shell\\open");
00372     (void)DeleteRegKey(HKEY_CURRENT_USER, "Software\\Classes\\feed\\DefaultIcon");
00373   }
00374 
00375   if (SetDefaultClientVista(aApps))
00376     return NS_OK;
00377 
00378   nsresult rv = NS_OK;
00379   if (aApps & nsIShellService::MAIL)
00380     rv |= setDefaultMail();
00381 
00382   if (aApps & nsIShellService::NEWS)
00383     rv |= setDefaultNews();
00384 
00385   if (aApps & nsIShellService::RSS)
00386     setKeysForSettings(gFeedSettings, sizeof(gFeedSettings)/sizeof(SETTING), 
00387                        NS_ConvertUTF16toUTF8(mBrandFullName).get());
00388 
00389   // Refresh the Shell
00390   SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, 0, 0);
00391   return rv;
00392 }
00393 
00394 NS_IMETHODIMP
00395 nsWindowsShellService::GetShouldCheckDefaultClient(PRBool* aResult)
00396 {
00397   if (mCheckedThisSession) 
00398   {
00399     *aResult = PR_FALSE;
00400     return NS_OK;
00401   }
00402 
00403   nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
00404   return prefs->GetBoolPref("mail.shell.checkDefaultClient", aResult);
00405 }
00406 
00407 NS_IMETHODIMP
00408 nsWindowsShellService::SetShouldCheckDefaultClient(PRBool aShouldCheck)
00409 {
00410   nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
00411   return prefs->SetBoolPref("mail.shell.checkDefaultClient", aShouldCheck);
00412 }
00413 
00414 nsresult
00415 nsWindowsShellService::setDefaultMail()
00416 {
00417   nsresult rv;
00418   NS_ConvertUTF16toUTF8 appName(mBrandFullName);
00419   setKeysForSettings(gMailSettings, sizeof(gMailSettings)/sizeof(SETTING), appName.get());
00420 
00421   // at least for now, this key needs to be written to HKLM instead of HKCU 
00422   // which is where the windows operating system looks (at least on Win XP and earlier)
00423   SetRegKey(NS_LITERAL_CSTRING(MOZ_CLIENT_MAIL_KEY).get(), "", appName.get(), PR_TRUE);
00424 
00425   nsCAutoString nativeFullName;
00426   // For now, we use 'A' APIs (see bug 240272, 239279)
00427   NS_CopyUnicodeToNative(mBrandFullName, nativeFullName);
00428 
00429   nsCAutoString key1(NS_LITERAL_CSTRING(MAILCLIENTS));
00430   key1.Append(appName);
00431   key1.Append("\\");
00432   SetRegKey(key1.get(), "", nativeFullName.get(), PR_TRUE);
00433 
00434   // Set the Options and Safe Mode start menu context menu item labels
00435   nsCOMPtr<nsIStringBundle> bundle;
00436   nsCOMPtr<nsIStringBundleService> bundleService(do_GetService("@mozilla.org/intl/stringbundle;1", &rv));
00437   NS_ENSURE_SUCCESS(rv, rv);
00438 
00439   rv = bundleService->CreateBundle("chrome://messenger/locale/shellservice.properties", getter_AddRefs(bundle));
00440   NS_ENSURE_SUCCESS(rv, rv);
00441   nsCAutoString optionsKey(NS_LITERAL_CSTRING(MAILCLIENTS "%APPNAME%\\shell\\properties"));
00442   optionsKey.ReplaceSubstring("%APPNAME%", appName.get());
00443 
00444   const PRUnichar* brandNameStrings[] = { mBrandShortName.get() };
00445 
00446   // Set the Options menu item
00447   nsXPIDLString optionsTitle;
00448   bundle->FormatStringFromName(NS_LITERAL_STRING("optionsLabel").get(),
00449                                brandNameStrings, 1, getter_Copies(optionsTitle));
00450   // Set the registry keys
00451   nsCAutoString nativeTitle;
00452   // For the now, we use 'A' APIs (see bug 240272,  239279)
00453   NS_CopyUnicodeToNative(optionsTitle, nativeTitle);
00454   SetRegKey(optionsKey.get(), "", nativeTitle.get(), PR_TRUE);
00455 #ifndef __MINGW32__
00456   // Tell the MAPI Service to register the mapi proxy dll now that we are the default mail application
00457   nsCOMPtr<nsIMapiSupport> mapiService (do_GetService(NS_IMAPISUPPORT_CONTRACTID, &rv));
00458   NS_ENSURE_SUCCESS(rv, rv);
00459   return mapiService->RegisterServer();
00460 #else
00461   return NS_OK;
00462 #endif
00463 }
00464 
00465 nsresult
00466 nsWindowsShellService::setDefaultNews()
00467 {
00468   NS_ConvertUTF16toUTF8 appName(mBrandFullName);
00469   setKeysForSettings(gNewsSettings, sizeof(gNewsSettings)/sizeof(SETTING), appName.get());
00470 
00471   // at least for now, this key needs to be written to HKLM instead of HKCU 
00472   // which is where the windows operating system looks (at least on Win XP and earlier)
00473   SetRegKey(NS_LITERAL_CSTRING(MOZ_CLIENT_NEWS_KEY).get(), "", appName.get(), PR_TRUE);
00474 
00475   nsCAutoString nativeFullName;
00476   // For now, we use 'A' APIs (see bug 240272, 239279)
00477   NS_CopyUnicodeToNative(mBrandFullName, nativeFullName);
00478   nsCAutoString key1(NS_LITERAL_CSTRING(NEWSCLIENTS));
00479   key1.Append(appName);
00480   key1.Append("\\");
00481   SetRegKey(key1.get(), "", nativeFullName.get(), PR_TRUE);
00482   return NS_OK;
00483 }
00484 
00485 // Utility function to delete a registry subkey.
00486 DWORD
00487 nsWindowsShellService::DeleteRegKey(HKEY baseKey, const char *keyName)
00488 {
00489  // Make sure input subkey isn't null. 
00490  if (!keyName || !::strlen(keyName))
00491    return ERROR_BADKEY;
00492 
00493  DWORD rc;
00494  // Open subkey.
00495  HKEY key;
00496  rc = ::RegOpenKeyEx(baseKey, keyName, 0, KEY_ENUMERATE_SUB_KEYS | DELETE, &key);
00497  
00498  // Continue till we get an error or are done.
00499  while (rc == ERROR_SUCCESS) 
00500  {
00501    char subkeyName[_MAX_PATH];
00502    DWORD len = sizeof subkeyName;
00503    // Get first subkey name.  Note that we always get the
00504    // first one, then delete it.  So we need to get
00505    // the first one next time, also.
00506    rc = ::RegEnumKeyEx(key, 0, subkeyName, &len, 0, 0, 0, 0);
00507    if (rc == ERROR_NO_MORE_ITEMS) 
00508    {
00509      // No more subkeys.  Delete the main one.
00510      rc = ::RegDeleteKey(baseKey, keyName);
00511      break;
00512    } 
00513    if (rc == ERROR_SUCCESS) 
00514    {
00515      // Another subkey, delete it, recursively.
00516      rc = DeleteRegKey(key, subkeyName);
00517    }
00518  }
00519  
00520  // Close the key we opened.
00521  ::RegCloseKey(key);
00522  return rc;
00523 }
00524 
00525 void
00526 nsWindowsShellService::SetRegKey(const char* aKeyName, const char* aValueName, 
00527                                  const char* aValue, PRBool aHKLMOnly)
00528 {
00529   char buf[MAX_BUF];
00530   DWORD len = sizeof buf;
00531 
00532   HKEY theKey;
00533   nsresult rv = OpenKeyForWriting(HKEY_LOCAL_MACHINE, aKeyName, &theKey, aHKLMOnly);
00534   if (NS_FAILED(rv))
00535     return;
00536 
00537   // Get the old value
00538   DWORD result = ::RegQueryValueEx(theKey, aValueName, NULL, NULL, (LPBYTE)buf, &len);
00539 
00540   // Set the new value
00541   if (REG_FAILED(result) || strcmp(buf, aValue) != 0)
00542     ::RegSetValueEx(theKey, aValueName, 0, REG_SZ, 
00543                     (LPBYTE)aValue, nsDependentCString(aValue).Length());
00544   
00545   // Close the key we opened.
00546   ::RegCloseKey(theKey);
00547 }
00548 
00549 /* helper routine. Iterate over the passed in settings object,
00550    testing each key with the USE_FOR_DEFAULT_TEST to see if 
00551    we are handling it. 
00552 */
00553 PRBool
00554 nsWindowsShellService::TestForDefault(SETTING aSettings[], PRInt32 aSize)
00555 {
00556   PRBool isDefault = PR_TRUE;
00557   NS_ConvertUTF16toUTF8 appName(mBrandFullName);
00558   char currValue[MAX_BUF];
00559   SETTING* end = aSettings + aSize;
00560   for (SETTING * settings = aSettings; settings < end; ++settings) 
00561   {
00562     if (settings->flags & USE_FOR_DEFAULT_TEST)
00563     {
00564       nsCAutoString dataLongPath(settings->valueData);
00565       nsCAutoString dataShortPath(settings->valueData);
00566       if (settings->flags & APP_PATH_SUBSTITUTION) {
00567         PRInt32 offset = dataLongPath.Find("%APPPATH%");
00568         dataLongPath.Replace(offset, 9, mAppLongPath);
00569         // Remove the quotes around %APPPATH% in VAL_OPEN for short paths
00570         PRInt32 offsetQuoted = dataShortPath.Find("\"%APPPATH%\"");
00571         if (offsetQuoted != -1)
00572           dataShortPath.Replace(offsetQuoted, 11, mAppShortPath);
00573         else
00574           dataShortPath.Replace(offset, 9, mAppShortPath);
00575       }
00576       
00577       nsCAutoString key(settings->keyName);
00578       if (settings->flags & APPNAME_SUBSTITUTION) 
00579       {
00580         PRInt32 offset = key.Find("%APPNAME%");
00581         key.Replace(offset, 9, appName);
00582       }
00583 
00584       ::ZeroMemory(currValue, sizeof(currValue));
00585       HKEY theKey;
00586       nsresult rv = OpenUserKeyForReading(HKEY_CURRENT_USER, key.get(), &theKey);
00587       if (NS_SUCCEEDED(rv)) 
00588       {
00589         DWORD len = sizeof currValue;
00590         DWORD result = ::RegQueryValueEx(theKey, settings->valueName, NULL, NULL, (LPBYTE)currValue, &len);
00591         // Close the key we opened.
00592         ::RegCloseKey(theKey);
00593         if (REG_FAILED(result) || !dataLongPath.EqualsIgnoreCase(currValue) && !dataShortPath.EqualsIgnoreCase(currValue))
00594         {
00595           // Key wasn't set, or was set to something else (something else became the default client)
00596           isDefault = PR_FALSE;
00597           break;
00598         }
00599       }
00600     }
00601   }  // for each registry key we want to look at
00602 
00603   return isDefault;
00604 }
00605 
00606 
00607 /* helper routine. Iterate over the passed in settings array, setting each key
00608  * in the windows registry.
00609 */
00610 
00611 void 
00612 nsWindowsShellService::setKeysForSettings(SETTING aSettings[], PRInt32 aSize, const char * aAppName)
00613 {
00614   SETTING* settings;
00615   SETTING* end = aSettings + aSize;
00616   PRInt32 offset;
00617 
00618   for (settings = aSettings; settings < end; ++settings) 
00619   {
00620     nsCAutoString data(settings->valueData);
00621     nsCAutoString key(settings->keyName);
00622     if (settings->flags & APP_PATH_SUBSTITUTION) 
00623     {
00624       offset = data.Find("%APPPATH%");
00625       data.Replace(offset, 9, mAppLongPath);
00626     }
00627     if (settings->flags & MAPIDLL_PATH_SUBSTITUTION) 
00628     {
00629       offset = data.Find("%MAPIDLLPATH%");
00630       data.Replace(offset, 13, mMapiDLLPath);
00631     }
00632     if (settings->flags & APPNAME_SUBSTITUTION) 
00633     {
00634       offset = key.Find("%APPNAME%");
00635       key.Replace(offset, 9, aAppName);
00636     }
00637     if (settings->flags & UNINST_PATH_SUBSTITUTION) 
00638     {
00639       offset = data.Find("%UNINSTPATH%"); 
00640       data.Replace(offset, 12, mUninstallPath);
00641     }
00642 
00643     SetRegKey(key.get(), settings->valueName, data.get(), settings->flags & HKLM_ONLY);
00644   }
00645 }
00646 
00647 // Support for versions of shlobj.h that don't include the Vista API's 
00648 #if !defined(IApplicationAssociationRegistration)
00649 
00650 typedef enum tagASSOCIATIONLEVEL
00651 {
00652   AL_MACHINE,
00653   AL_EFFECTIVE,
00654   AL_USER
00655 } ASSOCIATIONLEVEL;
00656 
00657 typedef enum tagASSOCIATIONTYPE
00658 {
00659   AT_FILEEXTENSION,
00660   AT_URLPROTOCOL,
00661   AT_STARTMENUCLIENT,
00662   AT_MIMETYPE
00663 } ASSOCIATIONTYPE;
00664 
00665 MIDL_INTERFACE("4e530b0a-e611-4c77-a3ac-9031d022281b")
00666 IApplicationAssociationRegistration : public IUnknown
00667 {
00668  public:
00669   virtual HRESULT STDMETHODCALLTYPE QueryCurrentDefault(LPCWSTR pszQuery,
00670                                                         ASSOCIATIONTYPE atQueryType,
00671                                                         ASSOCIATIONLEVEL alQueryLevel,
00672                                                         LPWSTR *ppszAssociation) = 0;
00673   virtual HRESULT STDMETHODCALLTYPE QueryAppIsDefault(LPCWSTR pszQuery,
00674                                                       ASSOCIATIONTYPE atQueryType,
00675                                                       ASSOCIATIONLEVEL alQueryLevel,
00676                                                       LPCWSTR pszAppRegistryName,
00677                                                       BOOL *pfDefault) = 0;
00678   virtual HRESULT STDMETHODCALLTYPE QueryAppIsDefaultAll(ASSOCIATIONLEVEL alQueryLevel,
00679                                                          LPCWSTR pszAppRegistryName,
00680                                                          BOOL *pfDefault) = 0;
00681   virtual HRESULT STDMETHODCALLTYPE SetAppAsDefault(LPCWSTR pszAppRegistryName,
00682                                                     LPCWSTR pszSet,
00683                                                     ASSOCIATIONTYPE atSetType) = 0;
00684   virtual HRESULT STDMETHODCALLTYPE SetAppAsDefaultAll(LPCWSTR pszAppRegistryName) = 0;
00685   virtual HRESULT STDMETHODCALLTYPE ClearUserAssociations( void) = 0;
00686 };
00687 #endif
00688 
00689 static const CLSID CLSID_ApplicationAssociationReg = {0x591209C7,0x767B,0x42B2,{0x9F,0xBA,0x44,0xEE,0x46,0x15,0xF2,0xC7}};
00690 static const IID   IID_IApplicationAssociationReg  = {0x4e530b0a,0xe611,0x4c77,{0xa3,0xac,0x90,0x31,0xd0,0x22,0x28,0x1b}};
00691 
00692 PRBool
00693 nsWindowsShellService::IsDefaultClientVista(PRBool aStartupCheck, PRUint16 aApps, PRBool* aIsDefaultClient)
00694 {
00695   IApplicationAssociationRegistration* pAAR;
00696 
00697   HRESULT hr = CoCreateInstance (CLSID_ApplicationAssociationReg,
00698                                  NULL,
00699                                  CLSCTX_INPROC,
00700                                  IID_IApplicationAssociationReg,
00701                                  (void**)&pAAR);
00702   
00703   if (SUCCEEDED(hr))
00704   {
00705     PRBool isDefaultMail = PR_TRUE;
00706     PRBool isDefaultNews = PR_TRUE;
00707     if (aApps & nsIShellService::MAIL)
00708       pAAR->QueryAppIsDefaultAll(AL_EFFECTIVE, APP_REG_NAME_MAIL, &isDefaultMail);
00709     if (aApps & nsIShellService::NEWS)
00710       pAAR->QueryAppIsDefaultAll(AL_EFFECTIVE, APP_REG_NAME_NEWS, &isDefaultNews);
00711 
00712     *aIsDefaultClient = isDefaultNews && isDefaultMail;    
00713     
00714     // If this is the first mail window, maintain internal state that we've
00715     // checked this session (so that subsequent window opens don't show the 
00716     // default browser dialog).
00717     if (aStartupCheck)
00718       mCheckedThisSession = PR_TRUE;
00719     
00720     pAAR->Release();
00721     return PR_TRUE;
00722   }
00723   
00724   return PR_FALSE;
00725 }
00726 
00727 PRBool
00728 nsWindowsShellService::SetDefaultClientVista(PRUint16 aApps)
00729 {
00730   IApplicationAssociationRegistration* pAAR;
00731 
00732   HRESULT hr = CoCreateInstance (CLSID_ApplicationAssociationReg,
00733                                  NULL,
00734                                  CLSCTX_INPROC,
00735                                  IID_IApplicationAssociationReg,
00736                                  (void**)&pAAR);
00737   
00738   if (SUCCEEDED(hr))
00739   {
00740     if (aApps & nsIShellService::MAIL)
00741       hr = pAAR->SetAppAsDefaultAll(APP_REG_NAME_MAIL);
00742     if (aApps & nsIShellService::NEWS)
00743       hr = pAAR->SetAppAsDefaultAll(APP_REG_NAME_NEWS);   
00744     
00745     pAAR->Release();
00746     return PR_TRUE;
00747   }
00748   
00749   return PR_FALSE;
00750 }