[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [sandboxed-tor-browser/master] Bug 20778: Refactor the update logic to prepare for background updates.
commit e5d543b50b146835783f27ccad5ca2beea6b6ac1
Author: Yawning Angel <yawning@xxxxxxxxxxxxxxx>
Date: Thu Dec 22 19:57:27 2016 +0000
Bug 20778: Refactor the update logic to prepare for background updates.
This refactors the update logic, and changes the config file flag
signalling an update is required to a bool from a unix timestamp.
Note: This breaks the update functionality since updates are never
triggered, but is required for the background check refactor.
---
.../internal/ui/config/config.go | 22 +--
.../sandboxed-tor-browser/internal/ui/gtk/ui.go | 2 +-
.../sandboxed-tor-browser/internal/ui/install.go | 155 +---------------
.../sandboxed-tor-browser/internal/ui/update.go | 205 +++++++++++++++++++++
4 files changed, 214 insertions(+), 170 deletions(-)
diff --git a/src/cmd/sandboxed-tor-browser/internal/ui/config/config.go b/src/cmd/sandboxed-tor-browser/internal/ui/config/config.go
index ff5ef9e..a1af1cd 100644
--- a/src/cmd/sandboxed-tor-browser/internal/ui/config/config.go
+++ b/src/cmd/sandboxed-tor-browser/internal/ui/config/config.go
@@ -25,7 +25,6 @@ import (
"os"
"path/filepath"
"runtime"
- "time"
butils "git.schwanenlied.me/yawning/bulb.git/utils"
xdg "github.com/cep21/xdgbasedir"
@@ -300,9 +299,8 @@ type Config struct {
// Locale is the Tor Browser locale to install ("en-US", "ja").
Locale string `json:"locale,omitempty"`
- // LastUpdateCheck is the UNIX time when the last update check was
- // sucessfully completed.
- LastUpdateCheck int64 `json:"lastUpdateCheck,omitEmpty"`
+ // ForceUpdate is set if the installed bundle is known to be obsolete.
+ ForceUpdate bool `json:"forceUpdate"`
// Tor is the Tor network configuration.
Tor Tor `json:"tor,omitEmpty"`
@@ -374,19 +372,11 @@ func (cfg *Config) SetFirstLaunch(b bool) {
}
}
-// NeedsUpdateCheck returns true if the bundle needs to be checked for updates,
-// and possibly updated.
-func (cfg *Config) NeedsUpdateCheck() bool {
- const updateInterval = 60 * 60 * 12 // 12 hours.
- now := time.Now().Unix()
- return (now > cfg.LastUpdateCheck+updateInterval) || cfg.LastUpdateCheck > now
-}
-
-// SetLastUpdateCheck sets the last update check time and marks the config
+// SetForceUpdate sets the bundle as needed an update and marks the config
// dirty.
-func (cfg *Config) SetLastUpdateCheck(t int64) {
- if cfg.LastUpdateCheck != t {
- cfg.LastUpdateCheck = t
+func (cfg *Config) SetForceUpdate(b bool) {
+ if cfg.ForceUpdate != b {
+ cfg.ForceUpdate = b
cfg.isDirty = true
}
}
diff --git a/src/cmd/sandboxed-tor-browser/internal/ui/gtk/ui.go b/src/cmd/sandboxed-tor-browser/internal/ui/gtk/ui.go
index 161b32f..4e526bb 100644
--- a/src/cmd/sandboxed-tor-browser/internal/ui/gtk/ui.go
+++ b/src/cmd/sandboxed-tor-browser/internal/ui/gtk/ui.go
@@ -159,7 +159,7 @@ func (ui *gtkUI) onDestroy() {
func (ui *gtkUI) launch() error {
// If we don't need to update, and would just launch, quash the UI.
- checkUpdate := ui.Cfg.NeedsUpdateCheck()
+ checkUpdate := ui.Cfg.ForceUpdate
squelchUI := !checkUpdate && ui.Cfg.UseSystemTor
async := async.NewAsync()
diff --git a/src/cmd/sandboxed-tor-browser/internal/ui/install.go b/src/cmd/sandboxed-tor-browser/internal/ui/install.go
index 0c9e80e..ac9f246 100644
--- a/src/cmd/sandboxed-tor-browser/internal/ui/install.go
+++ b/src/cmd/sandboxed-tor-browser/internal/ui/install.go
@@ -1,4 +1,4 @@
-// install.go - Install/Update logic.
+// install.go - Install logic.
// Copyright (C) 2016 Yawning Angel.
//
// This program is free software: you can redistribute it and/or modify
@@ -17,20 +17,15 @@
package ui
import (
- "bytes"
- "crypto/sha512"
- "encoding/hex"
"fmt"
"io/ioutil"
"log"
"os"
"path/filepath"
"runtime"
- "time"
"cmd/sandboxed-tor-browser/internal/data"
"cmd/sandboxed-tor-browser/internal/installer"
- "cmd/sandboxed-tor-browser/internal/sandbox"
. "cmd/sandboxed-tor-browser/internal/ui/async"
"cmd/sandboxed-tor-browser/internal/ui/config"
"cmd/sandboxed-tor-browser/internal/utils"
@@ -90,7 +85,6 @@ func (c *Common) DoInstall(async *Async) {
return
}
}
- checkAt := time.Now().Unix()
log.Printf("install: Version: %v Downloads: %v", version, downloads)
@@ -149,7 +143,7 @@ func (c *Common) DoInstall(async *Async) {
}
// Set the appropriate bits in the config.
- c.Cfg.SetLastUpdateCheck(checkAt)
+ c.Cfg.SetForceUpdate(false)
c.Cfg.SetFirstLaunch(true)
// Sync the config, and return.
@@ -173,148 +167,3 @@ func writeAutoconfig(cfg *config.Config) error {
return nil
}
-
-func (c *Common) doUpdate(async *Async, dialFn dialFunc) {
- // This attempts to follow the process that Firefox uses to check for
- // updates. https://wiki.mozilla.org/Software_Update:Checking_For_Updates
-
- // Check for updates.
- log.Printf("launch: Checking for updates.")
- async.UpdateProgress("Checking for updates.")
-
- // Create the async HTTP client.
- client := newHPKPGrabClient(dialFn)
-
- // Check the version, by downloading the XML file.
- // XXX: Fall back to https over clearnet if the onion fails.
- var update *installer.UpdateEntry
- if url, err := installer.UpdateURL(c.Manif, true); err != nil {
- async.Err = err
- return
- } else {
- log.Printf("launch: Update URL: %v", url)
- if b := async.Grab(client, url, nil); async.Err != nil {
- return
- } else if update, async.Err = installer.GetUpdateEntry(b); async.Err != nil {
- return
- }
- }
-
- checkAt := time.Now().Unix()
- if update == nil {
- log.Printf("launch: Installed bundle is current.")
-
- // Save the time that the update check was done.
- c.Cfg.SetLastUpdateCheck(checkAt)
- async.Err = c.Cfg.Sync()
- return
- }
-
- // Force an update check again if the user exits for any reason, since
- // we know there is an update available.
- c.Cfg.SetLastUpdateCheck(0)
- if async.Err = c.Cfg.Sync(); async.Err != nil {
- return
- }
-
- // Ensure that the update entry version is actually neweer.
- if !c.Manif.BundleUpdateVersionValid(update.AppVersion) {
- log.Printf("launch: Update server provided a downgrade: '%v'", update.AppVersion)
- async.Err = fmt.Errorf("update server provided a downgrade: '%v'", update.AppVersion)
- return
- }
-
- // Figure out the best MAR to download.
- patches := make(map[string]*installer.Patch)
- for _, v := range update.Patch {
- if patches[v.Type] != nil {
- async.Err = fmt.Errorf("duplicate patch entry for kind: '%v'", v.Type)
- return
- }
- patches[v.Type] = &v
- }
- patch := patches["partial"] // Favor the delta update mechanism.
- if patch == nil {
- if patch = patches["complete"]; patch == nil {
- async.Err = fmt.Errorf("no suitable MAR file found")
- return
- }
- }
-
- // Download the MAR file.
- log.Printf("update: Downloading %v", patch.Url)
- async.UpdateProgress("Downloading Tor Browser Update.")
-
- var mar []byte
- if mar = async.Grab(client, patch.Url, func(s string) { async.UpdateProgress(fmt.Sprintf("Downloading Tor Browser Update: %s", s)) }); async.Err != nil {
- return
- }
-
- log.Printf("update: Validating Tor Browser Update.")
- async.UpdateProgress("Validating Tor Browser Update.")
-
- // Validate the hash against that listed in the XML file.
- expectedHash, err := hex.DecodeString(patch.HashValue)
- if err != nil {
- async.Err = fmt.Errorf("failed to decode HashValue: %v", err)
- return
- }
- switch patch.HashFunction {
- case "SHA512":
- derivedHash := sha512.Sum512(mar)
- if !bytes.Equal(expectedHash, derivedHash[:]) {
- async.Err = fmt.Errorf("downloaded hash does not match patch metadata")
- return
- }
- default:
- async.Err = fmt.Errorf("unsupported hash function: '%v'", patch.HashFunction)
- return
- }
-
- // ... and verify the signature block in the MAR with our copy of the key.
- if async.Err = installer.VerifyTorBrowserMAR(mar); async.Err != nil {
- return
- }
-
- // Shutdown the old tor now.
- if c.tor != nil {
- log.Printf("update: Shutting down old tor.")
- c.tor.Shutdown()
- c.tor = nil
- }
-
- // Apply the update.
- log.Printf("update: Updating Tor Browser.")
- async.UpdateProgress("Updating Tor Browser.")
-
- async.ToUI <- false // Lock out canceling.
-
- if async.Err = sandbox.RunUpdate(c.Cfg, mar); async.Err != nil {
- return
- }
-
- // Reinstall the autoconfig stuff.
- if async.Err = writeAutoconfig(c.Cfg); async.Err != nil {
- return
- }
-
- // Update the maniftest and config.
- c.Manif.SetVersion(update.AppVersion)
- if async.Err = c.Manif.Sync(); async.Err != nil {
- return
- }
- c.Cfg.SetLastUpdateCheck(checkAt)
- if async.Err = c.Cfg.Sync(); async.Err != nil {
- return
- }
-
- async.ToUI <- true // Unlock canceling.
-
- // Restart tor if we launched it.
- if !c.Cfg.UseSystemTor {
- log.Printf("launch: Reconnecting to the Tor network.")
- async.UpdateProgress("Reconnecting to the Tor network.")
- _, async.Err = c.launchTor(async, false)
- }
- return
-}
diff --git a/src/cmd/sandboxed-tor-browser/internal/ui/update.go b/src/cmd/sandboxed-tor-browser/internal/ui/update.go
new file mode 100644
index 0000000..7c5f812
--- /dev/null
+++ b/src/cmd/sandboxed-tor-browser/internal/ui/update.go
@@ -0,0 +1,205 @@
+// update.go - Update logic.
+// Copyright (C) 2016 Yawning Angel.
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+package ui
+
+import (
+ "bytes"
+ "crypto/sha512"
+ "encoding/hex"
+ "fmt"
+ "log"
+
+ "cmd/sandboxed-tor-browser/internal/installer"
+ "cmd/sandboxed-tor-browser/internal/sandbox"
+ . "cmd/sandboxed-tor-browser/internal/ui/async"
+)
+
+func (c *Common) CheckUpdate(async *Async, dialFn dialFunc) *installer.UpdateEntry {
+ // Check for updates.
+ log.Printf("update: Checking for updates.")
+ async.UpdateProgress("Checking for updates.")
+
+ // Create the async HTTP client.
+ client := newHPKPGrabClient(dialFn)
+
+ // Determine where the update metadata should be fetched from.
+ updateURLs := []string{}
+ for _, b := range []bool{true, false} { // Prioritize .onions.
+ if url, err := installer.UpdateURL(c.Manif, b); err != nil {
+ log.Printf("update: Failed to get update URL (onion: %v): %v", b, err)
+ } else {
+ updateURLs = append(updateURLs, url)
+ }
+ }
+ if len(updateURLs) == 0 {
+ log.Printf("update: Failed to find any update URLs")
+ async.Err = fmt.Errorf("failed to find any update URLs")
+ return nil
+ }
+
+ // Check the version, by downloading the XML file.
+ var update *installer.UpdateEntry
+ fetchOk := false
+ for _, url := range updateURLs {
+ log.Printf("update: Metadata URL: %v", url)
+ async.Err = nil // Clear errors per fetch.
+ if b := async.Grab(client, url, nil); async.Err != nil {
+ log.Printf("update: Metadata download failed: %v", async.Err)
+ continue
+ } else if update, async.Err = installer.GetUpdateEntry(b); async.Err != nil {
+ log.Printf("update: Metadata parse failed: %v", async.Err)
+ continue
+ }
+ fetchOk = true
+ break
+ }
+
+ if !fetchOk {
+ // This should be set from the last update attempt...
+ if async.Err == nil {
+ async.Err = fmt.Errorf("failed to download update metadata")
+ }
+ return nil
+ }
+
+ if update == nil {
+ log.Printf("update: Installed bundle is current.")
+ c.Cfg.SetForceUpdate(false)
+ } else {
+ log.Printf("update: Installed bundle needs updating.")
+ c.Cfg.SetForceUpdate(true)
+ }
+
+ if async.Err = c.Cfg.Sync(); async.Err != nil {
+ return nil
+ }
+
+ return update
+}
+
+func (c *Common) doUpdate(async *Async, dialFn dialFunc) {
+ // This attempts to follow the process that Firefox uses to check for
+ // updates. https://wiki.mozilla.org/Software_Update:Checking_For_Updates
+
+ // Check for updates.
+ update := c.CheckUpdate(async, dialFn)
+ if async.Err != nil || update == nil {
+ return
+ }
+
+ // Ensure that the update entry version is actually neweer.
+ if !c.Manif.BundleUpdateVersionValid(update.AppVersion) {
+ log.Printf("update: Update server provided a downgrade: '%v'", update.AppVersion)
+ async.Err = fmt.Errorf("update server provided a downgrade: '%v'", update.AppVersion)
+ return
+ }
+
+ // Figure out the best MAR to download.
+ patches := make(map[string]*installer.Patch)
+ for _, v := range update.Patch {
+ if patches[v.Type] != nil {
+ async.Err = fmt.Errorf("duplicate patch entry for kind: '%v'", v.Type)
+ return
+ }
+ patches[v.Type] = &v
+ }
+ patch := patches["partial"] // Favor the delta update mechanism.
+ if patch == nil {
+ if patch = patches["complete"]; patch == nil {
+ async.Err = fmt.Errorf("no suitable MAR file found")
+ return
+ }
+ }
+
+ // Download the MAR file.
+ log.Printf("update: Downloading %v", patch.Url)
+ async.UpdateProgress("Downloading Tor Browser Update.")
+
+ var mar []byte
+ client := newHPKPGrabClient(dialFn)
+ if mar = async.Grab(client, patch.Url, func(s string) { async.UpdateProgress(fmt.Sprintf("Downloading Tor Browser Update: %s", s)) }); async.Err != nil {
+ return
+ }
+
+ log.Printf("update: Validating Tor Browser Update.")
+ async.UpdateProgress("Validating Tor Browser Update.")
+
+ // Validate the hash against that listed in the XML file.
+ expectedHash, err := hex.DecodeString(patch.HashValue)
+ if err != nil {
+ async.Err = fmt.Errorf("failed to decode HashValue: %v", err)
+ return
+ }
+ switch patch.HashFunction {
+ case "SHA512":
+ derivedHash := sha512.Sum512(mar)
+ if !bytes.Equal(expectedHash, derivedHash[:]) {
+ async.Err = fmt.Errorf("downloaded hash does not match patch metadata")
+ return
+ }
+ default:
+ async.Err = fmt.Errorf("unsupported hash function: '%v'", patch.HashFunction)
+ return
+ }
+
+ // ... and verify the signature block in the MAR with our copy of the key.
+ if async.Err = installer.VerifyTorBrowserMAR(mar); async.Err != nil {
+ return
+ }
+
+ // Shutdown the old tor now.
+ if c.tor != nil {
+ log.Printf("update: Shutting down old tor.")
+ c.tor.Shutdown()
+ c.tor = nil
+ }
+
+ // Apply the update.
+ log.Printf("update: Updating Tor Browser.")
+ async.UpdateProgress("Updating Tor Browser.")
+
+ async.ToUI <- false // Lock out canceling.
+
+ if async.Err = sandbox.RunUpdate(c.Cfg, mar); async.Err != nil {
+ return
+ }
+
+ // Reinstall the autoconfig stuff.
+ if async.Err = writeAutoconfig(c.Cfg); async.Err != nil {
+ return
+ }
+
+ // Update the maniftest and config.
+ c.Manif.SetVersion(update.AppVersion)
+ if async.Err = c.Manif.Sync(); async.Err != nil {
+ return
+ }
+ c.Cfg.SetForceUpdate(false)
+ if async.Err = c.Cfg.Sync(); async.Err != nil {
+ return
+ }
+
+ async.ToUI <- true // Unlock canceling.
+
+ // Restart tor if we launched it.
+ if !c.Cfg.UseSystemTor {
+ log.Printf("launch: Reconnecting to the Tor network.")
+ async.UpdateProgress("Reconnecting to the Tor network.")
+ _, async.Err = c.launchTor(async, false)
+ }
+ return
+}
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits