[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