From 7947cc20ddbfe9e87fd77555ea6f6ae4922219af Mon Sep 17 00:00:00 2001 From: Vulich Fernando <46904390+fvulich@users.noreply.github.com> Date: Fri, 14 Feb 2020 17:35:31 -0300 Subject: [PATCH 1/8] Grant camera/microphone permission to macOS Catalina --- app.js | 4 ++++ app/Application.js | 43 ++++++++++++++++++++++++++++++++++++++++++ electron/menu.js | 15 ++++++++++++++- entitlements.mac.plist | 14 ++++++++++++++ package.json | 8 +++++++- 5 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 entitlements.mac.plist diff --git a/app.js b/app.js index 0540da48..c7a3f9cb 100644 --- a/app.js +++ b/app.js @@ -28,6 +28,10 @@ ipc.on('showAbout', function(event, message) { ipc.on('showPreferences', function(event, message) { !Ext.cq1('preferences') ? Ext.create('Rambox.view.preferences.Preferences').show() : ''; }); +ipc.on('grantPermissions', async function() { + await require('electron').remote.systemPreferences.askForMediaAccess('microphone'); + await require('electron').remote.systemPreferences.askForMediaAccess('camera'); +}); ipc.on('autoUpdater:check-update', function() { Rambox.app.checkUpdate(); }); diff --git a/app/Application.js b/app/Application.js index 830a12b3..63b2ec06 100644 --- a/app/Application.js +++ b/app/Application.js @@ -60,6 +60,49 @@ Ext.define('Rambox.Application', { }) })(); + if ( !localStorage.getItem('hideMacPermissions') && process.platform === 'darwin' && (require('electron').remote.systemPreferences.getMediaAccessStatus('microphone') !== 'granted' || require('electron').remote.systemPreferences.getMediaAccessStatus('camera') !== 'granted') ) { + console.info('Checking mac permissions...'); + Ext.cq1('app-main').addDocked({ + xtype: 'toolbar' + ,dock: 'top' + ,style: {background: '#30BBF3'} + ,items: [ + '->' + ,{ + xtype: 'label' + ,html: 'Rambox Pro needs permissions to use Microphone and Camera for the apps.' + } + ,{ + xtype: 'button' + ,text: 'Grant permissions' + ,ui: 'decline' + ,handler: async function(btn) { + await require('electron').remote.systemPreferences.askForMediaAccess('microphone'); + await require('electron').remote.systemPreferences.askForMediaAccess('camera'); + Ext.cq1('app-main').removeDocked(btn.up('toolbar'), true); + } + } + ,{ + xtype: 'button' + ,text: 'Never ask again' + ,ui: 'decline' + ,handler: function(btn) { + Ext.cq1('app-main').removeDocked(btn.up('toolbar'), true); + localStorage.setItem('hideMacPermissions', true); + } + } + ,'->' + ,{ + glyph: 'xf00d@FontAwesome' + ,baseCls: '' + ,style: 'cursor:pointer;' + ,handler: function(btn) { Ext.cq1('app-main').removeDocked(btn.up('toolbar'), true); } + } + ] + }); + } + + Ext.getStore('ServicesList').load(function (records, operations, success) { if (!success) { diff --git a/electron/menu.js b/electron/menu.js index 4a5729a4..a49c9882 100644 --- a/electron/menu.js +++ b/electron/menu.js @@ -1,6 +1,7 @@ 'use strict'; const os = require('os'); const electron = require('electron'); +const { systemPreferences } = require('electron') const app = electron.app; const BrowserWindow = electron.BrowserWindow; const shell = electron.shell; @@ -229,7 +230,7 @@ module.exports = function(config) { } ]; - if (process.platform === 'darwin') { + if ( process.platform === 'darwin' ) { tpl.unshift({ label: appName, submenu: [ @@ -288,6 +289,18 @@ module.exports = function(config) { } ] }); + helpSubmenu.push({ + type: 'separator' + }); + helpSubmenu.push({ + label: 'Grant Microphone and Camera permissions', + visible: systemPreferences.getMediaAccessStatus('microphone') !== 'granted' || systemPreferences.getMediaAccessStatus('camera') !== 'granted', + click(item, win) { + const webContents = win.webContents; + const send = webContents.send.bind(win.webContents); + send('grantPermissions'); + } + }); } else { tpl.unshift({ label: '&'+locale['menu.file[0]'], diff --git a/entitlements.mac.plist b/entitlements.mac.plist new file mode 100644 index 00000000..7d10056a --- /dev/null +++ b/entitlements.mac.plist @@ -0,0 +1,14 @@ + + + + + com.apple.security.cs.allow-unsigned-executable-memory + + com.apple.security.device.microphone + + com.apple.security.device.camera + + com.apple.security.device.audio-input + + + diff --git a/package.json b/package.json index 830a0ab3..2f70d65a 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,13 @@ "artifactName": "Rambox-${version}-mac.${ext}", "target": [ "default" - ] + ], + "entitlements": "entitlements.mac.plist", + "entitlementsInherit": "entitlements.mac.plist", + "extendInfo": { + "NSMicrophoneUsageDescription": "Apps inside Rambox may need access to your microphone. Please, grant access to have a better experience.", + "NSCameraUsageDescription": "Apps inside Rambox may need access to your camera. Please, grant access to have a better experience." + } }, "dmg": { "title": "Rambox", From 14ab68acd767342cf39d05a141be1a878d883e9a Mon Sep 17 00:00:00 2001 From: Vulich Fernando <46904390+fvulich@users.noreply.github.com> Date: Fri, 28 Feb 2020 16:11:59 -0300 Subject: [PATCH 2/8] Reload service when fail at loading --- app/ux/WebView.js | 64 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/app/ux/WebView.js b/app/ux/WebView.js index 3e627164..f4848dd6 100644 --- a/app/ux/WebView.js +++ b/app/ux/WebView.js @@ -238,6 +238,7 @@ Ext.define('Rambox.ux.WebView',{ return { xtype: 'statusbar' + ,id: me.id+'statusbar' ,hidden: !me.record.get('statusbar') ,keep: me.record.get('statusbar') ,y: floating ? '-18px' : 'auto' @@ -275,7 +276,8 @@ Ext.define('Rambox.ux.WebView',{ if ( !me.record.get('enabled') ) return; var webview = me.getWebView(); - let googleLoginURLs = ['accounts.google.com/signin/oauth', 'accounts.google.com/ServiceLogin'] + let googleLoginURLs = ['accounts.google.com/signin', 'accounts.google.com/ServiceLogin', ] + me.errorCodeLog = [] // Google Analytics Event ga_storage._trackEvent('Services', 'load', me.type, 1, true); @@ -321,6 +323,60 @@ Ext.define('Rambox.ux.WebView',{ me.onSearchText(e.result) }); + // On search text + webview.addEventListener('did-fail-load', function(e) { + console.info('The service fail at loading', me.src, e); + me.errorCodeLog.push(e.errorCode) + + var attempt = me.errorCodeLog.filter(function(code) { return code === e.errorCode }); + + // Error codes: https://cs.chromium.org/chromium/src/net/base/net_error_list.h + var msg = [] + msg[-2] = 'NET error: failed.' + msg[-3] = 'An operation was aborted (due to user action)' + msg[-7] = 'Connection timeout.' + msg[-21] = 'Network change.' + msg[-100] = 'The connection was reset. Check your internet connection.' + msg[-101] = 'The connection was reset. Check your internet connection.' + msg[-105] = 'Name not resolved. Check your internet connection.' + msg[-106] = 'There is no active internet connection.' + msg[-118] = 'Connection timed out. Check your internet connection.' + msg[-130] = 'Proxy connection failed. Please, check the proxy configuration.' + msg[-300] = 'The URL is invalid.' + msg[-324] = 'Empty response. Check your internet connection.' + + switch ( e.errorCode ) { + case 0: + break + case -3: // An operation was aborted (due to user action) I think that gmail an other pages that use iframes stop some of them making this error fired + if ( attempt.length <= 4 ) return + setTimeout(() => me.reloadService(me), 200); + me.errorCodeLog = [] + break; + case -2: + case -7: + case -21: + case -118: + case -324: + case -100: + case -101: + case -105: + attempt.length > 4 ? me.onFailLoad(msg[e.errorCode]) : setTimeout(() => me.reloadService(me), 2000); + break; + case -106: + me.onFailLoad(msg[e.errorCode]) + break; + case -130: + // Could not create a connection to the proxy server. An error occurred + // either in resolving its name, or in connecting a socket to it. + // Note that this does NOT include failures during the actual "CONNECT" method + // of an HTTP proxy. + case -300: + attempt.length > 4 ? me.onFailLoad(msg[e.errorCode]) : me.reloadService(me); + break; + } + }); + // Open links in default browser webview.addEventListener('new-window', function(e) { switch ( me.type ) { @@ -691,6 +747,12 @@ Ext.define('Rambox.ux.WebView',{ } } + ,onFailLoad: function(v) { + let me = this + me.errorCodeLog = [] + setTimeout(() => Ext.getCmp(me.id+'statusbar').setStatus({ text: ' The service failed at loading, Error: '+ v }), 1000); + } + ,showSearchBox: function(v) { var me = this; if ( !me.record.get('enabled') ) return; From 634989427db693b6ed1d2b33eb0c0b951ccdcf9c Mon Sep 17 00:00:00 2001 From: Vulich Fernando <46904390+fvulich@users.noreply.github.com> Date: Fri, 28 Feb 2020 16:18:26 -0300 Subject: [PATCH 3/8] Get the services list from Github --- app/store/ServicesList.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/store/ServicesList.js b/app/store/ServicesList.js index 2a96cd5a..59d42328 100644 --- a/app/store/ServicesList.js +++ b/app/store/ServicesList.js @@ -10,7 +10,7 @@ Ext.define('Rambox.store.ServicesList', { ,proxy: { type: 'ajax', - url: 'https://us-central1-rambox-d1326.cloudfunctions.net/ceApps', + url: 'https://raw.githubusercontent.com/saenzramiro/rambox/gh-pages/api/services.json', reader: { type: 'json', rootProperty: 'responseText' From 3937f43ffef21f3f9285dd6eec229d03e7603df1 Mon Sep 17 00:00:00 2001 From: Vulich Fernando <46904390+fvulich@users.noreply.github.com> Date: Thu, 19 Mar 2020 15:32:01 -0300 Subject: [PATCH 4/8] Electron 7.1.12 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2f70d65a..848eacef 100644 --- a/package.json +++ b/package.json @@ -66,7 +66,7 @@ "appId": "com.grupovrs.ramboxce", "asar": true, "electronDownload": { - "version": "7.1.1" + "version": "7.1.12" }, "mac": { "category": "public.app-category.productivity", From 4dd81ccf743cc28d1007024a9048358262bdf975 Mon Sep 17 00:00:00 2001 From: Vulich Fernando <46904390+fvulich@users.noreply.github.com> Date: Thu, 19 Mar 2020 15:45:33 -0300 Subject: [PATCH 5/8] URL Regex update Fixed: Rambox allows http://172.30.18.128:8443 but doesn't allow https://172.30.18.128:8443 --- app/view/add/Add.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/view/add/Add.js b/app/view/add/Add.js index c26af425..5cddec02 100644 --- a/app/view/add/Add.js +++ b/app/view/add/Add.js @@ -68,7 +68,7 @@ Ext.define('Rambox.view.add.Add',{ ,emptyText: me.record.get('url') === '___' ? 'https://' : '' ,validator: function(v) { if ( !me.edit ? me.record.get('url') !== '___' : me.service.get('url').indexOf('https://___') === 0 ) return true - if ( v.match(/^(?:(?:https?|ftp):\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,}))\.?)(?::\d{2,5})?(?:[/?#]\S*)?$/i) === null && v.match(/^http:\/\/\w+(\.\w+)*(:[0-9]+)?\/?(\/[.\w]*)*$/) === null ) return false; + if ( v.match(/^(?:(?:https?|ftp):\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,}))\.?)(?::\d{2,5})?(?:[/?#]\S*)?$/i) === null && v.match(/^https:\/\/\w+(\.\w+)*(:[0-9]+)?\/?(\/[.\w]*)*$/) === null && v.match(/^http:\/\/\w+(\.\w+)*(:[0-9]+)?\/?(\/[.\w]*)*$/) === null ) return false; return true; } ,listeners: { From 2ae71facd5e217640dccd32ce2a6e63d6aa4920c Mon Sep 17 00:00:00 2001 From: Ramiro Saenz Date: Thu, 19 Mar 2020 20:34:37 -0300 Subject: [PATCH 6/8] Grant camera/microphone permission to macOS Catalina --- app/Application.js | 5 ++--- package.json | 8 ++++---- .../installer/entitlements.mac.plist | 0 3 files changed, 6 insertions(+), 7 deletions(-) rename entitlements.mac.plist => resources/installer/entitlements.mac.plist (100%) diff --git a/app/Application.js b/app/Application.js index 63b2ec06..17dbfe80 100644 --- a/app/Application.js +++ b/app/Application.js @@ -70,15 +70,14 @@ Ext.define('Rambox.Application', { '->' ,{ xtype: 'label' - ,html: 'Rambox Pro needs permissions to use Microphone and Camera for the apps.' + ,html: 'Rambox CE needs permissions to use Microphone and Camera for the apps.' } ,{ xtype: 'button' ,text: 'Grant permissions' ,ui: 'decline' ,handler: async function(btn) { - await require('electron').remote.systemPreferences.askForMediaAccess('microphone'); - await require('electron').remote.systemPreferences.askForMediaAccess('camera'); + ipc.send('grantPermissions'); Ext.cq1('app-main').removeDocked(btn.up('toolbar'), true); } } diff --git a/package.json b/package.json index 848eacef..8aaf2389 100644 --- a/package.json +++ b/package.json @@ -74,11 +74,11 @@ "target": [ "default" ], - "entitlements": "entitlements.mac.plist", - "entitlementsInherit": "entitlements.mac.plist", + "entitlements": "resources/installer/entitlements.mac.plist", + "entitlementsInherit": "resources/installer/entitlements.mac.plist", "extendInfo": { - "NSMicrophoneUsageDescription": "Apps inside Rambox may need access to your microphone. Please, grant access to have a better experience.", - "NSCameraUsageDescription": "Apps inside Rambox may need access to your camera. Please, grant access to have a better experience." + "NSMicrophoneUsageDescription": "Apps inside Rambox CE may need access to your microphone. Please, grant access to have a better experience.", + "NSCameraUsageDescription": "Apps inside Rambox CE may need access to your camera. Please, grant access to have a better experience." } }, "dmg": { diff --git a/entitlements.mac.plist b/resources/installer/entitlements.mac.plist similarity index 100% rename from entitlements.mac.plist rename to resources/installer/entitlements.mac.plist From 7a3e4b1e6a854f249750d0b2e855137b01fc0819 Mon Sep 17 00:00:00 2001 From: Ramiro Saenz Date: Thu, 19 Mar 2020 20:35:04 -0300 Subject: [PATCH 7/8] Notarization of MacOS app --- package-lock.json | 58 +++++++++++++++++++++++++++++++-- package.json | 4 +++ resources/installer/notarize.js | 17 ++++++++++ 3 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 resources/installer/notarize.js diff --git a/package-lock.json b/package-lock.json index 062f358c..89575469 100644 --- a/package-lock.json +++ b/package-lock.json @@ -843,7 +843,8 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.0.1.tgz", "integrity": "sha512-HRZPIjPcbwAVQvOTxR4YE3o8Xs98NqbbL1iEZDCz7CL8ql0Lt5iOyJFxfnAB0oFs8Oh02F/lLlg30Mexv46LjA==", - "dev": true + "dev": true, + "optional": true }, "boxen": { "version": "3.2.0", @@ -2280,6 +2281,59 @@ "resolved": "https://registry.npmjs.org/electron-log/-/electron-log-2.2.17.tgz", "integrity": "sha512-v+Af5W5z99ehhaLOfE9eTSXUwjzh2wFlQjz51dvkZ6ZIrET6OB/zAZPvsuwT6tm3t5x+M1r+Ed3U3xtPZYAyuQ==" }, + "electron-notarize": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/electron-notarize/-/electron-notarize-0.2.1.tgz", + "integrity": "sha512-oZ6/NhKeXmEKNROiFmRNfytqu3cxqC95sjooG7kBXQVEUSQkZnbiAhxVh5jXngL881G197pbwpeVPJyM7Ikmxw==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "fs-extra": "^8.1.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "graceful-fs": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", + "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==", + "dev": true + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, "electron-osx-sign": { "version": "0.4.10", "resolved": "https://registry.npmjs.org/electron-osx-sign/-/electron-osx-sign-0.4.10.tgz", @@ -3613,7 +3667,7 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true } diff --git a/package.json b/package.json index 8aaf2389..96965910 100644 --- a/package.json +++ b/package.json @@ -74,6 +74,8 @@ "target": [ "default" ], + "hardenedRuntime": true, + "gatekeeperAssess": false, "entitlements": "resources/installer/entitlements.mac.plist", "entitlementsInherit": "resources/installer/entitlements.mac.plist", "extendInfo": { @@ -84,6 +86,7 @@ "dmg": { "title": "Rambox", "iconSize": 128, + "sign": false, "contents": [ { "x": 355, @@ -195,6 +198,7 @@ "csvjson": "4.3.3", "electron": "7.1.12", "electron-builder": "21.2.0", + "electron-notarize": "0.2.1", "electron-packager": "^12.1.0", "mocha": "^5.2.0", "spectron": "^3.8.0" diff --git a/resources/installer/notarize.js b/resources/installer/notarize.js new file mode 100644 index 00000000..5d2b5562 --- /dev/null +++ b/resources/installer/notarize.js @@ -0,0 +1,17 @@ +const { notarize } = require('electron-notarize'); + +exports.default = async function notarizing(context) { + const { electronPlatformName, appOutDir } = context; + if (electronPlatformName !== 'darwin') { + return; + } + + const appName = context.packager.appInfo.productFilename; + + return await notarize({ + appBundleId: 'com.grupovrs.ramboxce', + appPath: `${appOutDir}/${appName}.app`, + appleId: 'saenzramiro@gmail.com', + appleIdPassword: process.env.APPLE_ID_PWD + }); +}; \ No newline at end of file From 313b29cd00532b482a62cde3a35ccf7db8fec598 Mon Sep 17 00:00:00 2001 From: Ramiro Saenz Date: Thu, 19 Mar 2020 20:45:11 -0300 Subject: [PATCH 8/8] Versioning --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 96965910..eab3e858 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "Rambox", "productName": "Rambox", - "version": "0.7.3", + "version": "0.7.4", "description": "Free and Open Source messaging and emailing app that combines common web applications into one.", "main": "electron/main.js", "repository": {