[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[vidalia-svn] r3413: Merge the auto-updates branch into trunk. It's still a compi (in vidalia/trunk: . src/vidalia src/vidalia/config src/vidalia/res src/vidalia/res/32x32 src/vidalia/res/48x48)



Author: edmanm
Date: 2009-01-03 15:10:16 -0500 (Sat, 03 Jan 2009)
New Revision: 3413

Added:
   vidalia/trunk/src/vidalia/packageinfo.cpp
   vidalia/trunk/src/vidalia/packageinfo.h
   vidalia/trunk/src/vidalia/res/32x32/system-software-update.png
   vidalia/trunk/src/vidalia/res/48x48/system-software-update.png
   vidalia/trunk/src/vidalia/updateprocess.cpp
   vidalia/trunk/src/vidalia/updateprocess.h
   vidalia/trunk/src/vidalia/updateprogressdialog.cpp
   vidalia/trunk/src/vidalia/updateprogressdialog.h
   vidalia/trunk/src/vidalia/updateprogressdialog.ui
   vidalia/trunk/src/vidalia/updatesavailabledialog.cpp
   vidalia/trunk/src/vidalia/updatesavailabledialog.h
   vidalia/trunk/src/vidalia/updatesavailabledialog.ui
Modified:
   vidalia/trunk/CMakeLists.txt
   vidalia/trunk/config.h.in
   vidalia/trunk/src/vidalia/CMakeLists.txt
   vidalia/trunk/src/vidalia/config/configdialog.cpp
   vidalia/trunk/src/vidalia/config/configdialog.h
   vidalia/trunk/src/vidalia/config/generalpage.cpp
   vidalia/trunk/src/vidalia/config/generalpage.h
   vidalia/trunk/src/vidalia/config/generalpage.ui
   vidalia/trunk/src/vidalia/config/vidaliasettings.cpp
   vidalia/trunk/src/vidalia/config/vidaliasettings.h
   vidalia/trunk/src/vidalia/mainwindow.cpp
   vidalia/trunk/src/vidalia/mainwindow.h
   vidalia/trunk/src/vidalia/res/vidalia.qrc
Log:
Merge the auto-updates branch into trunk. It's still a compile-time option
and is disabled by default.


Modified: vidalia/trunk/CMakeLists.txt
===================================================================
--- vidalia/trunk/CMakeLists.txt	2009-01-03 14:05:46 UTC (rev 3412)
+++ vidalia/trunk/CMakeLists.txt	2009-01-03 20:10:16 UTC (rev 3413)
@@ -78,6 +78,11 @@
 ## UPnP support is currently optional (enabled by default)
 option(USE_MINIUPNPC "Enable UPnP support using the MiniUPnPc library." ON)
 
+## Automatic osftware update is optional (disabled by default)
+if (WIN32)
+  option(USE_AUTOUPDATE "Enable automatic software update support." OFF)
+endif(WIN32)
+
 ## Check for system header files
 check_include_file("limits.h" HAVE_LIMITS_H)
 check_include_file("sys/limits.h" HAVE_SYS_LIMITS_H)

Modified: vidalia/trunk/config.h.in
===================================================================
--- vidalia/trunk/config.h.in	2009-01-03 14:05:46 UTC (rev 3412)
+++ vidalia/trunk/config.h.in	2009-01-03 20:10:16 UTC (rev 3413)
@@ -1,5 +1,5 @@
 /*
-**  $Id: $
+**  $Id$
 ** 
 **  This file is part of Vidalia, and is subject to the license terms in the 
 **  LICENSE file, found in the top level directory of this distribution. If  
@@ -28,5 +28,7 @@
 
 #cmakedefine USE_MINIUPNPC
 
+#cmakedefine USE_AUTOUPDATE
+
 #endif
 

Modified: vidalia/trunk/src/vidalia/CMakeLists.txt
===================================================================
--- vidalia/trunk/src/vidalia/CMakeLists.txt	2009-01-03 14:05:46 UTC (rev 3412)
+++ vidalia/trunk/src/vidalia/CMakeLists.txt	2009-01-03 20:10:16 UTC (rev 3413)
@@ -215,10 +215,29 @@
   log/messagelog.ui
   network/netviewer.ui
 )
+
 if (USE_MINIUPNPC)
   qt4_wrap_ui(vidalia_SRCS config/upnptestdialog.ui)
 endif(USE_MINIUPNPC)
 
+if (USE_AUTOUPDATE)
+  set(vidalia_SRCS ${vidalia_SRCS}
+    packageinfo.cpp
+    updateprocess.cpp
+    updateprogressdialog.cpp
+    updatesavailabledialog.cpp
+  )
+  qt4_wrap_cpp(vidalia_SRCS
+    updateprocess.h
+    updateprogressdialog.h
+    updatesavailabledialog.h
+  )
+  qt4_wrap_ui(vidalia_SRCS
+    updateprogressdialog.ui
+    updatesavailabledialog.ui
+  )
+endif(USE_AUTOUPDATE)
+
 ## Add the resource files (icons, etc.)
 qt4_add_resources(vidalia_SRCS
   res/vidalia.qrc

Modified: vidalia/trunk/src/vidalia/config/configdialog.cpp
===================================================================
--- vidalia/trunk/src/vidalia/config/configdialog.cpp	2009-01-03 14:05:46 UTC (rev 3412)
+++ vidalia/trunk/src/vidalia/config/configdialog.cpp	2009-01-03 20:10:16 UTC (rev 3413)
@@ -72,9 +72,12 @@
 
   /* Create the config pages and actions */
   QActionGroup *grp = new QActionGroup(this);
-  ui.stackPages->add(new GeneralPage(ui.stackPages),
+  GeneralPage *generalPage = new GeneralPage(ui.stackPages);
+  ui.stackPages->add(generalPage,
                      createPageAction(QIcon(IMAGE_GENERAL),
                                       tr("General"), "General", grp));
+  connect(generalPage, SIGNAL(checkForUpdates()),
+          this, SLOT(onCheckForUpdates()));
 
   ui.stackPages->add(new NetworkPage(ui.stackPages),
                      createPageAction(QIcon(IMAGE_NETWORK),
@@ -282,6 +285,15 @@
   }
 }
 
+/** Stub method that relays the checkForUpdates() signal from the General
+ * settings page to the owner of the config dialog (MainWindow). */
+void
+ConfigDialog::onCheckForUpdates()
+{
+  emit checkForUpdates();
+}
+
+
 /** Called when a ConfigPage in the dialog requests help on a specific
  * <b>topic</b>. */
 void

Modified: vidalia/trunk/src/vidalia/config/configdialog.h
===================================================================
--- vidalia/trunk/src/vidalia/config/configdialog.h	2009-01-03 14:05:46 UTC (rev 3412)
+++ vidalia/trunk/src/vidalia/config/configdialog.h	2009-01-03 20:10:16 UTC (rev 3413)
@@ -53,6 +53,11 @@
   /** Shows the config dialog with focus set to the given page. */
   void showWindow(Page page = General);
 
+signals:
+  /** Emitted when the user clicks "Check Now" to initiate a check
+   * for software updates. */
+  void checkForUpdates();
+
 protected:
   /** Called when the user changes the UI translation. */
   virtual void retranslateUi();
@@ -74,6 +79,9 @@
   /** Shows general help information for whichever settings page the user is
    * currently viewing. */
   void help();
+  /** Stub method that relays the checkForUpdates() signal from the General
+   * settings page to the owner of the config dialog (MainWindow). */
+  void onCheckForUpdates();
 
 private:
   /** Loads the current configuration settings */

Modified: vidalia/trunk/src/vidalia/config/generalpage.cpp
===================================================================
--- vidalia/trunk/src/vidalia/config/generalpage.cpp	2009-01-03 14:05:46 UTC (rev 3412)
+++ vidalia/trunk/src/vidalia/config/generalpage.cpp	2009-01-03 20:10:16 UTC (rev 3413)
@@ -16,6 +16,7 @@
 
 #include <QDateTime>
 #include <stringutil.h>
+#include <config.h>
 #include "generalpage.h"
 
 
@@ -35,12 +36,16 @@
           this, SLOT(browseTorExecutable()));
   connect(ui.btnBrowseProxyExecutable, SIGNAL(clicked()), 
           this, SLOT(browseProxyExecutable()));
+  connect(ui.btnUpdateNow, SIGNAL(clicked()), this, SLOT(updateNow()));
 
 #if !defined(Q_OS_WIN32)
   /* Hide platform specific features */
   ui.chkRunVidaliaAtSystemStartup->setVisible(false);
   ui.lineHorizontalSeparator->setVisible(false);
 #endif
+#if !defined(USE_AUTOUPDATE)
+  ui.grpSoftwareUpdates->setVisible(false);
+#endif
 }
 
 /** Destructor */
