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

[vidalia-svn] r3366: When a CAN_INSTALL thandy message is encountered, kick off a (vidalia/branches/auto-updates/src/vidalia)



Author: edmanm
Date: 2008-12-06 19:25:05 -0500 (Sat, 06 Dec 2008)
New Revision: 3366

Modified:
   vidalia/branches/auto-updates/src/vidalia/updateprocess.cpp
   vidalia/branches/auto-updates/src/vidalia/updateprocess.h
Log:
When a CAN_INSTALL thandy message is encountered, kick off another thandy
process to run json2xml, then parse the output to construct and return a
PackageInfo object. Collect all constructed PackageInfo objects until thandy
is done running, and then emit a signal containing a list of available updates
and their corresponding PackageInfo objects.


Modified: vidalia/branches/auto-updates/src/vidalia/updateprocess.cpp
===================================================================
--- vidalia/branches/auto-updates/src/vidalia/updateprocess.cpp	2008-12-07 00:20:07 UTC (rev 3365)
+++ vidalia/branches/auto-updates/src/vidalia/updateprocess.cpp	2008-12-07 00:25:05 UTC (rev 3366)
@@ -8,7 +8,10 @@
 **  terms described in the LICENSE file.
 */
 
-#include <QtDebug>
+#include <QDir>
+#include <QDomDocument>
+#include <QDomElement>
+#include <QDomNodeList>
 #include <vidalia.h>
 #include <stringutil.h>
 
@@ -18,10 +21,13 @@
 UpdateProcess::UpdateProcess(QObject *parent)
   : QProcess(parent)
 {
+  _currentCommand = NoCommand;
   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)));
 }
 
 void
@@ -38,6 +44,7 @@
                                            .arg(updateExecutable())
                                            .arg(args.join(" "));
   _currentBundle = bi;
+  _currentCommand = CheckForUpdates;
   start(updateExecutable(), args);
 }
 
@@ -55,6 +62,7 @@
                                            .arg(updateExecutable())
                                            .arg(args.join(" "));
   _currentBundle = bi;
+  _currentCommand = InstallUpdates;
   start(updateExecutable(), args);
 }
 
@@ -71,10 +79,6 @@
     line = readLine().trimmed();
     vInfo("updater (stderr): %1").arg(line);
 
-    /* Skip the first two fields. We can't just use split() or section() to
-     * get the message we're interested in though, since the delimiters for the
-     * first two fields can be contained (and are unescaped) in the third field.
-     */
     idx = line.indexOf(" ");
     if (idx < 0 || idx == line.length()-1)
       continue;
@@ -85,9 +89,14 @@
     if (! ok)
       continue;
 
-    /* All we can do is match on unspecified, free-form log message text. */
-    if (! type.compare("CAN_INSTALL", Qt::CaseInsensitive))
-      emit updatesAvailable(_currentBundle);
+    if (! type.compare("CAN_INSTALL", Qt::CaseInsensitive)) {
+      QString package = args.value("PKG");
+      if (! package.isEmpty()) {
+        PackageInfo pkgInfo = packageInfo(package);
+        if (pkgInfo.isValid())
+          _packageList << pkgInfo;
+      }
+    }
   }
 }
 
@@ -104,6 +113,22 @@
 }
 
 void
+UpdateProcess::onFinished(int exitCode, QProcess::ExitStatus exitStatus)
+{
+  Q_UNUSED(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."));
+    }
+    _packageList.clear();
+  }
+}
+
+void
 UpdateProcess::onError(QProcess::ProcessError error)
 {
   if (error == QProcess::FailedToStart) {
@@ -145,7 +170,7 @@
 QString
 UpdateProcess::updateRepositoryDir()
 {
-  return Vidalia::dataDirectory() + "/updates";
+  return QDir::convertSeparators(Vidalia::dataDirectory() + "/updates");
 }
 
 QString
@@ -159,3 +184,98 @@
   };
 }
 
+PackageInfo
+UpdateProcess::packageInfo(const QString &package)
+{
+  QProcess proc;
+  QStringList args;
+
+  args << "E:\\thandy\\lib\\thandy\\ClientCLI.py"
+       << "json2xml" 
+       << QDir::convertSeparators(updateRepositoryDir() + "/" + package);
+  
+  vNotice("updater: launching auto-update executable: %1 %2")
+                                           .arg(updateExecutable())
+                                           .arg(args.join(" "));
+  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();
+}
+

Modified: vidalia/branches/auto-updates/src/vidalia/updateprocess.h
===================================================================
--- vidalia/branches/auto-updates/src/vidalia/updateprocess.h	2008-12-07 00:20:07 UTC (rev 3365)
+++ vidalia/branches/auto-updates/src/vidalia/updateprocess.h	2008-12-07 00:25:05 UTC (rev 3366)
@@ -15,7 +15,9 @@
 #include <QDateTime>
 #include <QStringList>
 
+#include "packageinfo.h"
 
+
 class UpdateProcess : public QProcess
 {
   Q_OBJECT
@@ -65,13 +67,18 @@
   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);
 
-  void downloadProgressChanged(QString filename,
-                               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);
 
-  void updatesAvailable(UpdateProcess::BundleInfo bi);
-
 protected slots:
   /** Called when there is data to be read from the update process's stdout.
    * Reads and parses all available data.
@@ -83,18 +90,45 @@
    */
   void readStandardError();
 
-  /** Called when the underlying QProcess encounters an error. */
+  /** 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 Tandy-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;
 };
 
 #endif