diff options
Diffstat (limited to 'remoting')
12 files changed, 171 insertions, 100 deletions
diff --git a/remoting/android/java/src/org/chromium/chromoting/Chromoting.java b/remoting/android/java/src/org/chromium/chromoting/Chromoting.java index 42d66035fe..1928f3c715 100644 --- a/remoting/android/java/src/org/chromium/chromoting/Chromoting.java +++ b/remoting/android/java/src/org/chromium/chromoting/Chromoting.java @@ -281,9 +281,12 @@ public class Chromoting extends Activity implements JniInterface.ConnectionListe @Override public void onCancel(DialogInterface dialog) { JniInterface.disconnectFromHost(); + mTokenFetcher = null; } }); SessionConnector connector = new SessionConnector(this, this, mHostListLoader); + assert mTokenFetcher == null; + mTokenFetcher = createTokenFetcher(host); connector.connectToHost(mAccount.name, mToken, host); } @@ -459,9 +462,7 @@ public class Chromoting extends Activity implements JniInterface.ConnectionListe } } - public void fetchThirdPartyToken(String tokenUrl, String clientId, String scope) { - assert mTokenFetcher == null; - + private ThirdPartyTokenFetcher createTokenFetcher(HostInfo host) { ThirdPartyTokenFetcher.Callback callback = new ThirdPartyTokenFetcher.Callback() { public void onTokenFetched(String code, String accessToken) { // The native client sends the OAuth authorization code to the host as the token so @@ -476,10 +477,11 @@ public class Chromoting extends Activity implements JniInterface.ConnectionListe JniInterface.nativeOnThirdPartyTokenFetched(token, sharedSecret); } }; - - mTokenFetcher = new ThirdPartyTokenFetcher(this, tokenUrl, clientId, scope, callback); - mTokenFetcher.fetchToken(); + return new ThirdPartyTokenFetcher(this, host.getTokenUrlPatterns(), callback); } - + public void fetchThirdPartyToken(String tokenUrl, String clientId, String scope) { + assert mTokenFetcher != null; + mTokenFetcher.fetchToken(tokenUrl, clientId, scope); + } } diff --git a/remoting/android/java/src/org/chromium/chromoting/Desktop.java b/remoting/android/java/src/org/chromium/chromoting/Desktop.java index f7fad3fa50..8868d5ad95 100644 --- a/remoting/android/java/src/org/chromium/chromoting/Desktop.java +++ b/remoting/android/java/src/org/chromium/chromoting/Desktop.java @@ -210,40 +210,38 @@ public class Desktop extends Activity implements View.OnSystemUiVisibilityChange mPressedTextKeys.add(keyCode); int[] codePoints = { unicode }; JniInterface.sendTextEvent(new String(codePoints, 0, 1)); - return super.dispatchKeyEvent(event); + return true; } if (!pressed && mPressedTextKeys.contains(keyCode)) { mPressedTextKeys.remove(keyCode); - return super.dispatchKeyEvent(event); + return true; } switch (keyCode) { case KeyEvent.KEYCODE_AT: JniInterface.sendKeyEvent(KeyEvent.KEYCODE_SHIFT_LEFT, pressed); JniInterface.sendKeyEvent(KeyEvent.KEYCODE_2, pressed); - break; + return true; case KeyEvent.KEYCODE_POUND: JniInterface.sendKeyEvent(KeyEvent.KEYCODE_SHIFT_LEFT, pressed); JniInterface.sendKeyEvent(KeyEvent.KEYCODE_3, pressed); - break; + return true; case KeyEvent.KEYCODE_STAR: JniInterface.sendKeyEvent(KeyEvent.KEYCODE_SHIFT_LEFT, pressed); JniInterface.sendKeyEvent(KeyEvent.KEYCODE_8, pressed); - break; + return true; case KeyEvent.KEYCODE_PLUS: JniInterface.sendKeyEvent(KeyEvent.KEYCODE_SHIFT_LEFT, pressed); JniInterface.sendKeyEvent(KeyEvent.KEYCODE_EQUALS, pressed); - break; + return true; default: // We try to send all other key codes to the host directly. - JniInterface.sendKeyEvent(keyCode, pressed); + return JniInterface.sendKeyEvent(keyCode, pressed); } - - return super.dispatchKeyEvent(event); } } diff --git a/remoting/android/java/src/org/chromium/chromoting/HostInfo.java b/remoting/android/java/src/org/chromium/chromoting/HostInfo.java index 229764ff24..ebc08299f7 100644 --- a/remoting/android/java/src/org/chromium/chromoting/HostInfo.java +++ b/remoting/android/java/src/org/chromium/chromoting/HostInfo.java @@ -4,6 +4,12 @@ package org.chromium.chromoting; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; + /** Class to represent a Host returned by {@link HostListLoader}. */ public class HostInfo { public final String name; @@ -11,12 +17,46 @@ public class HostInfo { public final String jabberId; public final String publicKey; public final boolean isOnline; + private final ArrayList<String> mTokenUrlPatterns; - public HostInfo(String name, String id, String jabberId, String publicKey, boolean isOnline) { + public HostInfo(String name, + String id, + String jabberId, + String publicKey, + ArrayList<String> tokenUrlPatterns, + boolean isOnline) { this.name = name; this.id = id; this.jabberId = jabberId; this.publicKey = publicKey; + this.mTokenUrlPatterns = tokenUrlPatterns; this.isOnline = isOnline; } + + public ArrayList<String> getTokenUrlPatterns() { + return new ArrayList<String>(mTokenUrlPatterns); + } + + public static HostInfo create(JSONObject json) throws JSONException { + assert json != null; + + ArrayList<String> tokenUrlPatterns = new ArrayList<String>(); + JSONArray jsonPatterns = json.optJSONArray("tokenUrlPatterns"); + + if (jsonPatterns != null) { + for (int i = 0; i < jsonPatterns.length(); i++) { + String pattern = jsonPatterns.getString(i); + if (pattern != null && !pattern.isEmpty()) { + tokenUrlPatterns.add(pattern); + } + } + } + return new HostInfo( + json.getString("hostName"), + json.getString("hostId"), + json.optString("jabberId"), + json.optString("publicKey"), + tokenUrlPatterns, + json.optString("status").equals("ONLINE")); + } } diff --git a/remoting/android/java/src/org/chromium/chromoting/HostListLoader.java b/remoting/android/java/src/org/chromium/chromoting/HostListLoader.java index 8f3b6bd257..bfcdfd8ada 100644 --- a/remoting/android/java/src/org/chromium/chromoting/HostListLoader.java +++ b/remoting/android/java/src/org/chromium/chromoting/HostListLoader.java @@ -145,12 +145,7 @@ public class HostListLoader { // attempt will fail because of the missing keys. The failed attempt will // trigger reloading of the host-list, by which time the keys will hopefully be // present, and the retried connection can succeed. - HostInfo host = new HostInfo( - hostJson.getString("hostName"), - hostJson.getString("hostId"), - hostJson.optString("jabberId"), - hostJson.optString("publicKey"), - hostJson.optString("status").equals("ONLINE")); + HostInfo host = HostInfo.create(hostJson); hostList.add(host); ++index; } diff --git a/remoting/android/java/src/org/chromium/chromoting/ThirdPartyTokenFetcher.java b/remoting/android/java/src/org/chromium/chromoting/ThirdPartyTokenFetcher.java index a00038b175..ab593b849f 100644 --- a/remoting/android/java/src/org/chromium/chromoting/ThirdPartyTokenFetcher.java +++ b/remoting/android/java/src/org/chromium/chromoting/ThirdPartyTokenFetcher.java @@ -10,10 +10,12 @@ import android.content.ComponentName; import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; +import android.text.TextUtils; import android.util.Base64; import android.util.Log; import java.security.SecureRandom; +import java.util.ArrayList; import java.util.HashMap; /** @@ -51,46 +53,42 @@ public class ThirdPartyTokenFetcher { */ private final String mState; - /** URL of the third party login page. */ - private final String mTokenUrl; - - /** The client identifier. See http://tools.ietf.org/html/rfc6749#section-2.2. */ - private final String mClientId; - - /** The scope of access request. See http://tools.ietf.org/html/rfc6749#section-3.3. */ - private final String mScope; - private final Callback mCallback; + /** The list of TokenUrls allowed by the domain. */ + private final ArrayList<String> mTokenUrlPatterns; + private final String mRedirectUriScheme; private final String mRedirectUri; public ThirdPartyTokenFetcher(Activity context, - String tokenUrl, - String clientId, - String scope, + ArrayList<String> tokenUrlPatterns, Callback callback) { this.mContext = context; - this.mTokenUrl = tokenUrl; - this.mClientId = clientId; this.mState = generateXsrfToken(); - this.mScope = scope; this.mCallback = callback; + this.mTokenUrlPatterns = tokenUrlPatterns; this.mRedirectUriScheme = context.getApplicationContext().getPackageName(); this.mRedirectUri = mRedirectUriScheme + "://" + REDIRECT_URI_HOST; } - public void fetchToken() { - Uri.Builder uriBuilder = Uri.parse(mTokenUrl).buildUpon(); - uriBuilder.appendQueryParameter("redirect_uri", this.mRedirectUri); - uriBuilder.appendQueryParameter("scope", mScope); - uriBuilder.appendQueryParameter("client_id", mClientId); - uriBuilder.appendQueryParameter("state", mState); - uriBuilder.appendQueryParameter("response_type", RESPONSE_TYPE); + /** + * @param tokenUrl URL of the third party login page. + * @param clientId The client identifier. See http://tools.ietf.org/html/rfc6749#section-2.2. + * @param scope The scope of access request. See http://tools.ietf.org/html/rfc6749#section-3.3. + */ + public void fetchToken(String tokenUrl, String clientId, String scope) { + if (!isValidTokenUrl(tokenUrl)) { + failFetchToken( + "Token URL does not match the domain\'s allowed URL patterns." + + " URL: " + tokenUrl + + ", patterns: " + TextUtils.join(",", this.mTokenUrlPatterns)); + return; + } - Uri uri = uriBuilder.build(); + Uri uri = buildRequestUri(tokenUrl, clientId, scope); Intent intent = new Intent(Intent.ACTION_VIEW, uri); Log.i("ThirdPartyAuth", "fetchToken() url:" + uri); OAuthRedirectActivity.setEnabled(mContext, true); @@ -102,6 +100,27 @@ public class ThirdPartyTokenFetcher { } } + private Uri buildRequestUri(String tokenUrl, String clientId, String scope) { + Uri.Builder uriBuilder = Uri.parse(tokenUrl).buildUpon(); + uriBuilder.appendQueryParameter("redirect_uri", this.mRedirectUri); + uriBuilder.appendQueryParameter("scope", scope); + uriBuilder.appendQueryParameter("client_id", clientId); + uriBuilder.appendQueryParameter("state", mState); + uriBuilder.appendQueryParameter("response_type", RESPONSE_TYPE); + + return uriBuilder.build(); + } + + /** Verifies the host-supplied URL matches the domain's allowed URL patterns. */ + private boolean isValidTokenUrl(String tokenUrl) { + for (String pattern : mTokenUrlPatterns) { + if (tokenUrl.matches(pattern)) { + return true; + } + } + return false; + } + private boolean isValidIntent(Intent intent) { assert intent != null; diff --git a/remoting/android/java/src/org/chromium/chromoting/jni/JniInterface.java b/remoting/android/java/src/org/chromium/chromoting/jni/JniInterface.java index 6691e4f577..6d1dfd1274 100644 --- a/remoting/android/java/src/org/chromium/chromoting/jni/JniInterface.java +++ b/remoting/android/java/src/org/chromium/chromoting/jni/JniInterface.java @@ -322,16 +322,16 @@ public class JniInterface { private static native void nativeSendMouseWheelEvent(int deltaX, int deltaY); /** Presses or releases the specified (nonnegative) key. Called on the UI thread. */ - public static void sendKeyEvent(int keyCode, boolean keyDown) { + public static boolean sendKeyEvent(int keyCode, boolean keyDown) { if (!sConnected) { - return; + return false; } - nativeSendKeyEvent(keyCode, keyDown); + return nativeSendKeyEvent(keyCode, keyDown); } /** Passes key press information to the native handling code. */ - private static native void nativeSendKeyEvent(int keyCode, boolean keyDown); + private static native boolean nativeSendKeyEvent(int keyCode, boolean keyDown); /** Sends TextEvent to the host. Called on the UI thread. */ public static void sendTextEvent(String text) { diff --git a/remoting/client/jni/chromoting_jni_instance.cc b/remoting/client/jni/chromoting_jni_instance.cc index 9a929dd037..32aaa4be2c 100644 --- a/remoting/client/jni/chromoting_jni_instance.cc +++ b/remoting/client/jni/chromoting_jni_instance.cc @@ -210,23 +210,15 @@ void ChromotingJniInstance::SendMouseWheelEvent(int delta_x, int delta_y) { connection_->input_stub()->InjectMouseEvent(event); } -void ChromotingJniInstance::SendKeyEvent(int key_code, bool key_down) { - if (!jni_runtime_->network_task_runner()->BelongsToCurrentThread()) { - jni_runtime_->network_task_runner()->PostTask( - FROM_HERE, base::Bind(&ChromotingJniInstance::SendKeyEvent, - this, key_code, key_down)); - return; - } - - uint32 usb_code = AndroidKeycodeToUsbKeycode(key_code); - if (usb_code) { - protocol::KeyEvent event; - event.set_usb_keycode(usb_code); - event.set_pressed(key_down); - connection_->input_stub()->InjectKeyEvent(event); - } else { +bool ChromotingJniInstance::SendKeyEvent(int key_code, bool key_down) { + uint32 usb_key_code = AndroidKeycodeToUsbKeycode(key_code); + if (!usb_key_code) { LOG(WARNING) << "Ignoring unknown keycode: " << key_code; + return false; } + + SendKeyEventInternal(usb_key_code, key_down); + return true; } void ChromotingJniInstance::SendTextEvent(const std::string& text) { @@ -449,6 +441,22 @@ void ChromotingJniInstance::SetDeviceName(const std::string& device_name) { device_name_ = device_name; } +void ChromotingJniInstance::SendKeyEventInternal(int usb_key_code, + bool key_down) { + if (!jni_runtime_->network_task_runner()->BelongsToCurrentThread()) { + jni_runtime_->network_task_runner()->PostTask( + FROM_HERE, base::Bind(&ChromotingJniInstance::SendKeyEventInternal, + this, usb_key_code, key_down)); + return; + } + + + protocol::KeyEvent event; + event.set_usb_keycode(usb_key_code); + event.set_pressed(key_down); + connection_->input_stub()->InjectKeyEvent(event); +} + void ChromotingJniInstance::EnableStatsLogging(bool enabled) { DCHECK(jni_runtime_->network_task_runner()->BelongsToCurrentThread()); diff --git a/remoting/client/jni/chromoting_jni_instance.h b/remoting/client/jni/chromoting_jni_instance.h index 8fb60232df..91919b27e5 100644 --- a/remoting/client/jni/chromoting_jni_instance.h +++ b/remoting/client/jni/chromoting_jni_instance.h @@ -87,7 +87,7 @@ class ChromotingJniInstance void SendMouseWheelEvent(int delta_x, int delta_y); // Sends the provided keyboard scan code to the host. - void SendKeyEvent(int key_code, bool key_down); + bool SendKeyEvent(int key_code, bool key_down); void SendTextEvent(const std::string& text); @@ -136,6 +136,8 @@ class ChromotingJniInstance // Sets the device name. Can be called on any thread. void SetDeviceName(const std::string& device_name); + void SendKeyEventInternal(int usb_key_code, bool key_down); + // Enables or disables periodic logging of performance statistics. Called on // the network thread. void EnableStatsLogging(bool enabled); diff --git a/remoting/client/jni/chromoting_jni_runtime.cc b/remoting/client/jni/chromoting_jni_runtime.cc index cc84d4e791..f7a3ff1ea9 100644 --- a/remoting/client/jni/chromoting_jni_runtime.cc +++ b/remoting/client/jni/chromoting_jni_runtime.cc @@ -129,11 +129,11 @@ static void SendMouseWheelEvent(JNIEnv* env, delta_x, delta_y); } -static void SendKeyEvent(JNIEnv* env, +static jboolean SendKeyEvent(JNIEnv* env, jclass clazz, jint keyCode, jboolean keyDown) { - remoting::ChromotingJniRuntime::GetInstance()->session()->SendKeyEvent( + return remoting::ChromotingJniRuntime::GetInstance()->session()->SendKeyEvent( keyCode, keyDown); } diff --git a/remoting/host/linux/linux_me2me_host.py b/remoting/host/linux/linux_me2me_host.py index 8f87dce0fb..082ecc0edb 100755 --- a/remoting/host/linux/linux_me2me_host.py +++ b/remoting/host/linux/linux_me2me_host.py @@ -500,14 +500,15 @@ class Desktop: self.host_proc.stdin.close() -def get_daemon_pid(): +def get_daemon_proc(): """Checks if there is already an instance of this script running, and returns - its PID. + a psutil.Process instance for it. Returns: - The process ID of the existing daemon process, or 0 if the daemon is not - running. + A Process instance for the existing daemon process, or None if the daemon + is not running. """ + uid = os.getuid() this_pid = os.getpid() @@ -535,11 +536,11 @@ def get_daemon_pid(): if len(cmdline) < 2: continue if cmdline[0] == sys.executable and cmdline[1] == sys.argv[0]: - return process.pid + return process except (psutil.NoSuchProcess, psutil.AccessDenied): continue - return 0 + return None def choose_x_session(): @@ -977,8 +978,8 @@ Web Store: https://chrome.google.com/remotedesktop""" # Check for a modal command-line option (start, stop, etc.) if options.get_status: - pid = get_daemon_pid() - if pid != 0: + proc = get_daemon_proc() + if proc is not None: print "STARTED" elif is_supported_platform(): print "STOPPED" @@ -989,23 +990,28 @@ Web Store: https://chrome.google.com/remotedesktop""" # TODO(sergeyu): Remove --check-running once NPAPI plugin and NM host are # updated to always use get-status flag instead. if options.check_running: - pid = get_daemon_pid() - return 0 if pid != 0 else 1 + proc = get_daemon_proc() + return 1 if proc is None else 0 if options.stop: - pid = get_daemon_pid() - if pid == 0: + proc = get_daemon_proc() + if proc is None: print "The daemon is not currently running" else: - print "Killing process %s" % pid - os.kill(pid, signal.SIGTERM) + print "Killing process %s" % proc.pid + proc.terminate() + try: + proc.wait(timeout=30) + except psutil.TimeoutExpired: + print "Timed out trying to kill daemon process" + return 1 return 0 if options.reload: - pid = get_daemon_pid() - if pid == 0: + proc = get_daemon_proc() + if proc is None: return 1 - os.kill(pid, signal.SIGHUP) + proc.send_signal(signal.SIGHUP) return 0 if options.add_user: @@ -1097,8 +1103,8 @@ Web Store: https://chrome.google.com/remotedesktop""" # Determine whether a desktop is already active for the specified host # host configuration. - pid = get_daemon_pid() - if pid != 0: + proc = get_daemon_proc() + if proc is not None: # Debian policy requires that services should "start" cleanly and return 0 # if they are already running. print "Service already running." diff --git a/remoting/webapp/build-webapp.py b/remoting/webapp/build-webapp.py index 4cbe4f0c46..df934f83b9 100755 --- a/remoting/webapp/build-webapp.py +++ b/remoting/webapp/build-webapp.py @@ -272,7 +272,7 @@ def main(): arg_type = '' files = [] locales = [] - for arg in sys.argv[8:]: + for arg in sys.argv[7:]: if arg in ['--locales']: arg_type = arg elif arg_type == '--locales': diff --git a/remoting/webapp/html/ui_me2me.html b/remoting/webapp/html/ui_me2me.html index 057157aa18..ca2cc46b18 100644 --- a/remoting/webapp/html/ui_me2me.html +++ b/remoting/webapp/html/ui_me2me.html @@ -41,6 +41,19 @@ found in the LICENSE file. id="host-list-refresh-failed-button"></button> </div> + <div id="host-list-empty" hidden> + <div id="host-list-empty-hosting-supported" + class="host-list-empty-instructions" + i18n-content="HOST_LIST_EMPTY_HOSTING_SUPPORTED" + i18n-value-name-1="HOME_DAEMON_START_BUTTON"> + </div> + <div id="host-list-empty-hosting-unsupported" + class="host-list-empty-instructions" + i18n-content="HOST_LIST_EMPTY_HOSTING_UNSUPPORTED" + i18n-value-name-1="HOME_DAEMON_START_BUTTON"> + </div> + </div> + <div id="daemon-control" data-daemon-state="enabled disabled" hidden> <div class="section-row no-non-local-hosts" data-daemon-state="disabled"> @@ -88,18 +101,6 @@ found in the LICENSE file. i18n-content="HOME_DAEMON_MANAGE_PAIRINGS"></a> </div> </div> <!-- enabled --> - <div id="host-list-empty" hidden> - <div id="host-list-empty-hosting-supported" - class="host-list-empty-instructions" - i18n-content="HOST_LIST_EMPTY_HOSTING_SUPPORTED" - i18n-value-name-1="HOME_DAEMON_START_BUTTON"> - </div> - <div id="host-list-empty-hosting-unsupported" - class="host-list-empty-instructions" - i18n-content="HOST_LIST_EMPTY_HOSTING_UNSUPPORTED" - i18n-value-name-1="HOME_DAEMON_START_BUTTON"> - </div> - </div> </div> <!-- daemon-control --> </div> <!-- me2me-content --> |