diff --git a/omaha/codes.go b/omaha/codes.go new file mode 100644 index 0000000..8eefeb2 --- /dev/null +++ b/omaha/codes.go @@ -0,0 +1,170 @@ +// 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. + +package omaha + +import ( + "fmt" +) + +type EventType int + +const ( + EventTypeUnknown EventType = 0 + EventTypeDownloadComplete EventType = 1 + EventTypeInstallComplete EventType = 2 + EventTypeUpdateComplete EventType = 3 + EventTypeUninstall EventType = 4 + EventTypeDownloadStarted EventType = 5 + EventTypeInstallStarted EventType = 6 + EventTypeNewApplicationInstallStarted EventType = 9 + EventTypeSetupStarted EventType = 10 + EventTypeSetupFinished EventType = 11 + EventTypeUpdateApplicationStarted EventType = 12 + EventTypeUpdateDownloadStarted EventType = 13 + EventTypeUpdateDownloadFinished EventType = 14 + EventTypeUpdateInstallerStarted EventType = 15 + EventTypeSetupUpdateBegin EventType = 16 + EventTypeSetupUpdateComplete EventType = 17 + EventTypeRegisterProductComplete EventType = 20 + EventTypeOEMInstallFirstCheck EventType = 30 + EventTypeAppSpecificCommandStarted EventType = 40 + EventTypeAppSpecificCommandEnded EventType = 41 + EventTypeSetupFailure EventType = 100 + EventTypeComServerFailure EventType = 102 + EventTypeSetupUpdateFailure EventType = 103 +) + +func (e EventType) String() string { + switch e { + case EventTypeUnknown: + return "unknown" + case EventTypeDownloadComplete: + return "download complete" + case EventTypeInstallComplete: + return "install complete" + case EventTypeUpdateComplete: + return "update complete" + case EventTypeUninstall: + return "uninstall" + case EventTypeDownloadStarted: + return "download started" + case EventTypeInstallStarted: + return "install started" + case EventTypeNewApplicationInstallStarted: + return "new application install started" + case EventTypeSetupStarted: + return "setup started" + case EventTypeSetupFinished: + return "setup finished" + case EventTypeUpdateApplicationStarted: + return "update application started" + case EventTypeUpdateDownloadStarted: + return "update download started" + case EventTypeUpdateDownloadFinished: + return "update download finished" + case EventTypeUpdateInstallerStarted: + return "update installer started" + case EventTypeSetupUpdateBegin: + return "setup update begin" + case EventTypeSetupUpdateComplete: + return "setup update complete" + case EventTypeRegisterProductComplete: + return "register product complete" + case EventTypeOEMInstallFirstCheck: + return "OEM install first check" + case EventTypeAppSpecificCommandStarted: + return "app-specific command started" + case EventTypeAppSpecificCommandEnded: + return "app-specific command ended" + case EventTypeSetupFailure: + return "setup failure" + case EventTypeComServerFailure: + return "COM server failure" + case EventTypeSetupUpdateFailure: + return "setup update failure " + default: + return fmt.Sprintf("event %d", e) + } +} + +type EventResult int + +const ( + EventResultError EventResult = 0 + EventResultSuccess EventResult = 1 + EventResultSuccessReboot EventResult = 2 + EventResultSuccessRestartBrowser EventResult = 3 + EventResultCancelled EventResult = 4 + EventResultErrorInstallerMSI EventResult = 5 + EventResultErrorInstallerOther EventResult = 6 + EventResultNoUpdate EventResult = 7 + EventResultInstallerSystem EventResult = 8 + EventResultUpdateDeferred EventResult = 9 + EventResultHandoffError EventResult = 10 +) + +func (e EventResult) String() string { + switch e { + case EventResultError: + return "error" + case EventResultSuccess: + return "success" + case EventResultSuccessReboot: + return "success reboot" + case EventResultSuccessRestartBrowser: + return "success restart browser" + case EventResultCancelled: + return "cancelled" + case EventResultErrorInstallerMSI: + return "error installer MSI" + case EventResultErrorInstallerOther: + return "error installer other" + case EventResultNoUpdate: + return "noupdate" + case EventResultInstallerSystem: + return "error installer system" + case EventResultUpdateDeferred: + return "update deferred" + case EventResultHandoffError: + return "handoff error" + default: + return fmt.Sprintf("result %d", e) + } +} + +type AppStatus string + +const ( + // Standard values + AppOK AppStatus = "ok" + AppRestricted AppStatus = "restricted" + AppUnknownId AppStatus = "error-unknownApplication" + AppInvalidId AppStatus = "error-invalidAppId" + + // Extra error values + AppInvalidVersion AppStatus = "error-invalidVersion" +) + +type UpdateStatus string + +const ( + NoUpdate UpdateStatus = "noupdate" + UpdateOK UpdateStatus = "ok" + UpdateOSNotSupported UpdateStatus = "error-osnotsupported" + UpdateUnsupportedProtocol UpdateStatus = "error-unsupportedProtocol" + UpdatePluginRestrictedHost UpdateStatus = "error-pluginRestrictedHost" + UpdateHashError UpdateStatus = "error-hash" + UpdateInternalError UpdateStatus = "error-internal" +) diff --git a/omaha/protocol.go b/omaha/protocol.go index 347254c..c4af2eb 100644 --- a/omaha/protocol.go +++ b/omaha/protocol.go @@ -24,11 +24,13 @@ package omaha import ( "encoding/xml" + + "github.com/coreos/mantle/version" ) type Request struct { XMLName xml.Name `xml:"request" json:"-"` - Os Os `xml:"os"` + OS *OS `xml:"os"` Apps []*App `xml:"app"` Protocol string `xml:"protocol,attr"` Version string `xml:"version,attr,omitempty"` @@ -41,16 +43,20 @@ type Request struct { UpdaterVersion string `xml:"updaterversion,attr,omitempty"` } -func NewRequest(version string, platform string, sp string, arch string) *Request { - r := new(Request) - r.Protocol = "3.0" - r.Os = Os{Version: version, Platform: platform, Sp: sp, Arch: arch} - return r +func NewRequest() *Request { + return &Request{ + Protocol: "3.0", + Version: version.Version, + OS: &OS{ + Platform: LocalPlatform(), + Arch: LocalArch(), + // TODO(marineam): Version and ServicePack + }, + } } -func (r *Request) AddApp(id string, version string) *App { - a := NewApp(id) - a.Version = version +func (r *Request) AddApp(id, version string) *App { + a := &App{Id: id, Version: version} r.Apps = append(r.Apps, a) return a } @@ -63,18 +69,20 @@ type Response struct { Server string `xml:"server,attr"` } -func NewResponse(server string) *Response { - r := &Response{Server: server, Protocol: "3.0"} - r.DayStart.ElapsedSeconds = "0" - return r +func NewResponse() *Response { + return &Response{ + Protocol: "3.0", + Server: "mantle", + DayStart: DayStart{ElapsedSeconds: "0"}, + } } type DayStart struct { ElapsedSeconds string `xml:"elapsed_seconds,attr"` } -func (r *Response) AddApp(id string) *App { - a := NewApp(id) +func (r *Response) AddApp(id string, status AppStatus) *App { + a := &App{Id: id, Status: status} r.Apps = append(r.Apps, a) return a } @@ -89,7 +97,7 @@ type App struct { Lang string `xml:"lang,attr,omitempty"` Client string `xml:"client,attr,omitempty"` InstallAge string `xml:"installage,attr,omitempty"` - Status string `xml:"status,attr,omitempty"` + Status AppStatus `xml:"status,attr,omitempty"` // update engine extensions Track string `xml:"track,attr,omitempty"` @@ -101,11 +109,6 @@ type App struct { OEM string `xml:"oem,attr,omitempty"` } -func NewApp(id string) *App { - a := &App{Id: id} - return a -} - func (a *App) AddUpdateCheck() *UpdateCheck { a.UpdateCheck = new(UpdateCheck) return a.UpdateCheck @@ -123,19 +126,20 @@ func (a *App) AddEvent() *Event { } type UpdateCheck struct { - Urls *Urls `xml:"urls"` - Manifest *Manifest `xml:"manifest"` - TargetVersionPrefix string `xml:"targetversionprefix,attr,omitempty"` - Status string `xml:"status,attr,omitempty"` + URLs *URLs `xml:"urls"` + Manifest *Manifest `xml:"manifest"` + TargetVersionPrefix string `xml:"targetversionprefix,attr,omitempty"` + Status UpdateStatus `xml:"status,attr,omitempty"` } -func (u *UpdateCheck) AddUrl(codebase string) *Url { - if u.Urls == nil { - u.Urls = new(Urls) +func (u *UpdateCheck) AddURL(codebase string) *URL { + // An intermediate struct is used instead of a "urls>url" tag simply + // to keep Go from generating if the list is empty. + if u.URLs == nil { + u.URLs = new(URLs) } - url := new(Url) - url.CodeBase = codebase - u.Urls.Urls = append(u.Urls.Urls, *url) + url := &URL{CodeBase: codebase} + u.URLs.URLs = append(u.URLs.URLs, url) return url } @@ -149,58 +153,52 @@ type Ping struct { Status string `xml:"status,attr,omitempty"` } -type Os struct { - Platform string `xml:"platform,attr,omitempty"` - Version string `xml:"version,attr,omitempty"` - Sp string `xml:"sp,attr,omitempty"` - Arch string `xml:"arch,attr,omitempty"` -} - -func NewOs(platform string, version string, sp string, arch string) *Os { - o := &Os{Version: version, Platform: platform, Sp: sp, Arch: arch} - return o +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"` } type Event struct { - Type string `xml:"eventtype,attr,omitempty"` - Result string `xml:"eventresult,attr,omitempty"` - PreviousVersion string `xml:"previousversion,attr,omitempty"` - ErrorCode string `xml:"errorcode,attr,omitempty"` + Type EventType `xml:"eventtype,attr"` + Result EventResult `xml:"eventresult,attr"` + PreviousVersion string `xml:"previousversion,attr,omitempty"` + ErrorCode string `xml:"errorcode,attr,omitempty"` + Status string `xml:"status,attr,omitempty"` } -type Urls struct { - Urls []Url `xml:"url" json:",omitempty"` +type URLs struct { + URLs []*URL `xml:"url" json:",omitempty"` } -type Url struct { +type URL struct { CodeBase string `xml:"codebase,attr"` } type Manifest struct { - Packages Packages `xml:"packages"` - Actions Actions `xml:"actions"` - Version string `xml:"version,attr"` -} - -type Packages struct { - Packages []Package `xml:"package" json:",omitempty"` + Packages []*Package `xml:"packages>package"` + Actions []*Action `xml:"actions>action"` + Version string `xml:"version,attr"` } type Package struct { Hash string `xml:"hash,attr"` Name string `xml:"name,attr"` - Size string `xml:"size,attr"` + Size uint64 `xml:"size,attr"` Required bool `xml:"required,attr"` } -func (m *Manifest) AddPackage(hash string, name string, size string, required bool) *Package { - p := &Package{Hash: hash, Name: name, Size: size, Required: required} - m.Packages.Packages = append(m.Packages.Packages, *p) +func (m *Manifest) AddPackage() *Package { + p := &Package{} + m.Packages = append(m.Packages, p) return p } -type Actions struct { - Actions []*Action `xml:"action" json:",omitempty"` +func (m *Manifest) AddAction(event string) *Action { + a := &Action{Event: event} + m.Actions = append(m.Actions, a) + return a } type Action struct { @@ -216,50 +214,3 @@ type Action struct { MetadataSize string `xml:"MetadataSize,attr,omitempty"` Deadline string `xml:"deadline,attr,omitempty"` } - -func (m *Manifest) AddAction(event string) *Action { - a := &Action{Event: event} - m.Actions.Actions = append(m.Actions.Actions, a) - return a -} - -var EventTypes = map[int]string{ - 0: "unknown", - 1: "download complete", - 2: "install complete", - 3: "update complete", - 4: "uninstall", - 5: "download started", - 6: "install started", - 9: "new application install started", - 10: "setup started", - 11: "setup finished", - 12: "update application started", - 13: "update download started", - 14: "update download finished", - 15: "update installer started", - 16: "setup update begin", - 17: "setup update complete", - 20: "register product complete", - 30: "OEM install first check", - 40: "app-specific command started", - 41: "app-specific command ended", - 100: "setup failure", - 102: "COM server failure", - 103: "setup update failure", - 800: "ping", -} - -var EventResults = map[int]string{ - 0: "error", - 1: "success", - 2: "success reboot", - 3: "success restart browser", - 4: "cancelled", - 5: "error installer MSI", - 6: "error installer other", - 7: "noupdate", - 8: "error installer system", - 9: "update deferred", - 10: "handoff error", -} diff --git a/omaha/protocol_test.go b/omaha/protocol_test.go index cc0ff25..428aca2 100644 --- a/omaha/protocol_test.go +++ b/omaha/protocol_test.go @@ -35,8 +35,8 @@ func TestOmahaRequestUpdateCheck(t *testing.T) { v := Request{} xml.Unmarshal([]byte(SampleRequest), &v) - if v.Os.Version != "Indy" { - t.Error("Unexpected version", v.Os.Version) + if v.OS.Version != "Indy" { + t.Error("Unexpected version", v.OS.Version) } if v.Apps[0].Id != "{87efface-864d-49a5-9bb3-4b050a7c227a}" { @@ -71,22 +71,29 @@ func TestOmahaRequestUpdateCheck(t *testing.T) { t.Error("dev-channel") } - if v.Apps[0].Events[0].Type != "3" { - t.Error("developer-build") + if v.Apps[0].Events[0].Type != EventTypeUpdateComplete { + t.Error("Expected EventTypeUpdateComplete") + } + + if v.Apps[0].Events[0].Result != EventResultSuccessReboot { + t.Error("Expected EventResultSuccessReboot") } } func ExampleNewResponse() { - response := NewResponse("unit-test") - app := response.AddApp("{52F1B9BC-D31A-4D86-9276-CBC256AADF9A}") - app.Status = "ok" + response := NewResponse() + app := response.AddApp("{52F1B9BC-D31A-4D86-9276-CBC256AADF9A}", "ok") p := app.AddPing() p.Status = "ok" u := app.AddUpdateCheck() u.Status = "ok" - u.AddUrl("http://localhost/updates") + u.AddURL("http://localhost/updates") m := u.AddManifest("9999.0.0") - m.AddPackage("+LXvjiaPkeYDLHoNKlf9qbJwvnk=", "update.gz", "67546213", true) + k := m.AddPackage() + k.Hash = "+LXvjiaPkeYDLHoNKlf9qbJwvnk=" + k.Name = "update.gz" + k.Size = 67546213 + k.Required = true a := m.AddAction("postinstall") a.ChromeOSVersion = "9999.0.0" a.Sha256 = "0VAlQW3RE99SGtSB5R4m08antAHO8XDoBMKDyxQT/Mg=" @@ -103,7 +110,7 @@ func ExampleNewResponse() { // Output: // - // + // // // // @@ -125,13 +132,19 @@ func ExampleNewResponse() { } func ExampleNewRequest() { - request := NewRequest("Indy", "Chrome OS", "ForcedUpdate_x86_64", "") + request := NewRequest() + request.Version = "" + request.OS = &OS{ + Platform: "Chrome OS", + Version: "Indy", + ServicePack: "ForcedUpdate_x86_64", + } app := request.AddApp("{27BD862E-8AE8-4886-A055-F7F1A6460627}", "1.0.0.0") app.AddUpdateCheck() event := app.AddEvent() - event.Type = "1" - event.Result = "0" + event.Type = EventTypeDownloadComplete + event.Result = EventResultError if raw, err := xml.MarshalIndent(request, "", " "); err != nil { fmt.Println(err) diff --git a/omaha/system.go b/omaha/system.go new file mode 100644 index 0000000..1311a1c --- /dev/null +++ b/omaha/system.go @@ -0,0 +1,53 @@ +// Copyright 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. + +package omaha + +import ( + "runtime" +) + +// Translate GOARCH to Omaha's choice of names, because no two independent +// software projects *ever* use the same set of architecture names. ;-) +func LocalArch() string { + switch runtime.GOARCH { + case "386": + return "x86" + case "amd64": + return "x64" + case "amd64p32": + // Not actually specified by Omaha but it follows the above. + return "x32" + case "arm": + fallthrough + default: + // Nothing else is defined by Omaha so anything goes. + return runtime.GOARCH + } +} + +// Translate GOOS to Omaha's platform names as best as we can. +func LocalPlatform() string { + switch runtime.GOOS { + case "darwin": + return "mac" // or "ios" + case "linux": + return "linux" // or "android" + case "windows": + return "win" + default: + // Nothing else is defined by Omaha so anything goes. + return runtime.GOOS + } +}