@@ -141,3 +146,9 @@
   ui.chkRunProxyAtTorStartup->setChecked(_vidaliaSettings->runProxyAtStart());
 }
 
+void
+GeneralPage::updateNow()
+{
+  emit checkForUpdates();
+}
+

Modified: vidalia/trunk/src/vidalia/config/generalpage.h
===================================================================
--- vidalia/trunk/src/vidalia/config/generalpage.h	2009-01-03 14:05:46 UTC (rev 3412)
+++ vidalia/trunk/src/vidalia/config/generalpage.h	2009-01-03 20:10:16 UTC (rev 3413)
@@ -40,11 +40,17 @@
   /** Called when the user changes the UI translation. */
   virtual void retranslateUi();
 
+signals:
+  /** Emitted when the user clicks the "Check Now" button. */
+  void checkForUpdates();
+
 private slots:
   /** Open a QFileDialog to browse for a Tor executable file. */
   void browseTorExecutable();
   /** Open a QFileDialog to browse for a proxy executable file. */
   void browseProxyExecutable();
+  /** Initiate an immediate check for software updates. */
+  void updateNow();
 
 private:
   /** Displays a file dialog allowing the user to browse for an executable

Modified: vidalia/trunk/src/vidalia/config/generalpage.ui
===================================================================
--- vidalia/trunk/src/vidalia/config/generalpage.ui	2009-01-03 14:05:46 UTC (rev 3412)
+++ vidalia/trunk/src/vidalia/config/generalpage.ui	2009-01-03 20:10:16 UTC (rev 3413)
@@ -149,6 +149,55 @@
     </widget>
    </item>
    <item>
+    <widget class="QGroupBox" name="grpSoftwareUpdates" >
+     <property name="title" >
+      <string>Software Updates</string>
+     </property>
+     <layout class="QHBoxLayout" name="horizontalLayout" >
+      <item>
+       <widget class="QCheckBox" name="chkAutoUpdates" >
+        <property name="text" >
+         <string>Check for new software updates automatically</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <spacer name="horizontalSpacer_2" >
+        <property name="orientation" >
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="sizeHint" stdset="0" >
+         <size>
+          <width>40</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+      <item>
+       <spacer name="horizontalSpacer" >
+        <property name="orientation" >
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="sizeHint" stdset="0" >
+         <size>
+          <width>40</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+      <item>
+       <widget class="QPushButton" name="btnUpdateNow" >
+        <property name="text" >
+         <string>Check Now</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
     <spacer name="verticalSpacer" >
      <property name="orientation" >
       <enum>Qt::Vertical</enum>

Modified: vidalia/trunk/src/vidalia/config/vidaliasettings.cpp
===================================================================
--- vidalia/trunk/src/vidalia/config/vidaliasettings.cpp	2009-01-03 14:05:46 UTC (rev 3412)
+++ vidalia/trunk/src/vidalia/config/vidaliasettings.cpp	2009-01-03 20:10:16 UTC (rev 3413)
@@ -254,3 +254,27 @@
   setValue(SETTING_PROXY_EXECUTABLE_ARGUMENTS, proxyExecutableArguments);
 }
 
+bool
+VidaliaSettings::isAutoUpdateEnabled() const
+{
+  return value(SETTING_CHECK_FOR_UPDATES).toBool();
+}
+
+void
+VidaliaSettings::setAutoUpdateEnabled(bool enabled)
+{
+  setValue(SETTING_CHECK_FOR_UPDATES, enabled);
+}
+
+QDateTime
+VidaliaSettings::lastCheckedForUpdates() const
+{
+  return value(SETTING_LAST_UPDATE_CHECK).toDateTime();
+}
+
+void
+VidaliaSettings::setLastCheckedForUpdates(const QDateTime &checkedAt)
+{
+  setValue(SETTING_LAST_UPDATE_CHECK, checkedAt);
+}
+

Modified: vidalia/trunk/src/vidalia/config/vidaliasettings.h
===================================================================
--- vidalia/trunk/src/vidalia/config/vidaliasettings.h	2009-01-03 14:05:46 UTC (rev 3412)
+++ vidalia/trunk/src/vidalia/config/vidaliasettings.h	2009-01-03 20:10:16 UTC (rev 3413)
@@ -17,6 +17,7 @@
 #ifndef _VIDALIASETTINGS_H
 #define _VIDALIASETTINGS_H
 
+#include <QDateTime>
 #include "vsettings.h"
 
 
@@ -93,6 +94,19 @@
   QStringList getProxyExecutableArguments() const;
   /** Sets the additional arguments to be passed to Proxy Executable */
   void setProxyExecutableArguments(const QStringList &proxyExecutableArguments);
+  
+  /** Returns true if Vidalia should automatically check for software updates.
+   */
+  bool isAutoUpdateEnabled() const;
+  /** Sets to <b>enabled</b> whether Vidalia should automatically check for
+   * software updates or not. */
+  void setAutoUpdateEnabled(bool enabled);
+
+  /** Returns the time at which Vidalia last checked for software updates. */
+  QDateTime lastCheckedForUpdates() const;
+  /** Sets to <b>checkedAt</b> the time at which Vidalia last checked for
+   * available software updates. */
+  void setLastCheckedForUpdates(const QDateTime &checkedAt);
 };
 
 #endif

Modified: vidalia/trunk/src/vidalia/mainwindow.cpp
===================================================================
--- vidalia/trunk/src/vidalia/mainwindow.cpp	2009-01-03 14:05:46 UTC (rev 3412)
+++ vidalia/trunk/src/vidalia/mainwindow.cpp	2009-01-03 20:10:16 UTC (rev 3413)
@@ -34,6 +34,10 @@
 #include "mainwindow.h"
 #include "controlpasswordinputdialog.h"
 
+#ifdef USE_AUTOUPDATE
+#include "updatesavailabledialog.h"
+#endif
+
 #define IMG_BWGRAPH        ":/images/16x16/utilities-system-monitor.png"
 #define IMG_CONTROL_PANEL  ":/images/16x16/system-run.png"
 #define IMG_MESSAGELOG     ":/images/16x16/format-justify-fill.png"
@@ -120,6 +124,7 @@
   createTrayIcon();
   /* Start with Tor initially stopped */
   _status = Unset;
+  _isVidaliaRunningTor = false;
   updateTorStatus(Stopped);
   
   /* Create a new TorControl object, used to communicate with Tor */
@@ -163,6 +168,28 @@
   connect(vApp, SIGNAL(running()), this, SLOT(running()));
   connect(vApp, SIGNAL(shutdown()), this, SLOT(shutdown()));
 
