blob: e62256372fa8faf2ad94205553971d845ddd092d [file] [log] [blame]
rtc@google.comded22402009-10-26 22:36:21 +00001# Copyright (c) 2009 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5from xml.dom import minidom
6
7import os
8import web
9
rtc@google.com21a5ca32009-11-04 18:23:23 +000010class Autoupdate:
rtc@google.comded22402009-10-26 22:36:21 +000011
rtc@google.com21a5ca32009-11-04 18:23:23 +000012 def __init__(self, root_dir, static_dir):
13 # TODO(rtc): This is redundant with devserver.py. Move this code to a
14 # common location.
15 self.app_id = "87efface-864d-49a5-9bb3-4b050a7c227a"
16 self.root_dir = root_dir
17 self.scripts_dir = "%s/scripts" % self.root_dir
18 self.static_dir = static_dir
rtc@google.comded22402009-10-26 22:36:21 +000019
rtc@google.com21a5ca32009-11-04 18:23:23 +000020 # Basic functionality of handling ChromeOS autoupdate pings
21 # and building/serving update images.
22 # TODO(rtc): Clean this code up and write some tests.
rtc@google.comded22402009-10-26 22:36:21 +000023
rtc@google.com21a5ca32009-11-04 18:23:23 +000024 def GetUpdatePayload(self, hash, size, url):
25 payload = """<?xml version="1.0" encoding="UTF-8"?>
26 <gupdate xmlns="http://www.google.com/update2/response" protocol="2.0">
27 <app appid="{%s}" status="ok">
28 <ping status="ok"/>
29 <updatecheck
30 codebase="%s"
31 hash="%s"
32 needsadmin="false"
33 size="%s"
34 status="ok"/>
35 </app>
36 </gupdate>
37 """
38 return payload % (self.app_id, url, hash, size)
rtc@google.comded22402009-10-26 22:36:21 +000039
rtc@google.com21a5ca32009-11-04 18:23:23 +000040 def GetNoUpdatePayload(self):
41 payload = """<?xml version="1.0" encoding="UTF-8"?>
42 <gupdate xmlns="http://www.google.com/update2/response" protocol="2.0">
43 <app appid="{%s}" status="ok">
44 <ping status="ok"/>
45 <updatecheck status="noupdate"/>
46 </app>
47 </gupdate>
48 """
49 return payload % self.app_id
rtc@google.comded22402009-10-26 22:36:21 +000050
rtc@google.com21a5ca32009-11-04 18:23:23 +000051 def GetLatestImagePath(self):
52 cmd = "%s/get_latest_image.sh" % self.scripts_dir
53 return os.popen(cmd).read().strip()
rtc@google.comded22402009-10-26 22:36:21 +000054
rtc@google.com21a5ca32009-11-04 18:23:23 +000055 def GetLatestVersion(self, latest_image_path):
56 latest_version = latest_image_path.split('/')[-1]
57 return latest_version.split('-')[0]
rtc@google.comded22402009-10-26 22:36:21 +000058
rtc@google.com21a5ca32009-11-04 18:23:23 +000059 def CanUpdate(self, client_version, latest_version):
60 """
61 Returns true iff the latest_version is greater than the client_version.
62 """
63 client_tokens = client_version.split('.')
64 latest_tokens = latest_version.split('.')
65 web.debug("client version %s latest version %s" % (client_version, latest_version))
66 for i in range(0,4):
67 if int(latest_tokens[i]) == int(client_tokens[i]):
68 continue
69 return int(latest_tokens[i]) > int(client_tokens[i])
rtc@google.comded22402009-10-26 22:36:21 +000070 return False
rtc@google.comded22402009-10-26 22:36:21 +000071
rtc@google.com21a5ca32009-11-04 18:23:23 +000072 def BuildUpdateImage(self, image_path):
73 image_file = "%s/rootfs.image" % image_path
74 web.debug("checking image file %s/update.gz" % image_path)
75 if not os.path.exists("%s/update.gz" % image_path):
76 mkupdate = "%s/mk_memento_images.sh %s" % (self.scripts_dir, image_file)
77 web.debug(mkupdate)
78 err = os.system(mkupdate)
79 if err != 0:
80 web.debug("failed to create update image")
81 return False
rtc@google.comded22402009-10-26 22:36:21 +000082
rtc@google.com21a5ca32009-11-04 18:23:23 +000083 web.debug("Found an image, copying it to static")
84 err = os.system("cp %s/update.gz %s" % (image_path, self.static_dir))
85 if err != 0:
86 web.debug("Unable to move update.gz from %s to %s" % (image_path, self.static_dir))
87 return False
88 return True
rtc@google.comded22402009-10-26 22:36:21 +000089
rtc@google.com21a5ca32009-11-04 18:23:23 +000090 def GetSize(self, update_path):
91 return os.path.getsize(update_path)
rtc@google.comded22402009-10-26 22:36:21 +000092
rtc@google.com21a5ca32009-11-04 18:23:23 +000093 def GetHash(self, update_path):
94 cmd = "cat %s | openssl sha1 -binary | openssl base64 | tr \'\\n\' \' \';" % update_path
95 web.debug(cmd)
96 return os.popen(cmd).read()
rtc@google.comded22402009-10-26 22:36:21 +000097
rtc@google.com21a5ca32009-11-04 18:23:23 +000098 def HandleUpdatePing(self, data):
99 update_dom = minidom.parseString(data)
100 root = update_dom.firstChild
101 query = root.getElementsByTagName("o:app")[0]
102 client_version = query.attributes['version'].value
103 latest_image_path = self.GetLatestImagePath();
104 latest_version = self.GetLatestVersion(latest_image_path);
105 if not self.CanUpdate(client_version, latest_version):
106 web.debug("no update")
107 return self.GetNoUpdatePayload()
108
109 web.debug("update found %s " % latest_version)
110 ok = self.BuildUpdateImage(latest_image_path)
111 if ok != True:
112 web.debug("Failed to build an update image")
113 return self.GetNoUpdatePayload()
114
115 hash = self.GetHash("%s/update.gz" % self.static_dir)
116 size = self.GetSize("%s/update.gz" % self.static_dir)
117 hostname = web.ctx.host
118 url = "http://%s/static/update.gz" % hostname
119 return self.GetUpdatePayload(hash, size, url)
rtc@google.comded22402009-10-26 22:36:21 +0000120