Skip to main content

omaha_client/protocol/
request.rs

1// Copyright 2019 The Fuchsia Authors
2//
3// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
4// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
5// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
6// This file may not be copied, modified, or distributed except according to
7// those terms.
8
9use crate::protocol::Cohort;
10use serde::{Serialize, Serializer};
11use serde_repr::Serialize_repr;
12use std::collections::HashMap;
13
14#[cfg(test)]
15mod tests;
16
17/// This is the key for the http request header that identifies the 'updater' that is sending a
18/// request.
19pub const HEADER_UPDATER_NAME: &str = "X-Goog-Update-Updater";
20
21/// This is the key for the http request header that identifies whether this is an interactive
22/// or a background update (see InstallSource).
23pub const HEADER_INTERACTIVITY: &str = "X-Goog-Update-Interactivity";
24
25/// This is the key for the http request header that identifies the app id(s) that are included in
26/// this request.
27pub const HEADER_APP_ID: &str = "X-Goog-Update-AppId";
28
29/// An Omaha protocol request.
30///
31/// This holds the data for constructing a request to the Omaha service.
32///
33/// See https://github.com/google/omaha/blob/HEAD/doc/ServerProtocolV3.md#request
34#[derive(Debug, Default, Serialize)]
35pub struct Request {
36    /// The current Omaha protocol version (which this is meant to be used with, is 3.0.  This
37    /// should always be set to "3.0".
38    ///
39    /// This is the 'protocol' attribute of the request object.
40    #[serde(rename = "protocol")]
41    pub protocol_version: String,
42
43    /// This is the string identifying the updater software itself (this client). e.g. "fuchsia"
44    pub updater: String,
45
46    /// The version of the updater itself (e.g. "Fuchsia/Rust-0.0.0.1").  This is the version of the
47    /// updater implemented using this Crate.
48    ///
49    /// This is the 'updaterversion' attribute of the request object.
50    #[serde(rename = "updaterversion")]
51    pub updater_version: String,
52
53    /// The install source trigger for this request.
54    #[serde(rename = "installsource")]
55    pub install_source: InstallSource,
56
57    /// The system update is always done by "the machine" aka system-level or administrator
58    /// privileges.
59    ///
60    /// This is the 'ismachine' attribute of the request object.
61    #[serde(rename = "ismachine")]
62    pub is_machine: bool,
63
64    /// The randomly generated GUID for a single Omaha request.
65    ///
66    /// This is the 'requestid' attribute of the request object.
67    #[serde(rename = "requestid")]
68    #[serde(skip_serializing_if = "Option::is_none")]
69    pub request_id: Option<GUID>,
70
71    /// The randomly generated GUID for all Omaha requests in an update session.
72    ///
73    /// This is the 'sessionid' attribute of the request object.
74    #[serde(rename = "sessionid")]
75    #[serde(skip_serializing_if = "Option::is_none")]
76    pub session_id: Option<GUID>,
77
78    /// Information about the device operating system.
79    ///
80    /// This is the 'os' child object of the request object.
81    pub os: OS,
82
83    /// The applications to update.
84    ///
85    /// These are the 'app' children objects of the request object
86    #[serde(rename = "app")]
87    pub apps: Vec<App>,
88}
89
90/// RequestWrapper is a serialization wrapper for a Request.
91///
92/// A Request object serializes into a value for an object,
93/// not an object that is '{"request": {....} }'.
94/// This wrapper provides the request wrapping that Omaha expects to see.
95#[derive(Debug, Default, Serialize)]
96pub struct RequestWrapper {
97    pub request: Request,
98}
99
100/// Enum of the possible reasons that this update request was initiated.
101#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, Serialize)]
102#[serde(rename_all = "lowercase")]
103pub enum InstallSource {
104    /// This update check was triggered "on demand", by a user.
105    OnDemand,
106
107    /// This update check was triggered as part of a background task, unattended by a user.
108    #[default]
109    ScheduledTask,
110}
111
112/// Information about the platform / operating system.
113///
114/// See https://github.com/google/omaha/blob/HEAD/doc/ServerProtocolV3.md#os
115#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize)]
116pub struct OS {
117    /// The device platform (e.g. 'Fuchsia')
118    pub platform: String,
119
120    /// The version of the platform
121    pub version: String,
122
123    /// The patch level of the platform (e.g. "12345_arm64")
124    #[serde(rename = "sp")]
125    pub service_pack: String,
126
127    /// The platform architecture (e.g. "x86-64")
128    pub arch: String,
129}
130
131/// Information about an individual app that an update check is being performed for.
132///
133/// While unlikely, it's possible for a single request to have an update check, a ping, and for it
134/// to be reporting an event.
135///
136/// See https://github.com/google/omaha/blob/HEAD/doc/ServerProtocolV3.md#app-request
137#[derive(Debug, Default, Clone, Serialize)]
138pub struct App {
139    /// This is the GUID or product ID that uniquely identifies the product to Omaha.
140    ///
141    /// This is the 'appid' attribute of the app object.
142    #[serde(rename = "appid")]
143    pub id: String,
144
145    /// The version of the product that's currently installed.  This is in 'A.B.C.D' format.
146    ///
147    /// This is the version attribute of the app object.
148    pub version: String,
149
150    /// The fingerprint for the application.
151    ///
152    /// This is the fp attribute of the app object.
153    #[serde(rename = "fp")]
154    #[serde(skip_serializing_if = "Option::is_none")]
155    pub fingerprint: Option<String>,
156
157    /// This is the cohort id, as previously assigned by the Omaha service.  This is a machine-
158    /// readable string, not meant for user display.
159    ///
160    /// This holds the following fields of the app object:
161    ///   cohort
162    ///   cohorthint
163    ///   cohortname
164    #[serde(flatten)]
165    pub cohort: Option<Cohort>,
166
167    /// If present, this request is an update check.
168    #[serde(rename = "updatecheck")]
169    #[serde(skip_serializing_if = "Option::is_none")]
170    pub update_check: Option<UpdateCheck>,
171
172    /// These are events to report to Omaha.
173    #[serde(rename = "event")]
174    #[serde(skip_serializing_if = "Vec::is_empty")]
175    pub events: Vec<Event>,
176
177    /// An optional status ping.
178    #[serde(skip_serializing_if = "Option::is_none")]
179    pub ping: Option<Ping>,
180
181    /// Extra fields to include (App-specific fields used to extend the protocol).
182    ///
183    /// # NOTE:  Can break the omaha protocol if improperly used.
184    ///
185    /// This is listed last in the struct, and should remain so, due to how Serde behaves when
186    /// flattening fields into the parent.  If this map contains a field whose name matches that of
187    /// another field in the struct (such as `id`), it will overwrite that field.  If that field is
188    /// optionally serialized (such as `update_check`), it will still overwrite that field
189    /// (regardless of the presence or not of the field it's overwriting).
190    #[serde(flatten)]
191    pub extra_fields: HashMap<String, String>,
192}
193
194/// This is an update check for the parent App object.
195///
196/// See https://github.com/google/omaha/blob/HEAD/doc/ServerProtocolV3.md#updatecheck-request
197#[derive(Clone, Debug, Default, Eq, PartialEq, Serialize)]
198pub struct UpdateCheck {
199    /// If the update is disabled, the client will not honor an 'update' response.  The default
200    /// value of false indicates that the client will attempt an update if instructed that one is
201    /// available.
202    #[serde(skip_serializing_if = "std::ops::Not::not")]
203    #[serde(rename = "updatedisabled")]
204    pub disabled: bool,
205
206    /// If true, Omaha will offer an update even if the client is already running the same version.
207    #[serde(skip_serializing_if = "std::ops::Not::not")]
208    #[serde(rename = "sameversionupdate")]
209    pub offer_update_if_same_version: bool,
210}
211
212impl UpdateCheck {
213    /// Public constructor for an update check request on an app that will not honor an 'update'
214    /// response and will not perform an update if one is available.
215    pub fn disabled() -> Self {
216        UpdateCheck { disabled: true, offer_update_if_same_version: false }
217    }
218}
219
220/// This is a status ping to the Omaha service.
221///
222/// See https://github.com/google/omaha/blob/HEAD/doc/ServerProtocolV3.md#ping-request
223///
224/// These pings only support the Client-Regulated Counting method (Date-based).  For more info, see
225/// https://github.com/google/omaha/blob/HEAD/doc/ServerProtocolV3.md#client-regulated-Counting-days-based
226#[derive(Debug, Default, Clone, Eq, PartialEq, Serialize)]
227pub struct Ping {
228    /// This is the January 1, 2007 epoch-based value for the date that was previously sent to the
229    /// client by the service, as the elapsed_days value of the daystart object, if the application
230    /// is active.
231    ///
232    /// This is the 'ad' attribute of the ping object.
233    #[serde(rename = "ad")]
234    #[serde(skip_serializing_if = "Option::is_none")]
235    pub date_last_active: Option<u32>,
236
237    /// This is the January 1, 2007 epoch-based value for the date that was previously sent to the
238    /// client by the service, as the elapsed_days value of the daystart object, if the application
239    /// is active or not.
240    ///
241    /// This is the 'rd' attribute of the ping object.
242    #[serde(rename = "rd")]
243    #[serde(skip_serializing_if = "Option::is_none")]
244    pub date_last_roll_call: Option<u32>,
245}
246
247/// An event that is being reported to the Omaha service.
248///
249/// See https://github.com/google/omaha/blob/HEAD/doc/ServerProtocolV3.md#event-request
250#[derive(Debug, Default, Clone, Eq, PartialEq, Serialize)]
251pub struct Event {
252    /// This is the event type for the event (see the enum for more information).
253    ///
254    /// This is the eventtype attribute of the event object.
255    #[serde(rename = "eventtype")]
256    pub event_type: EventType,
257
258    /// This is the result code for the event.  All event types share a namespace for result codes.
259    ///
260    /// This is the eventresult attribute of the event object.
261    #[serde(rename = "eventresult")]
262    pub event_result: EventResult,
263
264    /// This is an opaque error value that may be provided.  It's meaning is application specific.
265    ///
266    /// This is the errorcode attribute of the event object.
267    #[serde(skip_serializing_if = "Option::is_none")]
268    pub errorcode: Option<EventErrorCode>,
269
270    /// The version of the app that was present on the machine at the time of the update-check of
271    /// this update flow, regardless of the success or failure of the update operation.
272    #[serde(skip_serializing_if = "Option::is_none")]
273    #[serde(rename = "previousversion")]
274    pub previous_version: Option<String>,
275
276    /// The version of the app that the update flow to which this event belongs attempted to
277    /// reach, regardless of success or failure of the update operation.
278    #[serde(skip_serializing_if = "Option::is_none")]
279    #[serde(rename = "nextversion")]
280    pub next_version: Option<String>,
281
282    /// For events representing a download, the time elapsed between the start of the download and
283    /// the end of the download, in milliseconds. For events representing an entire update flow,
284    /// the sum of all such download times over the course of the update flow.
285    /// Sent in <event>s that have an eventtype of "1", "2", "3", and "14" only.
286    #[serde(skip_serializing_if = "Option::is_none")]
287    pub download_time_ms: Option<u64>,
288}
289
290impl Event {
291    /// Creates a new successful event for the given event type.
292    pub fn success(event_type: EventType) -> Self {
293        Self { event_type, event_result: EventResult::Success, ..Self::default() }
294    }
295
296    /// Creates a new error event for the given event error code.
297    pub fn error(errorcode: EventErrorCode) -> Self {
298        Self {
299            event_type: EventType::UpdateComplete,
300            event_result: EventResult::Error,
301            errorcode: Some(errorcode),
302            ..Self::default()
303        }
304    }
305}
306
307/// The type of event that is being reported.  These are specified by the Omaha protocol.
308///
309/// See https://github.com/google/omaha/blob/HEAD/doc/ServerProtocolV3.md#event-request
310#[derive(Debug, Default, Clone, Eq, PartialEq, Serialize_repr)]
311#[repr(u8)]
312pub enum EventType {
313    #[default]
314    Unknown = 0,
315
316    /// The initial download of the application is complete.
317    DownloadComplete = 1,
318
319    /// The initial installation of the application is complete.
320    InstallComplete = 2,
321
322    /// The application update is complete.
323    UpdateComplete = 3,
324
325    /// The download of the update for the application has started.
326    UpdateDownloadStarted = 13,
327
328    /// The download of the update for the application is complete.
329    UpdateDownloadFinished = 14,
330
331    /// The application is now using the updated software.  This is sent after a successful boot
332    /// into the update software.
333    RebootedAfterUpdate = 54,
334}
335
336/// The result of event that is being reported.  These are specified by the Omaha protocol.
337///
338/// See https://github.com/google/omaha/blob/HEAD/doc/ServerProtocolV3.md#event-request
339#[derive(Debug, Default, Clone, Eq, PartialEq, Serialize_repr)]
340#[repr(u8)]
341pub enum EventResult {
342    #[default]
343    Error = 0,
344    Success = 1,
345    SuccessAndRestartRequired = 2,
346    SuccessAndAppRestartRequired = 3,
347    Cancelled = 4,
348    ErrorInSystemInstaller = 8,
349
350    /// The client acknowledges that it received the 'update' response, but it will not be acting
351    /// on the update at this time (deferred by Policy).
352    UpdateDeferred = 9,
353}
354
355/// The error code of the event.  These are application specific.
356#[derive(Debug, Default, Clone, Eq, PartialEq, Serialize_repr)]
357#[repr(i32)]
358pub enum EventErrorCode {
359    /// Error when parsing Omaha response.
360    #[default]
361    ParseResponse = 0,
362    /// Error when constructing install plan.
363    ConstructInstallPlan = 1,
364    /// Error when installing the update.
365    Installation = 2,
366    /// The update is denied by policy.
367    DeniedByPolicy = 3,
368}
369
370/// The GUID used in Omaha protocol for sessionid and requestid.
371///
372/// See https://github.com/google/omaha/blob/HEAD/doc/ServerProtocolV3.md#guids
373#[derive(Debug, Default, Clone, Eq, PartialEq)]
374pub struct GUID {
375    uuid: uuid::Uuid,
376}
377
378impl GUID {
379    /// Creates a new random GUID.
380    #[cfg(not(test))]
381    pub fn new() -> Self {
382        Self { uuid: uuid::Uuid::new_v4() }
383    }
384
385    // For unit tests, creates GUID using a thread local counter, so that for every test case,
386    // the first GUID will be {00000000-0000-0000-0000-000000000000},
387    // and the second will be {00000000-0000-0000-0000-000000000001}, and so on.
388    #[cfg(test)]
389    pub fn new() -> Self {
390        thread_local! {
391            static COUNTER: std::cell::RefCell<u128> =
392            const { std::cell::RefCell::new(0) };
393        }
394        COUNTER.with(|counter| {
395            let mut counter = counter.borrow_mut();
396            let guid = Self::from_u128(*counter);
397            *counter += 1;
398            guid
399        })
400    }
401
402    #[cfg(test)]
403    pub fn from_u128(n: u128) -> Self {
404        Self { uuid: uuid::Uuid::from_u128(n) }
405    }
406}
407
408// Wrap the uuid in {}.
409impl Serialize for GUID {
410    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
411    where
412        S: Serializer,
413    {
414        self.uuid.as_braced().serialize(serializer)
415    }
416}