+#if defined(USE_AUTOUPDATE)
+  /* Create a timer used to remind us to check for software updates */
+  connect(&_updateTimer, SIGNAL(timeout()), this, SLOT(checkForUpdates()));
+
+  /* Also check for updates in the foreground when the user clicks the
+   * "Check Now" button in the config dialog. */
+  connect(_configDialog, SIGNAL(checkForUpdates()),
+          this, SLOT(checkForUpdatesWithUI()));
+
+  /* The rest of these slots are called as the update process executes. */
+  connect(&_updateProcess, SIGNAL(downloadProgress(QString,int,int)),
+          &_updateProgressDialog, SLOT(setDownloadProgress(QString,int,int)));
+  connect(&_updateProcess, SIGNAL(updatesAvailable(UpdateProcess::BundleInfo,PackageList)),
+          this, SLOT(updatesAvailable(UpdateProcess::BundleInfo,PackageList)));
+  connect(&_updateProcess, SIGNAL(updatesInstalled(int)),
+          this, SLOT(updatesInstalled(int)));
+  connect(&_updateProcess, SIGNAL(installUpdatesFailed(QString)),
+          this, SLOT(installUpdatesFailed(QString)));
+  connect(&_updateProgressDialog, SIGNAL(cancelUpdate()),
+          &_updateProcess, SLOT(cancel()));
+#endif
+
 #if defined(USE_MINIUPNPC)
   /* Catch UPnP-related signals */
   connect(UPNPControl::instance(), SIGNAL(error(UPNPControl::UPNPError)),
@@ -280,6 +307,34 @@
   /* Start the proxy server, if configured */
   if (settings.runProxyAtStart())
     startProxy();
+
+#if defined(USE_AUTOUPDATE)
+  if (settings.isAutoUpdateEnabled()) {
+    QDateTime lastCheckedAt = settings.lastCheckedForUpdates();
+    if (UpdateProcess::shouldCheckForUpdates(lastCheckedAt)) {
+      if (settings.runTorAtStart() && ! _torControl->circuitEstablished()) {
+        /* We started Tor but it hasn't bootstrapped yet, so give it a bit
+         * before we decide to check for updates. If Tor manages to build a
+         * circuit before this timer times out, we will stop the timer and
+         * launch a check for updates immediately. (see circuitEstablished()).
+         */
+        _updateTimer.start(5*60*1000);
+      } else {
+        /* Initiate a background check for updates now */
+        checkForUpdates();
+      }
+    } else {
+      /* Schedule the next time to check for updates */
+      QDateTime nextCheckAt = UpdateProcess::nextCheckForUpdates(lastCheckedAt);
+      QDateTime now = QDateTime::currentDateTime().toUTC();
+
+      vInfo("Last checked for software updates at %1. Will check again at %2.")
+        .arg(lastCheckedAt.toLocalTime().toString("dd-MM-yyyy hh:mm:ss"))
+        .arg(nextCheckAt.toLocalTime().toString("dd-MM-yyyy hh:mm:ss"));
+      _updateTimer.start((nextCheckAt.toTime_t() - now.toTime_t()) * 1000);
+    }
+  }
+#endif
 }
 
 /** Terminate the Tor process if it is being run under Vidalia, disconnect all
@@ -1307,6 +1362,18 @@
   setStartupProgress(ui.progressBar->maximum(),
                      tr("Connected to the Tor network!"));
   startSubprocesses();
+
+#if defined(USE_AUTOUPDATE)
+  VidaliaSettings settings;
+  if (settings.isAutoUpdateEnabled()) {
+    QDateTime lastCheckedAt = settings.lastCheckedForUpdates();
+    if (UpdateProcess::shouldCheckForUpdates(lastCheckedAt)) {
+      /* Initiate a background check for updates now */
+      _updateTimer.stop();
+      checkForUpdates();
+    }
+  }
+#endif
 }
 
 /** Checks the status of the current version of Tor to see if it's old,
@@ -1332,17 +1399,30 @@
   static bool alreadyWarned = false;
 
   if (!alreadyWarned) {
+#if !defined(USE_AUTOUPDATE)
     QString website = "https://www.torproject.org/";;
-#if QT_VERSION >= 0x040200
+# if QT_VERSION >= 0x040200
     website = QString("<a href=\"%1\">%1</a>").arg(website);
-#endif
+# endif
 
-    VMessageBox::information(this,
-      tr("Tor Update Available"),
+    VMessageBox::information(this, tr("Tor Update Available"),
       p(tr("The currently installed version of Tor is out of date or no longer "
            "recommended. Please visit the Tor website to download the latest "
            "version.")) + p(tr("Tor website: %1").arg(website)),
       VMessageBox::Ok);
+#else
+    int ret = VMessageBox::information(this,
+                tr("Tor Update Available"),
+                p(tr("The currently installed version of Tor is out of date "
+                     "or no longer recommended."))
+                  + p(tr("Would you like to check if a newer package is "
+                         "available for installation?")),
+                VMessageBox::Yes|VMessageBox::Default,
+                VMessageBox::No|VMessageBox::Escape);
+
+    if (ret == VMessageBox::Yes)
+      checkForUpdatesWithUI();
+#endif
     alreadyWarned = true;
   }
 }
@@ -1474,3 +1554,141 @@
 }
 #endif
 
+#if defined(USE_AUTOUPDATE)
+/** Called when the user clicks the 'Check Now' button in the General
+ * settings page. */
+void
+MainWindow::checkForUpdatesWithUI()
+{
+  checkForUpdates(true);
+}
+
+/** Called when the update interval timer expires, notifying Vidalia that
+ * we should check for updates again. */
+void
+MainWindow::checkForUpdates(bool showProgress)
+{
+  VidaliaSettings settings;
+
+  if (_updateProcess.isRunning()) {
+    if (showProgress) {
+      /* A check for updates is already in progress, so just bring the update
+       * progress dialog into focus.
+       */
+      _updateProgressDialog.show();
+    }
+  } else {
+    /* If Tor is running and bootstrapped, then use Tor to check for updates */
+    if (_torControl->isRunning() && _torControl->circuitEstablished())
+      _updateProcess.setSocksPort(_torControl->getSocksPort());
+    else
+      _updateProcess.setSocksPort(0);
+
+    /* Initialize the UpdateProgressDialog and display it, if necessary. */
+    _updateProgressDialog.setStatus(UpdateProgressDialog::CheckingForUpdates);
+    if (showProgress)
+      _updateProgressDialog.show();
+
+    /* Initiate a check for available software updates. This check will
+     * be done in the background, notifying the user only if there are
+     * updates to be installed.
+     */
+    _updateProcess.checkForUpdates(UpdateProcess::TorBundleInfo);
+
+    /* Remember when we last checked for software updates */
+    settings.setLastCheckedForUpdates(QDateTime::currentDateTime().toUTC());
+
+    /* Restart the "Check for Updates" timer */
+    _updateTimer.start(UpdateProcess::checkForUpdatesInterval() * 1000);
+  }
+}
+
+/** Called when the check for software updates fails. */
+void
+MainWindow::checkForUpdatesFailed(const QString &errmsg)
+{
+  if (_updateProgressDialog.isVisible()) {
+    _updateProgressDialog.hide();
+    VMessageBox::warning(this, tr("Update Failed"), errmsg,
+                         VMessageBox::Ok);
+  }
+}
+
+/** Called when there is an update available for installation. */
+void
+MainWindow::updatesAvailable(UpdateProcess::BundleInfo bi,
+                             const PackageList &packageList)
+{
+  vInfo("%1 software update(s) available").arg(packageList.size());
+  if (packageList.size() > 0) {
+    UpdatesAvailableDialog dlg(packageList, &_updateProgressDialog);
+
+    switch (dlg.exec()) {
+      case UpdatesAvailableDialog::InstallUpdatesNow:
+        installUpdates(bi);
+        break;
+
+      default:
+        _updateProgressDialog.hide();
+        break;
+    }
+  } else {
+    if (_updateProgressDialog.isVisible()) {
+      _updateProgressDialog.hide();
+      VMessageBox::information(this, tr("No Updates Available"),
+                               tr("You are currently running the most recent "
+                                  "Tor software available."),
+                               VMessageBox::Ok);
+    }
+  }
+}
+
+/** Stops Tor (if necessary), installs any available for <b>bi</b>, and
+ * restarts Tor (if necessary). */
+void
+MainWindow::installUpdates(UpdateProcess::BundleInfo bi)
+{
+  _updateProgressDialog.setStatus(UpdateProgressDialog::InstallingUpdates);
+  _updateProgressDialog.show();
+
+  if (_isVidaliaRunningTor) {
+    _restartTorAfterUpgrade = true;
+    _isIntentionalExit = true;
+    _torControl->stop();
+  } else {
+    _restartTorAfterUpgrade = false;
+  }
+  _updateProcess.installUpdates(bi);
+}
+
+/** Called when all <b>numUpdates</b> software updates have been installed
+ * successfully. */
+void
+MainWindow::updatesInstalled(int numUpdates)
+{
+  _updateProgressDialog.setStatus(UpdateProgressDialog::UpdatesInstalled);
+  _updateProgressDialog.show();
+
+  if (_restartTorAfterUpgrade)
+    start();
+}
+
+/** Called when an update fails to install. <b>errmsg</b> contains details
+ * about the failure. */
+void
+MainWindow::installUpdatesFailed(const QString &errmsg)
+{
+  _updateProgressDialog.hide();
+
+  VMessageBox::warning(this, tr("Installation Failed"),
+                       p(tr("Vidalia was unable to install your software updates."))
+                         + p(tr("The following error occurred:")) 
+                         + p(errmsg),
+                       VMessageBox::Ok);
+
+  if (_restartTorAfterUpgrade)
+    start();
+}
+
+#endif
+

Modified: vidalia/trunk/src/vidalia/mainwindow.h
===================================================================
--- vidalia/trunk/src/vidalia/mainwindow.h	2009-01-03 14:05:46 UTC (rev 3412)
+++ vidalia/trunk/src/vidalia/mainwindow.h	2009-01-03 20:10:16 UTC (rev 3413)
@@ -34,6 +34,10 @@
 #include "helperprocess.h"
 #include "config.h"
 
+#if defined(USE_AUTOUPDATE)
+#include "updateprocess.h"
+#include "updateprogressdialog.h"
+#endif
 #if defined(USE_MINIUPNPC)
 #include "config/upnpcontrol.h"
 #endif
@@ -114,6 +118,28 @@
   /** Called when the proxy server fails to start */
   void onProxyFailed(QString errmsg);
 
