blob: 76dd081038d6a39c83ee6357dc1fcdd2baf9d553 [file] [log] [blame]
Chris Sosa52148582012-11-15 15:35:58 -08001# Copyright (c) 2012 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
5"""Module containing common autoupdate utilities and protocol dictionaries."""
6
Don Garrettfb15e322016-06-21 19:12:08 -07007from __future__ import print_function
8
Chris Sosa52148582012-11-15 15:35:58 -08009import datetime
10import os
11import time
12from xml.dom import minidom
13
14
Gilad Arnolde7819e72014-03-21 12:50:48 -070015# Update events and result codes.
16EVENT_TYPE_UNKNOWN = 0
17EVENT_TYPE_DOWNLOAD_COMPLETE = 1
18EVENT_TYPE_INSTALL_COMPLETE = 2
19EVENT_TYPE_UPDATE_COMPLETE = 3
20EVENT_TYPE_UPDATE_DOWNLOAD_STARTED = 13
21EVENT_TYPE_UPDATE_DOWNLOAD_FINISHED = 14
22
23EVENT_RESULT_ERROR = 0
24EVENT_RESULT_SUCCESS = 1
25EVENT_RESULT_SUCCESS_REBOOT = 2
26EVENT_RESULT_UPDATE_DEFERRED = 9
27
28
29# A default app_id value.
30_APP_ID = '87efface-864d-49a5-9bb3-4b050a7c227a'
31
Chris Sosa52148582012-11-15 15:35:58 -080032
33# Responses for the various Omaha protocols indexed by the protocol version.
Gilad Arnolde7819e72014-03-21 12:50:48 -070034#
35# Update available responses:
36_UPDATE_RESPONSE = {}
37_UPDATE_RESPONSE['2.0'] = """<?xml version="1.0" encoding="UTF-8"?>
Chris Sosa52148582012-11-15 15:35:58 -080038 <gupdate xmlns="http://www.google.com/update2/response" protocol="2.0">
39 <daystart elapsed_seconds="%(time_elapsed)s"/>
40 <app appid="{%(appid)s}" status="ok">
41 <ping status="ok"/>
42 <updatecheck
43 ChromeOSVersion="9999.0.0"
44 codebase="%(url)s"
45 hash="%(sha1)s"
46 sha256="%(sha256)s"
47 needsadmin="false"
48 size="%(size)s"
Gilad Arnolde96e8652013-05-22 11:36:32 -070049 IsDeltaPayload="%(is_delta_format)s"
Chris Sosa52148582012-11-15 15:35:58 -080050 status="ok"
51 %(extra_attr)s/>
52 </app>
Gilad Arnolde7819e72014-03-21 12:50:48 -070053 </gupdate>"""
54_UPDATE_RESPONSE['3.0'] = """<?xml version="1.0" encoding="UTF-8"?>
Chris Sosa52148582012-11-15 15:35:58 -080055 <response protocol="3.0">
56 <daystart elapsed_seconds="%(time_elapsed)s"/>
57 <app appid="{%(appid)s}" status="ok">
58 <ping status="ok"/>
59 <updatecheck status="ok">
60 <urls>
61 <url codebase="%(codebase)s/"/>
62 </urls>
63 <manifest version="9999.0.0">
64 <packages>
65 <package hash="%(sha1)s" name="%(filename)s" size="%(size)s"
66 required="true"/>
67 </packages>
68 <actions>
69 <action event="postinstall"
70 ChromeOSVersion="9999.0.0"
71 sha256="%(sha256)s"
72 needsadmin="false"
Gilad Arnolde96e8652013-05-22 11:36:32 -070073 IsDeltaPayload="%(is_delta_format)s"
Chris Sosa52148582012-11-15 15:35:58 -080074 %(extra_attr)s />
75 </actions>
76 </manifest>
77 </updatecheck>
78 </app>
Gilad Arnolde7819e72014-03-21 12:50:48 -070079 </response>"""
Chris Sosa52148582012-11-15 15:35:58 -080080
Gilad Arnolde7819e72014-03-21 12:50:48 -070081# No update responses:
82_NO_UPDATE_RESPONSE = {}
83_NO_UPDATE_RESPONSE['2.0'] = """<?xml version="1.0" encoding="UTF-8"?>
Chris Sosa52148582012-11-15 15:35:58 -080084 <gupdate xmlns="http://www.google.com/update2/response" protocol="2.0">
85 <daystart elapsed_seconds="%(time_elapsed)s"/>
86 <app appid="{%(appid)s}" status="ok">
87 <ping status="ok"/>
88 <updatecheck status="noupdate"/>
89 </app>
Gilad Arnolde7819e72014-03-21 12:50:48 -070090 </gupdate>"""
91_NO_UPDATE_RESPONSE['3.0'] = """<?xml version="1.0" encoding="UTF-8"?>
Jay Srinivasan00244952013-03-26 11:05:06 -070092 <response protocol="3.0">
Chris Sosa52148582012-11-15 15:35:58 -080093 <daystart elapsed_seconds="%(time_elapsed)s"/>
94 <app appid="{%(appid)s}" status="ok">
95 <ping status="ok"/>
96 <updatecheck status="noupdate"/>
97 </app>
Gilad Arnolde7819e72014-03-21 12:50:48 -070098 </response>"""
99
100
101# Non-update event responses:
102_EVENT_RESPONSE = {}
103_EVENT_RESPONSE['2.0'] = """<?xml version="1.0" encoding="UTF-8"?>
104 <gupdate xmlns="http://www.google.com/update2/response" protocol="2.0">
105 <daystart elapsed_seconds="%(time_elapsed)s"/>
106 <app appid="{%(appid)s}" status="ok">
107 <ping status="ok"/>
108 <event status="ok"/>
109 </app>
110 </gupdate>"""
111_EVENT_RESPONSE['3.0'] = """<?xml version="1.0" encoding="UTF-8"?>
112 <response protocol="3.0">
113 <daystart elapsed_seconds="%(time_elapsed)s"/>
114 <app appid="{%(appid)s}" status="ok">
115 <ping status="ok"/>
116 <event status="ok"/>
117 </app>
118 </response>"""
Chris Sosa52148582012-11-15 15:35:58 -0800119
120
121class UnknownProtocolRequestedException(Exception):
122 """Raised when an supported protocol is specified."""
123
124
125def GetSecondsSinceMidnight():
126 """Returns the seconds since midnight as a decimal value."""
127 now = time.localtime()
128 return now[3] * 3600 + now[4] * 60 + now[5]
129
130
131def GetCommonResponseValues():
132 """Returns a dictionary of default values for the response."""
133 response_values = {}
Gilad Arnolde7819e72014-03-21 12:50:48 -0700134 response_values['appid'] = _APP_ID
Chris Sosa52148582012-11-15 15:35:58 -0800135 response_values['time_elapsed'] = GetSecondsSinceMidnight()
136 return response_values
137
138
139def GetSubstitutedResponse(response_dict, protocol, response_values):
140 """Substitutes the protocol-specific response with response_values.
141
142 Args:
143 response_dict: Canned response messages indexed by protocol.
144 protocol: client's protocol version from the request Xml.
145 response_values: Values to be substituted in the canned response.
Gilad Arnolde7819e72014-03-21 12:50:48 -0700146
Chris Sosa52148582012-11-15 15:35:58 -0800147 Returns:
148 Xml string to be passed back to client.
149 """
150 response_xml = response_dict[protocol] % response_values
151 return response_xml
152
153
David Zeuthen52ccd012013-10-31 12:58:26 -0700154def GetUpdateResponse(sha1, sha256, size, url, is_delta_format, metadata_size,
155 signed_metadata_hash, public_key, protocol,
Chris Sosa52148582012-11-15 15:35:58 -0800156 critical_update=False):
157 """Returns a protocol-specific response to the client for a new update.
158
159 Args:
160 sha1: SHA1 hash of update blob
161 sha256: SHA256 hash of update blob
162 size: size of update blob
163 url: where to find update blob
164 is_delta_format: true if url refers to a delta payload
David Zeuthen52ccd012013-10-31 12:58:26 -0700165 metadata_size: the size of the metadata, in bytes.
166 signed_metadata_hash: the signed metadata hash or None if not signed.
167 public_key: the public key to transmit to the client or None if no key.
Chris Sosa52148582012-11-15 15:35:58 -0800168 protocol: client's protocol version from the request Xml.
169 critical_update: whether this is a critical update.
Gilad Arnolde7819e72014-03-21 12:50:48 -0700170
Chris Sosa52148582012-11-15 15:35:58 -0800171 Returns:
172 Xml string to be passed back to client.
173 """
174 response_values = GetCommonResponseValues()
175 response_values['sha1'] = sha1
176 response_values['sha256'] = sha256
177 response_values['size'] = size
178 response_values['url'] = url
179 (codebase, filename) = os.path.split(url)
180 response_values['codebase'] = codebase
181 response_values['filename'] = filename
Gilad Arnolde96e8652013-05-22 11:36:32 -0700182 response_values['is_delta_format'] = str(is_delta_format).lower()
Chris Sosa52148582012-11-15 15:35:58 -0800183 extra_attributes = []
184 if critical_update:
185 # The date string looks like '20111115' (2011-11-15). As of writing,
186 # there's no particular format for the deadline value that the
187 # client expects -- it's just empty vs. non-empty.
188 date_str = datetime.date.today().strftime('%Y%m%d')
189 extra_attributes.append('deadline="%s"' % date_str)
190
David Zeuthen52ccd012013-10-31 12:58:26 -0700191 if metadata_size:
192 extra_attributes.append('MetadataSize="%d"' % metadata_size)
193 if signed_metadata_hash:
194 extra_attributes.append('MetadataSignatureRsa="%s"' % signed_metadata_hash)
195 if public_key:
196 extra_attributes.append('PublicKeyRsa="%s"' % public_key)
197
Chris Sosa52148582012-11-15 15:35:58 -0800198 response_values['extra_attr'] = ' '.join(extra_attributes)
Gilad Arnolde7819e72014-03-21 12:50:48 -0700199 return GetSubstitutedResponse(_UPDATE_RESPONSE, protocol, response_values)
Chris Sosa52148582012-11-15 15:35:58 -0800200
201
202def GetNoUpdateResponse(protocol):
203 """Returns a protocol-specific response to the client for no update.
204
205 Args:
206 protocol: client's protocol version from the request Xml.
Gilad Arnolde7819e72014-03-21 12:50:48 -0700207
Chris Sosa52148582012-11-15 15:35:58 -0800208 Returns:
209 Xml string to be passed back to client.
210 """
211 response_values = GetCommonResponseValues()
Gilad Arnolde7819e72014-03-21 12:50:48 -0700212 return GetSubstitutedResponse(_NO_UPDATE_RESPONSE, protocol, response_values)
213
214
215def GetEventResponse(protocol):
216 """Returns a protocol-specific response to a client event notification.
217
218 Args:
219 protocol: client's protocol version from the request Xml.
220
221 Returns:
222 Xml string to be passed back to client.
223 """
224 response_values = GetCommonResponseValues()
225 return GetSubstitutedResponse(_EVENT_RESPONSE, protocol, response_values)
Chris Sosa52148582012-11-15 15:35:58 -0800226
227
228def ParseUpdateRequest(request_string):
229 """Returns a tuple containing information parsed from an update request.
230
231 Args:
Gilad Arnolde7819e72014-03-21 12:50:48 -0700232 request_string: an xml string containing the update request.
233
joychen121fc9b2013-08-02 14:30:30 -0700234 Returns:
235 Tuple consisting of protocol string, app element, event element, and
Chris Sosa52148582012-11-15 15:35:58 -0800236 update_check element.
Gilad Arnolde7819e72014-03-21 12:50:48 -0700237
Chris Sosa52148582012-11-15 15:35:58 -0800238 Raises UnknownProtocolRequestedException if we do not understand the
239 protocol.
240 """
241 request_dom = minidom.parseString(request_string)
242 protocol = request_dom.firstChild.getAttribute('protocol')
243 supported_protocols = '2.0', '3.0'
244 if protocol not in supported_protocols:
245 raise UnknownProtocolRequestedException('Supported protocols are %s' %
246 supported_protocols)
247
248 element_dict = {}
249 for name in ['event', 'app', 'updatecheck']:
250 element_dict[name] = 'o:' + name if protocol == '2.0' else name
251
252 app = request_dom.firstChild.getElementsByTagName(element_dict['app'])[0]
253 event = request_dom.getElementsByTagName(element_dict['event'])
254 update_check = request_dom.getElementsByTagName(element_dict['updatecheck'])
255
256 return protocol, app, event, update_check