From 2cb730ab19dd14dd4bbcdc95e011f121242b0a9b Mon Sep 17 00:00:00 2001 From: Jonathan Pevarnek Date: Tue, 23 Jun 2015 13:37:22 -0400 Subject: [PATCH 01/31] Rename fulfillLaunched to handlePopupLaunch The name fulfillLaunched was exposing too much information about the implimentation a single platform (Chrome) had for what to do when a popup was launched. This renames that to handlePopupLaunch and defines that it should be called once the popup is done registering callback. The time where it is called is moved from whenever the popup receives a full state update to calling it as soon as all callbacks have been registered. --- src/chrome/extension/scripts/chrome_browser_api.ts | 4 ++-- src/chrome/extension/scripts/chrome_core_connector.spec.ts | 3 ++- src/firefox/data/scripts/firefox_browser_api.ts | 5 ++--- src/generic_ui/scripts/ui.spec.ts | 2 +- src/generic_ui/scripts/ui.ts | 6 +++--- src/interfaces/browser_api.ts | 3 ++- 6 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/chrome/extension/scripts/chrome_browser_api.ts b/src/chrome/extension/scripts/chrome_browser_api.ts index 00e3c9ffcf..df8c1e12a0 100644 --- a/src/chrome/extension/scripts/chrome_browser_api.ts +++ b/src/chrome/extension/scripts/chrome_browser_api.ts @@ -57,7 +57,7 @@ class ChromeBrowserApi implements BrowserAPI { private popupState_ = PopupState.NOT_LAUNCHED; - public fulfillLaunched : () => void; + public handlePopupLaunch :() => void; private onceLaunched_ :Promise; constructor() { @@ -169,7 +169,7 @@ class ChromeBrowserApi implements BrowserAPI { // after webstore installation), then allow the popup to open at a default // location. this.onceLaunched_ = new Promise((F, R) => { - this.fulfillLaunched = F; + this.handlePopupLaunch = F; }); chrome.windows.create({url: this.POPUP_URL, type: "popup", diff --git a/src/chrome/extension/scripts/chrome_core_connector.spec.ts b/src/chrome/extension/scripts/chrome_core_connector.spec.ts index 9a61b6abef..b0f1984dbd 100644 --- a/src/chrome/extension/scripts/chrome_core_connector.spec.ts +++ b/src/chrome/extension/scripts/chrome_core_connector.spec.ts @@ -39,7 +39,8 @@ describe('core-connector', () => { chromeBrowserApi = jasmine.createSpyObj('ChromeBrowserApi', ['bringUproxyToFront', 'showNotification', - 'on']); + 'on', + 'handlePopupLaunch']); diff --git a/src/firefox/data/scripts/firefox_browser_api.ts b/src/firefox/data/scripts/firefox_browser_api.ts index b4463d58be..97124a35e1 100644 --- a/src/firefox/data/scripts/firefox_browser_api.ts +++ b/src/firefox/data/scripts/firefox_browser_api.ts @@ -33,9 +33,8 @@ class FirefoxBrowserApi implements BrowserAPI { port.on('emitRejected', this.handleEmitRejected_); } - // Firefox doesn't ever need to wait for popup to open, - // We don't have onceLaunched promise, so fulfillLaunched is empty. - public fulfillLaunched = () => { + // Firefox has no work to do on initial launch + public handlePopupLaunch = () => { } public setIcon = (iconFile :string) : void => { diff --git a/src/generic_ui/scripts/ui.spec.ts b/src/generic_ui/scripts/ui.spec.ts index a6147f370e..bc99481fd3 100644 --- a/src/generic_ui/scripts/ui.spec.ts +++ b/src/generic_ui/scripts/ui.spec.ts @@ -43,7 +43,7 @@ describe('UI.UserInterface', () => { }); mockBrowserApi = jasmine.createSpyObj('browserApi', - ['setIcon', 'startUsingProxy', 'stopUsingProxy', 'openTab', 'showNotification', 'on']); + ['setIcon', 'startUsingProxy', 'stopUsingProxy', 'openTab', 'showNotification', 'on', 'handlePopupLaunch']); ui = new user_interface.UserInterface(mockCore, mockBrowserApi); spyOn(console, 'log'); }); diff --git a/src/generic_ui/scripts/ui.ts b/src/generic_ui/scripts/ui.ts index 5f7222f644..08560c11b6 100644 --- a/src/generic_ui/scripts/ui.ts +++ b/src/generic_ui/scripts/ui.ts @@ -359,7 +359,9 @@ export class UserInterface implements ui_constants.UiApi { browserApi.on('notificationClicked', this.handleNotificationClick); browserApi.on('proxyDisconnected', this.proxyDisconnected); - core.getFullState().then(this.updateInitialState); + core.getFullState() + .then(this.updateInitialState) + .then(this.browserApi.handlePopupLaunch); } // Because of an observer (in root.ts) watching the value of @@ -955,8 +957,6 @@ export class UserInterface implements ui_constants.UiApi { this.view = ui_constants.View.COPYPASTE; } - this.browserApi.fulfillLaunched(); - while(model.onlineNetworks.length > 0) { model.onlineNetworks.pop(); } diff --git a/src/interfaces/browser_api.ts b/src/interfaces/browser_api.ts index c8725329f2..1a7c535893 100644 --- a/src/interfaces/browser_api.ts +++ b/src/interfaces/browser_api.ts @@ -43,5 +43,6 @@ export interface BrowserAPI { on(name :'notificationClicked', callback :(tag :string) => void) :void; on(name :'proxyDisconnected', callback :Function) :void; - fulfillLaunched() :void; + // should be called when popup is launched and ready for use + handlePopupLaunch() :void; } From 6b910f1a458f0a1c196b4f6a336453b67086531b Mon Sep 17 00:00:00 2001 From: Trevor Johnston Date: Mon, 29 Jun 2015 13:55:51 -0400 Subject: [PATCH 02/31] migrate to uproxy-lib v28+ --- package.json | 2 +- src/mocks/rtc-to-net.ts | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/package.json b/package.json index 7039d527b3..2468e0a861 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "lodash": "^3.7.0", "regex2dfa": "^0.1.6", "tsd": "^0.5.7", - "uproxy-lib": "^27.2.5", + "uproxy-lib": "^28.0.0", "utransformers": "^0.2.1", "xregexp": "^2.0.0" }, diff --git a/src/mocks/rtc-to-net.ts b/src/mocks/rtc-to-net.ts index ca5e83ccbd..c2c40ecb0e 100644 --- a/src/mocks/rtc-to-net.ts +++ b/src/mocks/rtc-to-net.ts @@ -34,6 +34,4 @@ export class RtcToNetMock { // TODO implements rtc_to_net.RtcToNet { public toString = () => { } - - public startFromConfig = () => {} } From ad50bb869044d46475ceed6b365cfcacae48797e Mon Sep 17 00:00:00 2001 From: dborkan Date: Mon, 29 Jun 2015 14:36:28 -0400 Subject: [PATCH 03/31] Update versions to 0.8.14 --- bower.json | 2 +- package.json | 2 +- src/chrome/app/manifest.json | 2 +- src/chrome/extension/manifest.json | 2 +- src/firefox/package.json | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/bower.json b/bower.json index 36ff7e909c..8d9d66245d 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "uProxy", - "version": "0.8.13", + "version": "0.8.14", "dependencies": { "polymer": "^0.5.6", "paper-elements": "Polymer/paper-elements#^0.5.6", diff --git a/package.json b/package.json index 7039d527b3..2a722bfc78 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "uProxy", "description": "Share your pathway to the Internet", - "version": "0.8.13", + "version": "0.8.14", "repository": { "type": "git", "url": "https://github.com/uproxy/uproxy" diff --git a/src/chrome/app/manifest.json b/src/chrome/app/manifest.json index 8dae33101f..a240694985 100644 --- a/src/chrome/app/manifest.json +++ b/src/chrome/app/manifest.json @@ -3,7 +3,7 @@ "name": "__MSG_appName__", "description": "__MSG_appDescription__", "minimum_chrome_version": "41.0.2272.63", - "version": "0.8.13", + "version": "0.8.14", "default_locale": "en", "icons": { "128": "icons/128_online.png" diff --git a/src/chrome/extension/manifest.json b/src/chrome/extension/manifest.json index ca774d206d..297dddfaf7 100644 --- a/src/chrome/extension/manifest.json +++ b/src/chrome/extension/manifest.json @@ -1,6 +1,6 @@ { "name": "__MSG_extName__", - "version": "0.8.13", + "version": "0.8.14", "manifest_version": 2, "description": "__MSG_extDescription__", "minimum_chrome_version": "41.0.2272.63", diff --git a/src/firefox/package.json b/src/firefox/package.json index 83c3bf04b4..81065d9ad1 100644 --- a/src/firefox/package.json +++ b/src/firefox/package.json @@ -5,7 +5,7 @@ "description": "This is the alpha version of uProxy.", "author": "uProxy Team ", "license": "Apache 2.0", - "version": "0.8.13", + "version": "0.8.14", "permissions": { "private-browsing": true } From e37c8d80f8bc140e2ef509cfbba25d6c8fa68f02 Mon Sep 17 00:00:00 2001 From: Ben Schwartz Date: Mon, 29 Jun 2015 17:57:41 -0400 Subject: [PATCH 04/31] Tell RtcToNet the user ID so it can enforce quotas. --- src/generic_core/remote-connection.spec.ts | 3 ++- src/generic_core/remote-connection.ts | 5 +++-- src/generic_core/remote-instance.ts | 3 ++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/generic_core/remote-connection.spec.ts b/src/generic_core/remote-connection.spec.ts index fb6074243b..d1dfef43cc 100644 --- a/src/generic_core/remote-connection.spec.ts +++ b/src/generic_core/remote-connection.spec.ts @@ -51,7 +51,8 @@ describe('remote_connection.RemoteConnection', () => { spyOn(rtc_to_net, 'RtcToNet').and.returnValue(rtcToNet); updateSpy = jasmine.createSpy('updateSpy'); - connection = new remote_connection.RemoteConnection(updateSpy); + connection = new remote_connection.RemoteConnection(updateSpy, + 'the userId'); }); describe('client setup', () => { diff --git a/src/generic_core/remote-connection.ts b/src/generic_core/remote-connection.ts index 69f429d0d9..605755e823 100644 --- a/src/generic_core/remote-connection.ts +++ b/src/generic_core/remote-connection.ts @@ -69,7 +69,8 @@ var generateProxyingSessionId_ = (): string => { private proxyingId_: string; constructor( - sendUpdate :(x :uproxy_core_api.Update, data?:Object) => void + sendUpdate :(x :uproxy_core_api.Update, data?:Object) => void, + private userId_?:string ) { this.sendUpdate_ = sendUpdate; this.resetSharerCreated(); @@ -142,7 +143,7 @@ var generateProxyingSessionId_ = (): string => { pc = bridge.best('rtctonet', config); } - this.rtcToNet_ = new rtc_to_net.RtcToNet(); + this.rtcToNet_ = new rtc_to_net.RtcToNet(this.userId_); this.rtcToNet_.start({ allowNonUnicast: globals.settings.allowNonUnicast }, pc); diff --git a/src/generic_core/remote-instance.ts b/src/generic_core/remote-instance.ts index 4f886d0e87..83744e596b 100644 --- a/src/generic_core/remote-instance.ts +++ b/src/generic_core/remote-instance.ts @@ -105,7 +105,8 @@ export var remoteProxyInstance :RemoteInstance = null; // The User which this instance belongs to. public user :remote_user.User, public instanceId :string) { - this.connection_ = new remote_connection.RemoteConnection(this.handleConnectionUpdate_); + this.connection_ = new remote_connection.RemoteConnection( + this.handleConnectionUpdate_, this.user.userId); storage.load(this.getStorePath()) .then((state:RemoteInstanceState) => { From 86210034f6b2cba5c0122631040400b24e779b90 Mon Sep 17 00:00:00 2001 From: Kenny Song Date: Mon, 29 Jun 2015 18:14:05 -0400 Subject: [PATCH 05/31] Changed all bind() to use 0.0.0.0 --- src/generic_core/diagnose-nat.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/generic_core/diagnose-nat.ts b/src/generic_core/diagnose-nat.ts index abc91abc68..12a41fdb3c 100644 --- a/src/generic_core/diagnose-nat.ts +++ b/src/generic_core/diagnose-nat.ts @@ -879,7 +879,7 @@ export function probePmpSupport(routerIp:string, privateIp:string) :Promise { if (result != 0) { R(new Error('Failed to bind to a port: Err= ' + result)); @@ -923,7 +923,7 @@ export function probePcpSupport(routerIp:string, privateIp:string) :Promise { if (result != 0) { R(new Error('Failed to bind to a port: Err= ' + result)); @@ -1007,7 +1007,7 @@ function sendSsdpRequest(privateIp:string) :Promise { }); // Bind a socket and send the SSDP request - socket.bind(privateIp, 0). + socket.bind('0.0.0.0', 0). then((result:number) => { if (result != 0) { R(new Error('Failed to bind to a port: Err= ' + result)); From f13ca3bfc29d7a771ceef84d218fdbc061baf369 Mon Sep 17 00:00:00 2001 From: Lucy He Date: Tue, 30 Jun 2015 11:02:31 -0400 Subject: [PATCH 06/31] Make icon look online on first install. --- src/chrome/app/scripts/chrome_ui_connector.ts | 6 +++++ src/chrome/app/scripts/main.core-env.ts | 26 +++++++++++++++++++ .../scripts/chrome_core_connector.ts | 9 +++++++ src/interfaces/uproxy_core_api.ts | 3 ++- 4 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/chrome/app/scripts/chrome_ui_connector.ts b/src/chrome/app/scripts/chrome_ui_connector.ts index 58e81c13c2..d7af84980a 100644 --- a/src/chrome/app/scripts/chrome_ui_connector.ts +++ b/src/chrome/app/scripts/chrome_ui_connector.ts @@ -17,6 +17,10 @@ class ChromeUIConnector { private extPort_:chrome.runtime.Port; // The port that the extension connects to. private onCredentials_ :(credentials:Object) => void; private INSTALL_INCOMPLETE_PAGE_ :string = '../install-incomplete.html'; + private fulfillConnect_ :Function; + public onceConnected :Promise = new Promise((F, R) => { + this.fulfillConnect_ = F; + }); constructor(private uProxyAppChannel_ :freedom_types.OnAndEmit) { this.extPort_ = null; @@ -74,11 +78,13 @@ class ChromeUIConnector { // is complete. chrome.app.runtime.onLaunched.removeListener(this.launchInstallIncompletePage_); chrome.app.runtime.onLaunched.addListener(this.launchUproxy_); + this.fulfillConnect_(); this.extPort_.onDisconnect.addListener(function(){ // If the extension disconnects, we should show an error // page. chrome.app.runtime.onLaunched.removeListener(this.launchUproxy_); chrome.app.runtime.onLaunched.addListener(this.launchInstallIncompletePage_); + this.onceConnected = new Promise((F, R) => { this.fulfillConnect_ = F; }); }.bind(this)); } diff --git a/src/chrome/app/scripts/main.core-env.ts b/src/chrome/app/scripts/main.core-env.ts index 72e339547b..30ed5a9032 100644 --- a/src/chrome/app/scripts/main.core-env.ts +++ b/src/chrome/app/scripts/main.core-env.ts @@ -12,6 +12,7 @@ import freedom_types = require('freedom.types'); import Chrome_oauth = require('./chrome_oauth'); import ChromeUIConnector = require('./chrome_ui_connector'); +import uproxy_core_api = require('../../../interfaces/uproxy_core_api'); export interface OnEmitModule extends freedom_types.OnAndEmit {}; export interface OnEmitModuleFactory extends @@ -24,6 +25,25 @@ var oauthOptions :{connector:ChromeUIConnector;} = { export var uProxyAppChannel :freedom_types.OnAndEmit; export var moduleName = 'uProxy App Top Level'; +var needToSendInstalledMsgToUi = false; + +// When the app is installed, inform the extension. +chrome.runtime.onInstalled.addListener((details :chrome.runtime.InstalledDetails) => { + if (details.reason !== 'install') { + return; + } + if (oauthOptions.connector) { + oauthOptions.connector.onceConnected.then(() => { + oauthOptions.connector.sendToUI(uproxy_core_api.Update.APP_INSTALLED); + }); + } else { + // If the ui connector has not been initialized yet, set a flag so that + // the installed message is sent once the connector is ready. + needToSendInstalledMsgToUi = true; + } + +}); + freedom('generic_core/freedom-module.json', { 'logger': 'uproxy-lib/loggingprovider/freedom-module.json', 'debug': 'debug', @@ -32,6 +52,12 @@ freedom('generic_core/freedom-module.json', { }).then((uProxyModuleFactory:OnEmitModuleFactory) => { uProxyAppChannel = uProxyModuleFactory(); oauthOptions.connector = new ChromeUIConnector(uProxyAppChannel); + if (needToSendInstalledMsgToUi) { + oauthOptions.connector.onceConnected.then(() => { + oauthOptions.connector.sendToUI(uproxy_core_api.Update.APP_INSTALLED); + needToSendInstalledMsgToUi = false; + }); + } }); // Reply to pings from the uproxy website that are checking if the diff --git a/src/chrome/extension/scripts/chrome_core_connector.ts b/src/chrome/extension/scripts/chrome_core_connector.ts index 560ed4b3ad..6a32fab05e 100644 --- a/src/chrome/extension/scripts/chrome_core_connector.ts +++ b/src/chrome/extension/scripts/chrome_core_connector.ts @@ -237,6 +237,15 @@ class ChromeCoreConnector implements browser_connector.CoreBrowserConnector { this.listeners_[msg.type](msg.data); // TODO: Fire a DOM update? Decide if this should happen here or during a // ui.sync call. + } else if (msg.type == '' + uproxy_core_api.Update.APP_INSTALLED) { + // If the app was just installed, set the icon to the online icon + // to make it more noticeable for first time users. + chrome.browserAction.setIcon({ + path: { + "19" : "icons/19_" + Constants.DEFAULT_ICON, + "38" : "icons/38_" + Constants.DEFAULT_ICON, + } + }); } } diff --git a/src/interfaces/uproxy_core_api.ts b/src/interfaces/uproxy_core_api.ts index c71b8add03..e612945283 100644 --- a/src/interfaces/uproxy_core_api.ts +++ b/src/interfaces/uproxy_core_api.ts @@ -117,7 +117,8 @@ export enum Update { FAILED_TO_GIVE = 2020, POST_TO_CLOUDFRONT = 2021, COPYPASTE_MESSAGE = 2022, - FAILED_TO_GET = 2023 + FAILED_TO_GET = 2023, + APP_INSTALLED = 2024, } // Action taken by the user. These values are not on the wire. They are passed From 17a9df83e205cb7c4c77a585a28bd803653c45e4 Mon Sep 17 00:00:00 2001 From: Lucy He Date: Tue, 30 Jun 2015 11:27:43 -0400 Subject: [PATCH 07/31] Use ChromeMessage instead of Update. --- src/chrome/app/scripts/chrome_ui_connector.ts | 8 ++++++++ src/chrome/app/scripts/main.core-env.ts | 5 ++--- src/chrome/extension/scripts/chrome_core_connector.ts | 2 +- src/interfaces/chrome.ts | 1 + src/interfaces/uproxy_core_api.ts | 1 - 5 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/chrome/app/scripts/chrome_ui_connector.ts b/src/chrome/app/scripts/chrome_ui_connector.ts index d7af84980a..3ee2818ecd 100644 --- a/src/chrome/app/scripts/chrome_ui_connector.ts +++ b/src/chrome/app/scripts/chrome_ui_connector.ts @@ -119,6 +119,14 @@ class ChromeUIConnector { } } + public sendInstalledMsgToUI = () => { + this.extPort_.postMessage({ + cmd: 'fired', + type: uproxy_chrome.ChromeMessage.APP_INSTALLED, + data: null + }); + } + public sendToUI = (type :uproxy_core_api.Update, data?:Object) => { this.extPort_.postMessage({ cmd: 'fired', diff --git a/src/chrome/app/scripts/main.core-env.ts b/src/chrome/app/scripts/main.core-env.ts index 30ed5a9032..bc3fdbbd4c 100644 --- a/src/chrome/app/scripts/main.core-env.ts +++ b/src/chrome/app/scripts/main.core-env.ts @@ -12,7 +12,6 @@ import freedom_types = require('freedom.types'); import Chrome_oauth = require('./chrome_oauth'); import ChromeUIConnector = require('./chrome_ui_connector'); -import uproxy_core_api = require('../../../interfaces/uproxy_core_api'); export interface OnEmitModule extends freedom_types.OnAndEmit {}; export interface OnEmitModuleFactory extends @@ -34,7 +33,7 @@ chrome.runtime.onInstalled.addListener((details :chrome.runtime.InstalledDetails } if (oauthOptions.connector) { oauthOptions.connector.onceConnected.then(() => { - oauthOptions.connector.sendToUI(uproxy_core_api.Update.APP_INSTALLED); + oauthOptions.connector.sendInstalledMsgToUI(); }); } else { // If the ui connector has not been initialized yet, set a flag so that @@ -54,7 +53,7 @@ freedom('generic_core/freedom-module.json', { oauthOptions.connector = new ChromeUIConnector(uProxyAppChannel); if (needToSendInstalledMsgToUi) { oauthOptions.connector.onceConnected.then(() => { - oauthOptions.connector.sendToUI(uproxy_core_api.Update.APP_INSTALLED); + oauthOptions.connector.sendInstalledMsgToUI(); needToSendInstalledMsgToUi = false; }); } diff --git a/src/chrome/extension/scripts/chrome_core_connector.ts b/src/chrome/extension/scripts/chrome_core_connector.ts index 6a32fab05e..ed7ed085d1 100644 --- a/src/chrome/extension/scripts/chrome_core_connector.ts +++ b/src/chrome/extension/scripts/chrome_core_connector.ts @@ -237,7 +237,7 @@ class ChromeCoreConnector implements browser_connector.CoreBrowserConnector { this.listeners_[msg.type](msg.data); // TODO: Fire a DOM update? Decide if this should happen here or during a // ui.sync call. - } else if (msg.type == '' + uproxy_core_api.Update.APP_INSTALLED) { + } else if (msg.type == ChromeMessage.APP_INSTALLED) { // If the app was just installed, set the icon to the online icon // to make it more noticeable for first time users. chrome.browserAction.setIcon({ diff --git a/src/interfaces/chrome.ts b/src/interfaces/chrome.ts index 6c2e779f54..4ffcbf772e 100644 --- a/src/interfaces/chrome.ts +++ b/src/interfaces/chrome.ts @@ -14,4 +14,5 @@ export interface OAuthInfo { export module ChromeMessage { export var CONNECT :string = 'connect'; export var ACK :string = 'ack'; + export var APP_INSTALLED :string ='appInstalled'; } diff --git a/src/interfaces/uproxy_core_api.ts b/src/interfaces/uproxy_core_api.ts index e612945283..6e40571242 100644 --- a/src/interfaces/uproxy_core_api.ts +++ b/src/interfaces/uproxy_core_api.ts @@ -118,7 +118,6 @@ export enum Update { POST_TO_CLOUDFRONT = 2021, COPYPASTE_MESSAGE = 2022, FAILED_TO_GET = 2023, - APP_INSTALLED = 2024, } // Action taken by the user. These values are not on the wire. They are passed From 30e848599d4f8103de87af4054c620696616d91f Mon Sep 17 00:00:00 2001 From: Lucy He Date: Tue, 30 Jun 2015 11:29:11 -0400 Subject: [PATCH 08/31] Nits. --- src/interfaces/chrome.ts | 2 +- src/interfaces/uproxy_core_api.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/interfaces/chrome.ts b/src/interfaces/chrome.ts index 4ffcbf772e..6f51fd29f8 100644 --- a/src/interfaces/chrome.ts +++ b/src/interfaces/chrome.ts @@ -14,5 +14,5 @@ export interface OAuthInfo { export module ChromeMessage { export var CONNECT :string = 'connect'; export var ACK :string = 'ack'; - export var APP_INSTALLED :string ='appInstalled'; + export var APP_INSTALLED :string = 'appInstalled'; } diff --git a/src/interfaces/uproxy_core_api.ts b/src/interfaces/uproxy_core_api.ts index 6e40571242..c71b8add03 100644 --- a/src/interfaces/uproxy_core_api.ts +++ b/src/interfaces/uproxy_core_api.ts @@ -117,7 +117,7 @@ export enum Update { FAILED_TO_GIVE = 2020, POST_TO_CLOUDFRONT = 2021, COPYPASTE_MESSAGE = 2022, - FAILED_TO_GET = 2023, + FAILED_TO_GET = 2023 } // Action taken by the user. These values are not on the wire. They are passed From 5db15b5dd24b479efe28d94a64d8e7f8f57b8b6d Mon Sep 17 00:00:00 2001 From: Lucy He Date: Mon, 6 Jul 2015 14:19:17 -0400 Subject: [PATCH 09/31] Add sendingFeedback string to messages.json. --- src/generic_ui/locales/en/messages.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/generic_ui/locales/en/messages.json b/src/generic_ui/locales/en/messages.json index 79bd2c3c1b..8710e4a2da 100644 --- a/src/generic_ui/locales/en/messages.json +++ b/src/generic_ui/locales/en/messages.json @@ -35,6 +35,10 @@ "description": "Submit feedback to the uProxy team.", "message": "Submit Feedback" }, + "sendingFeedback": { + "description": "Message in dialog indicating feedback is being sent to the uProxy team.", + "message": "Sending feedback" + }, "getHelp": { "description": "Get help from the frequently asked questions page.", "message": "Get Help" From 91f9e368d197c38215efb0e21830ec56cbb28b6b Mon Sep 17 00:00:00 2001 From: Lucy He Date: Mon, 6 Jul 2015 14:58:49 -0400 Subject: [PATCH 10/31] Set fallback language to English. --- src/generic_ui/scripts/translator.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/generic_ui/scripts/translator.ts b/src/generic_ui/scripts/translator.ts index 225f036dfc..7d680c7522 100644 --- a/src/generic_ui/scripts/translator.ts +++ b/src/generic_ui/scripts/translator.ts @@ -34,7 +34,8 @@ function createI18nDictionary(sourceFile :MessageResource) : IResourceStoreKey { window.i18nResources = {}; i18n.init({ - resStore: window.i18nResources + resStore: window.i18nResources, + fallbackLng: 'en' }); i18n.addResources('en', 'translation', createI18nDictionary(english_source)); From 77597c6399cea535c14d10cd7fcac7e549756974 Mon Sep 17 00:00:00 2001 From: Lucy He Date: Mon, 6 Jul 2015 17:00:21 -0400 Subject: [PATCH 11/31] Move logic to extension. --- src/chrome/app/scripts/chrome_ui_connector.ts | 13 ---------- src/chrome/app/scripts/main.core-env.ts | 25 ------------------- src/chrome/extension/scripts/background.ts | 17 +++++++++++++ src/interfaces/chrome.ts | 1 - 4 files changed, 17 insertions(+), 39 deletions(-) diff --git a/src/chrome/app/scripts/chrome_ui_connector.ts b/src/chrome/app/scripts/chrome_ui_connector.ts index 3ee2818ecd..dc5a536b26 100644 --- a/src/chrome/app/scripts/chrome_ui_connector.ts +++ b/src/chrome/app/scripts/chrome_ui_connector.ts @@ -18,9 +18,6 @@ class ChromeUIConnector { private onCredentials_ :(credentials:Object) => void; private INSTALL_INCOMPLETE_PAGE_ :string = '../install-incomplete.html'; private fulfillConnect_ :Function; - public onceConnected :Promise = new Promise((F, R) => { - this.fulfillConnect_ = F; - }); constructor(private uProxyAppChannel_ :freedom_types.OnAndEmit) { this.extPort_ = null; @@ -78,13 +75,11 @@ class ChromeUIConnector { // is complete. chrome.app.runtime.onLaunched.removeListener(this.launchInstallIncompletePage_); chrome.app.runtime.onLaunched.addListener(this.launchUproxy_); - this.fulfillConnect_(); this.extPort_.onDisconnect.addListener(function(){ // If the extension disconnects, we should show an error // page. chrome.app.runtime.onLaunched.removeListener(this.launchUproxy_); chrome.app.runtime.onLaunched.addListener(this.launchInstallIncompletePage_); - this.onceConnected = new Promise((F, R) => { this.fulfillConnect_ = F; }); }.bind(this)); } @@ -119,14 +114,6 @@ class ChromeUIConnector { } } - public sendInstalledMsgToUI = () => { - this.extPort_.postMessage({ - cmd: 'fired', - type: uproxy_chrome.ChromeMessage.APP_INSTALLED, - data: null - }); - } - public sendToUI = (type :uproxy_core_api.Update, data?:Object) => { this.extPort_.postMessage({ cmd: 'fired', diff --git a/src/chrome/app/scripts/main.core-env.ts b/src/chrome/app/scripts/main.core-env.ts index bc3fdbbd4c..72e339547b 100644 --- a/src/chrome/app/scripts/main.core-env.ts +++ b/src/chrome/app/scripts/main.core-env.ts @@ -24,25 +24,6 @@ var oauthOptions :{connector:ChromeUIConnector;} = { export var uProxyAppChannel :freedom_types.OnAndEmit; export var moduleName = 'uProxy App Top Level'; -var needToSendInstalledMsgToUi = false; - -// When the app is installed, inform the extension. -chrome.runtime.onInstalled.addListener((details :chrome.runtime.InstalledDetails) => { - if (details.reason !== 'install') { - return; - } - if (oauthOptions.connector) { - oauthOptions.connector.onceConnected.then(() => { - oauthOptions.connector.sendInstalledMsgToUI(); - }); - } else { - // If the ui connector has not been initialized yet, set a flag so that - // the installed message is sent once the connector is ready. - needToSendInstalledMsgToUi = true; - } - -}); - freedom('generic_core/freedom-module.json', { 'logger': 'uproxy-lib/loggingprovider/freedom-module.json', 'debug': 'debug', @@ -51,12 +32,6 @@ freedom('generic_core/freedom-module.json', { }).then((uProxyModuleFactory:OnEmitModuleFactory) => { uProxyAppChannel = uProxyModuleFactory(); oauthOptions.connector = new ChromeUIConnector(uProxyAppChannel); - if (needToSendInstalledMsgToUi) { - oauthOptions.connector.onceConnected.then(() => { - oauthOptions.connector.sendInstalledMsgToUI(); - needToSendInstalledMsgToUi = false; - }); - } }); // Reply to pings from the uproxy website that are checking if the diff --git a/src/chrome/extension/scripts/background.ts b/src/chrome/extension/scripts/background.ts index af7d49a5f8..3de79dd2f5 100644 --- a/src/chrome/extension/scripts/background.ts +++ b/src/chrome/extension/scripts/background.ts @@ -63,6 +63,12 @@ chrome.runtime.onMessageExternal.addListener((request :any, sender :chrome.runti * updates from the Chrome App side propogate to the UI. */ browserApi = new ChromeBrowserApi(); + +var fulfillCoreInitAndConnected_ :Function; +var onceCoreInitAndConnected :Promise = new Promise((F, R) => { + this.fulfillCoreInitAndConnected_ = F; +}); + // TODO (lucyhe): Make sure that the "install" event isn't missed if we // are adding the listener after the event is fired. chrome.runtime.onInstalled.addListener((details :chrome.runtime.InstalledDetails) => { @@ -70,6 +76,14 @@ chrome.runtime.onInstalled.addListener((details :chrome.runtime.InstalledDetails // we only want to launch the window on the first install return; } + onceCoreInitAndConnected.then(() => { + chrome.browserAction.setIcon({ + path: { + "19" : "icons/19_" + Constants.DEFAULT_ICON, + "38" : "icons/38_" + Constants.DEFAULT_ICON, + } + }); + }); chrome.tabs.query({currentWindow: true, active: true}, function(tabs){ // Do not open the extension when it's installed if the user is @@ -88,6 +102,9 @@ chrome.browserAction.onClicked.addListener((tab) => { browserConnector = new ChromeCoreConnector({ name: 'uproxy-extension-to-app-port' }); browserConnector.onUpdate(uproxy_core_api.Update.LAUNCH_UPROXY, browserApi.bringUproxyToFront); +browserConnector.onceConnected.then(() => { + fulfillCoreInitAndConnected_(); +}); core = new CoreConnector(browserConnector); var oAuth = new ChromeTabAuth(); diff --git a/src/interfaces/chrome.ts b/src/interfaces/chrome.ts index 6f51fd29f8..6c2e779f54 100644 --- a/src/interfaces/chrome.ts +++ b/src/interfaces/chrome.ts @@ -14,5 +14,4 @@ export interface OAuthInfo { export module ChromeMessage { export var CONNECT :string = 'connect'; export var ACK :string = 'ack'; - export var APP_INSTALLED :string = 'appInstalled'; } From 11842412ad2fa5a433272b11dcd482f6b802bf3a Mon Sep 17 00:00:00 2001 From: Lucy He Date: Mon, 6 Jul 2015 17:08:18 -0400 Subject: [PATCH 12/31] Remove old code from core connector. --- src/chrome/extension/scripts/background.ts | 3 ++- src/chrome/extension/scripts/chrome_core_connector.ts | 9 --------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/src/chrome/extension/scripts/background.ts b/src/chrome/extension/scripts/background.ts index 3de79dd2f5..ce4b8ea43f 100644 --- a/src/chrome/extension/scripts/background.ts +++ b/src/chrome/extension/scripts/background.ts @@ -15,6 +15,7 @@ import UiApi = require('../../../interfaces/ui'); import user_interface = require('../../../generic_ui/scripts/ui'); import CoreConnector = require('../../../generic_ui/scripts/core_connector'); import uproxy_core_api = require('../../../interfaces/uproxy_core_api'); +import Constants = require('../../../generic_ui/scripts/constants'); /// /// @@ -66,7 +67,7 @@ browserApi = new ChromeBrowserApi(); var fulfillCoreInitAndConnected_ :Function; var onceCoreInitAndConnected :Promise = new Promise((F, R) => { - this.fulfillCoreInitAndConnected_ = F; + fulfillCoreInitAndConnected_ = F; }); // TODO (lucyhe): Make sure that the "install" event isn't missed if we diff --git a/src/chrome/extension/scripts/chrome_core_connector.ts b/src/chrome/extension/scripts/chrome_core_connector.ts index ed7ed085d1..560ed4b3ad 100644 --- a/src/chrome/extension/scripts/chrome_core_connector.ts +++ b/src/chrome/extension/scripts/chrome_core_connector.ts @@ -237,15 +237,6 @@ class ChromeCoreConnector implements browser_connector.CoreBrowserConnector { this.listeners_[msg.type](msg.data); // TODO: Fire a DOM update? Decide if this should happen here or during a // ui.sync call. - } else if (msg.type == ChromeMessage.APP_INSTALLED) { - // If the app was just installed, set the icon to the online icon - // to make it more noticeable for first time users. - chrome.browserAction.setIcon({ - path: { - "19" : "icons/19_" + Constants.DEFAULT_ICON, - "38" : "icons/38_" + Constants.DEFAULT_ICON, - } - }); } } From b5991645077bea4d0cc033fa287ee0268e315ad6 Mon Sep 17 00:00:00 2001 From: Trevor Johnston Date: Mon, 6 Jul 2015 18:19:38 -0400 Subject: [PATCH 13/31] add fields to UserFeedback to distinguish and identify proxying failures --- src/interfaces/uproxy_core_api.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/interfaces/uproxy_core_api.ts b/src/interfaces/uproxy_core_api.ts index c71b8add03..04aa6e71f4 100644 --- a/src/interfaces/uproxy_core_api.ts +++ b/src/interfaces/uproxy_core_api.ts @@ -8,10 +8,17 @@ import ui = require('./ui'); // --- Core <--> UI Interfaces --- export interface UserFeedback { - email :string; - feedback :string; - logs :boolean; - browserInfo :string; + email :string; + feedback :string; + logs :string; + browserInfo ?:string; + proxyingId ?:string; + feedbackType ?:UserFeedbackType; +} + +export enum UserFeedbackType { + USER_INITIATED = 0, + PROXYING_FAILURE = 1 } // Object containing description so it can be saved to storage. From 6327c158ca2f0d1e0d3e2dd9f86ecfe822574bac Mon Sep 17 00:00:00 2001 From: Trevor Johnston Date: Mon, 6 Jul 2015 18:21:27 -0400 Subject: [PATCH 14/31] set feedbackType to PROXYING_FAILURE when feedback originates from the failed to give or get toast --- src/generic_ui/polymer/feedback.ts | 15 ++++++++++++--- src/generic_ui/polymer/troubleshoot.ts | 9 ++++++++- src/generic_ui/scripts/ui.ts | 16 ++++++++++++++-- 3 files changed, 34 insertions(+), 6 deletions(-) diff --git a/src/generic_ui/polymer/feedback.ts b/src/generic_ui/polymer/feedback.ts index 2966ebd267..a9e307ea2f 100644 --- a/src/generic_ui/polymer/feedback.ts +++ b/src/generic_ui/polymer/feedback.ts @@ -1,16 +1,24 @@ /// +import uproxy_core_api = require('../../interfaces/uproxy_core_api'); + Polymer({ email: '', feedback: '', logs: '', + feedbackType: '', close: function() { this.$.feedbackPanel.close(); }, - open: function(e :Event, detail :{ includeLogs: boolean }) { - if (detail && detail.includeLogs) { + open: function(e:Event, data?:{ + includeLogs: boolean; + feedbackType: uproxy_core_api.UserFeedbackType; + }) { + if (data && data.includeLogs) { this.$.logCheckbox.checked = true; } + this.feedbackType = (data && data.feedbackType) ? data.feedbackType : + uproxy_core_api.UserFeedbackType.USER_INITIATED; this.$.feedbackPanel.open(); }, sendFeedback: function() { @@ -19,7 +27,8 @@ Polymer({ email: this.email, feedback: this.feedback, logs: this.$.logCheckbox.checked, - browserInfo: navigator.userAgent + browserInfo: navigator.userAgent, + feedbackType: this.feedbackType }).then(() => { // Reset the placeholders, which seem to be cleared after the // user types input in the input fields. diff --git a/src/generic_ui/polymer/troubleshoot.ts b/src/generic_ui/polymer/troubleshoot.ts index 31cedcf095..ac759c68cc 100644 --- a/src/generic_ui/polymer/troubleshoot.ts +++ b/src/generic_ui/polymer/troubleshoot.ts @@ -1,3 +1,5 @@ +import uproxy_core_api = require('../../interfaces/uproxy_core_api'); + Polymer({ analyzingNetwork: false, analyzedNetwork: false, @@ -12,7 +14,12 @@ Polymer({ this.$.troubleshootDialog.open(); }, submitFeedback: function() { - this.fire('core-signal', {name: 'open-feedback', data: {includeLogs: this.analyzedNetwork}}); + this.fire('core-signal', { + name: 'open-feedback', data: { + includeLogs: this.analyzedNetwork, + feedbackType: uproxy_core_api.UserFeedbackType.PROXYING_FAILURE + } + }); this.close(); }, getNatType: function() { diff --git a/src/generic_ui/scripts/ui.ts b/src/generic_ui/scripts/ui.ts index f8d0299be8..44221c4a61 100644 --- a/src/generic_ui/scripts/ui.ts +++ b/src/generic_ui/scripts/ui.ts @@ -180,6 +180,9 @@ export class UserInterface implements ui_constants.UiApi { public unableToGet :boolean = false; public unableToShare :boolean = false; + // ID of the most recent failed proxying attempt. + public proxyingId: string; + // is a proxy currently set private proxySet_ :boolean = false; // Must be included in Chrome extension manifest's list of permissions. @@ -349,6 +352,7 @@ export class UserInterface implements ui_constants.UiApi { name: info.name }); this.unableToShare = true; + this.proxyingId = info.proxyingId; }); core.onUpdate(uproxy_core_api.Update.FAILED_TO_GET, @@ -360,6 +364,7 @@ export class UserInterface implements ui_constants.UiApi { }); this.instanceTryingToGetAccessFrom = null; this.unableToGet = true; + this.proxyingId = info.proxyingId; this.bringUproxyToFront(); }); @@ -921,11 +926,18 @@ export class UserInterface implements ui_constants.UiApi { logsPromise = Promise.resolve(''); } return logsPromise.then((logs) => { - var payload = { + var payload :uproxy_core_api.UserFeedback = { email: feedback.email, feedback: feedback.feedback, - logs: logs + logs: logs, + feedbackType: feedback.feedbackType }; + + if (payload.feedbackType === + uproxy_core_api.UserFeedbackType.PROXYING_FAILURE) { + payload.proxyingId = this.proxyingId; + } + return this.postToCloudfrontSite(payload, 'submit-feedback'); }); } From 94ac2b98f76aedec013a12f0ec7ac10d74aacd33 Mon Sep 17 00:00:00 2001 From: Trevor Johnston Date: Tue, 7 Jul 2015 11:31:57 -0400 Subject: [PATCH 15/31] add a force_message_version advanced setting, to help network-land debugging --- src/generic_core/globals.ts | 3 +- src/generic_core/remote-connection.ts | 40 +++++++++++++++++---------- src/generic_core/social.ts | 6 ++-- src/generic_core/uproxy_core.ts | 7 +++-- src/generic_ui/scripts/ui.ts | 3 +- src/interfaces/uproxy_core_api.ts | 1 + 6 files changed, 39 insertions(+), 21 deletions(-) diff --git a/src/generic_core/globals.ts b/src/generic_core/globals.ts index 9d097c62e2..641626cfcf 100644 --- a/src/generic_core/globals.ts +++ b/src/generic_core/globals.ts @@ -42,7 +42,8 @@ export var settings :uproxy_core_api.GlobalSettings = { splashState: 0, statsReportingEnabled: false, consoleFilter: loggingTypes.Level.warn, - language: 'en' + language: 'en', + force_message_version: 0 // zero means "don't override" }; export var natType :string = ''; diff --git a/src/generic_core/remote-connection.ts b/src/generic_core/remote-connection.ts index 605755e823..0ba196613a 100644 --- a/src/generic_core/remote-connection.ts +++ b/src/generic_core/remote-connection.ts @@ -274,21 +274,31 @@ var generateProxyingSessionId_ = (): string => { }; var pc: peerconnection.PeerConnection; - if (remoteVersion === 1) { - log.debug('peer is running client version 1, using old peerconnection'); - pc = new peerconnection.PeerConnectionClass( - freedom['core.rtcpeerconnection'](config), - 'sockstortc'); - } else if (remoteVersion === 2) { - log.debug('peer is running client version 2, using bridge without obfuscation'); - pc = bridge.preObfuscation('sockstortc', config); - } else if (remoteVersion === 3) { - log.debug('peer is running client version 3, using bridge with basicObfuscation'); - pc = bridge.basicObfuscation('sockstortc', config); - } else { - log.debug('peer is running client version >=4, using holographic ICE'); - pc = bridge.best('sockstortc', config); - } + + var localVersion = globals.settings.force_message_version || + globals.MESSAGE_VERSION; + var lcd = Math.min(localVersion, remoteVersion); + log.info('lowest shared client version is %1 (me: %2, peer: %3)', + lcd, localVersion, remoteVersion); + switch (lcd) { + case 1: + log.debug('using old peerconnection'); + pc = new peerconnection.PeerConnectionClass( + freedom['core.rtcpeerconnection'](config), + 'sockstortc'); + break; + case 2: + log.debug('using bridge without obfuscation'); + pc = bridge.preObfuscation('sockstortc', config); + break; + case 3: + log.debug('using bridge with basicObfuscation'); + pc = bridge.basicObfuscation('sockstortc', config); + break; + default: + log.debug('using holographic ICE'); + pc = bridge.best('sockstortc', config); + } return this.socksToRtc_.start(tcpServer, pc).then( (endpoint :net.Endpoint) => { diff --git a/src/generic_core/social.ts b/src/generic_core/social.ts index eadbd94c6a..6f0cfd0c49 100644 --- a/src/generic_core/social.ts +++ b/src/generic_core/social.ts @@ -565,7 +565,8 @@ export function notifyUI(networkName :string, userId :string) { var versionedMessage :social.VersionedPeerMessage = { type: message.type, data: message.data, - version: globals.MESSAGE_VERSION + version: globals.settings.force_message_version || + globals.MESSAGE_VERSION }; var messageString = JSON.stringify(versionedMessage); log.info('sending message', { @@ -685,7 +686,8 @@ export function notifyUI(networkName :string, userId :string) { var versionedMessage :social.VersionedPeerMessage = { type: message.type, data: message.data, - version: globals.MESSAGE_VERSION + version: globals.settings.force_message_version || + globals.MESSAGE_VERSION }; ui.update(uproxy_core_api.Update.MANUAL_NETWORK_OUTBOUND_MESSAGE, versionedMessage); diff --git a/src/generic_core/uproxy_core.ts b/src/generic_core/uproxy_core.ts index 03deb6f568..8ca983f89e 100644 --- a/src/generic_core/uproxy_core.ts +++ b/src/generic_core/uproxy_core.ts @@ -198,6 +198,7 @@ export class uProxyCore implements uproxy_core_api.CoreApi { loggingTypes.Destination.console, globals.settings.consoleFilter); globals.settings.language = newSettings.language; + globals.settings.force_message_version = newSettings.force_message_version; } public getFullState = () :Promise => { @@ -241,7 +242,8 @@ export class uProxyCore implements uproxy_core_api.CoreApi { remoteProxyInstance = null; } - return copyPasteConnection.startGet(globals.MESSAGE_VERSION); + return copyPasteConnection.startGet(globals.settings.force_message_version || + globals.MESSAGE_VERSION); } public stopCopyPasteGet = () :Promise => { @@ -250,7 +252,8 @@ export class uProxyCore implements uproxy_core_api.CoreApi { public startCopyPasteShare = () => { this.copyPasteSharingMessages_ = []; - copyPasteConnection.startShare(globals.MESSAGE_VERSION); + copyPasteConnection.startShare(globals.settings.force_message_version || + globals.MESSAGE_VERSION); } public stopCopyPasteShare = () :Promise => { diff --git a/src/generic_ui/scripts/ui.ts b/src/generic_ui/scripts/ui.ts index f8d0299be8..7e7e89e20d 100644 --- a/src/generic_ui/scripts/ui.ts +++ b/src/generic_ui/scripts/ui.ts @@ -59,7 +59,8 @@ export class Model { allowNonUnicast: false, statsReportingEnabled: false, consoleFilter: 2, // loggingTypes.Level.warn - language: 'en' + language: 'en', + force_message_version: 0 }; public reconnecting = false; diff --git a/src/interfaces/uproxy_core_api.ts b/src/interfaces/uproxy_core_api.ts index c71b8add03..41d05076df 100644 --- a/src/interfaces/uproxy_core_api.ts +++ b/src/interfaces/uproxy_core_api.ts @@ -27,6 +27,7 @@ export interface GlobalSettings { splashState : number; consoleFilter :loggingTypes.Level; language :string; + force_message_version :number; } export interface InitialState { networkNames :string[]; From 3d93661354df67a0cf97b62400a84ef296880a49 Mon Sep 17 00:00:00 2001 From: Jonathan Pevarnek Date: Tue, 7 Jul 2015 11:45:13 -0400 Subject: [PATCH 16/31] Stop exporting a model variable from ui.ts Currently we export a model variable from ui.ts, this variable is actually only updated from within the panel context even though it is avialable globally. Instead, we now simply have a public property, model, on our UI object that we can access. This removes an apparently-functional line from the chrome_tab_auth script that checked whether we were connected. Unfortunately, that was already broken due to the fact that state information never got updated on the model variable it was referring to, only on the model in the panel. There should be no change in functionality there and a fix will be coming soon (it requires Freedom API changes). --- src/chrome/extension/scripts/background.ts | 2 - .../extension/scripts/chrome_tab_auth.ts | 2 +- src/chrome/extension/scripts/context.ts | 2 +- src/firefox/data/scripts/background.ts | 1 - src/generic_ui/scripts/ui.spec.ts | 14 ++-- src/generic_ui/scripts/ui.ts | 75 +++++++++---------- src/generic_ui/scripts/user.spec.ts | 3 +- 7 files changed, 48 insertions(+), 51 deletions(-) diff --git a/src/chrome/extension/scripts/background.ts b/src/chrome/extension/scripts/background.ts index af7d49a5f8..79e8ed0111 100644 --- a/src/chrome/extension/scripts/background.ts +++ b/src/chrome/extension/scripts/background.ts @@ -19,8 +19,6 @@ import uproxy_core_api = require('../../../interfaces/uproxy_core_api'); /// /// -export import model = user_interface.model; - // --------------------- Communicating with the App ---------------------------- export var browserConnector :ChromeCoreConnector; // way for ui to speak to a uProxy.CoreApi export var core :CoreConnector; // way for ui to speak to a uProxy.CoreApi diff --git a/src/chrome/extension/scripts/chrome_tab_auth.ts b/src/chrome/extension/scripts/chrome_tab_auth.ts index 2aa63f791f..af49bb72c4 100644 --- a/src/chrome/extension/scripts/chrome_tab_auth.ts +++ b/src/chrome/extension/scripts/chrome_tab_auth.ts @@ -34,7 +34,7 @@ class ChromeTabAuth { } }; - var isActive = !user_interface.model.reconnecting; + var isActive = true; //TODO use actual value chrome.tabs.create({url: url, active: isActive}, function(tab: chrome.tabs.Tab) { if (isActive) { diff --git a/src/chrome/extension/scripts/context.ts b/src/chrome/extension/scripts/context.ts index 020261e3b5..bbe1ffdba6 100644 --- a/src/chrome/extension/scripts/context.ts +++ b/src/chrome/extension/scripts/context.ts @@ -13,7 +13,7 @@ export var browserConnector :browser_connector.CoreBrowserConnector = ui_context export var ui :user_interface.UserInterface = new user_interface.UserInterface(core, ui_context.browserApi); -export var model :user_interface.Model = user_interface.model; +export var model :user_interface.Model = ui.model; ui.browser = 'chrome'; console.log('Loaded dependencies for Chrome Extension.'); diff --git a/src/firefox/data/scripts/background.ts b/src/firefox/data/scripts/background.ts index 0c2ebd18ab..6794526539 100644 --- a/src/firefox/data/scripts/background.ts +++ b/src/firefox/data/scripts/background.ts @@ -3,7 +3,6 @@ import CoreConnector = require('../../../generic_ui/scripts/core_connector'); import FirefoxCoreConnector = require('./firefox_connector'); import FirefoxBrowserApi = require('./firefox_browser_api'); -export import model = user_interface.model; export var ui :user_interface.UserInterface; export var core :CoreConnector; export var browserConnector: FirefoxCoreConnector; diff --git a/src/generic_ui/scripts/ui.spec.ts b/src/generic_ui/scripts/ui.spec.ts index bc99481fd3..1719670525 100644 --- a/src/generic_ui/scripts/ui.spec.ts +++ b/src/generic_ui/scripts/ui.spec.ts @@ -99,11 +99,11 @@ describe('UI.UserInterface', () => { it('Adding a user with no information is categorized as untrusted', () => { ui.syncUser(getUserAndInstance('testUserId', 'Alice', 'instance1')); - var network = user_interface.model.getNetwork('testNetwork'); - var user = user_interface.model.getUser(network, 'testUsedId'); + var network = ui.model.getNetwork('testNetwork'); + var user = ui.model.getUser(network, 'testUsedId'); expect(user).toBeDefined(); - var contacts = user_interface.model.contacts; + var contacts = ui.model.contacts; expect(contacts.getAccessContacts.trustedUproxy.length).toEqual(0); expect(contacts.getAccessContacts.untrustedUproxy.length).toEqual(1); @@ -118,9 +118,9 @@ describe('UI.UserInterface', () => { afterEach(logout); it('Network visible in model', () => { - expect(user_interface.model.onlineNetworks.length).toEqual(1); + expect(ui.model.onlineNetworks.length).toEqual(1); - var network = user_interface.model.getNetwork('testNetwork'); + var network = ui.model.getNetwork('testNetwork'); expect(network.name).toEqual('testNetwork'); expect(network.userId).toEqual('fakeUser'); }); @@ -138,7 +138,7 @@ describe('UI.UserInterface', () => { } }); - var network = user_interface.model.getNetwork('testNetwork'); + var network = ui.model.getNetwork('testNetwork'); expect(network.userName).toEqual('testName'); expect(network.imageData).toEqual('imageData'); @@ -151,7 +151,7 @@ describe('UI.UserInterface', () => { it('Clears fields when network goes offline', () => { logout(); - expect(user_interface.model.onlineNetworks.length).toEqual(0); + expect(ui.model.onlineNetworks.length).toEqual(0); }); }); diff --git a/src/generic_ui/scripts/ui.ts b/src/generic_ui/scripts/ui.ts index f8d0299be8..d7592a6f9c 100644 --- a/src/generic_ui/scripts/ui.ts +++ b/src/generic_ui/scripts/ui.ts @@ -94,9 +94,6 @@ export class Model { } } -// Singleton model for data bindings. -export var model = new Model(); - export interface ContactCategory { [type :string] :User[]; pending :User[]; @@ -197,6 +194,8 @@ export class UserInterface implements ui_constants.UiApi { public i18n_t :Function = translator_module.i18n_t; public i18n_setLng :Function = translator_module.i18n_setLng; + public model = new Model(); + /** * UI must be constructed with hooks to Notifications and Core. * Upon construction, the UI installs update handlers on core. @@ -206,7 +205,7 @@ export class UserInterface implements ui_constants.UiApi { public browserApi :BrowserAPI) { // TODO: Determine the best way to describe view transitions. this.view = ui_constants.View.SPLASH; // Begin at the splash intro. - this.i18n_setLng(model.globalSettings.language); + this.i18n_setLng(this.model.globalSettings.language); var firefoxMatches = navigator.userAgent.match(/Firefox\/(\d+)/); if (firefoxMatches) { @@ -408,21 +407,21 @@ export class UserInterface implements ui_constants.UiApi { var data = JSON.parse(tag); if (data.network && data.user) { - var network = model.getNetwork(data.network); + var network = this.model.getNetwork(data.network); if (network) { - var contact = model.getUser(network, data.user); + var contact = this.model.getUser(network, data.user); } } if (data.mode === 'get') { - model.globalSettings.mode = ui_constants.Mode.GET; - this.core.updateGlobalSettings(model.globalSettings); + this.model.globalSettings.mode = ui_constants.Mode.GET; + this.core.updateGlobalSettings(this.model.globalSettings); if (contact) { contact.getExpanded = true; } } else if (data.mode === 'share' && !this.isSharingDisabled) { - model.globalSettings.mode = ui_constants.Mode.SHARE; - this.core.updateGlobalSettings(model.globalSettings); + this.model.globalSettings.mode = ui_constants.Mode.SHARE; + this.core.updateGlobalSettings(this.model.globalSettings); if (contact) { contact.shareExpanded = true; } @@ -471,7 +470,7 @@ export class UserInterface implements ui_constants.UiApi { var expectedType :social.PeerMessageType; console.log('received url data from browser'); - if (model.onlineNetworks.length > 0) { + if (this.model.onlineNetworks.length > 0) { console.log('Ignoring URL since we have an active network'); this.copyPasteError = ui_constants.CopyPasteError.LOGGED_IN; return; @@ -654,7 +653,7 @@ export class UserInterface implements ui_constants.UiApi { this.browserApi.setIcon(Constants.GETTING_ICON); } else if (isGiving) { this.browserApi.setIcon(Constants.SHARING_ICON); - } else if (model.onlineNetworks.length > 0) { + } else if (this.model.onlineNetworks.length > 0) { this.browserApi.setIcon(Constants.DEFAULT_ICON); } else { this.browserApi.setIcon(Constants.LOGGED_OUT_ICON); @@ -681,7 +680,7 @@ export class UserInterface implements ui_constants.UiApi { * Synchronize a new network to be visible on this UI. */ private syncNetwork_ = (networkMsg :social.NetworkMessage) => { - var existingNetwork = model.getNetwork(networkMsg.name, networkMsg.userId); + var existingNetwork = this.model.getNetwork(networkMsg.name, networkMsg.userId); if (networkMsg.online) { if (!existingNetwork) { @@ -693,19 +692,19 @@ export class UserInterface implements ui_constants.UiApi { userName: networkMsg.userName, imageData: networkMsg.imageData }; - model.onlineNetworks.push(existingNetwork); + this.model.onlineNetworks.push(existingNetwork); } } else { if (existingNetwork) { for (var userId in existingNetwork.roster) { var user = existingNetwork.roster[userId]; var userCategories = user.getCategories(); - this.categorizeUser_(user, model.contacts.getAccessContacts, + this.categorizeUser_(user, this.model.contacts.getAccessContacts, userCategories.getTab, null); - this.categorizeUser_(user, model.contacts.shareAccessContacts, + this.categorizeUser_(user, this.model.contacts.shareAccessContacts, userCategories.shareTab, null); } - model.removeNetwork(networkMsg.name); + this.model.removeNetwork(networkMsg.name); if (!existingNetwork.logoutExpected && (networkMsg.name === 'Google' || networkMsg.name === 'Facebook') && @@ -718,7 +717,7 @@ export class UserInterface implements ui_constants.UiApi { } this.showNotification(this.i18n_t('loggedOut', {network: networkMsg.name})); - if (!model.onlineNetworks.length) { + if (!this.model.onlineNetworks.length) { this.view = ui_constants.View.SPLASH; } } @@ -729,7 +728,7 @@ export class UserInterface implements ui_constants.UiApi { } private syncUserSelf_ = (payload :social.UserData) => { - var network = model.getNetwork(payload.network); + var network = this.model.getNetwork(payload.network); if (!network) { console.error('uproxy_core_api.Update.USER_SELF message for invalid network', payload.network); @@ -745,7 +744,7 @@ export class UserInterface implements ui_constants.UiApi { * Synchronize data about some friend. */ public syncUser = (payload :social.UserData) => { - var network = model.getNetwork(payload.network); + var network = this.model.getNetwork(payload.network); if (!network) { return; } @@ -755,7 +754,7 @@ export class UserInterface implements ui_constants.UiApi { // Update / create if necessary a user, both in the network-specific // roster and the global roster. var user :User; - user = model.getUser(network, profile.userId); + user = this.model.getUser(network, profile.userId); var oldUserCategories :UserCategories = { getTab: null, shareTab: null @@ -793,9 +792,9 @@ export class UserInterface implements ui_constants.UiApi { var newUserCategories = user.getCategories(); // Update the user's category in both get and share tabs. - this.categorizeUser_(user, model.contacts.getAccessContacts, + this.categorizeUser_(user, this.model.contacts.getAccessContacts, oldUserCategories.getTab, newUserCategories.getTab); - this.categorizeUser_(user, model.contacts.shareAccessContacts, + this.categorizeUser_(user, this.model.contacts.shareAccessContacts, oldUserCategories.shareTab, newUserCategories.shareTab); console.log('Synchronized user.', user); @@ -840,7 +839,7 @@ export class UserInterface implements ui_constants.UiApi { } public logout = (networkInfo :social.SocialNetworkInfo) : Promise => { - var network = model.getNetwork(networkInfo.name); + var network = this.model.getNetwork(networkInfo.name); if (network) { // if we know about the network, record that we expect this logout to // happen @@ -854,14 +853,14 @@ export class UserInterface implements ui_constants.UiApi { } public reconnect = (network :string) => { - model.reconnecting = true; + this.model.reconnecting = true; var pingUrl = network == 'Facebook' ? 'https://graph.facebook.com' : 'https://www.googleapis.com'; this.core.pingUntilOnline(pingUrl).then(() => { // Ensure that the user is still attempting to reconnect (i.e. they // haven't clicked to stop reconnecting while we were waiting for the // ping response). - if (model.reconnecting) { + if (this.model.reconnecting) { this.core.login({network: network, reconnect: true}).then(() => { this.stopReconnect(); }).catch((e) => { @@ -870,7 +869,7 @@ export class UserInterface implements ui_constants.UiApi { this.showNotification( this.i18n_t('loggedOut', { network: network })); - if (!model.onlineNetworks.length) { + if (!this.model.onlineNetworks.length) { this.view = ui_constants.View.SPLASH; } }); @@ -879,7 +878,7 @@ export class UserInterface implements ui_constants.UiApi { } public stopReconnect = () => { - model.reconnecting = false; + this.model.reconnecting = false; } private cloudfrontDomains_ = [ @@ -931,23 +930,23 @@ export class UserInterface implements ui_constants.UiApi { } public setMode = (mode :ui_constants.Mode) => { - model.globalSettings.mode = mode; - this.core.updateGlobalSettings(model.globalSettings); + this.model.globalSettings.mode = mode; + this.core.updateGlobalSettings(this.model.globalSettings); } public updateLanguage = (newLanguage :string) => { - model.globalSettings.language = newLanguage; - this.core.updateGlobalSettings(model.globalSettings); + this.model.globalSettings.language = newLanguage; + this.core.updateGlobalSettings(this.model.globalSettings); this.i18n_setLng(newLanguage); } public updateInitialState = (state :uproxy_core_api.InitialState) => { console.log('Received uproxy_core_api.Update.INITIAL_STATE:', state); - model.networkNames = state.networkNames; - if (state.globalSettings.language !== model.globalSettings.language) { + this.model.networkNames = state.networkNames; + if (state.globalSettings.language !== this.model.globalSettings.language) { this.i18n_setLng(state.globalSettings.language); } - model.updateGlobalSettings(state.globalSettings); + this.model.updateGlobalSettings(state.globalSettings); // Maybe refactor this to be copyPasteState. this.copyPasteState = state.copyPasteState.connectionState; @@ -960,8 +959,8 @@ export class UserInterface implements ui_constants.UiApi { this.view = ui_constants.View.COPYPASTE; } - while(model.onlineNetworks.length > 0) { - model.onlineNetworks.pop(); + while (this.model.onlineNetworks.length > 0) { + this.model.onlineNetworks.pop(); } for (var network in state.onlineNetworks) { @@ -981,7 +980,7 @@ export class UserInterface implements ui_constants.UiApi { } private addOnlineNetwork_ = (networkState :social.NetworkState) => { - model.onlineNetworks.push({ + this.model.onlineNetworks.push({ name: networkState.name, userId: networkState.profile.userId, userName: networkState.profile.name, diff --git a/src/generic_ui/scripts/user.spec.ts b/src/generic_ui/scripts/user.spec.ts index 663e087648..709fb85db4 100644 --- a/src/generic_ui/scripts/user.spec.ts +++ b/src/generic_ui/scripts/user.spec.ts @@ -13,7 +13,8 @@ describe('UI.User', () => { spyOn(console, 'log'); ui = jasmine.createSpyObj('UserInterface', ['showNotification']); var testNetwork :user_interface.Network = {name: 'testNetwork', userId: 'localUserId', roster: {}, logoutExpected: false}; - user_interface.model.onlineNetworks = [testNetwork]; + ui.model = new user_interface.Model(); + ui.model.onlineNetworks = [testNetwork]; sampleUser = new user.User('fakeuser', testNetwork, ui); sampleUser.update(makeUpdateMessage({})); From af04e727bbe23727b91c7c6ed047bc54b603e1c0 Mon Sep 17 00:00:00 2001 From: Jonathan Pevarnek Date: Tue, 7 Jul 2015 13:55:58 -0400 Subject: [PATCH 17/31] Move categorizeUser to be an unexported function Previously, we have categorizeUser_ as a private method on the ui object that would handle moving things in different arrays depending on what category we wanted a user to be in. With this change, it is now a global (unexported) method in the file. This makes sense because it has absolutely no tie to class state. --- src/generic_ui/scripts/ui.ts | 55 ++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/src/generic_ui/scripts/ui.ts b/src/generic_ui/scripts/ui.ts index d7592a6f9c..89d69a0927 100644 --- a/src/generic_ui/scripts/ui.ts +++ b/src/generic_ui/scripts/ui.ts @@ -699,9 +699,9 @@ export class UserInterface implements ui_constants.UiApi { for (var userId in existingNetwork.roster) { var user = existingNetwork.roster[userId]; var userCategories = user.getCategories(); - this.categorizeUser_(user, this.model.contacts.getAccessContacts, + categorizeUser(user, this.model.contacts.getAccessContacts, userCategories.getTab, null); - this.categorizeUser_(user, this.model.contacts.shareAccessContacts, + categorizeUser(user, this.model.contacts.shareAccessContacts, userCategories.shareTab, null); } this.model.removeNetwork(networkMsg.name); @@ -792,37 +792,14 @@ export class UserInterface implements ui_constants.UiApi { var newUserCategories = user.getCategories(); // Update the user's category in both get and share tabs. - this.categorizeUser_(user, this.model.contacts.getAccessContacts, + categorizeUser(user, this.model.contacts.getAccessContacts, oldUserCategories.getTab, newUserCategories.getTab); - this.categorizeUser_(user, this.model.contacts.shareAccessContacts, + categorizeUser(user, this.model.contacts.shareAccessContacts, oldUserCategories.shareTab, newUserCategories.shareTab); console.log('Synchronized user.', user); }; - private categorizeUser_ = (user :User, contacts :ContactCategory, oldCategory :string, newCategory :string) => { - if (oldCategory === newCategory) { - // no need to do any work if nothing changed - return; - } - - if (oldCategory) { - // remove user from old category - var oldCategoryArray = contacts[oldCategory]; - for (var i = 0; i < oldCategoryArray.length; ++i) { - if (oldCategoryArray[i] == user) { - oldCategoryArray.splice(i, 1); - break; - } - } - } - - if (newCategory) { - // add user to new category - contacts[newCategory].push(user); - } - } - public openTab = (url :string) => { this.browserApi.openTab(url); } @@ -994,3 +971,27 @@ export class UserInterface implements ui_constants.UiApi { } } } // class UserInterface + +// non-exported method to handle categorizing users +function categorizeUser(user :User, contacts :ContactCategory, oldCategory :string, newCategory :string) { + if (oldCategory === newCategory) { + // no need to do any work if nothing changed + return; + } + + if (oldCategory) { + // remove user from old category + var oldCategoryArray = contacts[oldCategory]; + for (var i = 0; i < oldCategoryArray.length; ++i) { + if (oldCategoryArray[i] == user) { + oldCategoryArray.splice(i, 1); + break; + } + } + } + + if (newCategory) { + // add user to new category + contacts[newCategory].push(user); + } +} From 0d355c92fb188c9bbb99ce6074a99628a9bb03f6 Mon Sep 17 00:00:00 2001 From: Jonathan Pevarnek Date: Tue, 7 Jul 2015 13:55:59 -0400 Subject: [PATCH 18/31] Remove users when removing a network This moves the code for removing all the users that were logged into a network to the method where we remove the network itself. --- src/generic_ui/scripts/ui.ts | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/generic_ui/scripts/ui.ts b/src/generic_ui/scripts/ui.ts index 89d69a0927..1a8b2b0530 100644 --- a/src/generic_ui/scripts/ui.ts +++ b/src/generic_ui/scripts/ui.ts @@ -71,7 +71,18 @@ export class Model { return _.find(this.onlineNetworks, { name: networkName }); } - public removeNetwork = (networkName :string) => { + public removeNetwork = (networkName :string, userId :string) => { + var network = this.getNetwork(networkName, userId); + + for (var otherUserId in network.roster) { + var user = this.getUser(network, otherUserId); + var userCategories = user.getCategories(); + categorizeUser(user, this.contacts.getAccessContacts, + userCategories.getTab, null); + categorizeUser(user, this.contacts.shareAccessContacts, + userCategories.shareTab, null); + } + _.remove(this.onlineNetworks, { name: networkName }); } @@ -696,15 +707,7 @@ export class UserInterface implements ui_constants.UiApi { } } else { if (existingNetwork) { - for (var userId in existingNetwork.roster) { - var user = existingNetwork.roster[userId]; - var userCategories = user.getCategories(); - categorizeUser(user, this.model.contacts.getAccessContacts, - userCategories.getTab, null); - categorizeUser(user, this.model.contacts.shareAccessContacts, - userCategories.shareTab, null); - } - this.model.removeNetwork(networkMsg.name); + this.model.removeNetwork(networkMsg.name, networkMsg.userId); if (!existingNetwork.logoutExpected && (networkMsg.name === 'Google' || networkMsg.name === 'Facebook') && @@ -970,7 +973,7 @@ export class UserInterface implements ui_constants.UiApi { this.syncUser(networkState.roster[userId]); } } -} // class UserInterface +} // class UserInterface // non-exported method to handle categorizing users function categorizeUser(user :User, contacts :ContactCategory, oldCategory :string, newCategory :string) { From 4f578293dc830125c7def8c9ea496aef1898b0be Mon Sep 17 00:00:00 2001 From: Jonathan Pevarnek Date: Tue, 7 Jul 2015 13:55:59 -0400 Subject: [PATCH 19/31] Handle removing networks correctly Instead of just calling pop on the network array, actually remove networks when we receive initial state. The ideal solution here would be to go through each network and find the difference between them so we only remove the users that are actually gon. For the sake of easy implementation, I'm passing on that without adding a TODO (the advantage that would give us does not seem to be worth the change). Fixes #1711 --- src/generic_ui/scripts/ui.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/generic_ui/scripts/ui.ts b/src/generic_ui/scripts/ui.ts index 1a8b2b0530..7f22062eb6 100644 --- a/src/generic_ui/scripts/ui.ts +++ b/src/generic_ui/scripts/ui.ts @@ -940,7 +940,9 @@ export class UserInterface implements ui_constants.UiApi { } while (this.model.onlineNetworks.length > 0) { - this.model.onlineNetworks.pop(); + var toRemove = this.model.onlineNetworks[0]; + + this.model.removeNetwork(toRemove.name, toRemove.userId); } for (var network in state.onlineNetworks) { From 9cf997a9ae7774b96c317d0ba8fb118fb4ff5304 Mon Sep 17 00:00:00 2001 From: Lucy He Date: Tue, 7 Jul 2015 15:47:10 -0400 Subject: [PATCH 20/31] Remove extra line --- src/chrome/app/scripts/chrome_ui_connector.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/chrome/app/scripts/chrome_ui_connector.ts b/src/chrome/app/scripts/chrome_ui_connector.ts index dc5a536b26..58e81c13c2 100644 --- a/src/chrome/app/scripts/chrome_ui_connector.ts +++ b/src/chrome/app/scripts/chrome_ui_connector.ts @@ -17,7 +17,6 @@ class ChromeUIConnector { private extPort_:chrome.runtime.Port; // The port that the extension connects to. private onCredentials_ :(credentials:Object) => void; private INSTALL_INCOMPLETE_PAGE_ :string = '../install-incomplete.html'; - private fulfillConnect_ :Function; constructor(private uProxyAppChannel_ :freedom_types.OnAndEmit) { this.extPort_ = null; From 9d611898cc57df6ea55d683c23a6f293dadd9db4 Mon Sep 17 00:00:00 2001 From: Lucy He Date: Tue, 7 Jul 2015 17:35:28 -0400 Subject: [PATCH 21/31] Remove onceCoreInitAndConnected --- src/chrome/extension/scripts/background.ts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/chrome/extension/scripts/background.ts b/src/chrome/extension/scripts/background.ts index ce4b8ea43f..e6e3e93995 100644 --- a/src/chrome/extension/scripts/background.ts +++ b/src/chrome/extension/scripts/background.ts @@ -64,6 +64,9 @@ chrome.runtime.onMessageExternal.addListener((request :any, sender :chrome.runti * updates from the Chrome App side propogate to the UI. */ browserApi = new ChromeBrowserApi(); +browserConnector = new ChromeCoreConnector({ name: 'uproxy-extension-to-app-port' }); +browserConnector.onUpdate(uproxy_core_api.Update.LAUNCH_UPROXY, + browserApi.bringUproxyToFront); var fulfillCoreInitAndConnected_ :Function; var onceCoreInitAndConnected :Promise = new Promise((F, R) => { @@ -77,7 +80,7 @@ chrome.runtime.onInstalled.addListener((details :chrome.runtime.InstalledDetails // we only want to launch the window on the first install return; } - onceCoreInitAndConnected.then(() => { + browserConnector.onceConnected.then(() => { chrome.browserAction.setIcon({ path: { "19" : "icons/19_" + Constants.DEFAULT_ICON, @@ -100,13 +103,6 @@ chrome.browserAction.onClicked.addListener((tab) => { browserApi.bringUproxyToFront(); }); -browserConnector = new ChromeCoreConnector({ name: 'uproxy-extension-to-app-port' }); -browserConnector.onUpdate(uproxy_core_api.Update.LAUNCH_UPROXY, - browserApi.bringUproxyToFront); -browserConnector.onceConnected.then(() => { - fulfillCoreInitAndConnected_(); -}); - core = new CoreConnector(browserConnector); var oAuth = new ChromeTabAuth(); browserConnector.onUpdate(uproxy_core_api.Update.GET_CREDENTIALS, From 15370296e290e6b3130b565481bab9d485cc916f Mon Sep 17 00:00:00 2001 From: Lucy He Date: Tue, 7 Jul 2015 17:36:25 -0400 Subject: [PATCH 22/31] old code --- src/chrome/extension/scripts/background.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/chrome/extension/scripts/background.ts b/src/chrome/extension/scripts/background.ts index e6e3e93995..700d7bc629 100644 --- a/src/chrome/extension/scripts/background.ts +++ b/src/chrome/extension/scripts/background.ts @@ -68,11 +68,6 @@ browserConnector = new ChromeCoreConnector({ name: 'uproxy-extension-to-app-port browserConnector.onUpdate(uproxy_core_api.Update.LAUNCH_UPROXY, browserApi.bringUproxyToFront); -var fulfillCoreInitAndConnected_ :Function; -var onceCoreInitAndConnected :Promise = new Promise((F, R) => { - fulfillCoreInitAndConnected_ = F; -}); - // TODO (lucyhe): Make sure that the "install" event isn't missed if we // are adding the listener after the event is fired. chrome.runtime.onInstalled.addListener((details :chrome.runtime.InstalledDetails) => { From c415cddd6dd3ca813c1803eb08808177cb1ccd1b Mon Sep 17 00:00:00 2001 From: Trevor Johnston Date: Wed, 8 Jul 2015 16:08:22 -0400 Subject: [PATCH 23/31] rename lcd (lowest common denominator) to commonVersion --- src/generic_core/remote-connection.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/generic_core/remote-connection.ts b/src/generic_core/remote-connection.ts index 0ba196613a..5b44304a67 100644 --- a/src/generic_core/remote-connection.ts +++ b/src/generic_core/remote-connection.ts @@ -277,10 +277,10 @@ var generateProxyingSessionId_ = (): string => { var localVersion = globals.settings.force_message_version || globals.MESSAGE_VERSION; - var lcd = Math.min(localVersion, remoteVersion); + var commonVersion = Math.min(localVersion, remoteVersion); log.info('lowest shared client version is %1 (me: %2, peer: %3)', - lcd, localVersion, remoteVersion); - switch (lcd) { + commonVersion, localVersion, remoteVersion); + switch (commonVersion) { case 1: log.debug('using old peerconnection'); pc = new peerconnection.PeerConnectionClass( From 30258dc45173ddaa06f2e5f44bed8844adbaae7a Mon Sep 17 00:00:00 2001 From: Trevor Johnston Date: Wed, 8 Jul 2015 16:39:26 -0400 Subject: [PATCH 24/31] add globals.effectiveMessageVersion --- src/generic_core/globals.ts | 6 ++++++ src/generic_core/remote-connection.ts | 3 +-- src/generic_core/social.ts | 6 ++---- src/generic_core/uproxy_core.ts | 6 ++---- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/generic_core/globals.ts b/src/generic_core/globals.ts index 641626cfcf..8d80abf46f 100644 --- a/src/generic_core/globals.ts +++ b/src/generic_core/globals.ts @@ -74,4 +74,10 @@ export var loadSettings :Promise = log.info('No global settings loaded', e.message); }); +// Client version to run as, which is globals.MESSAGE_VERSION unless +// overridden in advanced settings. +export var effectiveMessageVersion = () : number => { + return settings.force_message_version || MESSAGE_VERSION; +} + export var metrics = new metrics_module.Metrics(storage); diff --git a/src/generic_core/remote-connection.ts b/src/generic_core/remote-connection.ts index 5b44304a67..dc9dedf2d2 100644 --- a/src/generic_core/remote-connection.ts +++ b/src/generic_core/remote-connection.ts @@ -275,8 +275,7 @@ var generateProxyingSessionId_ = (): string => { var pc: peerconnection.PeerConnection; - var localVersion = globals.settings.force_message_version || - globals.MESSAGE_VERSION; + var localVersion = globals.effectiveMessageVersion(); var commonVersion = Math.min(localVersion, remoteVersion); log.info('lowest shared client version is %1 (me: %2, peer: %3)', commonVersion, localVersion, remoteVersion); diff --git a/src/generic_core/social.ts b/src/generic_core/social.ts index 6f0cfd0c49..e806e14109 100644 --- a/src/generic_core/social.ts +++ b/src/generic_core/social.ts @@ -565,8 +565,7 @@ export function notifyUI(networkName :string, userId :string) { var versionedMessage :social.VersionedPeerMessage = { type: message.type, data: message.data, - version: globals.settings.force_message_version || - globals.MESSAGE_VERSION + version: globals.effectiveMessageVersion() }; var messageString = JSON.stringify(versionedMessage); log.info('sending message', { @@ -686,8 +685,7 @@ export function notifyUI(networkName :string, userId :string) { var versionedMessage :social.VersionedPeerMessage = { type: message.type, data: message.data, - version: globals.settings.force_message_version || - globals.MESSAGE_VERSION + version: globals.effectiveMessageVersion() }; ui.update(uproxy_core_api.Update.MANUAL_NETWORK_OUTBOUND_MESSAGE, versionedMessage); diff --git a/src/generic_core/uproxy_core.ts b/src/generic_core/uproxy_core.ts index 8ca983f89e..fc7c5e6915 100644 --- a/src/generic_core/uproxy_core.ts +++ b/src/generic_core/uproxy_core.ts @@ -242,8 +242,7 @@ export class uProxyCore implements uproxy_core_api.CoreApi { remoteProxyInstance = null; } - return copyPasteConnection.startGet(globals.settings.force_message_version || - globals.MESSAGE_VERSION); + return copyPasteConnection.startGet(globals.effectiveMessageVersion()); } public stopCopyPasteGet = () :Promise => { @@ -252,8 +251,7 @@ export class uProxyCore implements uproxy_core_api.CoreApi { public startCopyPasteShare = () => { this.copyPasteSharingMessages_ = []; - copyPasteConnection.startShare(globals.settings.force_message_version || - globals.MESSAGE_VERSION); + copyPasteConnection.startShare(globals.effectiveMessageVersion()); } public stopCopyPasteShare = () :Promise => { From c3f99b6f2b52e7ae5f38659ae76dc47a8189636b Mon Sep 17 00:00:00 2001 From: Jonathan Pevarnek Date: Thu, 9 Jul 2015 17:18:15 -0400 Subject: [PATCH 25/31] Remove some dead storage code This removes a lot of code in storage.ts we do not use and fixes the indentation. --- src/generic_core/storage.ts | 146 +++++++++++++++--------------------- 1 file changed, 60 insertions(+), 86 deletions(-) diff --git a/src/generic_core/storage.ts b/src/generic_core/storage.ts index 54cd258b3e..92ace7e022 100644 --- a/src/generic_core/storage.ts +++ b/src/generic_core/storage.ts @@ -9,101 +9,75 @@ import logging = require('../../../third_party/uproxy-lib/logging/logging'); -import Persistent = require('../interfaces/persistent'); - var log :logging.Log = new logging.Log('storage'); - // Platform-independent storage provider. - var fStorage :freedom_Storage = freedom['core.storage'](); +// Platform-independent storage provider. +var fStorage :freedom_Storage = freedom['core.storage'](); - // Set false elsewhere to disable log messages (ie. from jasmine) - export var DEBUG_STATESTORAGE = true; +// Set false elsewhere to disable log messages (ie. from jasmine) +export var DEBUG_STATESTORAGE = true; +/** + * Contains all state for uProxy's core. + */ +export class Storage { /** - * Contains all state for uProxy's core. + * Resets state, and clears local storage. */ - export class Storage { + public reset = () : Promise => { + return fStorage.clear().then(() => { + log.info('Cleared all keys from storage'); + }); + } - /** - * Resets state, and clears local storage. - */ - public reset = () : Promise => { - return fStorage.clear().then(() => { - log.info('Cleared all keys from storage'); - // TODO: Determine if we actually need any 'initial' state. - }); - } + // -------------------------------------------------------------------------- + // Promise-based wrappers for Freedom storage API to work with json instead + // of strings. - // -------------------------------------------------------------------------- - // Promise-based wrappers for Freedom storage API to work with json instead - // of strings. - - /** - * Promise loading a key from storage, as a JSON object. - * Use Generic to indicate the type of the returned object. - * If the key does not exist, rejects the promise. - * - * TODO: Consider using a storage provider that works with JSON. - * TODO: Really reject the promise! - */ - public load(key :string) : Promise { - log.debug('loading', key); - return fStorage.get(key).then((result :string) => { - if (typeof result === 'undefined' || result === null) { - return Promise.reject('non-existing key'); - } - log.debug('Loaded [%1]: %2', key, result); - return JSON.parse(result); - }); - } + /** + * Promise loading a key from storage, as a JSON object. + * Use Generic to indicate the type of the returned object. + * If the key does not exist, rejects the promise. + * + * TODO: Consider using a storage provider that works with JSON. + */ + public load(key :string) : Promise { + log.debug('loading', key); + return fStorage.get(key).then((result :string) => { + if (typeof result === 'undefined' || result === null) { + return Promise.reject('non-existing key'); + } + log.debug('Loaded [%1]: %2', key, result); + return JSON.parse(result); + }); + } - /** - * Promise saving a key-value pair to storage, fulfilled with the previous - * value of |key| if it existed (according to the freedom interface.) - */ - // TODO: should not return a value in the promise. Should be Promise - public save(key :string, val :T) : Promise { - log.debug('Saving to storage', { + /** + * Promise saving a key-value pair to storage, fulfilled with the previous + * value of |key| if it existed (according to the freedom interface.) + */ + // TODO: should not return a value in the promise. Should be Promise + public save(key :string, val :T) : Promise { + log.debug('Saving to storage', { + key: key, + newVal: val + }); + return fStorage.set(key, JSON.stringify(val)).then((prev:string) => { + log.debug('Successfully saved to storage', { key: key, - newVal: val - }); - return fStorage.set(key, JSON.stringify(val)).then((prev:string) => { - log.debug('Successfully saved to storage', { - key: key, - oldVal: prev - }); - if (!prev) { - return undefined; - } - return JSON.parse(prev); - }).catch((e) => { - log.error('Save operation failed', e.message); - return {}; + oldVal: prev }); - } + if (!prev) { + return undefined; + } + return JSON.parse(prev); + }).catch((e) => { + log.error('Save operation failed', e.message); + return {}; + }); + } - public keys = () : Promise => { - return fStorage.keys(); - } - - // -------------------------------------------------------------------------- - // Options - // TODO: Move options to its own class and fix it. - // -------------------------------------------------------------------------- - /* - public saveOptionsToStorage = () : Promise => { - return this.save( - C.StateEntries.OPTIONS, - null); - // restrictKeys(C.DEFAULT_SAVE_STATE.options, this.state.options)); - } - - public loadOptionsFromStorage = () : Promise => { - return this.load(C.StateEntries.OPTIONS, {}).then((loadedOptions) => { - dbg('loaded options: ' + loadedOptions); - // this.state.options = - // restrictKeys(cloneDeep(C.DEFAULT_LOAD_STATE.options), loadedOptions); - }); - } - */ - } // class Storage + public keys = () : Promise => { + return fStorage.keys(); + } +} // class Storage From 0463be59e2b0e5138807d9a004c6e95791c181a2 Mon Sep 17 00:00:00 2001 From: Jonathan Pevarnek Date: Thu, 9 Jul 2015 17:18:41 -0400 Subject: [PATCH 26/31] Storage::save now returns a void promise This resolves a TODO in the file. At no point did we actually use the return value from save except for one place we logged it just because. --- src/generic_core/local-instance.ts | 2 +- src/generic_core/remote-instance.spec.ts | 4 +--- src/generic_core/remote-instance.ts | 4 ++-- src/generic_core/remote-user.spec.ts | 7 +++---- src/generic_core/remote-user.ts | 2 +- src/generic_core/social.spec.ts | 8 ++++---- src/generic_core/storage.ts | 9 ++------- src/generic_core/uproxy_core.ts | 2 +- 8 files changed, 15 insertions(+), 23 deletions(-) diff --git a/src/generic_core/local-instance.ts b/src/generic_core/local-instance.ts index 0dd1a480b2..9df617f356 100644 --- a/src/generic_core/local-instance.ts +++ b/src/generic_core/local-instance.ts @@ -109,7 +109,7 @@ import storage = globals.storage; } public saveToStorage = () :Promise => { - return storage.save(this.getStorePath(), this.currentState()) + return storage.save(this.getStorePath(), this.currentState()) .catch((e:Error) => { log.error('Could not save new LocalInstance: ', this.instanceId, e.toString()); diff --git a/src/generic_core/remote-instance.spec.ts b/src/generic_core/remote-instance.spec.ts index b86773c8d2..f1b329c0a6 100644 --- a/src/generic_core/remote-instance.spec.ts +++ b/src/generic_core/remote-instance.spec.ts @@ -61,13 +61,11 @@ describe('remote_instance.RemoteInstance', () => { }); describe('storage', () => { var realStorage = new local_storage.Storage; - var saved :Promise; var instance0 :remote_instance.RemoteInstance; it('fresh instance has no state', (done) => { globals.storage.save = function(key :string, value :Object) { - saved = realStorage.save(key, value); - return saved; + return realStorage.save(key, value); }; instance0 = new remote_instance.RemoteInstance(user, 'instanceId'); instance0.onceLoaded.then(() => { diff --git a/src/generic_core/remote-instance.ts b/src/generic_core/remote-instance.ts index 83744e596b..0a6d9f514b 100644 --- a/src/generic_core/remote-instance.ts +++ b/src/generic_core/remote-instance.ts @@ -344,8 +344,8 @@ export var remoteProxyInstance :RemoteInstance = null; private saveToStorage = () => { return this.onceLoaded.then(() => { var state = this.currentState(); - return storage.save(this.getStorePath(), state) - .then((old) => { + return storage.save(this.getStorePath(), state) + .then(() => { log.debug('Saved instance to storage', this.instanceId); }).catch((e) => { log.error('Failed saving instance to storage', this.instanceId, e.stack); diff --git a/src/generic_core/remote-user.spec.ts b/src/generic_core/remote-user.spec.ts index caf25cec7d..247ed0d512 100644 --- a/src/generic_core/remote-user.spec.ts +++ b/src/generic_core/remote-user.spec.ts @@ -182,11 +182,10 @@ describe('remote_user.User', () => { describe('client <---> instance', () => { it('syncs clientId <--> instanceId mapping', (done) => { var realStorage = new local_storage.Storage; - var saved :Promise; storage.save = function(key :string, value :Object) { - saved = realStorage.save(key, value); - return saved; + return realStorage.save(key, value); }; + spyOn(storage, 'save').and.callThrough(); expect(user.instanceToClient('fakeinstance')).toBeUndefined(); expect(user.clientToInstance('fakeclient')).toBeUndefined(); user.syncInstance_('fakeclient', instanceHandshake, @@ -195,7 +194,7 @@ describe('remote_user.User', () => { expect(user.clientToInstance('fakeclient')).toEqual('fakeinstance'); instance = user.getInstance('fakeinstance'); expect(instance).toBeDefined(); - expect(saved).toBeDefined(); + expect(storage.save).toHaveBeenCalled(); done(); }); }); diff --git a/src/generic_core/remote-user.ts b/src/generic_core/remote-user.ts index a715ff40e0..0c279b1fcc 100644 --- a/src/generic_core/remote-user.ts +++ b/src/generic_core/remote-user.ts @@ -447,7 +447,7 @@ var log :logging.Log = new logging.Log('remote-user'); this.onceLoaded.then(() => { if (!this.ignoreUser_()) { var state = this.currentState(); - storage.save(this.getStorePath(), state).catch(() => { + storage.save(this.getStorePath(), state).catch(() => { log.error('Could not save user to storage'); }); } diff --git a/src/generic_core/social.spec.ts b/src/generic_core/social.spec.ts index 394767b71d..c35e48e2b1 100644 --- a/src/generic_core/social.spec.ts +++ b/src/generic_core/social.spec.ts @@ -105,8 +105,8 @@ describe('social_network.FreedomNetwork', () => { }) }); - var promises :Promise[] = []; - promises.push(storage.save('mockmockmyself', { + var savedToStorage :Promise[] = []; + savedToStorage.push(storage.save('mockmockmyself', { instanceId: 'dummy-instance-id', keyHash: '', bytesReceived: 0, @@ -116,10 +116,10 @@ describe('social_network.FreedomNetwork', () => { localGettingFromRemote: social.GettingState.NONE, localSharingWithRemote: social.SharingState.NONE })); - promises.push(storage.save( + savedToStorage.push(storage.save( 'dummy-instance-id/roster/somefriend', '')); - Promise.all(promises).then(() => { + Promise.all(savedToStorage).then(() => { var loginPromise = network.login(false); return loginPromise; }).then(() => { diff --git a/src/generic_core/storage.ts b/src/generic_core/storage.ts index 92ace7e022..ad33711152 100644 --- a/src/generic_core/storage.ts +++ b/src/generic_core/storage.ts @@ -56,8 +56,7 @@ export class Storage { * Promise saving a key-value pair to storage, fulfilled with the previous * value of |key| if it existed (according to the freedom interface.) */ - // TODO: should not return a value in the promise. Should be Promise - public save(key :string, val :T) : Promise { + public save(key :string, val :Object) :Promise { log.debug('Saving to storage', { key: key, newVal: val @@ -67,13 +66,9 @@ export class Storage { key: key, oldVal: prev }); - if (!prev) { - return undefined; - } - return JSON.parse(prev); }).catch((e) => { log.error('Save operation failed', e.message); - return {}; + return Promise.reject(e); }); } diff --git a/src/generic_core/uproxy_core.ts b/src/generic_core/uproxy_core.ts index 03deb6f568..8174e86186 100644 --- a/src/generic_core/uproxy_core.ts +++ b/src/generic_core/uproxy_core.ts @@ -162,7 +162,7 @@ export class uProxyCore implements uproxy_core_api.CoreApi { if (newSettings.stunServers.length === 0) { newSettings.stunServers = globals.DEFAULT_STUN_SERVERS; } - globals.storage.save('globalSettings', newSettings) + globals.storage.save('globalSettings', newSettings) .catch((e) => { log.error('Could not save globalSettings to storage', e.stack); }); From da8720100626585978370a62af5ee0cdca9b6b97 Mon Sep 17 00:00:00 2001 From: Jonathan Pevarnek Date: Fri, 10 Jul 2015 17:03:27 -0400 Subject: [PATCH 27/31] Fix bug with not exporting model in Firefox --- src/firefox/data/scripts/background.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/firefox/data/scripts/background.ts b/src/firefox/data/scripts/background.ts index 6794526539..75ef962dd7 100644 --- a/src/firefox/data/scripts/background.ts +++ b/src/firefox/data/scripts/background.ts @@ -6,6 +6,7 @@ import FirefoxBrowserApi = require('./firefox_browser_api'); export var ui :user_interface.UserInterface; export var core :CoreConnector; export var browserConnector: FirefoxCoreConnector; +export var model :user_interface.Model; function initUI() { browserConnector = new FirefoxCoreConnector(); core = new CoreConnector(browserConnector); @@ -16,6 +17,7 @@ function initUI() { if (undefined === ui) { ui = initUI(); + model = ui.model; } ui.browser = 'firefox'; From a4c92acc3f42681643e06dbb801041779f69aeed Mon Sep 17 00:00:00 2001 From: iislucas Date: Fri, 10 Jul 2015 20:51:29 -0400 Subject: [PATCH 28/31] fixes to support changed freedom interface to udp socket bind: handle promise rejections instead of result values --- src/generic_core/diagnose-nat.ts | 159 +++++++++++++------------------ 1 file changed, 64 insertions(+), 95 deletions(-) diff --git a/src/generic_core/diagnose-nat.ts b/src/generic_core/diagnose-nat.ts index 12a41fdb3c..7c1b49af8d 100644 --- a/src/generic_core/diagnose-nat.ts +++ b/src/generic_core/diagnose-nat.ts @@ -613,13 +613,6 @@ export function doUdpTest() { } socket.bind('0.0.0.0', 0) - .then((result :number) => { - if (result != 0) { - return Promise.reject(new Error('listen failed to bind :5758' + - ' with result code ' + result)); - } - return Promise.resolve(result); - }) .then(socket.getInfo) .then((socketInfo: freedom_UdpSocket.SocketInfo) => { log.debug('listening on %1:%2', @@ -694,13 +687,7 @@ function pingStunServer(serverAddr: string) { var bytes = Turn.formatStunMessage(bindRequest); socket.bind('0.0.0.0', 0) - .then((result: number) => { - if (result != 0) { - return Promise.reject(new Error('listen failed to bind :5758' + - ' with result code ' + result)); - } - return Promise.resolve(result); - }).then(() => { + .then(() => { return socket.sendTo(bytes.buffer, parts[1], parseInt(parts[2])); }).then((written: number) => { log.debug('%1 bytes sent correctly', [written]); @@ -769,12 +756,6 @@ export function doNatProvoking() :Promise { socket.on('onData', onUdpData); socket.bind('0.0.0.0', 0) - .then((result: number) => { - if (result != 0) { - return Promise.reject(new Error('failed to bind to a port: err=' + result)); - } - return Promise.resolve(result); - }) .then(socket.getInfo) .then((socketInfo: freedom_UdpSocket.SocketInfo) => { log.debug('listening on %1:%2', @@ -880,27 +861,23 @@ export function probePmpSupport(routerIp:string, privateIp:string) :Promise { - if (result != 0) { - R(new Error('Failed to bind to a port: Err= ' + result)); - } - - // Construct the NAT-PMP map request as an ArrayBuffer - // Map internal port 55555 to external port 55555 w/ 120 sec lifetime - var pmpBuffer = new ArrayBuffer(12); - var pmpView = new DataView(pmpBuffer); - // Version and OP fields (1 byte each) - pmpView.setInt8(0, 0); - pmpView.setInt8(1, 1); - // Reserved, internal port, external port fields (2 bytes each) - pmpView.setInt16(2, 0, false); - pmpView.setInt16(4, 55555, false); - pmpView.setInt16(6, 55555, false); - // Mapping lifetime field (4 bytes) - pmpView.setInt32(8, 120, false); - - socket.sendTo(pmpBuffer, routerIp, 5351); - }); + then(() => { + // Construct the NAT-PMP map request as an ArrayBuffer + // Map internal port 55555 to external port 55555 w/ 120 sec lifetime + var pmpBuffer = new ArrayBuffer(12); + var pmpView = new DataView(pmpBuffer); + // Version and OP fields (1 byte each) + pmpView.setInt8(0, 0); + pmpView.setInt8(1, 1); + // Reserved, internal port, external port fields (2 bytes each) + pmpView.setInt16(2, 0, false); + pmpView.setInt16(4, 55555, false); + pmpView.setInt16(6, 55555, false); + // Mapping lifetime field (4 bytes) + pmpView.setInt32(8, 120, false); + + socket.sendTo(pmpBuffer, routerIp, 5351); + }).catch(R); }); // Give _probePmpSupport 2 seconds before timing out @@ -924,54 +901,50 @@ export function probePcpSupport(routerIp:string, privateIp:string) :Promise { - if (result != 0) { - R(new Error('Failed to bind to a port: Err= ' + result)); - } - - // Create the PCP MAP request as an ArrayBuffer - // Map internal port 55556 to external port 55556 w/ 120 sec lifetime - var pcpBuffer = new ArrayBuffer(60); - var pcpView = new DataView(pcpBuffer); - // Version field (1 byte) - pcpView.setInt8(0, 0b00000010); - // R and Opcode fields (1 bit + 7 bits) - pcpView.setInt8(1, 0b00000001); - // Reserved field (2 bytes) - pcpView.setInt16(2, 0, false); - // Requested lifetime (4 bytes) - pcpView.setInt32(4, 120, false); - // Client IP address (128 bytes; we use the IPv4 -> IPv6 mapping) - pcpView.setInt32(8, 0, false); - pcpView.setInt32(12, 0, false); - pcpView.setInt16(16, 0, false); - pcpView.setInt16(18, 0xffff, false); - // Start of IPv4 octets of the client's private IP - var ipOctets = ipaddr.IPv4.parse(privateIp).octets; - pcpView.setInt8(20, ipOctets[0]); - pcpView.setInt8(21, ipOctets[1]); - pcpView.setInt8(22, ipOctets[2]); - pcpView.setInt8(23, ipOctets[3]); - // Mapping Nonce (12 bytes) - pcpView.setInt32(24, randInt(0, 0xffffffff), false); - pcpView.setInt32(28, randInt(0, 0xffffffff), false); - pcpView.setInt32(32, randInt(0, 0xffffffff), false); - // Protocol (1 byte) - pcpView.setInt8(36, 17); - // Reserved (3 bytes) - pcpView.setInt16(37, 0, false); - pcpView.setInt8(39, 0); - // Internal and external ports - pcpView.setInt16(40, 55556, false); - pcpView.setInt16(42, 55556, false); - // External IP address (128 bytes; we use the all-zero IPv4 -> IPv6 mapping) - pcpView.setFloat64(44, 0, false); - pcpView.setInt16(52, 0, false); - pcpView.setInt16(54, 0xffff, false); - pcpView.setInt32(56, 0, false); - - socket.sendTo(pcpBuffer, routerIp, 5351); - }); + then(() => { + // Create the PCP MAP request as an ArrayBuffer + // Map internal port 55556 to external port 55556 w/ 120 sec lifetime + var pcpBuffer = new ArrayBuffer(60); + var pcpView = new DataView(pcpBuffer); + // Version field (1 byte) + pcpView.setInt8(0, 0b00000010); + // R and Opcode fields (1 bit + 7 bits) + pcpView.setInt8(1, 0b00000001); + // Reserved field (2 bytes) + pcpView.setInt16(2, 0, false); + // Requested lifetime (4 bytes) + pcpView.setInt32(4, 120, false); + // Client IP address (128 bytes; we use the IPv4 -> IPv6 mapping) + pcpView.setInt32(8, 0, false); + pcpView.setInt32(12, 0, false); + pcpView.setInt16(16, 0, false); + pcpView.setInt16(18, 0xffff, false); + // Start of IPv4 octets of the client's private IP + var ipOctets = ipaddr.IPv4.parse(privateIp).octets; + pcpView.setInt8(20, ipOctets[0]); + pcpView.setInt8(21, ipOctets[1]); + pcpView.setInt8(22, ipOctets[2]); + pcpView.setInt8(23, ipOctets[3]); + // Mapping Nonce (12 bytes) + pcpView.setInt32(24, randInt(0, 0xffffffff), false); + pcpView.setInt32(28, randInt(0, 0xffffffff), false); + pcpView.setInt32(32, randInt(0, 0xffffffff), false); + // Protocol (1 byte) + pcpView.setInt8(36, 17); + // Reserved (3 bytes) + pcpView.setInt16(37, 0, false); + pcpView.setInt8(39, 0); + // Internal and external ports + pcpView.setInt16(40, 55556, false); + pcpView.setInt16(42, 55556, false); + // External IP address (128 bytes; we use the all-zero IPv4 -> IPv6 mapping) + pcpView.setFloat64(44, 0, false); + pcpView.setInt16(52, 0, false); + pcpView.setInt16(54, 0xffff, false); + pcpView.setInt32(56, 0, false); + + socket.sendTo(pcpBuffer, routerIp, 5351); + }).catch(R); }); // Give _probePcpSupport 2 seconds before timing out @@ -1008,11 +981,7 @@ function sendSsdpRequest(privateIp:string) :Promise { // Bind a socket and send the SSDP request socket.bind('0.0.0.0', 0). - then((result:number) => { - if (result != 0) { - R(new Error('Failed to bind to a port: Err= ' + result)); - } - + then(() => { // Construct and send a UPnP SSDP message var ssdpStr = 'M-SEARCH * HTTP/1.1\r\n' + 'HOST: 239.255.255.250:1900\r\n' + @@ -1021,7 +990,7 @@ function sendSsdpRequest(privateIp:string) :Promise { 'ST: urn:schemas-upnp-org:device:InternetGatewayDevice:1'; var ssdpBuffer = arraybuffers.stringToArrayBuffer(ssdpStr); socket.sendTo(ssdpBuffer, '239.255.255.250', 1900); - }); + }).catch(R); }); // Give _sendSsdpRequest 1 second before timing out From 151c097075d9dc2a1232ea8b9f26f6c59025e421 Mon Sep 17 00:00:00 2001 From: Lucy He Date: Mon, 13 Jul 2015 15:18:48 -0400 Subject: [PATCH 29/31] Submit feedbackType as a string, not a number. --- src/generic_ui/scripts/ui.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/generic_ui/scripts/ui.ts b/src/generic_ui/scripts/ui.ts index cb22443654..f61f67a8d1 100644 --- a/src/generic_ui/scripts/ui.ts +++ b/src/generic_ui/scripts/ui.ts @@ -906,14 +906,14 @@ export class UserInterface implements ui_constants.UiApi { logsPromise = Promise.resolve(''); } return logsPromise.then((logs) => { - var payload :uproxy_core_api.UserFeedback = { + var payload :any = { email: feedback.email, feedback: feedback.feedback, logs: logs, - feedbackType: feedback.feedbackType + feedbackType: uproxy_core_api.UserFeedbackType[feedback.feedbackType], }; - if (payload.feedbackType === + if (feedback.feedbackType === uproxy_core_api.UserFeedbackType.PROXYING_FAILURE) { payload.proxyingId = this.proxyingId; } From cad911e7bc567dd18a390a8d38e9362b3a78f9c2 Mon Sep 17 00:00:00 2001 From: Lucy He Date: Mon, 13 Jul 2015 17:35:26 -0400 Subject: [PATCH 30/31] Always assign proxyingId (will be null if feedback is not a result of proxy failure) --- src/generic_ui/scripts/ui.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/generic_ui/scripts/ui.ts b/src/generic_ui/scripts/ui.ts index f61f67a8d1..c31c3bcefd 100644 --- a/src/generic_ui/scripts/ui.ts +++ b/src/generic_ui/scripts/ui.ts @@ -906,18 +906,14 @@ export class UserInterface implements ui_constants.UiApi { logsPromise = Promise.resolve(''); } return logsPromise.then((logs) => { - var payload :any = { + var payload = { email: feedback.email, feedback: feedback.feedback, logs: logs, feedbackType: uproxy_core_api.UserFeedbackType[feedback.feedbackType], + proxyingId: this.proxyingId }; - if (feedback.feedbackType === - uproxy_core_api.UserFeedbackType.PROXYING_FAILURE) { - payload.proxyingId = this.proxyingId; - } - return this.postToCloudfrontSite(payload, 'submit-feedback'); }); } From 92a841b250024c7f725979e491bec1aa45a958b0 Mon Sep 17 00:00:00 2001 From: Lucy He Date: Mon, 13 Jul 2015 17:41:05 -0400 Subject: [PATCH 31/31] postToCloudfront accepts Object type payload instead of any --- src/generic_ui/scripts/ui.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/generic_ui/scripts/ui.ts b/src/generic_ui/scripts/ui.ts index c31c3bcefd..ab8fd4250f 100644 --- a/src/generic_ui/scripts/ui.ts +++ b/src/generic_ui/scripts/ui.ts @@ -871,7 +871,7 @@ export class UserInterface implements ui_constants.UiApi { "d1wtwocg4wx1ih.cloudfront.net" ] - public postToCloudfrontSite = (payload :any, cloudfrontPath :string, + public postToCloudfrontSite = (payload :Object, cloudfrontPath :string, maxAttempts ?:number) : Promise => { console.log('postToCloudfrontSite: ', payload, cloudfrontPath);