+#if defined(USE_AUTOUPDATE)
+  /** Called when the user clicks the 'Check Now' button in the General
+   * settings page. */
+  void checkForUpdatesWithUI();
+  /** Called when the update interval timer expires, notifying Vidalia that
+   * we should check for updates again. */
+  void checkForUpdates(bool showProgress = false);
+  /** Called when the check for software updates fails. */
+  void checkForUpdatesFailed(const QString &errmsg);
+  /** Called when there is an update available for installation. */
+  void updatesAvailable(UpdateProcess::BundleInfo bi, const PackageList &packageList);
+  /** Stops Tor (if necessary), installs any available for <b>bi</b>, and
+   * restarts Tor (if necessary). */
+  void installUpdates(UpdateProcess::BundleInfo bi);
+  /** Called when all <b>numUpdates</b> software updates have been installed
+   * successfully. */
+  void updatesInstalled(int numUpdates);
+  /** Called when an update fails to install. <b>errmsg</b> contains details
+   * about the failure. */
+  void installUpdatesFailed(const QString &errmsg);
+#endif
+
 #if defined(USE_MINIUPNPC)
   /** Called when a UPnP error occurs. */
   void upnpError(UPNPControl::UPNPError error);
@@ -206,6 +232,18 @@
   bool _useSavedPassword;
   /** The Vidalia icon that sits in the tray. */
   TrayIcon _trayIcon;
+
+#if defined(USE_AUTOUPDATE)
+  /** Timer used to remind us to check for software updates. */
+  QTimer _updateTimer;
+  /** The auto-update process used to check for and download updates. */
+  UpdateProcess _updateProcess;
+  /** Dialog instance that is be used to show the progress of the auto-update
+   * executable. */
+  UpdateProgressDialog _updateProgressDialog;
+  /** Set to true if Vidalia should restart Tor after a software upgrade. */
+  bool _restartTorAfterUpgrade;
+#endif
   /** The menubar (Mac OS X only). */
   QMenuBar *_menuBar;
 

Copied: vidalia/trunk/src/vidalia/packageinfo.cpp (from rev 3409, vidalia/branches/auto-updates/src/vidalia/packageinfo.cpp)
===================================================================
--- vidalia/trunk/src/vidalia/packageinfo.cpp	                        (rev 0)
+++ vidalia/trunk/src/vidalia/packageinfo.cpp	2009-01-03 20:10:16 UTC (rev 3413)
@@ -0,0 +1,92 @@
+/*
+**  This file is part of Vidalia, and is subject to the license terms in the
+**  LICENSE file, found in the top level directory of this distribution. If you
+**  did not receive the LICENSE file with this file, you may obtain it from the
+**  Vidalia source package distributed by the Vidalia Project at
+**  http://www.vidalia-project.net/. No part of Vidalia, including this file,
+**  may be copied, modified, propagated, or distributed except according to the
+**  terms described in the LICENSE file.
+*/
+
+/*
+** \file packageinfo.cpp
+** \version $Id$
+** \brief Contains information about a single available updated software
+** package.
+*/
+
+#include "packageinfo.h"
+
+
+PackageInfo::PackageInfo()
+{
+}
+
+bool
+PackageInfo::isValid() const
+{
+  return (! _name.isEmpty() && ! _version.isEmpty());
+}
+
+void
+PackageInfo::setName(const QString &name)
+{
+  _name = name;
+}
+
+QString
+PackageInfo::name() const
+{
+  return _name;
+}
+
+void
+PackageInfo::setVersion(const QString &version)
+{
+  _version = version;
+}
+
+QString
+PackageInfo::version() const
+{
+  return _version;
+}
+
+void
+PackageInfo::setLongDescription(const QString &lang, const QString &desc)
+{
+  _longDescription.insert(lang, desc);
+}
+
+QString
+PackageInfo::longDescription(const QString &lang) const
+{
+  return _longDescription.value(lang);
+}
+
+bool
+PackageInfo::hasLongDescription(const QString &lang) const
+{
+  return _longDescription.contains(lang);
+}
+
+void
+PackageInfo::setShortDescription(const QString &lang, const QString &desc)
+{
+  _shortDescription.insert(lang, desc);
+}
+
+
+QString
+PackageInfo::shortDescription(const QString &lang) const
+{
+  return _shortDescription.value(lang);
+}
+
+
+bool
+PackageInfo::hasShortDescription(const QString &lang) const
+{
+  return _shortDescription.contains(lang);
+}
+

Copied: vidalia/trunk/src/vidalia/packageinfo.h (from rev 3409, vidalia/branches/auto-updates/src/vidalia/packageinfo.h)
===================================================================
--- vidalia/trunk/src/vidalia/packageinfo.h	                        (rev 0)
+++ vidalia/trunk/src/vidalia/packageinfo.h	2009-01-03 20:10:16 UTC (rev 3413)
@@ -0,0 +1,97 @@
+/*
+**  This file is part of Vidalia, and is subject to the license terms in the
+**  LICENSE file, found in the top level directory of this distribution. If you
+**  did not receive the LICENSE file with this file, you may obtain it from the
+**  Vidalia source package distributed by the Vidalia Project at
+**  http://www.vidalia-project.net/. No part of Vidalia, including this file,
+**  may be copied, modified, propagated, or distributed except according to the
+**  terms described in the LICENSE file.
+*/
+
+/*
+** \file packageinfo.h
+** \version $Id$
+** \brief Contains information about a single available updated software
+** package.
+*/
+
+#ifndef _PACKAGEINFO_H
+#define _PACKAGEINFO_H
+
+#include <QHash>
+#include <QList>
+#include <QString>
+
+
+class PackageInfo
+{
+public:
+  /** Default constructor. */
+  PackageInfo();
+
+  /** Returns true if this PackageInfo object is valid. A valid PackageInfo
+   * object must have a name and a version number set. All other fields are
+   * optional.
+   */
+  bool isValid() const;
+
+  /** Sets the name of this software package to <b>name</b>.
+   */
+  void setName(const QString &name);
+
+  /** Returns the name of this software package.
+   */
+  QString name() const;
+
+  /** Sets the version of this software package to <b>version</b>.
+   */
+  void setVersion(const QString &version);
+
+  /** Returns the version of this software package.
+   */
+  QString version() const;
+
+  /** Sets the long description of this software package to <b>desc</b> for
+   * the language <b>lang</b>.
+   */
+  void setLongDescription(const QString &lang, const QString &desc);
+
+  /** Returns true if there is a long description for this software package
+   * currently set for language <b>lang</b>.
+   */
+  bool hasLongDescription(const QString &lang) const;
+
+  /** Returns long description of this software package for language
+   * <b>lang</b>. If a description is not currently set for the specified
+   * language, a null QString object is returned.
+   */
+  QString longDescription(const QString &lang) const;
+
+  /** Sets the short description of this software package to <b>desc</b> for
+   * the language <b>lang</b>.
+   */
+  void setShortDescription(const QString &lang, const QString &desc);
+  
+  /** Returns true if there is a short description of this software package
+   * currently set for language <b>lang</b>.
+   */
+  bool hasShortDescription(const QString &lang) const;
+
+  /** Returns the short description of this software package for language
+   * <b>lang</b>. If a description is not currently set for the specified
+   * language, a null QString object is returned.
+   */
+  QString shortDescription(const QString &lang) const;
+
+private:
+  QString _name;
+  QString _version;
+  QHash<QString,QString> _longDescription;
+  QHash<QString,QString> _shortDescription;
+};
+
+/** An unordered collection of PackageInfo objects. */
+typedef QList<PackageInfo> PackageList;
+
+#endif
+

Copied: vidalia/trunk/src/vidalia/res/32x32/system-software-update.png (from rev 3409, vidalia/branches/auto-updates/src/vidalia/res/32x32/system-software-update.png)
===================================================================
(Binary files differ)

Copied: vidalia/trunk/src/vidalia/res/48x48/system-software-update.png (from rev 3409, vidalia/branches/auto-updates/src/vidalia/res/48x48/system-software-update.png)
===================================================================
(Binary files differ)

Modified: vidalia/trunk/src/vidalia/res/vidalia.qrc
===================================================================
--- vidalia/trunk/src/vidalia/res/vidalia.qrc	2009-01-03 14:05:46 UTC (rev 3412)
+++ vidalia/trunk/src/vidalia/res/vidalia.qrc	2009-01-03 20:10:16 UTC (rev 3413)
@@ -72,6 +72,7 @@
         <file>32x32/status-orange.png</file>
         <file>32x32/status-red.png</file>
         <file>32x32/system-help.png</file>
+        <file>32x32/system-software-update.png</file>
         <file>32x32/utilities-log-viewer.png</file>
         <file>32x32/utilities-system-monitor.png</file>
         <file>32x32/view-refresh.png</file>
@@ -91,6 +92,7 @@
         <file>48x48/status-green.png</file>
         <file>48x48/status-orange.png</file>
         <file>48x48/status-red.png</file>
+        <file>48x48/system-software-update.png</file>
         <file>48x48/vidalia.png</file>
         <file>48x48/view-media-artist.png</file>
     </qresource>

