/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Bill Law * Dean Tessman * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include #include #include #include "nsString.h" #include "nsIStringBundle.h" #include "nsDirectoryService.h" #include "nsAppDirectoryServiceDefs.h" #include "nsNativeCharsetUtils.h" #ifdef MOZ_XUL_APP #include "nsNativeAppSupportWin.h" #else #include "nsINativeAppSupportWin.h" #include "nsICmdLineHandler.h" #endif #define MOZ_HWND_BROADCAST_MSG_TIMEOUT 5000 #define MOZ_CLIENT_BROWSER_KEY "Software\\Clients\\StartMenuInternet" // Where Mozilla stores its own registry values. const char * const mozillaKeyName = "Software\\Mozilla\\Desktop"; static const char shortcutSuffix[] = " -osint -url \"%1\""; static const char chromeSuffix[] = " -osint -chrome \"%1\""; static const char iconSuffix[] = ",0"; // Returns the (fully-qualified) name of this executable. static nsCString thisApplication() { static nsCAutoString result; if ( result.IsEmpty() ) { char buffer[MAX_PATH] = { 0 }; DWORD len = ::GetModuleFileName( NULL, buffer, sizeof buffer ); len = ::GetShortPathName( buffer, buffer, sizeof buffer ); result = buffer; ToUpperCase(result); } return result; } // Returns the "short" name of this application (in upper case). This is for // use as a StartMenuInternet value. static nsCString shortAppName() { static nsCAutoString result; if ( result.IsEmpty() ) { // Find last backslash in thisApplication(). nsCAutoString thisApp( thisApplication() ); PRInt32 n = thisApp.RFind( "\\" ); if ( n != kNotFound ) { // Use what comes after the last backslash. result = (const char*)thisApp.get() + n + 1; } else { // Use entire string. result = thisApp; } } return result; } static PRBool IsNT() { static PRBool sInitialized = PR_FALSE; static PRBool sIsNT = PR_FALSE; if (!sInitialized) { OSVERSIONINFO osversion; ::ZeroMemory(&osversion, sizeof(OSVERSIONINFO)); osversion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); if (::GetVersionEx(&osversion) && osversion.dwPlatformId == VER_PLATFORM_WIN32_NT) sIsNT = PR_TRUE; sInitialized = PR_TRUE; } return sIsNT; } // RegistryEntry // // Generic registry entry (no saving of previous values). Each is comprised of: // o A base HKEY // o A subkey name. // o An optional value name (empty for the "default" value). // o The registry setting we'd like this entry to have when set. struct RegistryEntry { HKEY baseKey; // e.g., HKEY_CURRENT_USER PRBool isNull; // i.e., should use ::RegDeleteValue nsCString keyName; // Key name. nsCString valueName; // Value name (can be empty, which implies NULL). nsCString setting; // What we set it to (UTF-8). This had better be // nsString to avoid extra string copies, but // this code is not performance-critical. RegistryEntry( HKEY baseKey, const char* keyName, const char* valueName, const char* setting ) : baseKey( baseKey ), isNull( setting == 0 ), keyName( keyName ), valueName( valueName ), setting( setting ? setting : "" ) { } PRBool isAlreadySet() const; nsresult set(); nsresult reset(); nsCString currentSetting( PRBool *currentUndefined = 0 ) const; // Return value name in proper form for passing to ::Reg functions // (i.e., emptry string is converted to a NULL pointer). const char* valueNameArg() const { return valueName.IsEmpty() ? NULL : valueName.get(); } nsCString fullName() const; }; // BoolRegistryEntry // // These are used to store the "windows integration" preferences. // You can query the value via operator void* (i.e., if ( boolPref )... ). // These are stored under HKEY_LOCAL_MACHINE\Software\Mozilla\Desktop. // Set sets the stored value to "1". Reset deletes it (which implies 0). struct BoolRegistryEntry : public RegistryEntry { BoolRegistryEntry( const char *name ) : RegistryEntry( HKEY_LOCAL_MACHINE, mozillaKeyName, name, "1" ) { } operator PRBool(); }; // SavedRegistryEntry // // Like a plain RegistryEntry, but set/reset save/restore the // it had before we set it. struct SavedRegistryEntry : public RegistryEntry { SavedRegistryEntry( HKEY baseKey, const char *keyName, const char *valueName, const char *setting ) : RegistryEntry( baseKey, keyName, valueName, setting ) { } nsresult set(); nsresult reset(); }; // ProtocolRegistryEntry // // For setting entries for a given Internet Shortcut protocol. // The key name is calculated as // HKEY_LOCAL_MACHINE\Software\Classes\protocol\shell\open\command. // The setting is this executable (with appropriate suffix). // Set/reset are trickier in this case. struct ProtocolRegistryEntry : public SavedRegistryEntry { nsCString protocol; ProtocolRegistryEntry( const char* protocol ) : SavedRegistryEntry( HKEY_LOCAL_MACHINE, "", "", thisApplication().get() ), protocol( protocol ) { keyName = "Software\\Classes\\"; keyName += protocol; keyName += "\\shell\\open\\command"; // Append appropriate suffix to setting. if ( this->protocol.Equals( "chrome" ) || this->protocol.Equals( "MozillaXUL" ) ) { // Use "-chrome" command line flag. setting += chromeSuffix; } else { // Use standard "-url" command line flag. setting += shortcutSuffix; } } nsresult set(); nsresult reset(); }; // ProtocolIconRegistryEntry // // For setting icon entries for a given Internet Shortcut protocol. // The key name is calculated as // HKEY_LOCAL_MACHINE\Software\Classes\protocol\DefaultIcon. // The setting is this executable (with appropriate suffix). struct ProtocolIconRegistryEntry : public SavedRegistryEntry { nsCString protocol; ProtocolIconRegistryEntry( const char* aprotocol ) : SavedRegistryEntry( HKEY_LOCAL_MACHINE, "", "", thisApplication().get() ), protocol( aprotocol ) { keyName = NS_LITERAL_CSTRING("Software\\Classes\\") + protocol + NS_LITERAL_CSTRING("\\DefaultIcon"); // Append appropriate suffix to setting. setting += iconSuffix; } }; // DDERegistryEntry // // Like a protocol registry entry, but for the shell\open\ddeexec subkey. // // We need to remove this subkey entirely to ensure we work properly with // various programs on various platforms (see Bugzilla bugs 59078, 58770, etc.). // // We don't try to save everything, though. We do save the known useful info // under the ddeexec subkey: // ddeexec\@ // ddeexec\NoActivateHandler // ddeexec\Application\@ // ddeexec\Topic\@ // // set/reset save/restore these values and remove/restore the ddeexec subkey struct DDERegistryEntry : public SavedRegistryEntry { DDERegistryEntry( const char *protocol ) : SavedRegistryEntry( HKEY_LOCAL_MACHINE, "", "", 0 ), activate( HKEY_LOCAL_MACHINE, "", "NoActivateHandler", 0 ), app( HKEY_LOCAL_MACHINE, "", "", 0 ), topic( HKEY_LOCAL_MACHINE, "", "", 0 ) { // Derive keyName from protocol. keyName = "Software\\Classes\\"; keyName += protocol; keyName += "\\shell\\open\\ddeexec"; // Set subkey names. activate.keyName = keyName; app.keyName = keyName; app.keyName += "\\Application"; topic.keyName = keyName; topic.keyName += "\\Topic"; } nsresult set(); nsresult reset(); SavedRegistryEntry activate, app, topic; }; // FileTypeRegistryEntry // // For setting entries relating to a file extension (or extensions). // This object itself is for the "file type" associated with the extension. // Set/reset manage the mapping from extension to the file type, as well. // The description is taken from defDescKey if available. Otherwise desc // is used. struct FileTypeRegistryEntry : public ProtocolRegistryEntry { nsCString fileType; const char **ext; nsCString desc; nsCString defDescKey; nsCString iconFile; FileTypeRegistryEntry ( const char **ext, const char *fileType, const char *desc, const char *defDescKey, const char *iconFile ) : ProtocolRegistryEntry( fileType ), fileType( fileType ), ext( ext ), desc( desc ), defDescKey(defDescKey), iconFile(iconFile) { } nsresult set(); nsresult reset(); }; // EditableFileTypeRegistryEntry // // Extends FileTypeRegistryEntry by setting an additional handler for an "edit" command. struct EditableFileTypeRegistryEntry : public FileTypeRegistryEntry { EditableFileTypeRegistryEntry( const char **ext, const char *fileType, const char *desc, const char *defDescKey, const char *iconFile ) : FileTypeRegistryEntry( ext, fileType, desc, defDescKey, iconFile ) { } nsresult set(); }; // Generate the "full" name of this registry entry. nsCString RegistryEntry::fullName() const { nsCString result; if ( baseKey == HKEY_CURRENT_USER ) { result = "HKEY_CURRENT_USER\\"; } else if ( baseKey == HKEY_LOCAL_MACHINE ) { result = "HKEY_LOCAL_MACHINE\\"; } else { result = "\\"; } result += keyName; if ( !valueName.IsEmpty() ) { result += "["; result += valueName; result += "]"; } return result; } // Tests whether registry entry already has desired setting. PRBool RegistryEntry::isAlreadySet() const { PRBool result = FALSE; nsCAutoString current( currentSetting() ); result = ( current == setting ); return result; } // Gives registry entry the desired setting. nsresult RegistryEntry::set() { #ifdef DEBUG_law if ( isNull && setting.IsEmpty() ) printf( "Deleting %s\n", fullName().get() ); else printf( "Setting %s=%s\n", fullName().get(), setting.get() ); #endif nsresult result = NS_ERROR_FAILURE; HKEY key; LONG rc = ::RegOpenKey( baseKey, keyName.get(), &key ); // See if key doesn't exist yet... if ( rc == ERROR_FILE_NOT_FOUND ) { rc = ::RegCreateKey( baseKey, keyName.get(), &key ); } if ( rc == ERROR_SUCCESS ) { if ( isNull && setting.IsEmpty() ) { // This means we need to actually remove the value, not merely set it to an // empty string. rc = ::RegDeleteValue( key, valueNameArg() ); if ( rc == ERROR_SUCCESS ) { result = NS_OK; } } else { NS_ConvertUTF8toUTF16 utf16Setting(setting); if (!IsNT()) { // Get current value to see if it is set properly already. char buffer[4096] = { 0 }; DWORD len = sizeof buffer; rc = ::RegQueryValueExA( key, valueNameArg(), NULL, NULL, (LPBYTE)buffer, &len ); nsCAutoString cSetting; NS_CopyUnicodeToNative(utf16Setting, cSetting); if ( rc != ERROR_SUCCESS || !cSetting.Equals(buffer)) { rc = ::RegSetValueExA( key, valueNameArg(), 0, REG_SZ, (LPBYTE)cSetting.get(), cSetting.Length() ); #ifdef DEBUG_law NS_WARN_IF_FALSE( rc == ERROR_SUCCESS, fullName().get() ); #endif if ( rc == ERROR_SUCCESS ) { result = NS_OK; } } else { // Already has desired setting. result = NS_OK; } } else { // Get current value to see if it is set properly already. PRUnichar buffer[4096] = { 0 }; DWORD len = sizeof buffer; NS_ConvertASCIItoUTF16 wValueName(valueNameArg()); rc = ::RegQueryValueExW( key, wValueName.get(), NULL, NULL, (LPBYTE)buffer, &len ); if ( rc != ERROR_SUCCESS || !utf16Setting.Equals(buffer) ) { rc = ::RegSetValueExW( key, wValueName.get(), 0, REG_SZ, (LPBYTE) (utf16Setting.get()), utf16Setting.Length() * 2); if ( rc == ERROR_SUCCESS ) { result = NS_OK; } } else { // Already has desired setting. result = NS_OK; } } // NT } ::RegCloseKey( key ); } else { #ifdef DEBUG_law NS_WARN_IF_FALSE( rc == ERROR_SUCCESS, fullName().get() ); #endif } return result; } // Get current setting, set new one, then save the previous. nsresult SavedRegistryEntry::set() { nsresult rv = NS_OK; PRBool currentlyUndefined = PR_TRUE; nsCAutoString prev( currentSetting( ¤tlyUndefined ) ); // See if value is changing. // We need an extra check for the case where we have an empty entry // and we need to remove it entirely. if ( setting != prev || ( !currentlyUndefined && isNull ) ) { // Set new. rv = RegistryEntry::set(); if ( NS_SUCCEEDED( rv ) ) { // Save old. RegistryEntry tmp( HKEY_LOCAL_MACHINE, "Software\\Mozilla\\Desktop", fullName().get(), prev.get() ); tmp.set(); } } return rv; } // setWindowsXP // // We need to: // a. Make sure this application is registered as a "Start Menu // internet app" under HKLM\Software\Clients\StartMenuInternet. // b. Make this app the default "Start Menu internet app" for this // user. static void setWindowsXP() { // We test for the presence of this subkey as a WindowsXP test. // We do it this way so that vagueries of future Windows versions // are handled as best we can. HKEY key; nsCAutoString baseKey( NS_LITERAL_CSTRING( "Software\\Clients\\StartMenuInternet" ) ); LONG rc = ::RegOpenKey( HKEY_LOCAL_MACHINE, baseKey.get(), &key ); if ( rc == ERROR_SUCCESS ) { // OK, this is WindowsXP (or equivalent). Add this application to the // set registered as "Start Menu internet apps." These entries go // under the subkey MOZILLA.EXE (or whatever the name of this executable is), // that subkey name is generated by the utility function shortAppName. if ( rc == ERROR_SUCCESS ) { // The next 3 go under this subkey. nsCAutoString subkey( baseKey + NS_LITERAL_CSTRING( "\\" ) + shortAppName() ); // Pretty name. This goes into the LocalizedString value. It is the // name of the executable (preceded by '@'), followed by ",-nnn" where // nnn is the resource identifier of the string in the .exe. That value // comes from nsINativeAppSupportWin.h. char buffer[ _MAX_PATH + 8 ]; // Path, plus '@', comma, minus, and digits (5) _snprintf( buffer, sizeof buffer, "@%s,-%d", thisApplication().get(), IDS_STARTMENU_APPNAME ); RegistryEntry tmp_entry1( HKEY_LOCAL_MACHINE, subkey.get(), "LocalizedString", buffer ); tmp_entry1.set(); // Default icon (from .exe resource). RegistryEntry tmp_entry2( HKEY_LOCAL_MACHINE, nsCAutoString( subkey + NS_LITERAL_CSTRING( "\\DefaultIcon" ) ).get(), "", nsCAutoString( thisApplication() + NS_LITERAL_CSTRING( ",0" ) ).get() ); tmp_entry2.set(); // Command to open. RegistryEntry tmp_entry3( HKEY_LOCAL_MACHINE, nsCAutoString( subkey + NS_LITERAL_CSTRING( "\\shell\\open\\command" ) ).get(), "", thisApplication().get() ); tmp_entry3.set(); // "Properties" verb. The default value is the text that will appear in the menu. // The default value under the command subkey is the name of this application, with // arguments to cause the Preferences window to appear. nsCOMPtr bundleService( do_GetService( "@mozilla.org/intl/stringbundle;1" ) ); nsCOMPtr bundle; nsXPIDLString label; if ( bundleService && NS_SUCCEEDED( bundleService->CreateBundle( "chrome://global-platform/locale/nsWindowsHooks.properties", getter_AddRefs( bundle ) ) ) && NS_SUCCEEDED( bundle->GetStringFromName( NS_LITERAL_STRING( "prefsLabel" ).get(), getter_Copies( label ) ) ) ) { // Set the label that will appear in the start menu context menu. RegistryEntry tmp_entry4( HKEY_LOCAL_MACHINE, nsCAutoString( subkey + NS_LITERAL_CSTRING( "\\shell\\properties" ) ).get(), "", NS_ConvertUCS2toUTF8( label ).get() ); tmp_entry4.set(); } RegistryEntry tmp_entry5( HKEY_LOCAL_MACHINE, nsCAutoString( subkey + NS_LITERAL_CSTRING( "\\shell\\properties\\command" ) ).get(), "", nsCAutoString( thisApplication() + NS_LITERAL_CSTRING(" -chrome \"chrome://communicator/content/pref/pref.xul\"") ).get() ); tmp_entry5.set(); // Now we need to select our application as the default start menu internet application. // This is accomplished by first trying to store our subkey name in // HKLM\Software\Clients\StartMenuInternet's default value. See // http://support.microsoft.com/directory/article.asp?ID=KB;EN-US;Q297878 for detail. SavedRegistryEntry hklmAppEntry( HKEY_LOCAL_MACHINE, baseKey.get(), "", shortAppName().get() ); hklmAppEntry.set(); // That may or may not have worked (depending on whether we have sufficient access). if ( hklmAppEntry.currentSetting() == hklmAppEntry.setting ) { // We've set the hklm entry, so we can delete the one under hkcu. SavedRegistryEntry tmp_entry6( HKEY_CURRENT_USER, baseKey.get(), "", 0 ); tmp_entry6.set(); } else { // All we can do is set the default start menu internet app for this user. SavedRegistryEntry tmp_entry7( HKEY_CURRENT_USER, baseKey.get(), "", shortAppName().get() ); } // Notify the system of the changes. ::SendMessageTimeout( HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM)MOZ_CLIENT_BROWSER_KEY, SMTO_NORMAL|SMTO_ABORTIFHUNG, MOZ_HWND_BROADCAST_MSG_TIMEOUT, NULL); } } } // Set this entry and its corresponding DDE entry. The DDE entry // must be turned off to stop Windows from trying to use DDE. nsresult ProtocolRegistryEntry::set() { // If the protocol is http, then we have to do special stuff. // We must take care of this first because setting the "protocol entry" // for http will cause WindowsXP to do stuff automatically for us, // thereby making it impossible for us to propertly reset. if ( protocol.EqualsLiteral( "http" ) ) { setWindowsXP(); } // Set this entry. nsresult rv = SavedRegistryEntry::set(); // Save and set corresponding DDE entry(ies). DDERegistryEntry( protocol.get() ).set(); // Set icon. ProtocolIconRegistryEntry( protocol.get() ).set(); return rv; } // Not being a "saved" entry, we can't restore, so just delete it. nsresult RegistryEntry::reset() { HKEY key; LONG rc = ::RegOpenKey( baseKey, keyName.get(), &key ); if ( rc == ERROR_SUCCESS ) { rc = ::RegDeleteValue( key, valueNameArg() ); #ifdef DEBUG_law if ( rc == ERROR_SUCCESS ) printf( "Deleting key=%s\n", (const char*)fullName().get() ); #endif } return NS_OK; } // Resets registry entry to the saved value (if there is one). We first // ensure that we still "own" that entry (by comparing its value to what // we would set it to). nsresult SavedRegistryEntry::reset() { nsresult result = NS_OK; // Get current setting for this key/value. nsCAutoString current( currentSetting() ); // Test if we "own" it. if ( current == setting ) { // Unset it, then. First get saved value it had previously. PRBool noSavedValue = PR_TRUE; RegistryEntry saved = RegistryEntry( HKEY_LOCAL_MACHINE, mozillaKeyName, fullName().get(), "" ); // There are 3 cases: // - no saved entry // - empty saved entry // - a non-empty saved entry // We delete the current entry in the first case, and restore // the saved entry (empty or otherwise) in the other two. setting = saved.currentSetting( &noSavedValue ); if ( !setting.IsEmpty() || !noSavedValue ) { // Set to previous value. isNull = PR_FALSE; // Since we're resetting and the saved value may be empty, we // need to make sure set() doesn't mistakenly delete this entry. result = RegistryEntry::set(); // Remove saved entry. saved.reset(); } else { // No saved value, just delete this entry. result = RegistryEntry::reset(); } } return result; } // resetWindowsXP // // This function undoes "setWindowsXP," more or less. It only needs to restore the selected // default Start Menu internet application. The registration of this application as one of // the start menu internet apps can remain. There is no check for the presence of anything // because the SaveRegistryEntry::reset calls will have no effect if there is no value at that // location (or, if that value has been changed by another application). static void resetWindowsXP() { NS_NAMED_LITERAL_CSTRING( baseKey, "Software\\Clients\\StartMenuInternet" ); // First, try to restore the HKLM setting. This will fail if either we didn't // set that, or, if we don't have access). SavedRegistryEntry tmp_entry8( HKEY_LOCAL_MACHINE, baseKey.get(), "", shortAppName().get() ); tmp_entry8.reset(); // The HKCU setting is trickier. We may have set it, but we may also have // removed it (see setWindowsXP(), above). We first try to reverse the // setting. If we had removed it, then this will fail. SavedRegistryEntry tmp_entry9( HKEY_CURRENT_USER, baseKey.get(), "", shortAppName().get() ); tmp_entry9.reset(); // Now, try to reverse the removal of this key. This will fail if there is a current // setting, and will only work if this key is unset, and, we have a saved value. SavedRegistryEntry tmp_entry10( HKEY_CURRENT_USER, baseKey.get(), "", 0 ); tmp_entry10.reset(); // Notify the system of the changes. ::SendMessageTimeout( HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM)MOZ_CLIENT_BROWSER_KEY, SMTO_NORMAL|SMTO_ABORTIFHUNG, MOZ_HWND_BROADCAST_MSG_TIMEOUT, NULL); } // Restore this entry and corresponding DDE entry. nsresult ProtocolRegistryEntry::reset() { // Restore this entry. nsresult rv = SavedRegistryEntry::reset(); // Do same for corresponding DDE entry. DDERegistryEntry( protocol.get() ).reset(); // Reset icon. ProtocolIconRegistryEntry( protocol.get() ).reset(); // For http:, on WindowsXP, we need to do some extra cleanup. if ( protocol.EqualsLiteral( "http" ) ) { resetWindowsXP(); } return rv; } static DWORD deleteKey( HKEY baseKey, const char *keyName ) { // Make sure input subkey isn't null. DWORD rc; if ( keyName && ::strlen(keyName) ) { // Open subkey. HKEY key; rc = ::RegOpenKeyEx( baseKey, keyName, 0, KEY_ENUMERATE_SUB_KEYS | DELETE, &key ); // Continue till we get an error or are done. while ( rc == ERROR_SUCCESS ) { char subkeyName[_MAX_PATH]; DWORD len = sizeof subkeyName; // Get first subkey name. Note that we always get the // first one, then delete it. So we need to get // the first one next time, also. rc = ::RegEnumKeyEx( key, 0, subkeyName, &len, 0, 0, 0, 0 ); if ( rc == ERROR_NO_MORE_ITEMS ) { // No more subkeys. Delete the main one. rc = ::RegDeleteKey( baseKey, keyName ); break; } else if ( rc == ERROR_SUCCESS ) { // Another subkey, delete it, recursively. rc = deleteKey( key, subkeyName ); } } // Close the key we opened. ::RegCloseKey( key ); } else { rc = ERROR_BADKEY; } return rc; } // Set the "dde" entry by deleting the main ddexec subkey // under HKLM\Software\Classes\\shell\open. // We "set" the various subkeys in order to preserve useful // information. nsresult DDERegistryEntry::set() { nsresult rv = SavedRegistryEntry::set(); rv = activate.set(); rv = app.set(); rv = topic.set(); // We've saved what we can. Now recurse through this key and // subkeys. This is necessary due to the fact that // ::RegDeleteKey won't work on WinNT (and Win2k?) if there are // subkeys. if ( deleteKey( baseKey, keyName.get() ) != ERROR_SUCCESS ) { rv = NS_ERROR_FAILURE; } return rv; } // Reset the main (ddeexec) value but also the Application and Topic. // We reset the app/topic even though we no longer set them. This // handles cases where the user installed a prior version, and then // upgraded. nsresult DDERegistryEntry::reset() { nsresult rv = SavedRegistryEntry::reset(); rv = activate.reset(); rv = app.reset(); rv = topic.reset(); return rv; } // Return current setting for this registry entry. // Optionally, the caller can ask that a boolean be set to indicate whether // the registry value is undefined. This flag can be used to distinguish // between not defined at all versus simply empty (both of which return an // empty string). nsCString RegistryEntry::currentSetting( PRBool *currentlyUndefined ) const { nsCString result; if ( currentlyUndefined ) { *currentlyUndefined = PR_TRUE; } HKEY key; LONG rc = ::RegOpenKey( baseKey, keyName.get(), &key ); if ( rc == ERROR_SUCCESS ) { if (!IsNT()) { char buffer[4096] = { 0 }; DWORD len = sizeof buffer; rc = ::RegQueryValueExA( key, valueNameArg(), NULL, NULL, (LPBYTE)buffer, &len ); if ( rc == ERROR_SUCCESS ) { nsAutoString uResult; NS_CopyNativeToUnicode(nsDependentCString(buffer), uResult); CopyUTF16toUTF8(uResult, result); if ( currentlyUndefined ) { *currentlyUndefined = PR_FALSE; // Indicate entry is present } } } else { PRUnichar buffer[4096] = { 0 }; DWORD len = sizeof buffer; rc = ::RegQueryValueExW( key, NS_ConvertASCIItoUTF16(valueNameArg()).get(), NULL, NULL, (LPBYTE)buffer, &len ); if ( rc == ERROR_SUCCESS ) { CopyUTF16toUTF8(buffer, result); if ( currentlyUndefined ) { *currentlyUndefined = PR_FALSE; // Indicate entry is present } } } ::RegCloseKey( key ); } return result; } // For each file extension, map it to this entry's file type. // Set the file type so this application opens files of that type. nsresult FileTypeRegistryEntry::set() { nsresult rv = NS_OK; // Set file extensions. for ( int i = 0; NS_SUCCEEDED( rv ) && ext[i]; i++ ) { nsCAutoString thisExt( "Software\\Classes\\" ); thisExt += ext[i]; rv = SavedRegistryEntry( HKEY_LOCAL_MACHINE, thisExt.get(), "", fileType.get() ).set(); } // If OK, set file type opener. if ( NS_SUCCEEDED( rv ) ) { rv = ProtocolRegistryEntry::set(); // If we just created this file type entry, set description and default icon. if ( NS_SUCCEEDED( rv ) ) { nsCAutoString iconFileToUse( "%1" ); nsCAutoString descKey( "Software\\Classes\\" ); descKey += protocol; RegistryEntry descEntry( HKEY_LOCAL_MACHINE, descKey.get(), NULL, "" ); if ( descEntry.currentSetting().IsEmpty() ) { nsCAutoString defaultDescKey( "Software\\Classes\\" ); defaultDescKey += defDescKey; RegistryEntry defaultDescEntry( HKEY_LOCAL_MACHINE, defaultDescKey.get(), NULL, "" ); descEntry.setting = defaultDescEntry.currentSetting(); if ( descEntry.setting.IsEmpty() ) descEntry.setting = desc; descEntry.set(); } nsCAutoString iconKey( "Software\\Classes\\" ); iconKey += protocol; iconKey += "\\DefaultIcon"; if ( !iconFile.Equals(iconFileToUse) ) { iconFileToUse = thisApplication() + NS_LITERAL_CSTRING( ",0" ); // Check to see if there's an icon file name associated with this extension. // If there is, then we need to use this icon file. If not, then we default // to the main app's icon. if ( !iconFile.IsEmpty() ) { nsCOMPtr iconFileSpec; PRBool flagExists; // Use the directory service to get the path to the chrome folder. The // icons will be located in [chrome dir]\icons\default. // The abs path to the icon has to be in the short filename // format, else it won't work under win9x systems. rv = NS_GetSpecialDirectory( NS_APP_CHROME_DIR, getter_AddRefs( iconFileSpec ) ); if ( NS_SUCCEEDED( rv ) ) { iconFileSpec->AppendNative( NS_LITERAL_CSTRING( "icons" ) ); iconFileSpec->AppendNative( NS_LITERAL_CSTRING( "default" ) ); iconFileSpec->AppendNative( iconFile ); // Check to make sure that the icon file exists on disk. iconFileSpec->Exists( &flagExists ); if ( flagExists ) { rv = iconFileSpec->GetNativePath( iconFileToUse ); if ( NS_SUCCEEDED( rv ) ) { TCHAR buffer[MAX_PATH]; DWORD len; // Get the short path name (8.3) format for iconFileToUse // else it won't work under win9x. len = ::GetShortPathName( iconFileToUse.get(), buffer, sizeof buffer ); NS_ASSERTION ( (len > 0) && ( len < sizeof buffer ), "GetShortPathName failed!" ); iconFileToUse.Assign( buffer ); iconFileToUse.Append( NS_LITERAL_CSTRING( ",0" ) ); } } } } } RegistryEntry iconEntry( HKEY_LOCAL_MACHINE, iconKey.get(), NULL, iconFileToUse.get() ); if( !iconEntry.currentSetting().Equals( iconFileToUse ) ) iconEntry.set(); } } return rv; } // Basically, the inverse of set(). // First, reset the opener for this entry's file type. // Then, reset the file type associated with each extension. nsresult FileTypeRegistryEntry::reset() { nsresult rv = ProtocolRegistryEntry::reset(); for ( int i = 0; ext[ i ]; i++ ) { nsCAutoString thisExt( "Software\\Classes\\" ); thisExt += ext[i]; (void)SavedRegistryEntry( HKEY_LOCAL_MACHINE, thisExt.get(), "", fileType.get() ).reset(); } return rv; } // Do inherited set() and also set key for edit (with -edit option). // // Note: We make the rash assumption that we "own" this filetype (aka "protocol"). // If we ever start commandeering some other file type then this may have to be // rethought. The solution is to override reset() and undo this (and make the // "edit" entry a SavedRegistryEntry). nsresult EditableFileTypeRegistryEntry::set() { nsresult rv = FileTypeRegistryEntry::set(); #ifndef MOZ_XUL_APP if ( NS_SUCCEEDED( rv ) ) { // only set this if we support "-edit" on the command-line nsCOMPtr editorService = do_GetService( "@mozilla.org/commandlinehandler/general-startup;1?type=edit", &rv ); if ( NS_SUCCEEDED( rv) ) { nsCAutoString editKey( "Software\\Classes\\" ); editKey += protocol; editKey += "\\shell\\edit\\command"; nsCAutoString editor( thisApplication() ); editor += " -edit \"%1\""; rv = RegistryEntry( HKEY_LOCAL_MACHINE, editKey.get(), "", editor.get() ).set(); } } #endif return rv; } // Convert current registry setting to boolean. BoolRegistryEntry::operator PRBool() { return currentSetting().Equals( "1" ) ? PR_TRUE : PR_FALSE; }