ipcCommunicator.js 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. /**
  2. Window communication
  3. @module ipcCommunicator
  4. */
  5. const _ = global._;
  6. const fs = require('fs');
  7. const { app, ipcMain: ipc, shell, webContents } = require('electron');
  8. const Windows = require('./windows');
  9. const logger = require('./utils/logger');
  10. const appMenu = require('./menuItems');
  11. const Settings = require('./settings');
  12. const ethereumNode = require('./ethereumNode.js');
  13. const keyfileRecognizer = require('ethereum-keyfile-recognizer');
  14. import { getLanguage } from './core/settings/actions';
  15. const log = logger.create('ipcCommunicator');
  16. require('./abi.js');
  17. /*
  18. // windows including webviews
  19. windows = {
  20. 23: {
  21. type: 'requestWindow',
  22. window: obj,
  23. owner: 12
  24. },
  25. 12: {
  26. type: 'webview'
  27. window: obj
  28. owner: null
  29. }
  30. }
  31. */
  32. // UI ACTIONS
  33. ipc.on('backendAction_closeApp', () => {
  34. app.quit();
  35. });
  36. ipc.on('backendAction_openExternalUrl', (e, url) => {
  37. shell.openExternal(url);
  38. });
  39. ipc.on('backendAction_closePopupWindow', e => {
  40. const windowId = e.sender.id;
  41. const senderWindow = Windows.getById(windowId);
  42. if (senderWindow) {
  43. senderWindow.close();
  44. }
  45. });
  46. ipc.on('backendAction_setWindowSize', (e, width, height) => {
  47. const windowId = e.sender.id;
  48. const senderWindow = Windows.getById(windowId);
  49. if (senderWindow) {
  50. senderWindow.window.setSize(width, height | 0);
  51. senderWindow.window.center(); // ?
  52. }
  53. });
  54. ipc.on('backendAction_windowCallback', (e, value1, value2, value3) => {
  55. const windowId = e.sender.id;
  56. const senderWindow = Windows.getById(windowId);
  57. if (senderWindow.callback) {
  58. senderWindow.callback(value1, value2, value3);
  59. }
  60. });
  61. ipc.on('backendAction_windowMessageToOwner', (e, error, value) => {
  62. const windowId = e.sender.id;
  63. const senderWindow = Windows.getById(windowId);
  64. // If msg is from a generic window, use the "actingType" instead of type
  65. const senderWindowType = senderWindow.actingType || senderWindow.type;
  66. if (senderWindow.ownerId) {
  67. const ownerWindow = Windows.getById(senderWindow.ownerId);
  68. const mainWindow = Windows.getByType('main');
  69. if (ownerWindow) {
  70. ownerWindow.send(
  71. 'uiAction_windowMessage',
  72. senderWindowType,
  73. error,
  74. value
  75. );
  76. }
  77. // send through the mainWindow to the webviews
  78. if (mainWindow) {
  79. mainWindow.send(
  80. 'uiAction_windowMessage',
  81. senderWindowType,
  82. senderWindow.ownerId,
  83. error,
  84. value
  85. );
  86. }
  87. }
  88. });
  89. ipc.on('backendAction_getLanguage', e => {
  90. store.dispatch(getLanguage(e));
  91. });
  92. ipc.on('backendAction_stopWebviewNavigation', (e, id) => {
  93. console.log('webcontent ID', id);
  94. const webContent = webContents.fromId(id);
  95. if (webContent && !webContent.isDestroyed()) {
  96. webContent.stop();
  97. }
  98. e.returnValue = true;
  99. });
  100. // check wallet file
  101. ipc.on('backendAction_checkWalletFile', (e, path) => {
  102. fs.readFile(path, 'utf8', (event, data) => {
  103. try {
  104. const keyfile = JSON.parse(data);
  105. const result = keyfileRecognizer(keyfile);
  106. /** result
  107. * [ 'ethersale', undefined ] Ethersale keyfile
  108. * [ 'web3', 3 ] web3 (v3) keyfile
  109. * null no valid keyfile
  110. */
  111. const type = _.first(result);
  112. log.debug(`Importing ${type} account...`);
  113. if (type === 'ethersale') {
  114. e.sender.send('uiAction_checkedWalletFile', null, 'presale');
  115. } else if (type === 'web3') {
  116. e.sender.send('uiAction_checkedWalletFile', null, 'web3');
  117. let keystorePath = Settings.userHomePath;
  118. // eth
  119. if (ethereumNode.isEth) {
  120. if (process.platform === 'win32') {
  121. keystorePath = `${Settings.appDataPath}\\Web3\\keys`;
  122. } else {
  123. keystorePath += '/.web3/keys';
  124. }
  125. // geth
  126. } else {
  127. if (process.platform === 'darwin')
  128. keystorePath += '/Library/Ethereum/keystore';
  129. if (
  130. process.platform === 'freebsd' ||
  131. process.platform === 'linux' ||
  132. process.platform === 'sunos'
  133. )
  134. keystorePath += '/.ethereum/keystore';
  135. if (process.platform === 'win32')
  136. keystorePath = `${Settings.appDataPath}\\Ethereum\\keystore`;
  137. }
  138. if (!/^[0-9a-fA-F]{40}$/.test(keyfile.address)) {
  139. throw new Error('Invalid Address format.');
  140. }
  141. fs.writeFile(`${keystorePath}/0x${keyfile.address}`, data, err => {
  142. if (err) throw new Error("Can't write file to disk");
  143. });
  144. } else {
  145. throw new Error('Account import: Cannot recognize keyfile (invalid)');
  146. }
  147. } catch (err) {
  148. e.sender.send('uiAction_checkedWalletFile', null, 'invalid');
  149. if (
  150. /Unexpected token . in JSON at position 0/.test(err.message) === true
  151. ) {
  152. log.error('Account import: Cannot recognize keyfile (no JSON)');
  153. } else {
  154. log.error(err);
  155. }
  156. }
  157. });
  158. });
  159. // import presale wallet
  160. ipc.on('backendAction_importWalletFile', (e, path, pw) => {
  161. const spawn = require('child_process').spawn; // eslint-disable-line global-require
  162. const ClientBinaryManager = require('./clientBinaryManager'); // eslint-disable-line global-require
  163. let error = false;
  164. const binPath = ClientBinaryManager.getClient('geth').binPath;
  165. const nodeProcess = spawn(binPath, ['wallet', 'import', path]);
  166. nodeProcess.once('error', () => {
  167. error = true;
  168. e.sender.send(
  169. 'uiAction_importedWalletFile',
  170. 'Couldn\'t start the "geth wallet import <file.json>" process.'
  171. );
  172. });
  173. nodeProcess.stdout.on('data', _data => {
  174. const data = _data.toString();
  175. if (data) {
  176. log.info('Imported presale: ', data);
  177. }
  178. if (
  179. /Decryption failed|not equal to expected addr|could not decrypt/.test(
  180. data
  181. )
  182. ) {
  183. e.sender.send('uiAction_importedWalletFile', 'Decryption Failed');
  184. // if imported, return the address
  185. } else if (data.indexOf('Address:') !== -1) {
  186. const find = data.match(/\{([a-f0-9]+)\}/i);
  187. if (find.length && find[1]) {
  188. e.sender.send('uiAction_importedWalletFile', null, `0x${find[1]}`);
  189. } else {
  190. e.sender.send('uiAction_importedWalletFile', data);
  191. }
  192. // if not stop, so we don't kill the process
  193. } else {
  194. return;
  195. }
  196. nodeProcess.stdout.removeAllListeners('data');
  197. nodeProcess.removeAllListeners('error');
  198. nodeProcess.kill('SIGINT');
  199. });
  200. // file password
  201. setTimeout(() => {
  202. if (!error) {
  203. nodeProcess.stdin.write(`${pw}\n`);
  204. pw = null; // eslint-disable-line no-param-reassign
  205. }
  206. }, 2000);
  207. });
  208. const createAccountPopup = e => {
  209. Windows.createPopup('requestAccount', { ownerId: e.sender.id });
  210. };
  211. // MIST API
  212. ipc.on('mistAPI_createAccount', createAccountPopup);
  213. ipc.on('mistAPI_requestAccount', e => {
  214. if (global.mode === 'wallet') {
  215. createAccountPopup(e);
  216. } else {
  217. // Mist
  218. // if coming from wallet, skip connect, go straight to create
  219. if (e.sender.history[0].includes(`file://${dirname}/wallet/index.html`)) {
  220. createAccountPopup(e);
  221. } else {
  222. Windows.createPopup('connectAccount', { ownerId: e.sender.id });
  223. }
  224. }
  225. });
  226. const uiLoggers = {};
  227. ipc.on('console_log', (event, id, logLevel, logItemsStr) => {
  228. try {
  229. const loggerId = `(ui: ${id})`;
  230. let windowLogger = uiLoggers[loggerId];
  231. if (!windowLogger) {
  232. windowLogger = uiLoggers[loggerId] = logger.create(loggerId);
  233. }
  234. windowLogger[logLevel](..._.toArray(JSON.parse(logItemsStr)));
  235. } catch (err) {
  236. log.error(err);
  237. }
  238. });
  239. ipc.on('backendAction_reloadSelectedTab', event => {
  240. event.sender.send('uiAction_reloadSelectedTab');
  241. });