Added Qt-4 based update downloader client module.

git-svn-id: http://yate.null.ro/svn/yate/trunk@2377 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
paulc 2008-11-29 19:45:23 +00:00
parent 85c034adef
commit 1b6b361cb4
5 changed files with 842 additions and 0 deletions

View File

@ -65,6 +65,10 @@ ifneq (@HAVE_ALSA@,no)
PROGS := $(PROGS) client/alsachan.yate
endif
ifneq (@HAVE_QT4@,no)
PROGS := $(PROGS) qt4/updater.yate
endif
ifneq (@HAVE_ZAP@,no)
PROGS := $(PROGS) server/zapcard.yate
endif
@ -270,6 +274,10 @@ openssl.yate: LOCALLIBS = @OPENSSL_LIB@
rmanager.yate: LOCALFLAGS = $(COREDUMP_INC)
rmanager.yate: LOCALLIBS = $(COREDUMP_LIB)
qt4/updater.yate: qt4/updater.moc
qt4/updater.yate: LOCALFLAGS = @QT4_INC_NET@
qt4/updater.yate: LOCALLIBS = @QT4_LIB_NET@
../libyatesig.so ../libs/ysig/libyatesig.a:
$(MAKE) -C ../libs/ysig

456
modules/qt4/updater.cpp Normal file
View File

