diff options
author | ajwong@chromium.org <ajwong@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-24 23:13:21 +0000 |
---|---|---|
committer | ajwong@chromium.org <ajwong@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-24 23:13:21 +0000 |
commit | 999d862cc8675b78fdeab5b0a4b1689b0505f276 (patch) | |
tree | 316c8f12dc260addddf029fb249a286d3b68cc82 /remoting/client | |
parent | 6ee62b63cec739d231bf451168219576df623e20 (diff) | |
download | chromium_src-999d862cc8675b78fdeab5b0a4b1689b0505f276.zip chromium_src-999d862cc8675b78fdeab5b0a4b1689b0505f276.tar.gz chromium_src-999d862cc8675b78fdeab5b0a4b1689b0505f276.tar.bz2 |
Redo the Chromoting extension as an app-engine app.
This will allow for an easier develop/deploy cycle in the short term, which
will facilitate development.
BUG=none
TEST=none
Review URL: http://codereview.chromium.org/6580022
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@75987 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting/client')
17 files changed, 620 insertions, 242 deletions
diff --git a/remoting/client/appengine/api.py b/remoting/client/appengine/api.py new file mode 100644 index 0000000..1b48da0 --- /dev/null +++ b/remoting/client/appengine/api.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python + +# Copyright (c) 2011 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""API endpoints to get around javascript's single-origin restriction.""" + +import logging + +from django.utils import simplejson as json + +import gdata.client + +from google.appengine.ext import webapp +from google.appengine.ext.webapp import util +from google.appengine.ext.webapp.util import login_required +import auth + + +class GetXmppTokenHandler(webapp.RequestHandler): + """Retrieves the user's XMPP token.""" + @login_required + def get(self): + try: + self.response.headers['Content-Type'] = 'application/json' + self.response.out.write( + json.dumps({'xmpp_token': auth.GetXmppToken().token})) + except auth.NotAuthenticated: + self.response.out.write('User has not authenticated') + self.set_status(400) + return + pass + + +class GetHostListHandler(webapp.RequestHandler): + """Proxies the host-list handlers on the Chromoting directory.""" + @login_required + def get(self): + try: + client = gdata.client.GDClient() + host_list_json = client.Request( + method='GET', + uri="https://www.googleapis.com/chromoting/v1/@me/hosts", + converter=None, + desired_class=None, + auth_token=auth.GetChromotingToken()) + self.response.headers['Content-Type'] = 'application/json' + self.response.out.write(host_list_json.read()) + except auth.NotAuthenticated: + self.response.out.write('User has not authenticated') + self.response.set_status(400) + return + + +def main(): + application = webapp.WSGIApplication( + [ + ('/api/get_xmpp_token', GetXmppTokenHandler), + ('/api/get_host_list', GetHostListHandler) + ], + debug=True) + util.run_wsgi_app(application) + + +if __name__ == '__main__': + main() diff --git a/remoting/client/appengine/app.yaml b/remoting/client/appengine/app.yaml new file mode 100644 index 0000000..ff9db53 --- /dev/null +++ b/remoting/client/appengine/app.yaml @@ -0,0 +1,22 @@ +application: google.com:chromoting +version: 1 +runtime: python +api_version: 1 + +handlers: +- url: /static_files + static_dir: static_files + secure: always + +- url: /api/.* + script: api.py + secure: always + +- url: /auth/.* + script: auth.py + secure: always + +- url: .* + script: main.py + secure: always + diff --git a/remoting/client/appengine/auth.py b/remoting/client/appengine/auth.py new file mode 100644 index 0000000..f40cbdd --- /dev/null +++ b/remoting/client/appengine/auth.py @@ -0,0 +1,320 @@ +#!/usr/bin/env python + +# Copyright (c) 2011 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Provides authentcation related utilities and endpoint handlers. + +All authentication code for the webapp should go through this module. In +general, credentials should be used server-side. The URL endpoints are for +initiating authentication flows, and for managing credential storage per user. +""" + +import os + +import gdata.gauth +import gdata.client + +from google.appengine.ext import db +from google.appengine.api import users +from google.appengine.ext import webapp +from google.appengine.ext.webapp import template +from google.appengine.ext.webapp import util +from google.appengine.ext.webapp.util import login_required + + +SCOPES = ['https://www.googleapis.com/auth/chromoting', + 'https://www.googleapis.com/auth/googletalk' ] + + +class NotAuthenticated(Exception): + """API requiring authentication is called with credentials.""" + pass + + +class OAuthInvalidSetup(Exception): + """OAuth configuration on app is not complete.""" + pass + + +class OAuthConfig(db.Model): + """Stores the configuration data for OAuth. + + Currently used to store the consumer key and secret so that it does not need + to be checked into the source tree. + """ + consumer_key = db.StringProperty() + consumer_secret = db.StringProperty() + + +def GetChromotingToken(throws=True): + """Retrieves the Chromoting OAuth token for the user. + + Args: + throws: bool (optional) Default is True. Throws if no token. + + Returns: + An gdata.gauth.OAuthHmacToken for the current user. + """ + user = users.get_current_user() + access_token = None + if user: + access_token = LoadToken('chromoting_token') + if throws and not access_token: + raise NotAuthenticated() + return access_token + + +def GetXmppToken(throws=True): + """Retrieves the XMPP for Chromoting. + + Args: + throws: bool (optional) Default is True. Throws if no token. + + Returns: + An gdata.gauth.ClientLoginToken for the current user. + """ + user = users.get_current_user() + access_token = None + if user: + access_token = LoadToken('xmpp_token') + if throws and not access_token: + raise NotAuthenticated() + return access_token + + +def ClearChromotingToken(): + """Clears all Chromoting OAuth token state from the datastore.""" + DeleteToken('request_token') + DeleteToken('chromoting_token') + + +def ClearXmppToken(): + """Clears all Chromoting ClientLogin token state from the datastore.""" + DeleteToken('xmpp_token') + + +def GetUserId(): + """Retrieves the user id for the current user. + + Returns: + A string with the user id of the logged in user. + + Raises: + NotAuthenticated if the user is not logged in, or missing an id. + """ + user = users.get_current_user() + if not user: + raise NotAuthenticated() + + if not user.user_id(): + raise NotAuthenticated('no e-mail with google account!') + + return user.user_id() + + +def LoadToken(name): + """Leads a gdata auth token for the current user. + + Tokens are scoped to each user, and retrieved by a name. + + Args: + name: A string with the name of the token for the current user. + + Returns: + The token associated with the name for the user. + """ + user_id = GetUserId(); + return gdata.gauth.AeLoad(user_id + name) + + +def SaveToken(name, token): + """Saves a gdata auth token for the current user. + + Tokens are scoped to each user, and stored by a name. + + Args: + name: A string with the name of the token. + """ + user_id = GetUserId(); + gdata.gauth.AeSave(token, user_id + name) + + +def DeleteToken(name): + """Deletes a stored gdata auth token for the current user. + + Tokens are scoped to each user, and stored by a name. + + Args: + name: A string with the name of the token. + """ + user_id = GetUserId(); + gdata.gauth.AeDelete(user_id + name) + + +def OAuthConfigKey(): + """Generates a standard key path for this app's OAuth configuration.""" + return db.Key.from_path('OAuthConfig', 'oauth_config') + + +def GetOAuthConfig(throws=True): + """Retrieves the OAuthConfig for this app. + + Returns: + The OAuthConfig object for this app. + + Raises: + OAuthInvalidSetup if no OAuthConfig exists. + """ + config = db.get(OAuthConfigKey()) + if throws and not config: + raise OAuthInvalidSetup() + return config + + +class ChromotingAuthHandler(webapp.RequestHandler): + """Initiates getting the OAuth access token for the user. + + This webapp uses 3-legged OAuth. This handlers performs the first step + of getting the OAuth request token, and then forwarding on to the + Google Accounts authorization endpoint for the second step. The final + step is completed by the ChromotingAuthReturnHandler below. + + FYI, all three steps are collectively known as the "OAuth dance." + """ + @login_required + def get(self): + ClearChromotingToken() + client = gdata.client.GDClient() + + oauth_callback_url = ('http://%s/auth/chromoting_auth_return' % + self.request.host) + request_token = client.GetOAuthToken( + SCOPES, oauth_callback_url, GetOAuthConfig().consumer_key, + consumer_secret=GetOAuthConfig().consumer_secret) + + SaveToken('request_token', request_token) + domain = None # Not on an Google Apps domain. + auth_uri = request_token.generate_authorization_url() + self.redirect(str(auth_uri)) + + +class ChromotingAuthReturnHandler(webapp.RequestHandler): + """Finishes the authorization started in ChromotingAuthHandler.i + + After the user authorizes the OAuth request token at the OAuth request + URL they were redirected to in ChromotingAuthHandler, OAuth will send + them back here with an auth token in the URL. + + This handler retrievies the access token, and stores it completing the + OAuth dance. + """ + @login_required + def get(self): + saved_request_token = LoadToken('request_token') + DeleteToken('request_token') + request_token = gdata.gauth.AuthorizeRequestToken( + saved_request_token, self.request.uri) + + # Upgrade the token and save in the user's datastore + client = gdata.client.GDClient() + access_token = client.GetAccessToken(request_token) + SaveToken('chromoting_token', access_token) + self.redirect("/") + + +class XmppAuthHandler(webapp.RequestHandler): + """Prompts Google Accounts credentials and retrieves a ClientLogin token. + + This class takes the user's plaintext username and password, and then + posts a request to ClientLogin to get the access token. + + THIS CLASS SHOULD NOT EXIST. + + We should NOT be taking a user's Google Accounts credentials in our webapp. + However, we need a ClientLogin token for jingle, and this is currently the + only known workaround. + """ + @login_required + def get(self): + ClearXmppToken() + path = os.path.join(os.path.dirname(__file__), 'client_login.html') + self.response.out.write(template.render(path, {})) + + def post(self): + client = gdata.client.GDClient() + email = self.request.get('username') + password = self.request.get('password') + try: + client.ClientLogin( + email, password, 'chromoclient', 'chromiumsync') + SaveToken('xmpp_token', client.auth_token) + except gdata.client.CaptchaChallenge: + self.response.out.write('You need to solve a Captcha. ' + 'Unforutnately, we still have to implement that.') + self.redirect('/') + + +class ClearChromotingTokenHandler(webapp.RequestHandler): + """Endpoint for dropping the user's Chromoting token.""" + @login_required + def get(self): + ClearChromotingToken() + self.redirect('/') + + +class ClearXmppTokenHandler(webapp.RequestHandler): + """Endpoint for dropping the user's Xmpp token.""" + @login_required + def get(self): + ClearXmppToken() + self.redirect('/') + + +class SetupOAuthHandler(webapp.RequestHandler): + """Administrative page for specifying the OAuth consumer key/secret.""" + @login_required + def get(self): + path = os.path.join(os.path.dirname(__file__), + 'chromoting_oauth_setup.html') + self.response.out.write(template.render(path, {})) + + def post(self): + old_consumer_secret = self.request.get('old_consumer_secret') + + query = OAuthConfig.all() + + # If there is an existing key, only allow updating if you know the old + # key. This is a simple safeguard against random users hitting this page. + config = GetOAuthConfig(throws=False) + if config: + if config.consumer_secret != old_consumer_secret: + self.response.out.set_status(400) + self.response.out.write('Incorrect old consumer secret') + return + else: + config = OAuthConfig(key_name = OAuthConfigKey().id_or_name()) + + config.consumer_key = self.request.get('consumer_key') + config.consumer_secret = self.request.get('new_consumer_secret') + config.put() + self.redirect('/') + + +def main(): + application = webapp.WSGIApplication( + [ + ('/auth/chromoting_auth', ChromotingAuthHandler), + ('/auth/xmpp_auth', XmppAuthHandler), + ('/auth/chromoting_auth_return', ChromotingAuthReturnHandler), + ('/auth/clear_xmpp_token', ClearXmppTokenHandler), + ('/auth/clear_chromoting_token', ClearChromotingTokenHandler), + ('/auth/setup_oauth', SetupOAuthHandler) + ], + debug=True) + util.run_wsgi_app(application) + + +if __name__ == '__main__': + main() diff --git a/remoting/client/appengine/chromoting_oauth_setup.html b/remoting/client/appengine/chromoting_oauth_setup.html new file mode 100644 index 0000000..3fc4664 --- /dev/null +++ b/remoting/client/appengine/chromoting_oauth_setup.html @@ -0,0 +1,23 @@ +<!doctype html> +<!-- +Copyright (c) 2011 The Chromium Authors. All rights reserved. +Use of this source code is governed by a BSD-style license that can be +found in the LICENSE file. +--> + +<html> + <head> + <title>Chromoting OAuth setup page.</title> + </head> + <body> + <p>This is an administration page to setup the consumer key and secret + to be used with chromoting. Please don't play with this unless you are + on the Chromoting dev team. + <form method="post" name="auth"> + Consumer Key: <input type="text" name="consumer_key" /> + New Consumer Secret: <input type="password" name="new_consumer_secret" /> + Old Consumer Secret: <input type="password" name="old_consumer_secret" /> + <input type="submit" value="Submit" /> + </form> + </body> +</html> diff --git a/remoting/client/extension/chromoting_tab.html b/remoting/client/appengine/chromoting_session.html index c3c7454..2d26b45 100644 --- a/remoting/client/extension/chromoting_tab.html +++ b/remoting/client/appengine/chromoting_session.html @@ -1,5 +1,6 @@ +<!doctype html> <!-- -Copyright (c) 2010 The Chromium Authors. All rights reserved. +Copyright (c) 2011 The Chromium Authors. All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. --> @@ -7,9 +8,19 @@ found in the LICENSE file. <html> <head> <title id="title">Chromoting Session</title> - <link rel="stylesheet" type="text/css" href="main.css" /> - <script type="text/javascript" src="base.js"></script> - <script type="text/javascript" src="chromoting_tab.js"></script> + <link rel="stylesheet" type="text/css" href="static_files/main.css" /> + <script type="text/javascript"> + <!-- + // TODO(ajwong): Total Hack. Do this nicer. + document.xmpp_auth_token="{{xmpp_token.token_string}}"; + document.username="{{username}}"; + document.hostname="{{hostname}}"; + document.hostjid="{{hostjid}}"; + --> + </script> + <script type="text/javascript" src="static_files/base.js"></script> + <script type="text/javascript" src="static_files/chromoting_session.js"> + </script> </head> <body class="chromoting_body" onload="init();"> <div id="status_msg" class="status_msg"></div> @@ -40,7 +51,7 @@ found in the LICENSE file. onclick="submitLogin();"/></td> </tr> </table> - </tr></td></table> + </td></tr></table> </div> <div id="plugin_scroll_panel" class="plugin-scroll-panel"> <embed name="chromoting" id="chromoting" diff --git a/remoting/client/appengine/client_login.html b/remoting/client/appengine/client_login.html new file mode 100644 index 0000000..9284109 --- /dev/null +++ b/remoting/client/appengine/client_login.html @@ -0,0 +1,40 @@ +<!doctype html> +<!-- +Copyright (c) 2011 The Chromium Authors. All rights reserved. +Use of this source code is governed by a BSD-style license that can be +found in the LICENSE file. +--> + +<html> + <head> + <title>Chromoting Google Accounts Login (needed for XMPP)</title> + </head> + + <body> + <p>Please provide your Google Accounts username/password to get a + ClientLogin token for Google Talk. + + <p> + <form method="post" name="auth"> + Username: <input type="text" name="username" /><br /> + Password: <input type="password" name="password" /><br /> + <input type="submit" value="Submit" /> + </form> + + <hr /> + <p> + Yes, we know we're asking for your Google Accounts credentials in a webapp. + + <p> + No we're not happy with this. + + <p> + Yes, we (think we) need to do it this way. + + <p> + libjingle needs a ClientLogin token and due to single-origin issues, we + have to proxy the request through the webapp. If anyone has a better idea, + please <b>please</b> <b><i>please</i></b> let us know. + + </body> +</html> diff --git a/remoting/client/appengine/hostlist.html b/remoting/client/appengine/hostlist.html new file mode 100644 index 0000000..2f5eb95 --- /dev/null +++ b/remoting/client/appengine/hostlist.html @@ -0,0 +1,51 @@ +<!-- +Copyright (c) 2011 The Chromium Authors. All rights reserved. +Use of this source code is governed by a BSD-style license that can be +found in the LICENSE file. +--> + +<html> + <head> + <script type="text/javascript" src="static_files/base.js"></script> + <script type="text/javascript" src="static_files/client.js"></script> + <link rel="stylesheet" type="text/css" href="static_files/main.css" /> + <title>Remote Access Host List</title> + </head> + <body class="hostlist_body" +{% ifnotequal chromoting_token None %} + onload="populateHostList();" +{% endifnotequal %} +> + + <h1>Remote Access</h1> + + <div id="auth_status" class="authstatus"> + <p>Chromoting Token: +{% ifnotequal chromoting_token None %} + OK (<a href="/auth/clear_chromoting_token">clear token</a>) +{% else %} + <a href="/auth/chromoting_auth">Not Authenticated</a> +{% endifnotequal %} + <p>Xmpp Token: +{% ifnotequal xmpp_token None %} + OK (<a href="/auth/clear_xmpp_token">clear token</a>) +{% else %} + <a href="/auth/xmpp_auth">Not Authenticated</a> +{% endifnotequal %} + </div> + <hr /> + + <p class="reload"> + <a href="javascript:populateHostList()">Reload host list</a> + </p> + + <input type=checkbox name="show_offline" id="show_offline" + onClick="updateShowOfflineHosts(this)"/>Show offline hosts + + <div id="hostlist_div" class="hostlist"> + <p class='message'>Initializing...</p> + </div> + + <br /> + </body> +</html> diff --git a/remoting/client/appengine/main.py b/remoting/client/appengine/main.py new file mode 100644 index 0000000..78900c2 --- /dev/null +++ b/remoting/client/appengine/main.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python + +import logging +import os + +from django.utils import simplejson as json + +import gdata.gauth +import gdata.client + +from google.appengine.api import users +from google.appengine.ext import webapp +from google.appengine.ext.webapp import template +from google.appengine.ext.webapp import util +from google.appengine.ext.webapp.util import login_required + +import auth + +class HostListHandler(webapp.RequestHandler): + """Renders the main hostlist page.""" + @login_required + def get(self): + template_params = { + 'chromoting_token': auth.GetChromotingToken(throws=False), + 'xmpp_token': auth.GetXmppToken(throws=False) + } + path = os.path.join(os.path.dirname(__file__), 'hostlist.html') + self.response.out.write(template.render(path, template_params)) + + +class ChromotingSessionHandler(webapp.RequestHandler): + """Renders one Chromoting session.""" + @login_required + def get(self): + template_params = { + 'hostname': self.request.get('hostname'), + 'username': users.get_current_user().email(), + 'hostjid': self.request.get('hostjid'), + 'xmpp_token': auth.GetXmppToken(), + } + path = os.path.join(os.path.dirname(__file__), 'chromoting_session.html') + self.response.out.write(template.render(path, template_params)) + + +def main(): + application = webapp.WSGIApplication( + [ + ('/', HostListHandler), + ('/session', ChromotingSessionHandler), + ], + debug=True) + util.run_wsgi_app(application) + + +if __name__ == '__main__': + main() diff --git a/remoting/client/extension/base.js b/remoting/client/appengine/static_files/base.js index 2c3188d..2c3188d 100644 --- a/remoting/client/extension/base.js +++ b/remoting/client/appengine/static_files/base.js diff --git a/remoting/client/extension/chromoticon.png b/remoting/client/appengine/static_files/chromoticon.png Binary files differindex a5cb2b2..a5cb2b2 100644 --- a/remoting/client/extension/chromoticon.png +++ b/remoting/client/appengine/static_files/chromoticon.png diff --git a/remoting/client/extension/chromoting_tab.js b/remoting/client/appengine/static_files/chromoting_session.js index ce13c0f4..18ee990 100644 --- a/remoting/client/extension/chromoting_tab.js +++ b/remoting/client/appengine/static_files/chromoting_session.js @@ -7,42 +7,13 @@ chromoting.messageId = 1; function init() { - // This page should only get one request, and it should be - // from the chromoting extension asking for initial connection. - // Later we may need to create a more persistent channel for - // better UI communication. Then, we should probably switch - // to chrome.extension.connect(). - chrome.extension.onRequest.addListener(requestListener); -} - -function submitLogin() { - var username = document.getElementById("username").value; - var password = document.getElementById("password").value; - - // Make the login panel invisible and submit login info. - document.getElementById("login_panel").style.display = "none"; - chromoting.plugin.submitLoginInfo(username, password); -} - -/** - * A listener function to be called when the extension fires a request. - * - * @param request The request sent by the calling script. - * @param sender The MessageSender object with info about the calling script's - * context. - * @param sendResponse Function to call with response. - */ -function requestListener(request, sender, sendResponse) { - console.log(sender.tab ? - 'from a content script: ' + sender.tab.url : - 'from the extension'); - // Kick off the connection. var plugin = document.getElementById('chromoting'); chromoting.plugin = plugin; - chromoting.username = request.username; - chromoting.hostname = request.hostName; + chromoting.username = document.username; + chromoting.hostname = document.hostname; + chromoting.hostjid = document.hostjid; // Setup the callback that the plugin will call when the connection status // has changes and the UI needs to be updated. It needs to be an object with @@ -55,16 +26,22 @@ function requestListener(request, sender, sendResponse) { // TODO(garykac): Clean exit if |connect| isn't a funtion. if (typeof plugin.connect === 'function') { - plugin.connect(request.username, request.hostJid, - request.xmppAuth); + plugin.connect(chromoting.username, chromoting.hostjid, + document.xmpp_auth_token); } else { console.log('ERROR: chromoting plugin not loaded'); } - document.getElementById('title').innerText = request.hostName; + document.getElementById('title').innerText = chromoting.hostname; +} - // Send an empty response since we have nothing to say. - sendResponse({}); +function submitLogin() { + var username = document.getElementById("username").value; + var password = document.getElementById("password").value; + + // Make the login panel invisible and submit login info. + document.getElementById("login_panel").style.display = "none"; + chromoting.plugin.submitLoginInfo(username, password); } /** diff --git a/remoting/client/extension/client.js b/remoting/client/appengine/static_files/client.js index 3ff12ac..067cedf 100644 --- a/remoting/client/extension/client.js +++ b/remoting/client/appengine/static_files/client.js @@ -219,25 +219,17 @@ function appendMessage(e, classname, message) { } function populateHostList() { - var username = getCookie('username'); - var hostlistDiv = document.getElementById('hostlist_div'); - if (!username) { - displayMessage(hostlistDiv, 'message', - 'Please sign in to see a list of available hosts.'); - return; - } + displayMessage(hostlistDiv, 'message', + 'Hosts will appaer if Chromoting Token is "OK".'); var xhr = new XMLHttpRequest(); - var token = getCookie('chromoting_auth'); - // Unhide host list. hostlistDiv.style.display = "block"; xhr.onreadystatechange = function() { if (xhr.readyState == 1) { - displayMessage(hostlistDiv, 'message', 'Loading host list for ' + - username); + displayMessage(hostlistDiv, 'message', 'Loading host list'); } if (xhr.readyState != 4) { return; @@ -256,18 +248,15 @@ function populateHostList() { clear(hostlistDiv); if (errorResponse.error.message == "Token expired") { appendMessage(hostlistDiv, 'message', - 'The authentication token for ' + username + - ' has expired. Please sign in again.'); + 'Authentication token expired. Please sign in again.'); logout(); } else if (errorResponse.error.message == "Token invalid") { appendMessage(hostlistDiv, 'message', - 'Invalid authentication token for ' + username + '. ' + - 'Please sign in again.'); + 'Invalid authentication token. Please sign in again.'); logout(); } else { appendMessage(hostlistDiv, 'message', - 'Unable to load host list for ' + username + '. ' + - 'Please try again later.'); + 'Unable to load host list. Please try again later.'); appendMessage(hostlistDiv, 'message', 'Error code: ' + errorResponse.error.code); appendMessage(hostlistDiv, 'message', @@ -276,9 +265,8 @@ function populateHostList() { } }; - xhr.open('GET', 'https://www.googleapis.com/chromoting/v1/@me/hosts'); + xhr.open('GET', 'api/get_host_list', true); xhr.setRequestHeader('Content-Type', 'text/plain;charset=UTF-8'); - xhr.setRequestHeader('Authorization', 'GoogleLogin auth=' + token); xhr.send(null); } @@ -332,7 +320,7 @@ function addHostInfo(host) { hostEntry.setAttribute('class', 'hostentry'); var hostIcon = document.createElement('img'); - hostIcon.setAttribute('src', 'machine.png'); + hostIcon.setAttribute('src', 'static_files/machine.png'); hostIcon.setAttribute('class', 'hosticon'); hostEntry.appendChild(hostIcon); @@ -342,8 +330,9 @@ function addHostInfo(host) { var connect = document.createElement('input'); connect.setAttribute('type', 'button'); connect.setAttribute('value', 'Connect'); - connect.setAttribute('onclick', "openChromotingTab('" + host.hostName + - "', '" + host.jabberId + "'); return false;"); + connect.setAttribute('onclick', "window.open('session?hostname=" + + encodeURIComponent(host.hostName) + "&hostjid=" + + encodeURIComponent(host.jabberId) + "');"); span.appendChild(connect); hostEntry.appendChild(span); } diff --git a/remoting/client/extension/machine.png b/remoting/client/appengine/static_files/machine.png Binary files differindex 635a9bb..635a9bb 100644 --- a/remoting/client/extension/machine.png +++ b/remoting/client/appengine/static_files/machine.png diff --git a/remoting/client/extension/main.css b/remoting/client/appengine/static_files/main.css index 5e5cf29..5e5cf29 100644 --- a/remoting/client/extension/main.css +++ b/remoting/client/appengine/static_files/main.css diff --git a/remoting/client/extension/background.html b/remoting/client/extension/background.html deleted file mode 100644 index 3e17fad..0000000 --- a/remoting/client/extension/background.html +++ /dev/null @@ -1,13 +0,0 @@ -<!-- -Copyright (c) 2010 The Chromium Authors. All rights reserved. -Use of this source code is governed by a BSD-style license that can be -found in the LICENSE file. ---> - -<html> - <head> - <script type="text/javascript" src="base.js"></script> - <script type="text/javascript" src="background.js"></script> - </head> - <body/> -</html> diff --git a/remoting/client/extension/background.js b/remoting/client/extension/background.js deleted file mode 100644 index f417407..0000000 --- a/remoting/client/extension/background.js +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -/** - * If there is already a tab with the given URL, switch focus to it. Otherwise, - * create a new tab and open the URL. - * - * @param url The URL to open. - */ -function focusOrCreateTab(url) { - chrome.windows.getAll({"populate":true}, function(windows) { - var existing_tab = null; - for (var i in windows) { - var tabs = windows[i].tabs; - for (var j in tabs) { - var tab = tabs[j]; - if (tab.url == url) { - existing_tab = tab; - break; - } - } - } - if (existing_tab) { - chrome.tabs.update(existing_tab.id, {"selected": true}); - } else { - chrome.tabs.create({"url": url, "selected": true}); - } - }); -} - -/** - * In the current tab, navigate to the specified URL. - * - * @param url The URL to navigate to. - */ -function navigate(url, callback) { - chrome.tabs.getSelected(null, function(tab) { - chrome.tabs.update(tab.id, {url: url}, callback); - }); -} - -// Open the Chromoting HostList tab when -chrome.browserAction.onClicked.addListener(function(tab) { - var hostlist_url = chrome.extension.getURL("hostlist.html"); - focusOrCreateTab(hostlist_url); - }); - - -function openChromotingTab(hostName, hostJid) { - var username = getCookie('username'); - var xmppAuth = getCookie('xmpp_auth'); - var newTabUrl = chrome.extension.getURL("chromoting_tab.html"); - var request = { - username: getCookie('username'), - xmppAuth: getCookie('xmpp_auth'), - hostName: hostName, - hostJid: hostJid, - }; - - console.log("Attempt to connect with" + - " username='" + request.username + "'" + - " hostName='" + request.hostName + "'" + - " hostJid='" + request.hostJid + "'" + - " auth_token='" + request.xmppAuth + "'"); - - var sendRequestFunc = function (tab) { - console.log("We're trying now to send to " + tab.id); - chrome.tabs.sendRequest( - tab.id, request, function() { - console.log('Tab finished connect.'); - }); - }; - - // This function will run when after the url for the tab is updated. If - // the tab is not yet loaded it will wait for another 500ms to inspect - // again. - var checkStatusFunc = function (tab) { - if (tab.status == "complete") { - sendRequestFunc(tab); - return; - } - - // Wait for 500ms and then get the tab and check its status. - setTimeout(function() { - chrome.tabs.get(tab.id, checkStatusFunc); - }, 500); - } - - navigate(newTabUrl, checkStatusFunc); -} diff --git a/remoting/client/extension/hostlist.html b/remoting/client/extension/hostlist.html deleted file mode 100644 index 2e55a36..0000000 --- a/remoting/client/extension/hostlist.html +++ /dev/null @@ -1,74 +0,0 @@ -<!-- -Copyright (c) 2010 The Chromium Authors. All rights reserved. -Use of this source code is governed by a BSD-style license that can be -found in the LICENSE file. ---> - -<html> - <head> - <script type="text/javascript" src="base.js"></script> - <script type="text/javascript" src="client.js"></script> - <link rel="stylesheet" type="text/css" href="main.css" /> - <title>Remote Access</title> - </head> - <body class="hostlist_body" onload="init();"> - - <h1>Remote Access</h1> - - <div id="login_panel" class="gaia_login_panel"> - <table cellspacing="3" cellpadding="5" border="0"><tr><td valign="top" - style="text-align:center" nowrap="nowrap" bgcolor="#e8eefa"> - <table align="center" border="0" cellpadding="1" cellspacing="0"> - <tr> - <td align="center" colspan="2"> - <font size="-1">Sign in with your</font> - <table><tr> - <td valign="top"><img src="https://www.google.com/accounts/google_transparent.gif" - alt="Google"></img></td> - <td valign="middle"><font size="+0"><b>Account</b></font></td> - </tr></table> - </td> - </tr><tr> - <td align="right" nowrap="nowrap"> - <span class="gaia_font">Username:</span> - </td><td> - <input type="text" id="username" value="" class="gaia_font"/> - </td> - </tr><tr> - <td align="right" nowrap="nowrap"> - <span class="gaia_font">Password:</span> - </td><td> - <input type="password" id="password" value="" - class="gaia_font"/> - </td> - </tr><tr> - <td></td> - <td align="left"> - <div class="error_msg" id="errormsg_div"></div> - </td> - </tr><tr> - <td></td> - <td><input type="button" value="Sign in" class="gaia_font" - onclick="login();"/></td> - </tr> - </table> - </tr></td></table> - </div> - - <div id="login_div" class="login"> - </div> - - <p class="reload"> - <a href="javascript:populateHostList()">Reload host list</a> - </p> - - <input type=checkbox name="show_offline" id="show_offline" - onClick="updateShowOfflineHosts(this)"/>Show offline hosts - - <div id="hostlist_div" class="hostlist"> - <p class='message'>Initializing...</p> - </div> - - <br /> - </body> -</html> |