Added: vidalia/trunk/src/vidalia/updateprocess.cpp
===================================================================
--- vidalia/trunk/src/vidalia/updateprocess.cpp	                        (rev 0)
+++ vidalia/trunk/src/vidalia/updateprocess.cpp	2009-01-03 20:10:16 UTC (rev 3413)
@@ -0,0 +1,352 @@
+/*
+**  This file is part of Vidalia, and is subject to the license terms in the
+**  LICENSE file, found in the top level directory of this distribution. If you
+**  did not receive the LICENSE file with this file, you may obtain it from the
+**  Vidalia source package distributed by the Vidalia Project at
+**  http://www.vidalia-project.net/. No part of Vidalia, including this file,
+**  may be copied, modified, propagated, or distributed except according to the
+**  terms described in the LICENSE file.
+*/
+
+#include <QDir>
+#include <QDomDocument>
+#include <QDomElement>
+#include <QDomNodeList>
+#include <vidalia.h>
+#include <stringutil.h>
+
+#include "updateprocess.h"
+
+
+UpdateProcess::UpdateProcess(QObject *parent)
+  : QProcess(parent)
+{
+  _currentCommand = NoCommand;
+  _socksPort = 0;
+
+  connect(this, SIGNAL(readyReadStandardError()),
+          this, SLOT(readStandardError()));
+  connect(this, SIGNAL(readyReadStandardOutput()),
+          this, SLOT(readStandardOutput()));
+  connect(this, SIGNAL(finished(int, QProcess::ExitStatus)),
+          this, SLOT(onFinished(int, QProcess::ExitStatus)));
+
+  setEnvironment(systemEnvironment());
+}
+
+void
+UpdateProcess::checkForUpdates(BundleInfo bi)
+{
+  QStringList args;
+
+  args << "update"  << "--controller-log-format"
+       << "--repo=" + updateRepositoryDir()
+       << "--debug";
+  if (_socksPort)
+    args << "--socks-port=" + QString::number(_socksPort);
+
+  args << bundleInfoToString(bi);
+
+  vNotice("updater: launching auto-update executable: %1 %2")
+                                           .arg(updateExecutable())
+                                           .arg(args.join(" "));
+  _currentBundle = bi;
+  _currentCommand = CheckForUpdates;
+  start(updateExecutable(), args);
+}
+
+void
+UpdateProcess::installUpdates(BundleInfo bi)
+{
+  QStringList args;
+
+  args << "update" << "--controller-log-format"
+       << "--repo=" + updateRepositoryDir()
+       << "--install";
+  if (_socksPort)
+    args << "--socks-port=" + QString::number(_socksPort);
+
+  args << bundleInfoToString(bi);
+
+  vNotice("updater: launching auto-update executable: %1 %2")
+                                           .arg(updateExecutable())
+                                           .arg(args.join(" "));
+  _currentBundle = bi;
+  _currentCommand = InstallUpdates;
+  start(updateExecutable(), args);
+}
+
+void
+UpdateProcess::setSocksPort(quint16 port)
+{
+  _socksPort = port;
+}
+
+bool
+UpdateProcess::isRunning() const
+{
+  return (state() != QProcess::NotRunning);
+}
+
+void
+UpdateProcess::cancel()
+{
+  if (_currentCommand == CheckForUpdates) {
+#if defined(Q_OS_WIN32)
+    kill();
+#else
+    terminate();
+#endif
+  }
+}
+
+void
+UpdateProcess::readStandardError()
+{
+  int idx;
+  bool ok;
+  QString line, type;
+  QHash<QString,QString> args;
+
+  setReadChannel(QProcess::StandardError);
+  while (canReadLine()) {
+    line = readLine().trimmed();
+    vInfo("updater (stderr): %1").arg(line);
+
+    idx = line.indexOf(" ");
+    if (idx < 0 || idx == line.length()-1)
+      continue;
+    type = line.mid(0, idx);
+    line = line.mid(idx + 1);
+
+    args = string_parse_keyvals(line, &ok);
+    if (! ok)
+      continue;
+    else if (line.startsWith("thandy.InstallFailed: ", Qt::CaseInsensitive)) {
+      /** XXX: This is a fucking kludge. If installation fails, Thandy just
+       *       dumps a Python traceback that (for obvious reasons) doesn't
+       *       follow the expected format. There isn't a defined control
+       *       message type for this yet we'd really like the error, so treat
+       *       this one specially.
+       */
+      emit installUpdatesFailed(line);
+      continue;
+    }
+
+    if (! type.compare("CAN_INSTALL", Qt::CaseInsensitive)) {
+      QString package = args.value("PKG");
+      if (! package.isEmpty()) {
+        PackageInfo pkgInfo = packageInfo(package);
+        if (pkgInfo.isValid())
+          _packageList << pkgInfo;
+      }
+    } else if (_currentCommand == CheckForUpdates
+                 && ! type.compare("DEBUG") 
+                 && args.value("msg").startsWith("Got ")) {
+      /* XXX: This is an even worse fucking kludge. Thandy only reports
+       *      download progress in a not-so-parser-friendly log message,
+       *      though, so we must kludge again.
+       *
+       *      Here's an example of what we're parsing:
+       *        "Got 1666048/1666560 bytes from http://updates.torproject.org/thandy/data/win32/tor-0.2.1.9-alpha.msi";
+       *
+       *      (Note that the kludge above would even match on "Got milk?".)
+       */
+      QStringList parts = args.value("msg").split(" ");
+      if (parts.size() == 5) {
+        QStringList progress = parts.at(1).split("/");
+        if (progress.size() == 2) {
+          int bytesReceived = progress.at(0).toUInt();
+          int bytesTotal = progress.at(1).toUInt();
+          vInfo("updater: Downloaded %1 of %2 bytes of file %3").arg(bytesReceived)
+                                                                .arg(bytesTotal)
+                                                                .arg(parts.at(4));
+          emit downloadProgress(parts.at(4), bytesReceived, bytesTotal);
+        }
+      }
+    }
+  }
+}
+
+void
+UpdateProcess::readStandardOutput()
+{
+  QString line;
+
+  setReadChannel(QProcess::StandardOutput);
+  while (canReadLine()) {
+    line = readLine().trimmed();
+    vInfo("updater (stdout): %1").arg(line);
+  }
+}
+
+void
+UpdateProcess::onFinished(int exitCode, QProcess::ExitStatus exitStatus)
+{
+  vInfo("updater: update process finished with exit code %1").arg(exitCode);
+
+  if (_currentCommand == CheckForUpdates) {
+    if (exitStatus == QProcess::NormalExit) {
+      emit updatesAvailable(_currentBundle, _packageList);
+    } else {
+      emit checkForUpdatesFailed(tr("Vidalia was unable to check for available "
+                                    "software updates because Tor's handy pocket "
+                                    "creature died. Sorry."));
+    }
+  } else if (_currentCommand == InstallUpdates) {
+    if (exitStatus == QProcess::NormalExit && exitCode == 0)
+      emit updatesInstalled(_packageList.size());
+  }
+  _packageList.clear();
+}
+
+void
+UpdateProcess::onError(QProcess::ProcessError error)
+{
+  if (error == QProcess::FailedToStart) {
+    vWarn("updater: failed to start");
+    emit checkForUpdatesFailed(tr("Vidalia was unable to check for available "
+                                  "software updates because it could not find "
+                                  "'%1'.").arg(updateExecutable()));
+  }
+}
+
+int
+UpdateProcess::checkForUpdatesInterval()
+{
+  /* XXX: Check twice a day. Why? Because arma said so. */
+  return 12*60*60;
+}
+
+QDateTime
+UpdateProcess::nextCheckForUpdates(const QDateTime &lastCheckedAt)
+{
+  return lastCheckedAt.addSecs(checkForUpdatesInterval()).toUTC();
+}
+
+bool
+UpdateProcess::shouldCheckForUpdates(const QDateTime &lastCheckedAt)
+{
+  QDateTime nextCheckAt = nextCheckForUpdates(lastCheckedAt);
+  return (QDateTime::currentDateTime().toUTC() >= nextCheckAt);
+}
+
+QString
+UpdateProcess::updateExecutable()
+{
+  return "thandy.exe";
+}
+
+QString
+UpdateProcess::updateRepositoryDir()
+{
+  return QDir::convertSeparators(Vidalia::dataDirectory() + "/updates");
+}
+
+QString
+UpdateProcess::bundleInfoToString(BundleInfo bi)
+{
+  switch (bi) {
+    case TorBundleInfo:
+      return "/bundleinfo/tor/win32/";
+    default:
+      return QString();
+  };
+}
+
+PackageInfo
+UpdateProcess::packageInfo(const QString &package)
+{
+  QProcess proc;
+  QStringList args;
+
+  args << "json2xml"
+       << QDir::convertSeparators(updateRepositoryDir() + "/" + package);
+
+  vNotice("updater: launching auto-update executable: %1 %2")
+                                           .arg(updateExecutable())
+                                           .arg(args.join(" "));
+
+  proc.setEnvironment(proc.systemEnvironment());
+  proc.start(updateExecutable(), args);
+  if (! proc.waitForStarted())
+    return PackageInfo();
+  if (! proc.waitForFinished())
+    return PackageInfo();
+  return packageInfoFromXml(proc.readAll());
+}
+
+PackageInfo
+UpdateProcess::packageInfoFromXml(const QByteArray &xml)
+{
+  QDomDocument doc;
+  QDomElement dict, elem;
+  QDomNodeList nodeList;
+  QString errmsg;
+  QStringList versionParts;
+  PackageInfo pkgInfo;
+
+  if (! doc.setContent(xml, false, &errmsg, 0, 0))
+    goto err;
+
+  /* XXX: Qt 4.4 introduced XPath support, which would make the following
+   * parsing much easier. Whenever we drop support for Qt < 4.4, this should
+   * be updated.
+   */
+  elem = doc.documentElement().firstChildElement("signed");
+  if (elem.isNull()) {
+    errmsg = "Signed element not found";
+    goto err;
+  }
+
+  dict = elem.firstChildElement("dict");
+  if (dict.isNull()) {
+    errmsg = "no Dict element as a child of Signed";
+    goto err;
+  }
+
+  elem = dict.firstChildElement("name");
+  if (elem.isNull()) {
+    errmsg = "Name element not found";
+    goto err;
+  }
+  pkgInfo.setName(elem.text());
+
+  elem = dict.firstChildElement("version").firstChildElement("list");
+  if (elem.isNull()) {
+    errmsg = "no valid Version element found";
+    goto err;
+  }
+  elem = elem.firstChildElement("item");
+  for ( ; ! elem.isNull(); elem = elem.nextSiblingElement("item")) {
+    versionParts << elem.text();
+  }
+  pkgInfo.setVersion(versionParts.join("."));
+
+  elem = dict.firstChildElement("shortdesc").firstChildElement("dict");
+  if (elem.isNull()) {
+    errmsg = "no valid Shortdesc element found";
+    goto err;
+  }
+  elem = elem.firstChildElement();
+  for ( ; ! elem.isNull(); elem = elem.nextSiblingElement()) {
+    pkgInfo.setShortDescription(elem.tagName(), elem.text());
+  }
+
+  elem = dict.firstChildElement("longdesc").firstChildElement("dict");
+  if (elem.isNull()) {
+    errmsg = "no valid Longdesc element found";
+    goto err;
+  }
+  elem = elem.firstChildElement();
+  for ( ; ! elem.isNull(); elem = elem.nextSiblingElement()) {
+    pkgInfo.setLongDescription(elem.tagName(), elem.text());
+  }
+
+  return pkgInfo;
+
+err:
+  vWarn("updater: invalid package info XML document: %1").arg(errmsg);
+  return PackageInfo();
+}
+