@ -0,0 +1,456 @@
/**
* updater.cpp
* This file is part of the YATE Project http://YATE.null.ro
*
* Auto updater logic and downloader for Qt-4 clients.
*
* Yet Another Telephony Engine - a fully featured software PBX and IVR
* Copyright (C) 2004-2006 Null Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "updater.h"
#include <unistd.h>
#include <stdio.h>
#include <QUrl>
#include <QFile>
#include <QFileInfo>
#include <QProcess>
#define MIN_SIZE 1024
#define MAX_SIZE (16*1024*1024)
#define TMP_EXT ".tmp"
#ifdef _WINDOWS
#define EXE_EXT ".exe"
#else
#define EXE_EXT ".bin"
#endif
using namespace TelEngine;
namespace { // anonymous
/**
* UI logic interaction
*/
class UpdateLogic : public ClientLogic
{
public:
enum Policy {
Invalid,
Never,
Check,
Download,
Install,
};
inline UpdateLogic(const char* name)
: ClientLogic(name,100),
m_policy(Invalid), m_checking(false),
m_checked(false), m_install(false),
m_http(0), m_file(0), m_httpSlots(0)
{ }
virtual ~UpdateLogic();
inline Policy policy() const
{ return static_cast<Policy>(m_policy); }
virtual bool initializedClient();
virtual void exitingClient();
virtual bool action(Window* wnd, const String& name, NamedList* params);
virtual bool toggle(Window* wnd, const String& name, bool active);
void gotPercentage(int percent);
void endHttp(bool error);
protected:
void setPolicy(int policy, bool save);
void startChecking(bool start = true);
void finishedChecking();
void startDownloading(bool start = true);
void finishedDownloading();
void startInstalling();
private:
QString filePath(bool temp);
bool startHttp(const char* url, const QString& saveAs);
void stopHttp();
void stopFile();
int m_policy;
bool m_checking;
bool m_checked;
bool m_install;
String m_url;
QHttp* m_http;
QFile* m_file;
QtUpdateHttp* m_httpSlots;
};
/**
* Plugin registration
*/
class Updater : public Plugin
{
public:
Updater();
virtual ~Updater();
virtual void initialize();
private:
UpdateLogic* m_logic;
};
static Updater s_plugin;
static const TokenDict s_policies[] = {
{ "never", UpdateLogic::Never },
{ "check", UpdateLogic::Check },
{ "download", UpdateLogic::Download },
{ "install", UpdateLogic::Install },
{ 0, UpdateLogic::Invalid }
};
UpdateLogic::~UpdateLogic()
{
}
bool UpdateLogic::initializedClient()
{
m_install = Client::s_settings.getBoolValue(toString(),"install") &&
QFile::exists(filePath(false));
Client::self()->setActive("upd_install",m_install);
setPolicy(Client::s_settings.getIntValue(toString(),"policy",s_policies,Never),false);
if (m_install && (m_policy >= Install))
startInstalling();
else if (m_policy >= Check)
startChecking();
return false;
}
void UpdateLogic::exitingClient()
{
startDownloading(false);
startChecking(false);
stopHttp();
delete m_httpSlots;
m_httpSlots = 0;
}
bool UpdateLogic::action(Window* wnd, const String& name, NamedList* params)
{
if (name == "upd_install")
startInstalling();
else if (name == "upd_check")
startChecking();
else if (name == "upd_download")
startDownloading();
else
return false;
return true;
}
bool UpdateLogic::toggle(Window* wnd, const String& name, bool active)
{
if (!name.startsWith("upd_"))
return false;
if (name == "upd_check")
startChecking(active);
else if (name == "upd_download")
startDownloading(active);
else if (name == "upd_automatic")
setPolicy(active ? Install : Never,true);
else if (active) {
String tmp = name;
if (tmp.startSkip("upd_policy_",false))
setPolicy(lookup(tmp,s_policies,Invalid),true);
}
return true;
}
void UpdateLogic::setPolicy(int policy, bool save)
{
if ((policy == Invalid) || (policy == m_policy))
return;
const char* pol = lookup(policy,s_policies);
if (!pol)
return;
m_policy = policy;
if (save) {
Client::s_settings.setValue(toString(),"policy",pol);
Client::save(Client::s_settings);
}
if (!Client::self())
return;
for (policy = Never; policy <= Install; policy++) {
String tmp = "upd_policy_";
tmp += lookup(policy,s_policies);
Client::self()->setCheck(tmp,(policy == m_policy));
}
Client::self()->setCheck("upd_automatic",(Install == m_policy));
}
void UpdateLogic::startChecking(bool start)
{
String url = Engine::config().getValue("client","updateurl");
Engine::runParams().replaceParams(url);
if (url.trimBlanks().null()) {
start = false;
if (Client::self()) {
Client::self()->setActive("upd_check",false);
Client::self()->setActive("upd_download",false);
Client::self()->setActive("upd_install",false);
}
}
if (start) {
Debug(toString(),DebugNote,"Checking new version: %s",url.c_str());
m_checked = false;
m_checking = true;
start = startHttp(url,"");
if (Client::self()) {
Client::self()->setActive("upd_download",false);
Client::self()->setSelect("upd_progress","0");
Client::self()->setText("upd_version","");
}
}
else
stopHttp();
if (Client::self())
Client::self()->setCheck("upd_check",start);
}
void UpdateLogic::startDownloading(bool start)
{
m_checking = false;
if (start && m_install) {
m_install = false;
Client::s_settings.setValue(toString(),"install",String::boolText(false));
Client::save(Client::s_settings);
}
if (start) {
Debug(toString(),DebugNote,"Downloading from: %s",m_url.c_str());
start = startHttp(m_url,filePath(true));
}
else {
stopHttp();
QFile::remove(filePath(true));
}
if (Client::self()) {
Client::self()->setActive("upd_check",!start);
Client::self()->setActive("upd_install",m_install);
Client::self()->setCheck("upd_download",start);
Client::self()->setSelect("upd_progress","0");
}
}
void UpdateLogic::startInstalling()
{
if (!QFile::exists(filePath(false)))
return;
QString cmd = Engine::config().getValue("client","updatecmd");
if (!cmd.isEmpty()) {
String tmp = cmd.toUtf8().constData();
NamedList params(Engine::runParams());
params.setParam("filename",filePath(false).toUtf8().constData());
params.replaceParams(tmp);
if (tmp.trimBlanks().null())
return;
cmd = QString::fromUtf8(tmp.c_str());
}
else
cmd = filePath(false);
if (QProcess::startDetached(cmd)) {
Debug(toString(),DebugNote,"Executing: %s",cmd.toUtf8().constData());
if (Client::self())
Client::self()->quit();
else
Engine::halt(0);
return;
}
Debug(toString(),DebugWarn,"Failed to execute: %s",cmd.toUtf8().constData());
}
void UpdateLogic::finishedChecking()
{
if (Client::self()) {
Client::self()->setCheck("upd_check",false);
Client::self()->setActive("upd_download",m_checked);
Client::self()->setSelect("upd_progress","0");
}
if (m_checked && (m_policy >= Download))
startDownloading();
}
void UpdateLogic::finishedDownloading()
{
if (Client::self()) {
Client::self()->setCheck("upd_download",false);
Client::self()->setActive("upd_check",true);
Client::self()->setActive("upd_install",m_install);
if (!m_install)
Client::self()->setSelect("upd_progress","0");
}
Client::s_settings.setValue(toString(),"install",String::boolText(m_install));
Client::save(Client::s_settings);
}
QString UpdateLogic::filePath(bool temp)
{
return QString::fromUtf8((Engine::configPath(true) + Engine::pathSeparator() + toString() +
(temp ? TMP_EXT : EXE_EXT)));
}
bool UpdateLogic::startHttp(const char* url, const QString& saveAs)
{
stopHttp();
QUrl qurl(QString::fromUtf8(url));
if (!qurl.isValid())
return false;
QFile* file = 0;
if (!saveAs.isEmpty()) {
QFile::remove(saveAs);
file = new QFile(saveAs);
if (!(file->open(QIODevice::WriteOnly) &&
file->setPermissions(QFile::ReadOwner|QFile::WriteOwner|QFile::ExeOwner))) {
file->remove();
delete file;
return false;
}
m_file = file;
}
if (!m_httpSlots)
m_httpSlots = new QtUpdateHttp(this);
m_http = m_httpSlots->http();
const char* proxy = Client::s_settings.getValue(toString(),"proxy_host");
if (proxy)
m_http->setProxy(proxy,
Client::s_settings.getIntValue(toString(),"proxy_port",8080),
Client::s_settings.getValue(toString(),"proxy_user"),
Client::s_settings.getValue(toString(),"proxy_pass"));
m_http->setHost(qurl.host(),qurl.port(80));
m_http->get(qurl.path(),file);
return true;
}
void UpdateLogic::stopHttp()
{
QHttp* http = m_http;
m_http = 0;
if (http) {
http->abort();
delete http;
}
stopFile();
}
void UpdateLogic::stopFile()
{
QFile* file = m_file;
m_file = 0;
delete file;
}
void UpdateLogic::gotPercentage(int percent)
{
if (!Client::self())
return;
Client::self()->setSelect("upd_progress",String(percent));
}
void UpdateLogic::endHttp(bool error)
{
stopFile();
if (!m_http)
return;
if (m_checking) {
if (!error) {
QByteArray data = m_http->readAll();
if (data.size() <= 1024) {
String str(data.constData());
// 1st row is the URL, everything else description
int nl = str.find('\n');
if (nl > 0) {
int len = (str.at(nl - 1) == '\r') ? (nl - 1) : nl;
URI url(str.substr(0,len));
url.trimBlanks();
if (url.getProtocol() == "http") {
m_checked = true;
m_url = url;
if (Client::self())
Client::self()->setText("upd_version",str.substr(nl+1));
}
}
}
}
finishedChecking();
}
else {
if (!error) {
QFileInfo info(filePath(true));
if ((info.size() >= MIN_SIZE) && (info.size() <= MAX_SIZE)) {
QFile::remove(filePath(false));
m_install = QFile::rename(filePath(true),filePath(false));
}
}
QFile::remove(filePath(true));
finishedDownloading();
}
}
QHttp* QtUpdateHttp::http()
{
QHttp* h = new QHttp(this);
connect(h,SIGNAL(dataReadProgress(int,int)),this,SLOT(dataProgress(int,int)));
connect(h,SIGNAL(done(bool)),this,SLOT(requestDone(bool)));
return h;
}
void QtUpdateHttp::dataProgress(int done, int total)
{
if (!m_logic)
return;
int percent = 0;
if (done)
percent = (done <= total) ? (done * 100 / total) : 50;
m_logic->gotPercentage(percent);
}
void QtUpdateHttp::requestDone(bool error)
{
if (m_logic)
m_logic->endHttp(error);
}
Updater::Updater()
: Plugin("updater",true), m_logic(0)
{
Output("Loaded module Updater");
}
Updater::~Updater()
{
Output("Unloading module Updater");
TelEngine::destruct(m_logic);
}
void Updater::initialize()
{
Output("Initializing module Updater");
if (m_logic)
return;
m_logic = new UpdateLogic("updater");
}
}; // anonymous namespace
#include "updater.moc"
/* vi: set ts=8 sw=4 sts=4 noet: */

