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