blob: 2e7a08b35ef4c76c31df39061f3acb0996b05c16 [file] [log] [blame]
Chris Sosa52148582012-11-15 23:35:581# 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
7import datetime
8import os
9import time
10from xml.dom import minidom
11
12
Gilad Arnolde7819e72014-03-21 19:50:4813# Update events and result codes.
14EVENT_TYPE_UNKNOWN = 0
15EVENT_TYPE_DOWNLOAD_COMPLETE = 1
16EVENT_TYPE_INSTALL_COMPLETE = 2
17EVENT_TYPE_UPDATE_COMPLETE = 3
18EVENT_TYPE_UPDATE_DOWNLOAD_STARTED = 13
19EVENT_TYPE_UPDATE_DOWNLOAD_FINISHED = 14
20
21EVENT_RESULT_ERROR = 0
22EVENT_RESULT_SUCCESS = 1
23EVENT_RESULT_SUCCESS_REBOOT = 2
24EVENT_RESULT_UPDATE_DEFERRED = 9
25
26
27# A default app_id value.
28_APP_ID = '87efface-864d-49a5-9bb3-4b050a7c227a'
29
Chris Sosa52148582012-11-15 23:35:5830
31# Responses for the various Omaha protocols indexed by the protocol version.
Gilad Arnolde7819e72014-03-21 19:50:4832#
33# Update available responses:
34_UPDATE_RESPONSE = {}
35_UPDATE_RESPONSE['2.0'] = """<?xml version="1.0" encoding="UTF-8"?>
Chris Sosa52148582012-11-15 23:35:5836 <gupdate xmlns="http://www.google.com/update2/response" protocol="2.0">
37 <daystart elapsed_seconds="%(time_elapsed)s"/>
38 <app appid="{%(appid)s}" status="ok">
39 <ping status="ok"/>
40 <updatecheck
41 ChromeOSVersion="9999.0.0"
42 codebase="%(url)s"
43 hash="%(sha1)s"
44 sha256="%(sha256)s"
45 needsadmin="false"
46 size="%(size)s"
Gilad Arnolde96e8652013-05-22 18:36:3247 IsDeltaPayload="%(is_delta_format)s"
Chris Sosa52148582012-11-15 23:35:5848 status="ok"
49 %(extra_attr)s/>
50 </app>
Gilad Arnolde7819e72014-03-21 19:50:4851 </gupdate>"""
52_UPDATE_RESPONSE['3.0'] = """<?xml version="1.0" encoding="UTF-8"?>
Chris Sosa52148582012-11-15 23:35:5853 <response protocol="3.0">
54 <daystart elapsed_seconds="%(time_elapsed)s"/>
55 <app appid="{%(appid)s}" status="ok">
56 <ping status="ok"/>
57 <updatecheck status="ok">
58 <urls>
59 <url codebase="%(codebase)s/"/>
60 </urls>
61 <manifest version="9999.0.0">
62 <packages>
63 <package hash="%(sha1)s" name="%(filename)s" size="%(size)s"
64 required="true"/>
65 </packages>
66 <actions>
67 <action event="postinstall"
68 ChromeOSVersion="9999.0.0"
69 sha256="%(sha256)s"
70 needsadmin="false"
Gilad Arnolde96e8652013-05-22 18:36:3271 IsDeltaPayload="%(is_delta_format)s"
Chris Sosa52148582012-11-15 23:35:5872 %(extra_attr)s />
73 </actions>
74 </manifest>
75 </updatecheck>
76 </app>
Gilad Arnolde7819e72014-03-21 19:50:4877 </response>"""
Chris Sosa52148582012-11-15 23:35:5878
Gilad Arnolde7819e72014-03-21 19:50:4879# No update responses:
80_NO_UPDATE_RESPONSE = {}
81_NO_UPDATE_RESPONSE['2.0'] = """<?xml version="1.0" encoding="UTF-8"?>
Chris Sosa52148582012-11-15 23:35:5882 <gupdate xmlns="http://www.google.com/update2/response" protocol="2.0">
83 <daystart elapsed_seconds="%(time_elapsed)s"/>
84 <app appid="{%(appid)s}" status="ok">
85 <ping status="ok"/>
86 <updatecheck status="noupdate"/>
87 </app>
Gilad Arnolde7819e72014-03-21 19:50:4888 </gupdate>"""
89_NO_UPDATE_RESPONSE['3.0'] = """<?xml version="1.0" encoding="UTF-8"?>
Jay Srinivasan00244952013-03-26 18:05:0690 <response protocol="3.0">
Chris Sosa52148582012-11-15 23:35:5891 <daystart elapsed_seconds="%(time_elapsed)s"/>
92 <app appid="{%(appid)s}" status="ok">
93 <ping status="ok"/>
94 <updatecheck status="noupdate"/>
95 </app>
Gilad Arnolde7819e72014-03-21 19:50:4896 </response>"""
97
98
99# Non-update event responses:
100_EVENT_RESPONSE = {}
101_EVENT_RESPONSE['2.0'] = """<?xml version="1.0" encoding="UTF-8"?>
102 <gupdate xmlns="http://www.google.com/update2/response" protocol="2.0">
103 <daystart elapsed_seconds="%(time_elapsed)s"/>
104 <app appid="{%(appid)s}" status="ok">
105 <ping status="ok"/>
106 <event status="ok"/>
107 </app>
108 </gupdate>"""
109_EVENT_RESPONSE['3.0'] = """<?xml version="1.0" encoding="UTF-8"?>
110 <response protocol="3.0">
111 <daystart elapsed_seconds="%(time_elapsed)s"/>
112 <app appid="{%(appid)s}" status="ok">
113 <ping status="ok"/>
114 <event status="ok"/>
115 </app>
116 </response>"""
Chris Sosa52148582012-11-15 23:35:58117
118
119class UnknownProtocolRequestedException(Exception):
120 """Raised when an supported protocol is specified."""
121
122
123def GetSecondsSinceMidnight():
124 """Returns the seconds since midnight as a decimal value."""
125 now = time.localtime()
126 return now[3] * 3600 + now[4] * 60 + now[5]
127
128
129def GetCommonResponseValues():
130 """Returns a dictionary of default values for the response."""
131 response_values = {}
Gilad Arnolde7819e72014-03-21 19:50:48132 response_values['appid'] = _APP_ID
Chris Sosa52148582012-11-15 23:35:58133 response_values['time_elapsed'] = GetSecondsSinceMidnight()
134 return response_values
135
136
137def GetSubstitutedResponse(response_dict, protocol, response_values):
138 """Substitutes the protocol-specific response with response_values.
139
140 Args:
141 response_dict: Canned response messages indexed by protocol.
142 protocol: client's protocol version from the request Xml.
143 response_values: Values to be substituted in the canned response.
Gilad Arnolde7819e72014-03-21 19:50:48144
Chris Sosa52148582012-11-15 23:35:58145 Returns:
146 Xml string to be passed back to client.
147 """
148 response_xml = response_dict[protocol] % response_values
149 return response_xml
150
151
David Zeuthen52ccd012013-10-31 19:58:26152def GetUpdateResponse(sha1, sha256, size, url, is_delta_format, metadata_size,
153 signed_metadata_hash, public_key, protocol,
Chris Sosa52148582012-11-15 23:35:58154 critical_update=False):
155 """Returns a protocol-specific response to the client for a new update.
156
157 Args:
158 sha1: SHA1 hash of update blob
159 sha256: SHA256 hash of update blob
160 size: size of update blob
161 url: where to find update blob
162 is_delta_format: true if url refers to a delta payload
David Zeuthen52ccd012013-10-31 19:58:26163 metadata_size: the size of the metadata, in bytes.
164 signed_metadata_hash: the signed metadata hash or None if not signed.
165 public_key: the public key to transmit to the client or None if no key.
Chris Sosa52148582012-11-15 23:35:58166 protocol: client's protocol version from the request Xml.
167 critical_update: whether this is a critical update.
Gilad Arnolde7819e72014-03-21 19:50:48168
Chris Sosa52148582012-11-15 23:35:58169 Returns:
170 Xml string to be passed back to client.
171 """
172 response_values = GetCommonResponseValues()
173 response_values['sha1'] = sha1
174 response_values['sha256'] = sha256
175 response_values['size'] = size
176 response_values['url'] = url
177 (codebase, filename) = os.path.split(url)
178 response_values['codebase'] = codebase
179 response_values['filename'] = filename
Gilad Arnolde96e8652013-05-22 18:36:32180 response_values['is_delta_format'] = str(is_delta_format).lower()
Chris Sosa52148582012-11-15 23:35:58181 extra_attributes = []
182 if critical_update:
183 # The date string looks like '20111115' (2011-11-15). As of writing,
184 # there's no particular format for the deadline value that the
185 # client expects -- it's just empty vs. non-empty.
186 date_str = datetime.date.today().strftime('%Y%m%d')
187 extra_attributes.append('deadline="%s"' % date_str)
188
David Zeuthen52ccd012013-10-31 19:58:26189 if metadata_size:
190 extra_attributes.append('MetadataSize="%d"' % metadata_size)
191 if signed_metadata_hash:
192 extra_attributes.append('MetadataSignatureRsa="%s"' % signed_metadata_hash)
193 if public_key:
194 extra_attributes.append('PublicKeyRsa="%s"' % public_key)
195
Chris Sosa52148582012-11-15 23:35:58196 response_values['extra_attr'] = ' '.join(extra_attributes)
Gilad Arnolde7819e72014-03-21 19:50:48197 return GetSubstitutedResponse(_UPDATE_RESPONSE, protocol, response_values)
Chris Sosa52148582012-11-15 23:35:58198
199
200def GetNoUpdateResponse(protocol):
201 """Returns a protocol-specific response to the client for no update.
202
203 Args:
204 protocol: client's protocol version from the request Xml.
Gilad Arnolde7819e72014-03-21 19:50:48205
Chris Sosa52148582012-11-15 23:35:58206 Returns:
207 Xml string to be passed back to client.
208 """
209 response_values = GetCommonResponseValues()
Gilad Arnolde7819e72014-03-21 19:50:48210 return GetSubstitutedResponse(_NO_UPDATE_RESPONSE, protocol, response_values)
211
212
213def GetEventResponse(protocol):
214 """Returns a protocol-specific response to a client event notification.
215
216 Args:
217 protocol: client's protocol version from the request Xml.
218
219 Returns:
220 Xml string to be passed back to client.
221 """
222 response_values = GetCommonResponseValues()
223 return GetSubstitutedResponse(_EVENT_RESPONSE, protocol, response_values)
Chris Sosa52148582012-11-15 23:35:58224
225
226def ParseUpdateRequest(request_string):
227 """Returns a tuple containing information parsed from an update request.
228
229 Args:
Gilad Arnolde7819e72014-03-21 19:50:48230 request_string: an xml string containing the update request.
231
joychen121fc9b2013-08-02 21:30:30232 Returns:
233 Tuple consisting of protocol string, app element, event element, and
Chris Sosa52148582012-11-15 23:35:58234 update_check element.
Gilad Arnolde7819e72014-03-21 19:50:48235
Chris Sosa52148582012-11-15 23:35:58236 Raises UnknownProtocolRequestedException if we do not understand the
237 protocol.
238 """
239 request_dom = minidom.parseString(request_string)
240 protocol = request_dom.firstChild.getAttribute('protocol')
241 supported_protocols = '2.0', '3.0'
242 if protocol not in supported_protocols:
243 raise UnknownProtocolRequestedException('Supported protocols are %s' %
244 supported_protocols)
245
246 element_dict = {}
247 for name in ['event', 'app', 'updatecheck']:
248 element_dict[name] = 'o:' + name if protocol == '2.0' else name
249
250 app = request_dom.firstChild.getElementsByTagName(element_dict['app'])[0]
251 event = request_dom.getElementsByTagName(element_dict['event'])
252 update_check = request_dom.getElementsByTagName(element_dict['updatecheck'])
253
254 return protocol, app, event, update_check