Added: vidalia/trunk/src/vidalia/updateprocess.h
===================================================================
--- vidalia/trunk/src/vidalia/updateprocess.h	                        (rev 0)
+++ vidalia/trunk/src/vidalia/updateprocess.h	2009-01-03 20:10:16 UTC (rev 3413)
@@ -0,0 +1,170 @@
+/*
+**  This file is part of Vidalia, and is subject to the license terms in the
+**  LICENSE file, found in the top level directory of this distribution. If you
+**  did not receive the LICENSE file with this file, you may obtain it from the
+**  Vidalia source package distributed by the Vidalia Project at
+**  http://www.vidalia-project.net/. No part of Vidalia, including this file,
+**  may be copied, modified, propagated, or distributed except according to the
+**  terms described in the LICENSE file.
+*/
+
+#ifndef _UPDATEPROCESS_H
+#define _UPDATEPROCESS_H
+
+#include <QProcess>
+#include <QDateTime>
+#include <QStringList>
+#include <QUrl>
+
+#include "packageinfo.h"
+
+
+class UpdateProcess : public QProcess
+{
+  Q_OBJECT
+
+public:
+  enum BundleInfo {
+    TorBundleInfo,
+  };
+
+  /** Default constructor.
+   */
+  UpdateProcess(QObject *parent = 0);
+
+  /** Begin a check for software updates that may be available for the
+   * software package specified by <b>bi</b>.
+   */
+  void checkForUpdates(BundleInfo bi);
+
+  /** Instructs the software update process to install previously downloaded
+   * files for <b>bi</b>.
+   */
+  void installUpdates(BundleInfo bi);
+
+  /** Returns true if the update process is currently in the middle of an
+   * operation, such as checking for or installing updates.
+   */
+  bool isRunning() const;
+
+  /** Sets the port to use as a SOCKS proxy to <b>port</b>. If <b>port</b> is
+   * set to 0, then no SOCKS proxy will be used when checking for updates.
+   */
+  void setSocksPort(quint16 port);
+
+  /** Return the time at which we should next check for available updates,
+   * given the last we checked was at <b>lastCheckedAt</b>.
+   */
+  static QDateTime nextCheckForUpdates(const QDateTime &lastCheckedAt);
+
+  /** Return true if we should check for available software udpates, given
+   * the last time we checked was at <b>lastCheckedAt</b>. The returned
+   * QDateTime will be in UTC.
+   */
+  static bool shouldCheckForUpdates(const QDateTime &lastCheckedAt);
+
+  /** Returns the preferred interval (in seconds) between executions of the
+   * Glider process to check for available software updates.
+   */
+  static int checkForUpdatesInterval();
+
+  /** Returns the path and filename of the software update executable.
+   */
+  static QString updateExecutable();
+
+  /** Returns the path in which the software update executable should write
+   * all of its state information.
+   */
+  static QString updateRepositoryDir();
+
+signals:
+  /** Emitted when the check for available software updates failed.
+   * <b>errmsg</b> contains a human-readable description of the problem
+   * encountered.
+   */
+  void checkForUpdatesFailed(QString errmsg);
+
+  /** Emitted while an updated package download is in progress. <b>url</b> is
+   * location of the update, <b>bytesReceived</b> is how many bytes have been
+   * downloaded so far and <b>bytesTotal</b> is the total size of the package
+   * being downloaded. */
+  void downloadProgress(QString url, int bytesReceived, int bytesTotal);
+
+  /** Emitted when updated software packages in bundle <b>bi</b> are
+   * are available. <b>packages</b> contains a collection of PackageInfo objects
+   * describing the updates available for installation.
+   */
+  void updatesAvailable(UpdateProcess::BundleInfo bi, PackageList packages);
+
+  /** Emitted after all available updated packages have been successfully
+   * installed.
+   */
+  void updatesInstalled(int nPackagesInstalled);
+
+  /** Emitted when there is an error installing one or more updated software
+   * packages. <b>errmsg</b> might even contain a useful description of the
+   * error encountered (but don't bet the farm on it).
+   */
+  void installUpdatesFailed(QString errmsg);
+
+public slots:
+    /** Cancels the currently running software update operation immediately. */
+  void cancel();
+
+protected slots:
+  /** Called when there is data to be read from the update process's stdout.
+   * Reads and parses all available data.
+   */
+  void readStandardOutput();
+
+  /** Called when there is data to be read from the update process's stderr.
+   * Reads and parses all available data.
+   */
+  void readStandardError();
+
+  /** Called when the underlying QProcess encounters an error.
+   */
+  void onError(QProcess::ProcessError error);
+
+  /** Called when the auto-update process has terminated.
+   */
+  void onFinished(int exitCode, QProcess::ExitStatus exitStatus);
+
+protected:
+  enum UpdateCommand {
+    NoCommand,
+    CheckForUpdates,
+    InstallUpdates,
+  };
+
+  /** Converts a BundleInfo enum value to its proper Thandy-recognized URL
+   * for the current OS and architecture. */
+  QString bundleInfoToString(BundleInfo bundleInfo);
+
+  /** Returns a PackageInfo object containing information about the updated
+   * package specified by the /pkginfo/ URL in <b>package</b>.
+   */
+  static PackageInfo packageInfo(const QString &package);
+
+  /** Returns a PackageInfo object populated with information extracted
+   * from a Thandy-formatted XML document given by <b>xml</b>.
+   */
+  static PackageInfo packageInfoFromXml(const QByteArray &xml);
+
+private:
+  /** Enum value of the current auto-update operation. */
+  UpdateCommand _currentCommand;
+
+  /** Enum value of the last bundle for which we performed some action
+   * (e.g., check for updates, install an update, etc. */
+  BundleInfo _currentBundle;
+
+  /** List of packages that have available updates. */
+  PackageList _packageList;
+
+  /** Currently configured SOCKS port. */
+  quint16 _socksPort;
+};
+
+#endif
+

