commit e5d543b50b146835783f27ccad5ca2beea6b6ac1
Author: Yawning Angel <[email protected]>
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
[email protected]
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits

Reply via email to