From 6fcc9c23b8428193a53d51474207a8b05cdfca91 Mon Sep 17 00:00:00 2001 From: Feross Aboukhadijeh Date: Mon, 4 Apr 2016 04:57:23 -0700 Subject: [PATCH] OS X: Use app.setAsDefaultProtocolClient - OS X: Register as default handler for "magnet" on startup Also: - Log errors in the renderer process, like the rest of errors in the main process - Windows: Less chance of registry write race condition Fix #285 --- main/handlers.js | 235 +++++++++++++++++++++++++++++------------------ 1 file changed, 148 insertions(+), 87 deletions(-) diff --git a/main/handlers.js b/main/handlers.js index 371d4bfe..1a54a828 100644 --- a/main/handlers.js +++ b/main/handlers.js @@ -7,18 +7,160 @@ var path = require('path') var log = require('./log') function init () { + if (process.platform === 'darwin') { + initDarwin() + } if (process.platform === 'win32') { - initWindows() + initWin32() } if (process.platform === 'linux') { initLinux() } } -function initWindows () { +function initDarwin () { + var electron = require('electron') + var app = electron.app + + // On OS X, only protocols that are listed in Info.plist can be set as the default + // handler at runtime. + app.setAsDefaultProtocolClient('magnet') + + // File handlers are registered in the Info.plist. +} + +function initWin32 () { + var Registry = require('winreg') + var iconPath = path.join(process.resourcesPath, 'app.asar.unpacked', 'static', 'WebTorrentFile.ico') + registerProtocolHandlerWin32('magnet', 'URL:BitTorrent Magnet URL', iconPath, process.execPath) registerFileHandlerWin32('.torrent', 'io.webtorrent.torrent', 'BitTorrent Document', iconPath, process.execPath) + + /** + * To add a protocol handler, the following keys must be added to the Windows registry: + * + * HKEY_CLASSES_ROOT + * $PROTOCOL + * (Default) = "$NAME" + * URL Protocol = "" + * DefaultIcon + * (Default) = "$ICON" + * shell + * open + * command + * (Default) = "$COMMAND" "%1" + * + * Source: https://msdn.microsoft.com/en-us/library/aa767914.aspx + * + * However, the "HKEY_CLASSES_ROOT" key can only be written by the Administrator user. + * So, we instead write to "HKEY_CURRENT_USER\Software\Classes", which is inherited by + * "HKEY_CLASSES_ROOT" anyway, and can be written by unprivileged users. + */ + + function registerProtocolHandlerWin32 (protocol, name, icon, command) { + setProtocol() + + var protocolKey = new Registry({ + hive: Registry.HKCU, // HKEY_CURRENT_USER + key: '\\Software\\Classes\\' + protocol + }) + + function setProtocol (err) { + if (err) log.error(err.message) + protocolKey.set('', Registry.REG_SZ, name, setURLProtocol) + } + + function setURLProtocol (err) { + if (err) log.error(err.message) + protocolKey.set('URL Protocol', Registry.REG_SZ, '', setIcon) + } + + function setIcon (err) { + if (err) log.error(err.message) + + var iconKey = new Registry({ + hive: Registry.HKCU, + key: '\\Software\\Classes\\' + protocol + '\\DefaultIcon' + }) + iconKey.set('', Registry.REG_SZ, icon, setCommand) + } + + function setCommand (err) { + if (err) log.error(err.message) + + var commandKey = new Registry({ + hive: Registry.HKCU, + key: '\\Software\\Classes\\' + protocol + '\\shell\\open\\command' + }) + commandKey.set('', Registry.REG_SZ, '"' + command + '" "%1"', done) + } + + function done (err) { + if (err) log.error(err.message) + } + } + + /** + * To add a file handler, the following keys must be added to the Windows registry: + * + * HKEY_CLASSES_ROOT + * $EXTENSION + * (Default) = "$EXTENSION_ID" + * $EXTENSION_ID + * (Default) = "$NAME" + * DefaultIcon + * (Default) = "$ICON" + * shell + * open + * command + * (Default) = "$COMMAND" "%1" + */ + function registerFileHandlerWin32 (ext, id, name, icon, command) { + setExt() + + function setExt () { + var extKey = new Registry({ + hive: Registry.HKCU, // HKEY_CURRENT_USER + key: '\\Software\\Classes\\' + ext + }) + extKey.set('', Registry.REG_SZ, id, setId) + } + + function setId (err) { + if (err) log.error(err.message) + + var idKey = new Registry({ + hive: Registry.HKCU, + key: '\\Software\\Classes\\' + id + }) + idKey.set('', Registry.REG_SZ, name, setIcon) + } + + function setIcon (err) { + if (err) log.error(err.message) + + var iconKey = new Registry({ + hive: Registry.HKCU, + key: '\\Software\\Classes\\' + id + '\\DefaultIcon' + }) + iconKey.set('', Registry.REG_SZ, icon, setCommand) + } + + function setCommand (err) { + if (err) log.error(err.message) + + var commandKey = new Registry({ + hive: Registry.HKCU, + key: '\\Software\\Classes\\' + id + '\\shell\\open\\command' + }) + commandKey.set('', Registry.REG_SZ, '"' + command + '" "%1"', done) + } + + function done (err) { + if (err) log.error(err.message) + } + } } function initLinux () { @@ -37,7 +179,7 @@ function initLinux () { } function writeDesktopFile (err, desktopFile) { - if (err) return console.error(err.message) + if (err) return log.error(err.message) var appPath = config.IS_PRODUCTION ? path.dirname(process.execPath) : config.ROOT_PATH var execPath = process.execPath + (config.IS_PRODUCTION ? '' : ' \.') @@ -57,7 +199,7 @@ function initLinux () { ) mkdirp(path.dirname(desktopFilePath)) fs.writeFile(desktopFilePath, desktopFile, function (err) { - if (err) return console.error(err.message) + if (err) return log.error(err.message) }) } @@ -67,7 +209,7 @@ function initLinux () { } function writeIconFile (err, iconFile) { - if (err) return console.error(err.message) + if (err) return log.error(err.message) var iconFilePath = path.join( os.homedir(), @@ -78,88 +220,7 @@ function initLinux () { ) mkdirp(path.dirname(iconFilePath)) fs.writeFile(iconFilePath, iconFile, function (err) { - if (err) return console.error(err.message) + if (err) return log.error(err.message) }) } } - -/** - * To add a protocol handler on Windows, the following keys must be added to the Windows - * registry: - * - * HKEY_CLASSES_ROOT - * $PROTOCOL - * (Default) = "$NAME" - * URL Protocol = "" - * DefaultIcon - * (Default) = "$ICON" - * shell - * open - * command - * (Default) = "$COMMAND" "%1" - * - * Source: https://msdn.microsoft.com/en-us/library/aa767914.aspx - * - * However, the "HKEY_CLASSES_ROOT" key can only be written by the Administrator user. - * So, we instead write to "HKEY_CURRENT_USER\Software\Classes", which is inherited by - * "HKEY_CLASSES_ROOT" anyway, and can be written by unprivileged users. - */ - -function registerProtocolHandlerWin32 (protocol, name, icon, command) { - var Registry = require('winreg') - - var protocolKey = new Registry({ - hive: Registry.HKCU, // HKEY_CURRENT_USER - key: '\\Software\\Classes\\' + protocol - }) - protocolKey.set('', Registry.REG_SZ, name, callback) - protocolKey.set('URL Protocol', Registry.REG_SZ, '', callback) - - var iconKey = new Registry({ - hive: Registry.HKCU, - key: '\\Software\\Classes\\' + protocol + '\\DefaultIcon' - }) - iconKey.set('', Registry.REG_SZ, icon, callback) - - var commandKey = new Registry({ - hive: Registry.HKCU, - key: '\\Software\\Classes\\' + protocol + '\\shell\\open\\command' - }) - commandKey.set('', Registry.REG_SZ, '"' + command + '" "%1"', callback) - - function callback (err) { - if (err) log.error(err.message || err) - } -} - -function registerFileHandlerWin32 (ext, id, name, icon, command) { - var Registry = require('winreg') - - var extKey = new Registry({ - hive: Registry.HKCU, // HKEY_CURRENT_USER - key: '\\Software\\Classes\\' + ext - }) - extKey.set('', Registry.REG_SZ, id, callback) - - var idKey = new Registry({ - hive: Registry.HKCU, - key: '\\Software\\Classes\\' + id - }) - idKey.set('', Registry.REG_SZ, name, callback) - - var iconKey = new Registry({ - hive: Registry.HKCU, - key: '\\Software\\Classes\\' + id + '\\DefaultIcon' - }) - iconKey.set('', Registry.REG_SZ, icon, callback) - - var commandKey = new Registry({ - hive: Registry.HKCU, - key: '\\Software\\Classes\\' + id + '\\shell\\open\\command' - }) - commandKey.set('', Registry.REG_SZ, '"' + command + '" "%1"', callback) - - function callback (err) { - if (err) log.error(err.message || err) - } -}