Copied: vidalia/trunk/src/vidalia/updateprogressdialog.cpp (from rev 3409, vidalia/branches/auto-updates/src/vidalia/updateprogressdialog.cpp)
===================================================================
--- vidalia/trunk/src/vidalia/updateprogressdialog.cpp	                        (rev 0)
+++ vidalia/trunk/src/vidalia/updateprogressdialog.cpp	2009-01-03 20:10:16 UTC (rev 3413)
@@ -0,0 +1,92 @@
+/*
+**  This file is part of Vidalia, and is subject to the license terms in the
+**  LICENSE file, found in the top level directory of this distribution. If you
+**  did not receive the LICENSE file with this file, you may obtain it from the
+**  Vidalia source package distributed by the Vidalia Project at
+**  http://www.vidalia-project.net/. No part of Vidalia, including this file,
+**  may be copied, modified, propagated, or distributed except according to the
+**  terms described in the LICENSE file.
+*/
+
+#include "updateprogressdialog.h"
+
+
+UpdateProgressDialog::UpdateProgressDialog(QWidget *parent)
+  : QDialog(parent)
+{
+  ui.setupUi(this);
+
+  connect(ui.btnHide, SIGNAL(clicked()), this, SLOT(onHide()));
+  connect(ui.btnCancel, SIGNAL(clicked()), this, SLOT(onCancel()));
+
+  setModal(true);
+}
+
+void
+UpdateProgressDialog::setStatus(UpdateProgressDialog::Status status)
+{
+  switch (status) {
+    case CheckingForUpdates:
+      ui.lblCurrentAction->setText(tr("Checking for available updates..."));
+
+      ui.progressBar->setMinimum(0);
+      ui.progressBar->setMaximum(0);
+
+      ui.btnHide->setText(tr("Hide"));
+      ui.btnCancel->setVisible(true);
+      ui.btnCancel->setEnabled(true);
+      break;
+
+    case DownloadingUpdates:
+      ui.lblCurrentAction->setText(tr("Downloading updates..."));
+      break;
+
+    case InstallingUpdates:
+      ui.lblCurrentAction->setText(tr("Installing updated software..."));
+
+      ui.progressBar->setMinimum(0);
+      ui.progressBar->setMaximum(0);
+
+      ui.btnCancel->setEnabled(false);
+      break;
+
+    case UpdatesInstalled:
+      ui.lblCurrentAction->setText(tr("Done! Your software is now up to date."));
+
+      ui.progressBar->setMinimum(0);
+      ui.progressBar->setMaximum(1);
+      ui.progressBar->setValue(1);
+
+      ui.btnHide->setText(tr("OK"));
+      ui.btnCancel->setVisible(false);
+      break;
+
+    default:
+      break;
+  }
+}
+
+void
+UpdateProgressDialog::setDownloadProgress(const QString &url,
+                                          int bytesReceived, int bytesTotal)
+{
+  Q_UNUSED(url);
+
+  setStatus(DownloadingUpdates);
+  ui.progressBar->setMaximum(bytesTotal);
+  ui.progressBar->setValue(bytesReceived);
+}
+
+void
+UpdateProgressDialog::onHide()
+{
+  setVisible(false);
+}
+
+void
+UpdateProgressDialog::onCancel()
+{
+  emit cancelUpdate();
+  hide();
+}
+

Copied: vidalia/trunk/src/vidalia/updateprogressdialog.h (from rev 3409, vidalia/branches/auto-updates/src/vidalia/updateprogressdialog.h)
===================================================================
--- vidalia/trunk/src/vidalia/updateprogressdialog.h	                        (rev 0)
+++ vidalia/trunk/src/vidalia/updateprogressdialog.h	2009-01-03 20:10:16 UTC (rev 3413)
@@ -0,0 +1,70 @@
+/*
+**  This file is part of Vidalia, and is subject to the license terms in the
+**  LICENSE file, found in the top level directory of this distribution. If you
+**  did not receive the LICENSE file with this file, you may obtain it from the
+**  Vidalia source package distributed by the Vidalia Project at
+**  http://www.vidalia-project.net/. No part of Vidalia, including this file,
+**  may be copied, modified, propagated, or distributed except according to the
+**  terms described in the LICENSE file.
+*/
+
+#ifndef _UPDATEPROGRESSDIALOG_H
+#define _UPDATEPROGRESSDIALOG_H
+
+#include <QDialog>
+
+#include "ui_updateprogressdialog.h"
+
+
+class UpdateProgressDialog : public QDialog
+{
+  Q_OBJECT
+
+public:
+  enum Status {
+    CheckingForUpdates,
+    DownloadingUpdates,
+    InstallingUpdates,
+    UpdatesInstalled,
+  };
+
+  /** Default constructor.
+   */
+  UpdateProgressDialog(QWidget *parent = 0);
+
+  /** Updates the dialog's display to reflect the current action indicated
+   * by <b>status</b>.
+   */
+  void setStatus(UpdateProgressDialog::Status status);
+
+signals:
+  /** Emitted when the user clicks the "Cancel" button indicating they
+   * want to terminate the current check for available updates.
+   */
+  void cancelUpdate();
+
+public slots:
+  /** Called when more bytes of <b>url</b> have been received.
+   * <b>bytesReceived</b> indicates how many bytes have been downloaded so
+   * far and <b>bytesTotal</b> indicates the total size of the update to be
+   * downloaded.
+   */
+  void setDownloadProgress(const QString &url,
+                           int bytesReceived, int bytesTotal);
+
+private slots:
+  /** Called when the user clicks the "Cancel" button. Emits the
+   * cancelUpdate() signal.
+   */
+  void onHide();
+
+  /** Called when the user clicks the "Hide" button. Hides the dialog
+   * box.
+   */
+  void onCancel();
+
+private:
+  Ui::UpdateProgressDialog ui; /**< Qt Designer generated object. */
+};
+
+#endif

Copied: vidalia/trunk/src/vidalia/updateprogressdialog.ui (from rev 3409, vidalia/branches/auto-updates/src/vidalia/updateprogressdialog.ui)
===================================================================
--- vidalia/trunk/src/vidalia/updateprogressdialog.ui	                        (rev 0)
+++ vidalia/trunk/src/vidalia/updateprogressdialog.ui	2009-01-03 20:10:16 UTC (rev 3413)
@@ -0,0 +1,102 @@
+<ui version="4.0" >
+ <class>UpdateProgressDialog</class>
+ <widget class="QDialog" name="UpdateProgressDialog" >
+  <property name="geometry" >
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>366</width>
+    <height>104</height>
+   </rect>
+  </property>
+  <property name="windowTitle" >
+   <string>Software Updates</string>
+  </property>
+  <property name="windowIcon" >
+   <iconset resource="res/vidalia.qrc" >
+    <normaloff>:/images/32x32/system-software-update.png</normaloff>:/images/32x32/system-software-update.png</iconset>
+  </property>
+  <layout class="QGridLayout" name="gridLayout" >
+   <item rowspan="2" row="0" column="0" >
+    <widget class="QLabel" name="label" >
+     <property name="text" >
+      <string/>
+     </property>
+     <property name="pixmap" >
+      <pixmap resource="res/vidalia.qrc" >:/images/48x48/system-software-update.png</pixmap>
+     </property>
+     <property name="alignment" >
+      <set>Qt::AlignBottom|Qt::AlignHCenter</set>
+     </property>
+    </widget>
+   </item>
+   <item row="0" column="1" >
+    <widget class="QLabel" name="lblCurrentAction" >
+     <property name="text" >
+      <string>Checking for updates...</string>
+     </property>
+     <property name="alignment" >
+      <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
+     </property>
+     <property name="wordWrap" >
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="1" >
+    <widget class="QProgressBar" name="progressBar" >
+     <property name="maximum" >
+      <number>0</number>
+     </property>
+     <property name="value" >
+      <number>-1</number>
+     </property>
+     <property name="textVisible" >
+      <bool>false</bool>
+     </property>
+     <property name="format" >
+      <string/>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="0" colspan="2" >
+    <layout class="QHBoxLayout" name="horizontalLayout" >
+     <item>
+      <spacer name="horizontalSpacer" >
+       <property name="orientation" >
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0" >
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QPushButton" name="btnCancel" >
+       <property name="text" >
+        <string>Cancel</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="btnHide" >
+       <property name="text" >
+        <string>Hide</string>
+       </property>
+       <property name="default" >
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <resources>
+  <include location="res/vidalia.qrc" />
+ </resources>
+ <connections/>
+</ui>

