1#!/usr/bin/env python 2# 3# Copyright (c) 2010 The Chromium Authors. All rights reserved. 4# Use of this source code is governed by a BSD-style license that can be 5# found in the LICENSE file. 6 7# Chromoting Directory API client implementation. Used for testing/debugging 8# purposes. Requires Python 2.6: json module is not available in earlier 9# versions. 10 11import os 12import httplib 13import json 14import urllib 15import urllib2 16import random 17import sys 18 19DEFAULT_DIRECTORY_SERVER = 'www.googleapis.com' 20 21auth_filepath = os.path.join(os.path.expanduser('~'), 22 '.chromotingDirectoryAuthToken') 23 24def random_uuid(): 25 return ("%04x%04x-%04x-%04x-%04x-%04x%04x%04x" % 26 tuple(map(lambda x: random.randrange(0,65536), range(8)))) 27 28class Host: 29 def __init__(self, parameters=None): 30 if parameters != None: 31 self.host_id = parameters[u"hostId"] 32 self.host_name = parameters[u"hostName"] 33 self.public_key = parameters[u"publicKey"] 34 # Following fields may be missing, use get() for them. 35 self.jabber_id = parameters.get(u"jabberId") 36 self.created_time = parameters.get(u"createdTime") 37 self.updated_time = parameters.get(u"updatedTime") 38 self.status = parameters.get(u"status") 39 else: 40 self.host_id = random_uuid() 41 import socket 42 self.host_name = socket.gethostname() 43 self.public_key = None 44 self.jabber_id = None 45 self.created_time = None 46 self.updated_time = None 47 self.status = None 48 49class HostDirectoryError(Exception): 50 def __init__(self, message, response): 51 Exception.__init__(self, message) 52 print response 53 self._response = response 54 55class HostDirectory: 56 def __init__(self, username, auth_token, server=DEFAULT_DIRECTORY_SERVER): 57 self._username = username 58 self._auth_token = auth_token 59 self._base_url = '/chromoting/v1/@me/hosts' 60 61 self._http = httplib.HTTPSConnection(server) 62 self._headers = {"Authorization": "GoogleLogin auth=" + self._auth_token, 63 "Content-Type": "application/json" } 64 65 def add_host(self, host): 66 host_json = { 'data': 67 { 'hostId': host.host_id, 68 'hostName': host.host_name, 69 'publicKey': host.public_key, 70 } 71 } 72 if host.jabber_id: 73 host_json['data']['jabberId'] = host.jabber_id 74 post_data = json.dumps(host_json) 75 self._http.request("POST", self._base_url, post_data, self._headers) 76 response = self._http.getresponse() 77 if response.status != 200: 78 raise HostDirectoryError(response.reason, response.read()) 79 data = response.read() 80 81 def get_hosts(self): 82 self._http.request("GET", self._base_url, headers=self._headers) 83 response = self._http.getresponse() 84 if response.status != 200: 85 raise HostDirectoryError(response.reason, response.read()) 86 data = response.read() 87 data = json.loads(data)[u'data'] 88 results = [] 89 if data.has_key(u'items'): 90 for item in data[u'items']: 91 results.append(Host(item)) 92 return results 93 94 def delete_host(self, host_id): 95 url = self._base_url + '/' + host_id 96 self._http.request("DELETE", url, headers=self._headers) 97 response = self._http.getresponse() 98 if response.status / 100 != 2: # Normally 204 is returned 99 raise HostDirectoryError(response.reason, response.read()) 100 data = response.read() 101 102def usage(): 103 sys.stderr.write( 104 ("Usage:\n" + 105 " Login: \t\t%(cmd)s login\n" + 106 " Register host: \t%(cmd)s insert --hostId=<hostId>" + 107 " --hostName=<hostName> \\\n" + 108 "\t\t\t --publicKey=<publicKey> --jabberId=<jabberId>\n" + 109 " List hosts: \t\t%(cmd)s list\n" + 110 " Delete a host: \t%(cmd)s delete <host_id>\n") 111 % {"cmd" : sys.argv[0]}) 112 return 1 113 114class CommandError(Exception): 115 def __init__(self, message): 116 Exception.__init__(self, message) 117 118def load_auth_token(): 119 try: 120 lines = open(auth_filepath).readlines() 121 except IOError: 122 raise CommandError(("Can't open file (%s). Please run " + 123 "'%s login' and try again.") % 124 (auth_filepath, sys.argv[0])) 125 if len(lines) != 2: 126 raise CommandError(("Invalid auth file (%s). Please run " + 127 "'%s login' and try again.") % 128 (auth_filepath, sys.argv[0])) 129 return map(lambda x: x.strip(), lines) 130 131def login_cmd(args): 132 """login command""" 133 if len(args) != 0: 134 return usage() 135 136 import getpass 137 import gaia_auth 138 139 print "Email:", 140 email = raw_input() 141 passwd = getpass.getpass("Password: ") 142 143 authenticator = gaia_auth.GaiaAuthenticator('chromoting'); 144 auth_token = authenticator.authenticate(email, passwd) 145 146 # Set permission mask for created file. 147 os.umask(0066) 148 auth_file = open(auth_filepath, 'w') 149 auth_file.write(email) 150 auth_file.write('\n') 151 auth_file.write(auth_token) 152 auth_file.close() 153 154 print 'Auth token: ', auth_token 155 print '...saved in', auth_filepath 156 157def list_cmd(args): 158 """list command""" 159 if len(args) != 0: 160 return usage() 161 (username, token) = load_auth_token() 162 client = HostDirectory(username, token) 163 print '%36s %30s %s' % ("HOST ID", "HOST NAME", "JABBER ID") 164 for host in client.get_hosts(): 165 print '%36s %30s %s' % (host.host_id, host.host_name, host.jabber_id) 166 return 0 167 168def insert_cmd(args): 169 """insert command""" 170 (username, token) = load_auth_token() 171 client = HostDirectory(username, token) 172 173 host = Host() 174 for arg in args: 175 if arg.startswith("--hostId="): 176 host.host_id = arg[len("--hostId="):] 177 elif arg.startswith("--hostName="): 178 host.host_name = arg[len("--hostName="):] 179 elif arg.startswith("--publicKey="): 180 host.public_key = arg[len("--publicKey="):] 181 elif arg.startswith("--jabberId="): 182 host.jabber_id = arg[len("--jabberId="):] 183 else: 184 return usage() 185 186 client.add_host(host) 187 return 0 188 189def delete_cmd(args): 190 """delete command""" 191 if len(args) != 1: 192 return usage() 193 host_id = args[0] 194 (username, token) = load_auth_token() 195 client = HostDirectory(username, token) 196 client.delete_host(host_id) 197 return 0 198 199def main(): 200 import sys 201 args = sys.argv[1:] 202 if len(args) == 0: 203 return usage() 204 command = args[0] 205 206 try: 207 if command == "help": 208 usage() 209 elif command == "login": 210 return login_cmd(args[1:]) 211 elif command == "list": 212 return list_cmd(args[1:]) 213 elif command == "insert": 214 return insert_cmd(args[1:]) 215 elif command == "delete": 216 return delete_cmd(args[1:]) 217 else: 218 raise CommandError("Unknown command: %s" % command); 219 220 except CommandError as e: 221 sys.stderr.write("%s\n" % e.args[0]) 222 return 1 223 224 return 0 225 226if __name__ == '__main__': 227 sys.exit(main()) 228