summaryrefslogtreecommitdiffstats
path: root/comm/mail/components/shell/nsWindowsShellService.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mail/components/shell/nsWindowsShellService.cpp')
-rw-r--r--comm/mail/components/shell/nsWindowsShellService.cpp329
1 files changed, 329 insertions, 0 deletions
diff --git a/comm/mail/components/shell/nsWindowsShellService.cpp b/comm/mail/components/shell/nsWindowsShellService.cpp
new file mode 100644
index 0000000000..e82cf0ed0e
--- /dev/null
+++ b/comm/mail/components/shell/nsWindowsShellService.cpp
@@ -0,0 +1,329 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsWindowsShellService.h"
+#include "nsIServiceManager.h"
+#include "nsICategoryManager.h"
+#include "nsNativeCharsetUtils.h"
+#include "nsIPrefService.h"
+#include "windows.h"
+#include "shellapi.h"
+#include "nsIFile.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsUnicharUtils.h"
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+#include "nsIProperties.h"
+#include "nsString.h"
+
+#ifdef _WIN32_WINNT
+# undef _WIN32_WINNT
+#endif
+#define _WIN32_WINNT 0x0600
+#define INITGUID
+#include <shlobj.h>
+
+#include <mbstring.h>
+
+#ifndef MAX_BUF
+# define MAX_BUF 4096
+#endif
+
+#define REG_FAILED(val) (val != ERROR_SUCCESS)
+
+NS_IMPL_ISUPPORTS(nsWindowsShellService, nsIShellService,
+ nsIToolkitShellService)
+
+static nsresult OpenKeyForReading(HKEY aKeyRoot, const nsAString& aKeyName,
+ HKEY* aKey) {
+ const nsString& flatName = PromiseFlatString(aKeyName);
+
+ DWORD res = ::RegOpenKeyExW(aKeyRoot, flatName.get(), 0, KEY_READ, aKey);
+ switch (res) {
+ case ERROR_SUCCESS:
+ break;
+ case ERROR_ACCESS_DENIED:
+ return NS_ERROR_FILE_ACCESS_DENIED;
+ case ERROR_FILE_NOT_FOUND:
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ return NS_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Default Mail Registry Settings
+///////////////////////////////////////////////////////////////////////////////
+
+typedef enum {
+ NO_SUBSTITUTION = 0x00,
+ APP_PATH_SUBSTITUTION = 0x01
+} SettingFlags;
+
+// APP_REG_NAME_MAIL and APP_REG_NAME_NEWS should be kept in synch with
+// AppRegNameMail and AppRegNameNews in the installer file: defines.nsi.in
+#define APP_REG_NAME_MAIL L"Thunderbird"
+#define APP_REG_NAME_NEWS L"Thunderbird (News)"
+#define APP_REG_NAME_CALENDAR L"Thunderbird (Calendar)"
+#define CLS_EML "ThunderbirdEML"
+#define CLS_MAILTOURL "Thunderbird.Url.mailto"
+#define CLS_MIDURL "Thunderbird.Url.mid"
+#define CLS_NEWSURL "Thunderbird.Url.news"
+#define CLS_FEEDURL "Thunderbird.Url.feed"
+#define CLS_WEBCALURL "Thunderbird.Url.webcal"
+#define CLS_ICS "ThunderbirdICS"
+#define SOP "\\shell\\open\\command"
+#define VAL_OPEN "\"%APPPATH%\" \"%1\""
+#define VAL_MAIL_OPEN "\"%APPPATH%\" -osint -mail \"%1\""
+#define VAL_COMPOSE_OPEN "\"%APPPATH%\" -osint -compose \"%1\""
+
+#define MAKE_KEY_NAME1(PREFIX, MID) PREFIX MID
+
+static SETTING gMailSettings[] = {
+ // File Extension Class
+ {".eml", "", CLS_EML, NO_SUBSTITUTION},
+
+ // File Extension Class
+ {MAKE_KEY_NAME1(CLS_EML, SOP), "", VAL_OPEN, APP_PATH_SUBSTITUTION},
+
+ // Protocol Handler Class - for Vista and above
+ {MAKE_KEY_NAME1(CLS_MAILTOURL, SOP), "", VAL_COMPOSE_OPEN,
+ APP_PATH_SUBSTITUTION},
+ {MAKE_KEY_NAME1(CLS_MIDURL, SOP), "", VAL_OPEN, APP_PATH_SUBSTITUTION},
+
+ // Protocol Handlers
+ {MAKE_KEY_NAME1("mailto", SOP), "", VAL_COMPOSE_OPEN,
+ APP_PATH_SUBSTITUTION},
+ {MAKE_KEY_NAME1("mid", SOP), "", VAL_OPEN, APP_PATH_SUBSTITUTION},
+};
+
+static SETTING gNewsSettings[] = {
+ // Protocol Handler Class - for Vista and above
+ {MAKE_KEY_NAME1(CLS_NEWSURL, SOP), "", VAL_MAIL_OPEN,
+ APP_PATH_SUBSTITUTION},
+
+ // Protocol Handlers
+ {MAKE_KEY_NAME1("news", SOP), "", VAL_MAIL_OPEN, APP_PATH_SUBSTITUTION},
+ {MAKE_KEY_NAME1("nntp", SOP), "", VAL_MAIL_OPEN, APP_PATH_SUBSTITUTION},
+};
+
+static SETTING gCalendarSettings[] = {
+ // File Extension Class
+ {".ics", "", CLS_ICS, NO_SUBSTITUTION},
+
+ // File Extension Class
+ {MAKE_KEY_NAME1(CLS_ICS, SOP), "", VAL_OPEN, APP_PATH_SUBSTITUTION},
+
+ // Protocol Handlers
+ {MAKE_KEY_NAME1(CLS_WEBCALURL, SOP), "", VAL_OPEN, APP_PATH_SUBSTITUTION},
+ {MAKE_KEY_NAME1("webcal", SOP), "", VAL_OPEN, APP_PATH_SUBSTITUTION},
+ {MAKE_KEY_NAME1("webcals", SOP), "", VAL_OPEN, APP_PATH_SUBSTITUTION},
+};
+
+nsresult GetHelperPath(nsAutoString& aPath) {
+ nsresult rv;
+ nsCOMPtr<nsIProperties> directoryService =
+ do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIFile> appHelper;
+ rv = directoryService->Get(NS_XPCOM_CURRENT_PROCESS_DIR, NS_GET_IID(nsIFile),
+ getter_AddRefs(appHelper));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = appHelper->Append(u"uninstall"_ns);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = appHelper->Append(u"helper.exe"_ns);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return appHelper->GetPath(aPath);
+}
+
+nsresult LaunchHelper(nsAutoString& aPath, nsAutoString& aParams) {
+ SHELLEXECUTEINFOW executeInfo = {0};
+
+ executeInfo.cbSize = sizeof(SHELLEXECUTEINFOW);
+ executeInfo.hwnd = NULL;
+ executeInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
+ executeInfo.lpDirectory = NULL;
+ executeInfo.lpFile = aPath.get();
+ executeInfo.lpParameters = aParams.get();
+ executeInfo.nShow = SW_SHOWNORMAL;
+
+ if (ShellExecuteExW(&executeInfo))
+ // Block until the program exits
+ WaitForSingleObject(executeInfo.hProcess, INFINITE);
+ else
+ return NS_ERROR_ABORT;
+
+ // We're going to ignore errors here since there's nothing we can do about
+ // them, and helper.exe seems to return non-zero ret on success.
+ return NS_OK;
+}
+
+nsresult nsWindowsShellService::Init() {
+ WCHAR appPath[MAX_BUF];
+ if (!::GetModuleFileNameW(0, appPath, MAX_BUF)) return NS_ERROR_FAILURE;
+
+ // Convert the path to a long path since GetModuleFileNameW returns the path
+ // that was used to launch the app which is not necessarily a long path.
+ if (!::GetLongPathNameW(appPath, appPath, MAX_BUF)) return NS_ERROR_FAILURE;
+
+ mAppLongPath = appPath;
+
+ return NS_OK;
+}
+
+nsWindowsShellService::nsWindowsShellService() : mCheckedThisSession(false) {}
+
+NS_IMETHODIMP
+nsWindowsShellService::IsDefaultClient(bool aStartupCheck, uint16_t aApps,
+ bool* aIsDefaultClient) {
+ // If this is the first mail window, maintain internal state that we've
+ // checked this session (so that subsequent window opens don't show the
+ // default client dialog).
+ if (aStartupCheck) mCheckedThisSession = true;
+
+ *aIsDefaultClient = true;
+
+ // for each type,
+ if (aApps & nsIShellService::MAIL) {
+ *aIsDefaultClient &=
+ TestForDefault(gMailSettings, sizeof(gMailSettings) / sizeof(SETTING));
+ // Only check if this app is default on Vista if the previous checks
+ // indicate that this app is the default.
+ if (*aIsDefaultClient)
+ IsDefaultClientVista(nsIShellService::MAIL, aIsDefaultClient);
+ }
+ if (aApps & nsIShellService::NEWS) {
+ *aIsDefaultClient &=
+ TestForDefault(gNewsSettings, sizeof(gNewsSettings) / sizeof(SETTING));
+ // Only check if this app is default on Vista if the previous checks
+ // indicate that this app is the default.
+ if (*aIsDefaultClient)
+ IsDefaultClientVista(nsIShellService::NEWS, aIsDefaultClient);
+ }
+ if (aApps & nsIShellService::CALENDAR) {
+ *aIsDefaultClient &= TestForDefault(
+ gCalendarSettings, sizeof(gCalendarSettings) / sizeof(SETTING));
+ // Only check if this app is default on Vista if the previous checks
+ // indicate that this app is the default.
+ if (*aIsDefaultClient)
+ IsDefaultClientVista(nsIShellService::CALENDAR, aIsDefaultClient);
+ }
+ // RSS / feed protocol shell integration is not working so return true
+ // until it is fixed (bug 445823).
+ if (aApps & nsIShellService::RSS) *aIsDefaultClient &= true;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsWindowsShellService::SetDefaultClient(bool aForAllUsers, uint16_t aApps) {
+ nsAutoString appHelperPath;
+ if (NS_FAILED(GetHelperPath(appHelperPath))) return NS_ERROR_FAILURE;
+
+ nsAutoString params;
+ if (aForAllUsers) {
+ params.AppendLiteral(" /SetAsDefaultAppGlobal");
+ } else {
+ params.AppendLiteral(" /SetAsDefaultAppUser");
+ if (aApps & nsIShellService::MAIL) params.AppendLiteral(" Mail");
+
+ if (aApps & nsIShellService::NEWS) params.AppendLiteral(" News");
+
+ if (aApps & nsIShellService::CALENDAR) params.AppendLiteral(" Calendar");
+ }
+
+ return LaunchHelper(appHelperPath, params);
+}
+
+NS_IMETHODIMP
+nsWindowsShellService::GetShouldCheckDefaultClient(bool* aResult) {
+ if (mCheckedThisSession) {
+ *aResult = false;
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
+ return prefs->GetBoolPref("mail.shell.checkDefaultClient", aResult);
+}
+
+NS_IMETHODIMP
+nsWindowsShellService::SetShouldCheckDefaultClient(bool aShouldCheck) {
+ nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
+ return prefs->SetBoolPref("mail.shell.checkDefaultClient", aShouldCheck);
+}
+
+/* helper routine. Iterate over the passed in settings object. */
+bool nsWindowsShellService::TestForDefault(SETTING aSettings[], int32_t aSize) {
+ bool isDefault = true;
+ char16_t currValue[MAX_BUF];
+ SETTING* end = aSettings + aSize;
+ for (SETTING* settings = aSettings; settings < end; ++settings) {
+ NS_ConvertUTF8toUTF16 dataLongPath(settings->valueData);
+ NS_ConvertUTF8toUTF16 key(settings->keyName);
+ NS_ConvertUTF8toUTF16 value(settings->valueName);
+ if (settings->flags & APP_PATH_SUBSTITUTION) {
+ int32_t offset = dataLongPath.Find(u"%APPPATH%");
+ dataLongPath.Replace(offset, 9, mAppLongPath);
+ }
+
+ ::ZeroMemory(currValue, sizeof(currValue));
+ HKEY theKey;
+ nsresult rv = OpenKeyForReading(HKEY_CLASSES_ROOT, key, &theKey);
+ if (NS_FAILED(rv)) {
+ // Key doesn't exist
+ isDefault = false;
+ break;
+ }
+
+ DWORD len = sizeof currValue;
+ DWORD result = ::RegQueryValueExW(theKey, value.get(), NULL, NULL,
+ (LPBYTE)currValue, &len);
+ // Close the key we opened.
+ ::RegCloseKey(theKey);
+ if (REG_FAILED(result) ||
+ !dataLongPath.Equals(currValue, nsCaseInsensitiveStringComparator)) {
+ // Key wasn't set, or was set to something else (something else became the
+ // default client)
+ isDefault = false;
+ break;
+ }
+ } // for each registry key we want to look at
+
+ return isDefault;
+}
+
+bool nsWindowsShellService::IsDefaultClientVista(uint16_t aApps,
+ bool* aIsDefaultClient) {
+ IApplicationAssociationRegistration* pAAR;
+
+ HRESULT hr = CoCreateInstance(
+ CLSID_ApplicationAssociationRegistration, NULL, CLSCTX_INPROC,
+ IID_IApplicationAssociationRegistration, (void**)&pAAR);
+
+ if (SUCCEEDED(hr)) {
+ BOOL isDefaultMail = true;
+ BOOL isDefaultNews = true;
+ BOOL isDefaultCalendar = true;
+ if (aApps & nsIShellService::MAIL)
+ pAAR->QueryAppIsDefaultAll(AL_EFFECTIVE, APP_REG_NAME_MAIL,
+ &isDefaultMail);
+ if (aApps & nsIShellService::NEWS)
+ pAAR->QueryAppIsDefaultAll(AL_EFFECTIVE, APP_REG_NAME_NEWS,
+ &isDefaultNews);
+ if (aApps & nsIShellService::CALENDAR)
+ pAAR->QueryAppIsDefaultAll(AL_EFFECTIVE, APP_REG_NAME_CALENDAR,
+ &isDefaultCalendar);
+
+ *aIsDefaultClient = isDefaultNews && isDefaultMail && isDefaultCalendar;
+
+ pAAR->Release();
+ return true;
+ }
+ return false;
+}