Added: vidalia/trunk/src/vidalia/updatesavailabledialog.cpp
===================================================================
--- vidalia/trunk/src/vidalia/updatesavailabledialog.cpp	                        (rev 0)
+++ vidalia/trunk/src/vidalia/updatesavailabledialog.cpp	2009-01-03 20:10:16 UTC (rev 3413)
@@ -0,0 +1,100 @@
+/*
+**  This file is part of Vidalia, and is subject to the license terms in the
+**  LICENSE file, found in the top level directory of this distribution. If you
+**  did not receive the LICENSE file with this file, you may obtain it from the
+**  Vidalia source package distributed by the Vidalia Project at
+**  http://www.vidalia-project.net/. No part of Vidalia, including this file,
+**  may be copied, modified, propagated, or distributed except according to the
+**  terms described in the LICENSE file.
+*/
+
+/*
+** \file InstallUpdatesAvailableDialog.cpp
+** \version $Id$
+** \brief Displays a list of available updates and details, such as release
+** notes. The user can choose to either install the updates now or later, or
+** skip the updates entirely.
+*/
+
+#include <vidalia.h>
+#include <QHeaderView>
+
+#include "updatesavailabledialog.h"
+
+
+UpdatesAvailableDialog::UpdatesAvailableDialog(const PackageList &packageList,
+                                               QWidget *parent)
+  : QDialog(parent)
+{
+  ui.setupUi(this);
+
+  connect(ui.btnInstall, SIGNAL(clicked()),
+          this, SLOT(installUpdatesNow()));
+  connect(ui.btnInstallLater, SIGNAL(clicked()),
+          this, SLOT(installUpdatesLater()));
+
+  connect(ui.treeUpdates,
+          SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)),
+          this, SLOT(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)));
+
+  loadPackagesTable(packageList);
+}
+
+void
+UpdatesAvailableDialog::showEvent(QShowEvent *e)
+{
+  ui.treeUpdates->header()->resizeSection(0, 240);
+  ui.treeUpdates->header()->setResizeMode(1, QHeaderView::ResizeToContents);
+  QDialog::showEvent(e);
+}
+
+void
+UpdatesAvailableDialog::loadPackagesTable(const PackageList &packageList)
+{
+  int row = 0;
+  QString language;
+  QTreeWidgetItem *item;
+ 
+  language = Vidalia::language();
+
+  foreach (PackageInfo package, packageList) {
+    item = new QTreeWidgetItem(ui.treeUpdates);
+
+    if (package.hasShortDescription(language))
+      item->setText(0, package.shortDescription(language));
+    else
+      item->setText(0, package.shortDescription("en"));
+
+    if (package.hasLongDescription(language))
+      item->setData(0, Qt::UserRole, package.longDescription(language));
+    else
+      item->setData(0, Qt::UserRole, package.longDescription("en"));
+
+    item->setText(1, package.version());
+    ui.treeUpdates->insertTopLevelItem(row++, item);
+  }
+}
+
+void
+UpdatesAvailableDialog::currentItemChanged(QTreeWidgetItem *current,
+                                         QTreeWidgetItem *previous)
+{ 
+  Q_UNUSED(previous);
+
+  ui.textDetails->clear();
+  if (current)
+    ui.textDetails->setText(current->data(0, Qt::UserRole).toString());
+}
+
+void
+UpdatesAvailableDialog::installUpdatesNow()
+{
+  done(InstallUpdatesNow);
+}
+
+void
+UpdatesAvailableDialog::installUpdatesLater()
+{
+  done(InstallUpdatesLater);
+}
+


Property changes on: vidalia/trunk/src/vidalia/updatesavailabledialog.cpp
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: vidalia/trunk/src/vidalia/updatesavailabledialog.h
===================================================================
--- vidalia/trunk/src/vidalia/updatesavailabledialog.h	                        (rev 0)
+++ vidalia/trunk/src/vidalia/updatesavailabledialog.h	2009-01-03 20:10:16 UTC (rev 3413)
@@ -0,0 +1,76 @@
+/*
+**  This file is part of Vidalia, and is subject to the license terms in the
+**  LICENSE file, found in the top level directory of this distribution. If you
+**  did not receive the LICENSE file with this file, you may obtain it from the
+**  Vidalia source package distributed by the Vidalia Project at
+**  http://www.vidalia-project.net/. No part of Vidalia, including this file,
+**  may be copied, modified, propagated, or distributed except according to the
+**  terms described in the LICENSE file.
+*/
+
+/*
+** \file UpdatesAvailableDialog.h
+** \version $Id$
+** \brief Displays a list of available updates and details, such as release
+** notes. The user can choose to either install the updates now or later, or
+** skip the updates entirely.
+*/
+
+#ifndef _UpdatesAvailableDialog_H
+#define _UpdatesAvailableDialog_H
+
+#include <QDialog>
+#include <QShowEvent>
+#include <QTreeWidgetItem>
+
+#include "packageinfo.h"
+#include "ui_updatesavailabledialog.h"
+
+
+class UpdatesAvailableDialog : public QDialog
+{
+  Q_OBJECT
+
+public:
+  enum UpdatesAvailableDialogExitCode {
+    InstallUpdatesNow   = 100,
+    InstallUpdatesLater = 101,
+  };
+
+  /** Constructor. */
+  UpdatesAvailableDialog(const PackageList &packageList, QWidget *parent = 0);
+
+protected:
+  /** Called when the dialog receives a QShowEvent. This simply adjusts
+   * the column widths to something close to sane and forwards the event
+   * to the parent.
+   */
+  virtual void showEvent(QShowEvent *e);
+
+private slots:
+  /** Called when the user selects a different package in the list. The widget
+   * displaying details on the selected package will be updated.
+   */
+  void currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous);
+
+  /** Called when the user opts to install the displayed software updates
+   * immediately.
+   */
+  void installUpdatesNow();
+
+  /** Called when the user opts to install the display software updates at
+   * a later time.
+   */
+  void installUpdatesLater();
+
+private:
+  /** Populates the table of available updates with package information
+   * from <b>packageList</b>.
+   */
+  void loadPackagesTable(const PackageList &packageList);
+
+  Ui::UpdatesAvailableDialog ui; /**< Qt Designer generated object. */
+};
+
+#endif
+


Property changes on: vidalia/trunk/src/vidalia/updatesavailabledialog.h
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: vidalia/trunk/src/vidalia/updatesavailabledialog.ui
===================================================================
--- vidalia/trunk/src/vidalia/updatesavailabledialog.ui	                        (rev 0)
+++ vidalia/trunk/src/vidalia/updatesavailabledialog.ui	2009-01-03 20:10:16 UTC (rev 3413)
@@ -0,0 +1,96 @@
+<ui version="4.0" >
+ <class>UpdatesAvailableDialog</class>
+ <widget class="QDialog" name="UpdatesAvailableDialog" >
+  <property name="geometry" >
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>365</width>
+    <height>350</height>
+   </rect>
+  </property>
+  <property name="windowTitle" >
+   <string>Software Updates Available</string>
+  </property>
+  <property name="windowIcon" >
+   <iconset resource="res/vidalia.qrc" >
+    <normaloff>:/images/32x32/system-software-update.png</normaloff>:/images/32x32/system-software-update.png</iconset>
+  </property>
+  <layout class="QGridLayout" name="gridLayout" >
+   <item row="2" column="0" colspan="2" >
+    <layout class="QHBoxLayout" name="horizontalLayout" >
+     <item>
+      <spacer name="horizontalSpacer" >
+       <property name="orientation" >
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0" >
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QPushButton" name="btnInstallLater" >
+       <property name="text" >
+        <string>Remind Me Later</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="btnInstall" >
+       <property name="text" >
+        <string>Install</string>
+       </property>
+       <property name="default" >
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item row="0" column="0" colspan="2" >
+    <widget class="QLabel" name="lblUpdatesAvailable" >
+     <property name="text" >
+      <string>The following updated software packages are ready for installation:</string>
+     </property>
+     <property name="wordWrap" >
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="0" colspan="2" >
+    <widget class="QSplitter" name="splitter" >
+     <property name="orientation" >
+      <enum>Qt::Vertical</enum>
+     </property>
+     <widget class="QTreeWidget" name="treeUpdates" >
+      <property name="rootIsDecorated" >
+       <bool>false</bool>
+      </property>
+      <property name="itemsExpandable" >
+       <bool>false</bool>
+      </property>
+      <column>
+       <property name="text" >
+        <string>Package</string>
+       </property>
+      </column>
+      <column>
+       <property name="text" >
+        <string>Version</string>
+       </property>
+      </column>
+     </widget>
+     <widget class="QTextBrowser" name="textDetails" />
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources>
+  <include location="res/vidalia.qrc" />
+ </resources>
+ <connections/>
+</ui>