2015-07-23 05:31:47 +00:00
|
|
|
// Copyright 2013-2015 CoreOS, Inc.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
2013-03-27 23:58:22 +00:00
|
|
|
|
2015-07-23 05:31:47 +00:00
|
|
|
// Google's Omaha application update protocol, version 3.
|
|
|
|
//
|
|
|
|
// Omaha is a poll based protocol using XML. Requests are made by clients to
|
|
|
|
// check for updates or report events of an update process. Responses are given
|
|
|
|
// by the server to provide update information, if any, or to simply
|
|
|
|
// acknowledge the receipt of event status.
|
|
|
|
//
|
2017-04-24 19:04:41 +00:00
|
|
|
// https://github.com/google/omaha/blob/master/doc/ServerProtocolV3.md
|
2013-03-27 23:58:22 +00:00
|
|
|
package omaha
|
|
|
|
|
2013-04-01 16:53:53 +00:00
|
|
|
import (
|
|
|
|
"encoding/xml"
|
2017-05-04 20:00:46 +00:00
|
|
|
"io"
|
2013-04-01 16:53:53 +00:00
|
|
|
)
|
2013-03-27 23:58:22 +00:00
|
|
|
|
2015-07-25 23:44:18 +00:00
|
|
|
// Request sent by the Omaha client
|
2013-03-27 23:58:22 +00:00
|
|
|
type Request struct {
|
2015-07-26 01:00:13 +00:00
|
|
|
XMLName xml.Name `xml:"request" json:"-"`
|
|
|
|
OS *OS `xml:"os"`
|
|
|
|
Apps []*AppRequest `xml:"app"`
|
|
|
|
Protocol string `xml:"protocol,attr"`
|
2017-05-02 20:07:50 +00:00
|
|
|
InstallSource string `xml:"installsource,attr,omitempty"`
|
2015-07-26 01:00:13 +00:00
|
|
|
IsMachine string `xml:"ismachine,attr,omitempty"`
|
2017-05-03 19:38:39 +00:00
|
|
|
RequestID string `xml:"requestid,attr,omitempty"`
|
|
|
|
SessionID string `xml:"sessionid,attr,omitempty"`
|
2015-07-26 01:00:13 +00:00
|
|
|
TestSource string `xml:"testsource,attr,omitempty"`
|
2017-05-03 19:38:39 +00:00
|
|
|
UserID string `xml:"userid,attr,omitempty"`
|
2017-05-02 20:07:50 +00:00
|
|
|
Version string `xml:"version,attr,omitempty"`
|
2015-07-26 01:00:13 +00:00
|
|
|
|
|
|
|
// update engine extension, duplicates the version attribute.
|
|
|
|
UpdaterVersion string `xml:"updaterversion,attr,omitempty"`
|
2013-04-01 16:53:53 +00:00
|
|
|
}
|
|
|
|
|
2015-07-24 06:14:36 +00:00
|
|
|
func NewRequest() *Request {
|
|
|
|
return &Request{
|
|
|
|
Protocol: "3.0",
|
2017-04-24 19:07:47 +00:00
|
|
|
// TODO(marineam) set a default client Version
|
2015-07-24 06:14:36 +00:00
|
|
|
OS: &OS{
|
|
|
|
Platform: LocalPlatform(),
|
|
|
|
Arch: LocalArch(),
|
|
|
|
// TODO(marineam): Version and ServicePack
|
|
|
|
},
|
|
|
|
}
|
2013-03-27 23:58:22 +00:00
|
|
|
}
|
|
|
|
|
2017-05-04 20:00:46 +00:00
|
|
|
// ParseRequest verifies and returns the parsed Request document.
|
|
|
|
// The MIME Content-Type header may be provided to sanity check its
|
|
|
|
// value; if blank it is assumed to be XML in UTF-8.
|
|
|
|
func ParseRequest(contentType string, body io.Reader) (*Request, error) {
|
|
|
|
if err := checkContentType(contentType); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
r := &Request{}
|
|
|
|
if err := parseReqOrResp(body, r); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return r, nil
|
|
|
|
}
|
|
|
|
|
2015-07-25 23:44:18 +00:00
|
|
|
func (r *Request) AddApp(id, version string) *AppRequest {
|
2017-05-03 19:38:39 +00:00
|
|
|
a := &AppRequest{ID: id, Version: version}
|
2013-04-01 16:53:53 +00:00
|
|
|
r.Apps = append(r.Apps, a)
|
|
|
|
return a
|
|
|
|
}
|
|
|
|
|
2017-05-12 19:56:21 +00:00
|
|
|
func (r *Request) GetApp(id string) *AppRequest {
|
|
|
|
for _, app := range r.Apps {
|
|
|
|
if app.ID == id {
|
|
|
|
return app
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-07-25 23:44:18 +00:00
|
|
|
type AppRequest struct {
|
|
|
|
Ping *PingRequest `xml:"ping"`
|
|
|
|
UpdateCheck *UpdateRequest `xml:"updatecheck"`
|
|
|
|
Events []*EventRequest `xml:"event" json:",omitempty"`
|
2017-05-03 19:38:39 +00:00
|
|
|
ID string `xml:"appid,attr,omitempty"`
|
2015-07-25 23:44:18 +00:00
|
|
|
Client string `xml:"client,attr,omitempty"`
|
|
|
|
InstallAge string `xml:"installage,attr,omitempty"`
|
2017-05-02 20:07:50 +00:00
|
|
|
Lang string `xml:"lang,attr,omitempty"`
|
|
|
|
NextVersion string `xml:"nextversion,attr,omitempty"`
|
|
|
|
Version string `xml:"version,attr,omitempty"`
|
2015-07-25 23:44:18 +00:00
|
|
|
|
|
|
|
// update engine extensions
|
2015-07-26 01:00:13 +00:00
|
|
|
Board string `xml:"board,attr,omitempty"`
|
|
|
|
DeltaOK bool `xml:"delta_okay,attr,omitempty"`
|
2017-05-02 20:07:50 +00:00
|
|
|
FromTrack string `xml:"from_track,attr,omitempty"`
|
|
|
|
Track string `xml:"track,attr,omitempty"`
|
2015-07-25 23:44:18 +00:00
|
|
|
|
|
|
|
// coreos update engine extensions
|
2017-05-02 20:07:50 +00:00
|
|
|
AlephVersion string `xml:"alephversion,attr,omitempty"`
|
2017-05-03 19:38:39 +00:00
|
|
|
BootID string `xml:"bootid,attr,omitempty"`
|
2015-07-26 01:00:13 +00:00
|
|
|
MachineID string `xml:"machineid,attr,omitempty"`
|
|
|
|
OEM string `xml:"oem,attr,omitempty"`
|
|
|
|
OEMVersion string `xml:"oemversion,attr,omitempty"`
|
2015-07-25 23:44:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (a *AppRequest) AddUpdateCheck() *UpdateRequest {
|
|
|
|
a.UpdateCheck = &UpdateRequest{}
|
|
|
|
return a.UpdateCheck
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *AppRequest) AddPing() *PingRequest {
|
2015-07-26 01:00:13 +00:00
|
|
|
a.Ping = &PingRequest{Active: 1}
|
2015-07-25 23:44:18 +00:00
|
|
|
return a.Ping
|
|
|
|
}
|
|
|
|
|
|
|
|
func (a *AppRequest) AddEvent() *EventRequest {
|
|
|
|
event := &EventRequest{}
|
|
|
|
a.Events = append(a.Events, event)
|
|
|
|
return event
|
|
|
|
}
|
|
|
|
|
|
|
|
type UpdateRequest struct {
|
|
|
|
TargetVersionPrefix string `xml:"targetversionprefix,attr,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type PingRequest struct {
|
2017-04-24 20:05:53 +00:00
|
|
|
Active int `xml:"active,attr,omitempty"`
|
|
|
|
LastActiveReportDays *int `xml:"a,attr,omitempty"`
|
|
|
|
LastReportDays int `xml:"r,attr,omitempty"`
|
2015-07-25 23:44:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type EventRequest struct {
|
|
|
|
Type EventType `xml:"eventtype,attr"`
|
|
|
|
Result EventResult `xml:"eventresult,attr"`
|
2017-05-02 20:07:50 +00:00
|
|
|
ErrorCode string `xml:"errorcode,attr,omitempty"`
|
2015-07-26 01:00:13 +00:00
|
|
|
NextVersion string `xml:"nextversion,attr,omitempty"`
|
2015-07-25 23:44:18 +00:00
|
|
|
PreviousVersion string `xml:"previousversion,attr,omitempty"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// Response sent by the Omaha server
|
2013-04-01 16:53:53 +00:00
|
|
|
type Response struct {
|
2015-07-25 23:44:18 +00:00
|
|
|
XMLName xml.Name `xml:"response" json:"-"`
|
|
|
|
DayStart DayStart `xml:"daystart"`
|
|
|
|
Apps []*AppResponse `xml:"app"`
|
|
|
|
Protocol string `xml:"protocol,attr"`
|
|
|
|
Server string `xml:"server,attr"`
|
2013-04-01 16:53:53 +00:00
|
|
|
}
|
|
|
|
|
2015-07-24 06:14:36 +00:00
|
|
|
func NewResponse() *Response {
|
|
|
|
return &Response{
|
|
|
|
Protocol: "3.0",
|
2017-04-24 19:07:47 +00:00
|
|
|
Server: "go-omaha",
|
2015-07-24 06:14:36 +00:00
|
|
|
DayStart: DayStart{ElapsedSeconds: "0"},
|
|
|
|
}
|
2013-04-01 16:53:53 +00:00
|
|
|
}
|
|
|
|
|
2017-05-04 20:00:46 +00:00
|
|
|
// ParseResponse verifies and returns the parsed Response document.
|
|
|
|
// The MIME Content-Type header may be provided to sanity check its
|
|
|
|
// value; if blank it is assumed to be XML in UTF-8.
|
|
|
|
func ParseResponse(contentType string, body io.Reader) (*Response, error) {
|
|
|
|
if err := checkContentType(contentType); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
r := &Response{}
|
|
|
|
if err := parseReqOrResp(body, r); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return r, nil
|
|
|
|
}
|
|
|
|
|
2013-04-01 16:53:53 +00:00
|
|
|
type DayStart struct {
|
|
|
|
ElapsedSeconds string `xml:"elapsed_seconds,attr"`
|
|
|
|
}
|
|
|
|
|
2015-07-25 23:44:18 +00:00
|
|
|
func (r *Response) AddApp(id string, status AppStatus) *AppResponse {
|
2017-05-03 19:38:39 +00:00
|
|
|
a := &AppResponse{ID: id, Status: status}
|
2013-04-01 16:53:53 +00:00
|
|
|
r.Apps = append(r.Apps, a)
|
|
|
|
return a
|
2013-03-27 23:58:22 +00:00
|
|
|
}
|
|
|
|
|
2017-05-12 19:56:21 +00:00
|
|
|
func (r *Response) GetApp(id string) *AppResponse {
|
|
|
|
for _, app := range r.Apps {
|
|
|
|
if app.ID == id {
|
|
|
|
return app
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-07-25 23:44:18 +00:00
|
|
|
type AppResponse struct {
|
|
|
|
Ping *PingResponse `xml:"ping"`
|
|
|
|
UpdateCheck *UpdateResponse `xml:"updatecheck"`
|
|
|
|
Events []*EventResponse `xml:"event" json:",omitempty"`
|
2017-05-03 19:38:39 +00:00
|
|
|
ID string `xml:"appid,attr,omitempty"`
|
2015-07-25 23:44:18 +00:00
|
|
|
Status AppStatus `xml:"status,attr,omitempty"`
|
2013-03-27 23:58:22 +00:00
|
|
|
}
|
|
|
|
|
2015-07-25 23:44:18 +00:00
|
|
|
func (a *AppResponse) AddUpdateCheck(status UpdateStatus) *UpdateResponse {
|
|
|
|
a.UpdateCheck = &UpdateResponse{Status: status}
|
2013-04-01 16:53:53 +00:00
|
|
|
return a.UpdateCheck
|
|
|
|
}
|
|
|
|
|
2015-07-25 23:44:18 +00:00
|
|
|
func (a *AppResponse) AddPing() *PingResponse {
|
|
|
|
a.Ping = &PingResponse{"ok"}
|
2013-04-01 16:53:53 +00:00
|
|
|
return a.Ping
|
|
|
|
}
|
|
|
|
|
2015-07-25 23:44:18 +00:00
|
|
|
func (a *AppResponse) AddEvent() *EventResponse {
|
|
|
|
event := &EventResponse{"ok"}
|
2013-06-25 15:08:19 +00:00
|
|
|
a.Events = append(a.Events, event)
|
|
|
|
return event
|
|
|
|
}
|
|
|
|
|
2015-07-25 23:44:18 +00:00
|
|
|
type UpdateResponse struct {
|
2015-08-09 00:01:00 +00:00
|
|
|
URLs []*URL `xml:"urls>url" json:",omitempty"`
|
2015-07-25 23:44:18 +00:00
|
|
|
Manifest *Manifest `xml:"manifest"`
|
|
|
|
Status UpdateStatus `xml:"status,attr,omitempty"`
|
2013-03-27 23:58:22 +00:00
|
|
|
}
|
|
|
|
|
2015-07-25 23:44:18 +00:00
|
|
|
func (u *UpdateResponse) AddURL(codebase string) *URL {
|
2015-07-24 06:14:36 +00:00
|
|
|
url := &URL{CodeBase: codebase}
|
2015-08-09 00:01:00 +00:00
|
|
|
u.URLs = append(u.URLs, url)
|
2013-04-14 04:35:44 +00:00
|
|
|
return url
|
|
|
|
}
|
|
|
|
|
2015-07-25 23:44:18 +00:00
|
|
|
func (u *UpdateResponse) AddManifest(version string) *Manifest {
|
2013-04-14 04:35:44 +00:00
|
|
|
u.Manifest = &Manifest{Version: version}
|
|
|
|
return u.Manifest
|
|
|
|
}
|
|
|
|
|
2015-07-25 23:44:18 +00:00
|
|
|
type PingResponse struct {
|
|
|
|
Status string `xml:"status,attr"` // Always "ok".
|
|
|
|
}
|
|
|
|
|
|
|
|
type EventResponse struct {
|
|
|
|
Status string `xml:"status,attr"` // Always "ok".
|
2013-03-27 23:58:22 +00:00
|
|
|
}
|
|
|
|
|
2015-07-24 06:14:36 +00:00
|
|
|
type OS struct {
|
|
|
|
Platform string `xml:"platform,attr,omitempty"`
|
|
|
|
Version string `xml:"version,attr,omitempty"`
|
|
|
|
ServicePack string `xml:"sp,attr,omitempty"`
|
|
|
|
Arch string `xml:"arch,attr,omitempty"`
|
2013-03-27 23:58:22 +00:00
|
|
|
}
|
|
|
|
|
2015-07-24 06:14:36 +00:00
|
|
|
type URL struct {
|
2015-07-23 06:17:32 +00:00
|
|
|
CodeBase string `xml:"codebase,attr"`
|
2013-04-01 16:53:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type Manifest struct {
|
2015-07-24 06:14:36 +00:00
|
|
|
Packages []*Package `xml:"packages>package"`
|
|
|
|
Actions []*Action `xml:"actions>action"`
|
|
|
|
Version string `xml:"version,attr"`
|
2013-04-01 16:53:53 +00:00
|
|
|
}
|
|
|
|
|
2015-07-24 06:14:36 +00:00
|
|
|
func (m *Manifest) AddPackage() *Package {
|
|
|
|
p := &Package{}
|
|
|
|
m.Packages = append(m.Packages, p)
|
2013-04-01 16:53:53 +00:00
|
|
|
return p
|
|
|
|
}
|
|
|
|
|
2015-08-10 22:50:31 +00:00
|
|
|
func (m *Manifest) AddPackageFromPath(path string) (*Package, error) {
|
|
|
|
p := &Package{}
|
|
|
|
if err := p.FromPath(path); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
m.Packages = append(m.Packages, p)
|
|
|
|
return p, nil
|
|
|
|
}
|
|
|
|
|
2015-07-24 06:14:36 +00:00
|
|
|
func (m *Manifest) AddAction(event string) *Action {
|
|
|
|
a := &Action{Event: event}
|
|
|
|
m.Actions = append(m.Actions, a)
|
|
|
|
return a
|
2013-04-01 16:53:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type Action struct {
|
2015-07-23 06:17:32 +00:00
|
|
|
Event string `xml:"event,attr"`
|
2013-06-26 17:24:43 +00:00
|
|
|
|
2015-07-26 01:00:13 +00:00
|
|
|
// update engine extensions for event="postinstall"
|
|
|
|
DisplayVersion string `xml:"DisplayVersion,attr,omitempty"`
|
2017-05-03 19:38:39 +00:00
|
|
|
SHA256 string `xml:"sha256,attr,omitempty"`
|
2015-07-26 01:00:13 +00:00
|
|
|
NeedsAdmin bool `xml:"needsadmin,attr,omitempty"`
|
|
|
|
IsDeltaPayload bool `xml:"IsDeltaPayload,attr,omitempty"`
|
2013-06-26 22:53:31 +00:00
|
|
|
DisablePayloadBackoff bool `xml:"DisablePayloadBackoff,attr,omitempty"`
|
2015-07-26 01:00:13 +00:00
|
|
|
MaxFailureCountPerURL uint `xml:"MaxFailureCountPerUrl,attr,omitempty"`
|
2013-06-26 22:53:31 +00:00
|
|
|
MetadataSignatureRsa string `xml:"MetadataSignatureRsa,attr,omitempty"`
|
|
|
|
MetadataSize string `xml:"MetadataSize,attr,omitempty"`
|
|
|
|
Deadline string `xml:"deadline,attr,omitempty"`
|
2015-07-26 01:00:13 +00:00
|
|
|
MoreInfo string `xml:"MoreInfo,attr,omitempty"`
|
|
|
|
Prompt bool `xml:"Prompt,attr,omitempty"`
|
2013-04-01 16:53:53 +00:00
|
|
|
}
|