75
modules/qt4/updater.h Normal file
View File

@ -0,0 +1,75 @@
/**
* updater.h
* This file is part of the YATE Project http://YATE.null.ro
*
* Auto updater logic and downloader for Qt-4 clients.
*
* Yet Another Telephony Engine - a fully featured software PBX and IVR
* Copyright (C) 2004-2006 Null Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __UPDATER_H
#define __UPDATER_H
#include <yatecbase.h>
#define QT_NO_DEBUG
#define QT_DLL
#define QT_GUI_LIB
#define QT_CORE_LIB
#define QT_THREAD_SUPPORT
#include <QObject>
#include <QHttp>
using namespace TelEngine;
namespace { // anonymous
class UpdateLogic;
/**
* Proxy object so HTTP notification slots are created in the GUI thread
*/
class QtUpdateHttp : public QObject
{
Q_CLASSINFO("QtUpdateHttp","Yate")
Q_OBJECT
public:
/**
* Constructor
* @param logic Qt update logic owning this object
*/
inline QtUpdateHttp(UpdateLogic* logic)
: m_logic(logic)
{ }
/**
* Create a QHttp object and attach its signals to this object
* @return New QHttp object attached to this object's slots
*/
QHttp* http();
private slots:
void dataProgress(int done, int total);
void requestDone(bool error);
private:
UpdateLogic* m_logic;
};
}; // anonymous namespace
#endif /* __UPDATER_H */
/* vi: set ts=8 sw=4 sts=4 noet: */

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -0,0 +1,303 @@
<ui version="4.0" >
<class>updater</class>
<widget class="QWidget" name="updater" >
<property name="windowModality" >
<enum>Qt::NonModal</enum>
</property>
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>380</width>
<height>310</height>
</rect>
</property>
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>0</hsizetype>
<vsizetype>0</vsizetype>
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize" >
<size>
<width>380</width>
<height>310</height>
</size>
</property>
<property name="maximumSize" >
<size>
<width>380</width>
<height>310</height>
</size>
</property>
<property name="windowTitle" >
<string>Application Update</string>
</property>
<property name="windowIcon" >
<iconset>update.png</iconset>
</property>
<layout class="QVBoxLayout" >
<property name="margin" >
<number>9</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<widget class="QGroupBox" name="policy" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>5</hsizetype>
<vsizetype>5</vsizetype>
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize" >
<size>
<width>0</width>
<height>112</height>
</size>
</property>
<property name="title" >
<string>Automatic update policy</string>
</property>
<layout class="QVBoxLayout" >
<property name="margin" >
<number>9</number>
</property>
<property name="spacing" >
<number>0</number>
</property>
<item>
<widget class="QRadioButton" name="upd_policy_never" >
<property name="text" >
<string>Do not update automatically</string>
</property>
<property name="checked" >
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="upd_policy_check" >
<property name="text" >
<string>Only check if a new version is available</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="upd_policy_download" >
<property name="text" >
<string>Download new version automatically</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="upd_policy_install" >
<property name="text" >
<string>Automatically install at next start</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QLabel" name="label_1" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>5</hsizetype>
<vsizetype>0</vsizetype>
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text" >
<string>Available update:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="upd_version" >
<property name="minimumSize" >
<size>
<width>0</width>
<height>51</height>
</size>
</property>
<property name="font" >
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text" >
<string/>
</property>
<property name="alignment" >
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>5</hsizetype>
<vsizetype>0</vsizetype>
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text" >
<string>Download status:</string>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="upd_progress" >
<property name="value" >
<number>0</number>
</property>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" >
<property name="margin" >
<number>6</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<widget class="QPushButton" name="upd_check" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>0</hsizetype>
<vsizetype>0</vsizetype>
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize" >
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="maximumSize" >
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="text" >
<string>Check now</string>
</property>
<property name="checkable" >
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" >
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="upd_download" >
<property name="enabled" >
<bool>false</bool>
</property>
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>0</hsizetype>
<vsizetype>0</vsizetype>
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize" >
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="maximumSize" >
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="text" >
<string>Download</string>
</property>
<property name="checkable" >
<bool>true</bool>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" >
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="upd_install" >
<property name="enabled" >
<bool>false</bool>
</property>
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>0</hsizetype>
<vsizetype>0</vsizetype>
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize" >
<size>
<width>100</width>
<height>0</height>
</size>
</property>
<property name="maximumSize" >
<size>
<width>100</width>
<height>16777215</height>
</size>
</property>
<property name="text" >
<string>Install</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>