Move SSH functionality out of the Qt4ProjectManager.

It does not conceptually belong there, and several people have
asked for an interface to use the functionality in their own
plugins.

Task-number: QTCREATORBUG-1204
Reviewed-by: kh1
This commit is contained in:
ck
2010-04-26 11:43:25 +02:00
parent 71b9057b0b
commit 9ece8a4110
28 changed files with 960 additions and 620 deletions

2
src/libs/3rdparty/botan/botan.pri vendored Normal file
View File

@@ -0,0 +1,2 @@
INCLUDEPATH += $$PWD/build
LIBS *= -l$$qtLibraryTarget(Botan)

3
src/libs/3rdparty/net7ssh/net7ssh.pri vendored Normal file
View File

@@ -0,0 +1,3 @@
include(net7ssh_dependencies.pri)
INCLUDEPATH += $$PWD/src
LIBS *= -l$$qtLibraryTarget(Net7ssh)

View File

@@ -0,0 +1 @@
include(../botan/botan.pri)

View File

@@ -8,7 +8,7 @@ include(../../../../qtcreatorlibrary.pri)
DEPENDPATH += . DEPENDPATH += .
INCLUDEPATH += $$PWD $$PWD/../../botan $$PWD/../../botan/build INCLUDEPATH += $$PWD $$PWD/../../botan $$PWD/../../botan/build
LIBS += -l$$qtLibraryTarget(Botan) include(../net7ssh_dependencies.pri)
win32 { win32 {
LIBS += -lWs2_32 LIBS += -lWs2_32

View File

@@ -34,6 +34,7 @@
#include "modemanager.h" #include "modemanager.h"
#include "fileiconprovider.h" #include "fileiconprovider.h"
#include "designmode.h" #include "designmode.h"
#include "ssh/ne7sshobject.h"
#include <extensionsystem/pluginmanager.h> #include <extensionsystem/pluginmanager.h>
@@ -88,6 +89,7 @@ bool CorePlugin::initialize(const QStringList &arguments, QString *errorMessage)
m_designMode = new DesignMode(editorManager); m_designMode = new DesignMode(editorManager);
addObject(m_designMode); addObject(m_designMode);
Ne7SshObject::instance();
} }
return success; return success;
} }
@@ -111,6 +113,7 @@ void CorePlugin::fileOpenRequest(const QString &f)
void CorePlugin::shutdown() void CorePlugin::shutdown()
{ {
m_mainWindow->shutdown(); m_mainWindow->shutdown();
Ne7SshObject::removeInstance();
} }
Q_EXPORT_PLUGIN(CorePlugin) Q_EXPORT_PLUGIN(CorePlugin)

View File

@@ -83,7 +83,10 @@ SOURCES += mainwindow.cpp \
imode.cpp \ imode.cpp \
editormanager/systemeditor.cpp \ editormanager/systemeditor.cpp \
designmode.cpp \ designmode.cpp \
editortoolbar.cpp editortoolbar.cpp \
ssh/ne7sshobject.cpp \
ssh/sshconnection.cpp \
ssh/sshkeygenerator.cpp
HEADERS += mainwindow.h \ HEADERS += mainwindow.h \
editmode.h \ editmode.h \
@@ -165,7 +168,10 @@ HEADERS += mainwindow.h \
eventfilteringmainwindow.h \ eventfilteringmainwindow.h \
editormanager/systemeditor.h \ editormanager/systemeditor.h \
designmode.h \ designmode.h \
editortoolbar.h editortoolbar.h \
ssh/ne7sshobject.h \
ssh/sshconnection.h \
ssh/sshkeygenerator.h
FORMS += dialogs/newdialog.ui \ FORMS += dialogs/newdialog.ui \
actionmanager/commandmappings.ui \ actionmanager/commandmappings.ui \

View File

@@ -1,2 +1,3 @@
include(../../libs/extensionsystem/extensionsystem.pri) include(../../libs/extensionsystem/extensionsystem.pri)
include(../../libs/utils/utils.pri) include(../../libs/utils/utils.pri)
include(../../libs/3rdparty/net7ssh/net7ssh.pri)

View File

@@ -45,7 +45,7 @@
#include <ne7ssh.h> #include <ne7ssh.h>
namespace Qt4ProjectManager { namespace Core {
namespace Internal { namespace Internal {
Ne7SshObject *Ne7SshObject::instance() Ne7SshObject *Ne7SshObject::instance()
@@ -60,7 +60,7 @@ void Ne7SshObject::removeInstance()
delete m_instance; delete m_instance;
} }
QSharedPointer<ne7ssh> Ne7SshObject::get() Ne7SshObject::Ptr Ne7SshObject::get()
{ {
QMutexLocker locker(&m_mutex); QMutexLocker locker(&m_mutex);
QSharedPointer<ne7ssh> shared = m_weakRef.toStrongRef(); QSharedPointer<ne7ssh> shared = m_weakRef.toStrongRef();
@@ -78,4 +78,4 @@ Ne7SshObject::Ne7SshObject()
Ne7SshObject *Ne7SshObject::m_instance = 0; Ne7SshObject *Ne7SshObject::m_instance = 0;
} // namespace Internal } // namespace Internal
} // namespace Qt4ProjectManager } // namespace Core

View File

@@ -42,22 +42,26 @@
#ifndef NE7SSHOBJECT_H #ifndef NE7SSHOBJECT_H
#define NE7SSHOBJECT_H #define NE7SSHOBJECT_H
#include <coreplugin/core_global.h>
#include <QtCore/QMutex> #include <QtCore/QMutex>
#include <QtCore/QSharedPointer> #include <QtCore/QSharedPointer>
#include <QtCore/QWeakPointer> #include <QtCore/QWeakPointer>
class ne7ssh; class ne7ssh;
namespace Qt4ProjectManager { namespace Core {
namespace Internal { namespace Internal {
class Ne7SshObject class Ne7SshObject
{ {
public: public:
typedef QSharedPointer<ne7ssh> Ptr;
static Ne7SshObject *instance(); static Ne7SshObject *instance();
static void removeInstance(); static void removeInstance();
QSharedPointer<ne7ssh> get(); Ptr get();
private: private:
Ne7SshObject(); Ne7SshObject();
@@ -71,6 +75,6 @@ private:
}; };
} // namespace Internal } // namespace Internal
} // namespace Qt4ProjectManager } // namespace Core
#endif // NE7SSHOBJECT_H #endif // NE7SSHOBJECT_H

View File

@@ -0,0 +1,422 @@
/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of Qt Creator.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "sshconnection.h"
#include "ne7sshobject.h"
#include <QtCore/QCoreApplication>
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
#include <QtCore/QThread>
#include <ne7ssh.h>
#include <exception>
namespace Core {
namespace {
class GenericSshConnection
{
Q_DECLARE_TR_FUNCTIONS(GenericSshConnection)
public:
GenericSshConnection(const SshServerInfo &server)
: ssh(Internal::Ne7SshObject::instance()->get()),
m_server(server),
m_channel(-1)
{ }
~GenericSshConnection()
{
quit();
}
bool start(bool shell)
{
Q_ASSERT(m_channel == -1);
try {
const QString *authString;
int (ne7ssh::*connFunc)(const char *, int, const char *,
const char *, bool, int);
if (m_server.authType == SshServerInfo::AuthByPwd) {
authString = &m_server.pwd;
connFunc = &ne7ssh::connectWithPassword;
} else {
authString = &m_server.privateKeyFile;
connFunc = &ne7ssh::connectWithKey;
}
m_channel = (ssh.data()->*connFunc)(m_server.host.toLatin1(),
m_server.port, m_server.uname.toAscii(),
authString->toLatin1(), shell, m_server.timeout);
if (m_channel == -1) {
setError(tr("Could not connect to host."), false);
return false;
}
} catch (const std::exception &e) {
// Should in theory not be necessary, but Net7 leaks Botan exceptions.
setError(tr("Error in cryptography backend: %1")
.arg(QLatin1String(e.what())), false);
return false;
}
return true;
}
void quit()
{
const int channel = m_channel;
if (channel != -1) {
m_channel = -1;
if (!ssh->close(channel))
qWarning("%s: close() failed.", Q_FUNC_INFO);
}
}
bool hasError() const { return !m_error.isEmpty(); }
QString error() const { return m_error; }
int channel() const { return m_channel; }
QString lastNe7Error() { return ssh->errors()->pop(channel()); }
const SshServerInfo &server() { return m_server; }
void setError(const QString error, bool appendNe7ErrMsg)
{
m_error = error;
if (appendNe7ErrMsg)
m_error += QLatin1String(": ") + lastNe7Error();
}
QSharedPointer<ne7ssh> ssh;
private:
const SshServerInfo m_server;
QString m_error;
int m_channel;
};
char *alloc(size_t n)
{
return new char[n];
}
} // anonymous namespace
namespace Internal {
struct InteractiveSshConnectionPrivate
{
InteractiveSshConnectionPrivate(const SshServerInfo &server)
: conn(server), outputReader(0) {}
GenericSshConnection conn;
ConnectionOutputReader *outputReader;
};
struct NonInteractiveSshConnectionPrivate
{
NonInteractiveSshConnectionPrivate(const SshServerInfo &server)
: conn(server) {}
GenericSshConnection conn;
Ne7SftpSubsystem sftp;
};
class ConnectionOutputReader : public QThread
{
public:
ConnectionOutputReader(InteractiveSshConnection *parent)
: QThread(parent), m_conn(parent), m_stopRequested(false)
{}
~ConnectionOutputReader()
{
stop();
wait();
}
// TODO: Use a wakeup mechanism here as soon as we no longer poll for output
// from Net7.
void stop()
{
m_stopRequested = true;
}
private:
virtual void run()
{
while (!m_stopRequested) {
const int channel = m_conn->d->conn.channel();
if (channel != -1) {
QScopedPointer<char, QScopedPointerArrayDeleter<char> >
output(m_conn->d->conn.ssh->readAndReset(channel, alloc));
if (output)
emit m_conn->remoteOutput(QByteArray(output.data()));
}
sleep(1); // TODO: Hack Net7 to enable wait() functionality.
}
}
InteractiveSshConnection *m_conn;
bool m_stopRequested;
};
} // namespace Internal
InteractiveSshConnection::InteractiveSshConnection(const SshServerInfo &server)
: d(new Internal::InteractiveSshConnectionPrivate(server))
{
d->outputReader = new Internal::ConnectionOutputReader(this);
}
InteractiveSshConnection::~InteractiveSshConnection()
{
d->conn.ssh->send("exit\n", d->conn.channel());
quit();
delete d;
}
bool InteractiveSshConnection::start()
{
if (!d->conn.start(true))
return false;
d->outputReader->start();
return true;
}
bool InteractiveSshConnection::sendInput(const QByteArray &input)
{
if (!d->conn.ssh->send(input.data(), d->conn.channel())) {
d->conn.setError(tr("Error sending input"), true);
return false;
}
return true;
}
void InteractiveSshConnection::quit()
{
d->outputReader->stop();
d->conn.quit();
}
InteractiveSshConnection::Ptr InteractiveSshConnection::create(const SshServerInfo &server)
{
return Ptr(new InteractiveSshConnection(server));
}
bool InteractiveSshConnection::hasError() const
{
return d->conn.hasError();
}
QString InteractiveSshConnection::error() const
{
return d->conn.error();
}
namespace {
class FileMgr
{
public:
FileMgr(const QString &filePath, const char *mode)
: m_file(fopen(filePath.toLatin1().data(), mode)) {}
~FileMgr() { if (m_file) fclose(m_file); }
FILE *file() const { return m_file; }
private:
FILE * const m_file;
};
} // Anonymous namespace
SftpConnection::SftpConnection(const SshServerInfo &server)
: d(new Internal::NonInteractiveSshConnectionPrivate(server))
{ }
SftpConnection::~SftpConnection()
{
quit();
delete d;
}
bool SftpConnection::start()
{
if (!d->conn.start(false))
return false;
if (!d->conn.ssh->initSftp(d->sftp, d->conn.channel())
|| !d->sftp.setTimeout(d->conn.server().timeout)) {
d->conn.setError(tr("Error setting up SFTP subsystem"), true);
return false;
}
return true;
}
bool SftpConnection::transferFiles(const QList<SftpTransferInfo> &transferList)
{
for (int i = 0; i < transferList.count(); ++i) {
const SftpTransferInfo &transfer = transferList.at(i);
bool success;
if (transfer.type == SftpTransferInfo::Upload) {
success = upload(transfer.localFilePath, transfer.remoteFilePath);
} else {
success = download(transfer.remoteFilePath, transfer.localFilePath);
}
if (!success)
return false;
}
return true;
}
bool SftpConnection::upload(const QString &localFilePath,
const QByteArray &remoteFilePath)
{
FileMgr fileMgr(localFilePath, "rb");
if (!fileMgr.file()) {
d->conn.setError(tr("Could not open file '%1'").arg(localFilePath),
false);
return false;
}
if (!d->sftp.put(fileMgr.file(), remoteFilePath.data())) {
d->conn.setError(tr("Could not uplodad file '%1'")
.arg(localFilePath), true);
return false;
}
emit fileCopied(localFilePath);
return true;
}
bool SftpConnection::download(const QByteArray &remoteFilePath,
const QString &localFilePath)
{
FileMgr fileMgr(localFilePath, "wb");
if (!fileMgr.file()) {
d->conn.setError(tr("Could not open file '%1'").arg(localFilePath),
false);
return false;
}
if (!d->sftp.get(remoteFilePath.data(), fileMgr.file())) {
d->conn.setError(tr("Could not copy remote file '%1' to local file '%2'")
.arg(remoteFilePath, localFilePath), false);
return false;
}
emit fileCopied(remoteFilePath);
return true;
}
bool SftpConnection::createRemoteDir(const QByteArray &remoteDir)
{
if (!d->sftp.mkdir(remoteDir.data())) {
d->conn.setError(tr("Could not create remote directory"), true);
return false;
}
return true;
}
bool SftpConnection::removeRemoteDir(const QByteArray &remoteDir)
{
if (!d->sftp.rmdir(remoteDir.data())) {
d->conn.setError(tr("Could not remove remote directory"), true);
return false;
}
return true;
}
QByteArray SftpConnection::listRemoteDirContents(const QByteArray &remoteDir,
bool withAttributes, bool &ok)
{
const char * const buffer = d->sftp.ls(remoteDir.data(), withAttributes);
if (!buffer) {
d->conn.setError(tr("Could not get remote directory contents"), true);
ok = false;
return QByteArray();
}
ok = true;
return QByteArray(buffer);
}
bool SftpConnection::removeRemoteFile(const QByteArray &remoteFile)
{
if (!d->sftp.rm(remoteFile.data())) {
d->conn.setError(tr("Could not remove remote file"), true);
return false;
}
return true;
}
bool SftpConnection::changeRemoteWorkingDir(const QByteArray &newRemoteDir)
{
if (!d->sftp.cd(newRemoteDir.data())) {
d->conn.setError(tr("Could not change remote working directory"), true);
return false;
}
return true;
}
void SftpConnection::quit()
{
d->conn.quit();
}
bool SftpConnection::hasError() const
{
return d->conn.hasError();
}
QString SftpConnection::error() const
{
return d->conn.error();
}
SftpConnection::Ptr SftpConnection::create(const SshServerInfo &server)
{
return Ptr(new SftpConnection(server));
}
} // namespace Core

View File

@@ -0,0 +1,150 @@
/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of Qt Creator.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef SSHCONNECTION_H
#define SSHCONNECTION_H
#include <coreplugin/core_global.h>
#include <QtCore/QByteArray>
#include <QtCore/QObject>
#include <QtCore/QSharedPointer>
#include <QtCore/QString>
namespace Core {
namespace Internal {
struct InteractiveSshConnectionPrivate;
struct NonInteractiveSshConnectionPrivate;
class ConnectionOutputReader;
}
struct CORE_EXPORT SshServerInfo
{
QString host;
QString uname;
QString pwd;
QString privateKeyFile;
int timeout;
enum AuthType { AuthByPwd, AuthByKey } authType;
quint16 port;
};
class CORE_EXPORT InteractiveSshConnection : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(InteractiveSshConnection)
friend class Internal::ConnectionOutputReader;
public:
typedef QSharedPointer<InteractiveSshConnection> Ptr;
static Ptr create(const SshServerInfo &server);
bool start();
void quit();
bool sendInput(const QByteArray &input); // Should normally end in newline.
bool hasError() const;
QString error() const;
~InteractiveSshConnection();
signals:
void remoteOutput(const QByteArray &output);
private:
InteractiveSshConnection(const SshServerInfo &server);
struct Internal::InteractiveSshConnectionPrivate *d;
};
struct CORE_EXPORT SftpTransferInfo
{
enum Type { Upload, Download };
SftpTransferInfo(const QString &localFilePath,
const QByteArray &remoteFilePath, Type type)
: localFilePath(localFilePath),
remoteFilePath(remoteFilePath),
type(type)
{
}
QString localFilePath;
QByteArray remoteFilePath;
Type type;
};
class CORE_EXPORT SftpConnection : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(SftpConnection)
public:
typedef QSharedPointer<SftpConnection> Ptr;
static Ptr create(const SshServerInfo &server);
bool start();
void quit();
bool hasError() const;
QString error() const;
bool upload(const QString &localFilePath, const QByteArray &remoteFilePath);
bool download(const QByteArray &remoteFilePath, const QString &localFilePath);
bool transferFiles(const QList<SftpTransferInfo> &transferList);
bool createRemoteDir(const QByteArray &remoteDir);
bool removeRemoteDir(const QByteArray &remoteDir);
bool removeRemoteFile(const QByteArray &remoteFile);
bool changeRemoteWorkingDir(const QByteArray &newRemoteDir);
QByteArray listRemoteDirContents(const QByteArray &remoteDir,
bool withAttributes, bool &ok);
~SftpConnection();
signals:
void fileCopied(const QString &filePath);
private:
SftpConnection(const SshServerInfo &server);
Internal::NonInteractiveSshConnectionPrivate *d;
};
} // namespace Core
#endif // SSHCONNECTION_H

View File

@@ -0,0 +1,56 @@
#include "sshkeygenerator.h"
#include "ne7sshobject.h"
#include <QtCore/QFile>
#include <QtCore/QTemporaryFile>
#include <ne7ssh.h>
namespace Core {
SshKeyGenerator::SshKeyGenerator()
{
}
bool SshKeyGenerator::generateKeys(KeyType type, const QString &id, int keySize)
{
QTemporaryFile tmpPubKeyFile;
QTemporaryFile tmpPrivKeyFile;
if (!tmpPubKeyFile.open() || !tmpPrivKeyFile.open()) {
m_error = tr("Error creating temporary files.");
return false;
}
tmpPubKeyFile.setAutoRemove(false);
tmpPubKeyFile.close();
tmpPrivKeyFile.close();
const char * const typeStr = type == Rsa ? "rsa" : "dsa";
Internal::Ne7SshObject::Ptr ne7Object
= Internal::Ne7SshObject::instance()->get();
if (!ne7Object->generateKeyPair(typeStr, id.toUtf8(),
tmpPrivKeyFile.fileName().toUtf8(),
tmpPubKeyFile.fileName().toUtf8(), keySize)) {
// TODO: Race condition on pop() call. Perhaps not use Net7 errors? Or hack API
m_error = tr("Error generating keys: %1")
.arg(ne7Object->errors()->pop());
return false;
}
if (!tmpPubKeyFile.open() || !tmpPrivKeyFile.open()) {
m_error = tr("Error reading temporary files.");
return false;
}
m_publicKey = tmpPubKeyFile.readAll();
m_privateKey = tmpPrivKeyFile.readAll();
if (tmpPubKeyFile.error() != QFile::NoError
|| tmpPrivKeyFile.error() != QFile::NoError) {
m_error = tr("Error reading temporary files.");
return false;
}
m_type = type;
return true;
}
} // namespace Core

View File

@@ -0,0 +1,33 @@
#ifndef SSHKEYGENERATOR_H
#define SSHKEYGENERATOR_H
#include <coreplugin/core_global.h>
#include <QtCore/QCoreApplication>
#include <QtCore/QPair>
namespace Core {
class CORE_EXPORT SshKeyGenerator
{
Q_DECLARE_TR_FUNCTIONS(SshKeyGenerator)
public:
enum KeyType { Rsa, Dsa };
SshKeyGenerator();
bool generateKeys(KeyType type, const QString &id, int keySize);
QString error() const { return m_error; }
QString privateKey() const { return m_privateKey; }
QString publicKey() const { return m_publicKey; }
KeyType type() const { return m_type; }
private:
QString m_error;
QString m_publicKey;
QString m_privateKey;
KeyType m_type;
};
} // namespace Core
#endif // SSHKEYGENERATOR_H

View File

@@ -105,7 +105,7 @@ void MaemoConfigTestDialog::startConfigTest()
"|sed 's/[[:space:]][[:space:]]*/ /g' " "|sed 's/[[:space:]][[:space:]]*/ /g' "
"|cut -d ' ' -f 2,3 |sed 's/~.*//g'"); "|cut -d ' ' -f 2,3 |sed 's/~.*//g'");
QString command(sysInfoCmd + " && " + qtInfoCmd); QString command(sysInfoCmd + " && " + qtInfoCmd);
m_deviceTester = new MaemoSshRunner(m_config, command); m_deviceTester = new MaemoSshRunner(m_config.server, command);
connect(m_deviceTester, SIGNAL(remoteOutput(QString)), connect(m_deviceTester, SIGNAL(remoteOutput(QString)),
this, SLOT(processSshOutput(QString))); this, SLOT(processSshOutput(QString)));
connect(m_deviceTester, SIGNAL(finished()), connect(m_deviceTester, SIGNAL(finished()),
@@ -126,12 +126,12 @@ void MaemoConfigTestDialog::handleTestThreadFinished()
output.append(tr("\nDid you start Qemu?")); output.append(tr("\nDid you start Qemu?"));
} else { } else {
output = parseTestOutput(); output = parseTestOutput();
if (!m_qtVersionOk) {
m_ui->errorLabel->setText(tr("Qt version mismatch! "
" Expected Qt on device: 4.6.2 or later."));
}
} }
m_ui->testResultEdit->setPlainText(output); m_ui->testResultEdit->setPlainText(output);
if (!m_qtVersionOk) {
m_ui->errorLabel->setText(tr("Qt version mismatch! Expected Qt on device: "
"4.6.2 or later."));
}
stopConfigTest(); stopConfigTest();
} }

View File

@@ -42,6 +42,8 @@
#include <algorithm> #include <algorithm>
typedef Core::SshServerInfo::AuthType AuthType;
namespace Qt4ProjectManager { namespace Qt4ProjectManager {
namespace Internal { namespace Internal {
@@ -78,7 +80,7 @@ namespace {
const QString DefaultHostNameHW(QLatin1String("192.168.2.15")); const QString DefaultHostNameHW(QLatin1String("192.168.2.15"));
const QString DefaultHostNameSim(QLatin1String("localhost")); const QString DefaultHostNameSim(QLatin1String("localhost"));
const QString DefaultUserName(QLatin1String("developer")); const QString DefaultUserName(QLatin1String("developer"));
const MaemoDeviceConfig::AuthType DefaultAuthType(MaemoDeviceConfig::Key); const AuthType DefaultAuthType(Core::SshServerInfo::AuthByKey);
const int DefaultTimeout(30); const int DefaultTimeout(30);
const MaemoDeviceConfig::DeviceType DefaultDeviceType(MaemoDeviceConfig::Physical); const MaemoDeviceConfig::DeviceType DefaultDeviceType(MaemoDeviceConfig::Physical);
}; };
@@ -98,33 +100,35 @@ private:
MaemoDeviceConfig::MaemoDeviceConfig(const QString &name, MaemoDeviceConfig::DeviceType devType) MaemoDeviceConfig::MaemoDeviceConfig(const QString &name, MaemoDeviceConfig::DeviceType devType)
: name(name), : name(name),
type(devType), type(devType),
host(defaultHost(type)),
sshPort(defaultSshPort(type)),
gdbServerPort(defaultGdbServerPort(type)), gdbServerPort(defaultGdbServerPort(type)),
uname(DefaultUserName),
authentication(DefaultAuthType),
keyFile(DefaultKeyFile),
timeout(DefaultTimeout),
internalId(MaemoDeviceConfigurations::instance().m_nextId++) internalId(MaemoDeviceConfigurations::instance().m_nextId++)
{ {
server.host = defaultHost(type);
server.port = defaultSshPort(type);
server.uname = DefaultUserName;
server.authType = DefaultAuthType;
server.privateKeyFile = DefaultKeyFile;
server.timeout = DefaultTimeout;
} }
MaemoDeviceConfig::MaemoDeviceConfig(const QSettings &settings, MaemoDeviceConfig::MaemoDeviceConfig(const QSettings &settings,
quint64 &nextId) quint64 &nextId)
: name(settings.value(NameKey).toString()), : name(settings.value(NameKey).toString()),
type(static_cast<DeviceType>(settings.value(TypeKey, DefaultDeviceType).toInt())), type(static_cast<DeviceType>(settings.value(TypeKey, DefaultDeviceType).toInt())),
host(settings.value(HostKey, defaultHost(type)).toString()),
sshPort(settings.value(SshPortKey, defaultSshPort(type)).toInt()),
gdbServerPort(settings.value(GdbServerPortKey, defaultGdbServerPort(type)).toInt()), gdbServerPort(settings.value(GdbServerPortKey, defaultGdbServerPort(type)).toInt()),
uname(settings.value(UserNameKey, DefaultUserName).toString()),
authentication(static_cast<AuthType>(settings.value(AuthKey, DefaultAuthType).toInt())),
pwd(settings.value(PasswordKey).toString()),
keyFile(settings.value(KeyFileKey, DefaultKeyFile).toString()),
timeout(settings.value(TimeoutKey, DefaultTimeout).toInt()),
internalId(settings.value(InternalIdKey, nextId).toInt()) internalId(settings.value(InternalIdKey, nextId).toInt())
{ {
if (internalId == nextId) if (internalId == nextId)
++nextId; ++nextId;
server.host = settings.value(HostKey, defaultHost(type)).toString();
server.port = settings.value(SshPortKey, defaultSshPort(type)).toInt();
server.uname = settings.value(UserNameKey, DefaultUserName).toString();
server.authType
= static_cast<AuthType>(settings.value(AuthKey, DefaultAuthType).toInt());
server.pwd = settings.value(PasswordKey).toString();
server.privateKeyFile
= settings.value(KeyFileKey, DefaultKeyFile).toString();
server.timeout = settings.value(TimeoutKey, DefaultTimeout).toInt();
} }
MaemoDeviceConfig::MaemoDeviceConfig() MaemoDeviceConfig::MaemoDeviceConfig()
@@ -156,14 +160,14 @@ void MaemoDeviceConfig::save(QSettings &settings) const
{ {
settings.setValue(NameKey, name); settings.setValue(NameKey, name);
settings.setValue(TypeKey, type); settings.setValue(TypeKey, type);
settings.setValue(HostKey, host); settings.setValue(HostKey, server.host);
settings.setValue(SshPortKey, sshPort); settings.setValue(SshPortKey, server.port);
settings.setValue(GdbServerPortKey, gdbServerPort); settings.setValue(GdbServerPortKey, gdbServerPort);
settings.setValue(UserNameKey, uname); settings.setValue(UserNameKey, server.uname);
settings.setValue(AuthKey, authentication); settings.setValue(AuthKey, server.authType);
settings.setValue(PasswordKey, pwd); settings.setValue(PasswordKey, server.pwd);
settings.setValue(KeyFileKey, keyFile); settings.setValue(KeyFileKey, server.privateKeyFile);
settings.setValue(TimeoutKey, timeout); settings.setValue(TimeoutKey, server.timeout);
settings.setValue(InternalIdKey, internalId); settings.setValue(InternalIdKey, internalId);
} }

View File

@@ -35,6 +35,8 @@
#ifndef MAEMODEVICECONFIGURATIONS_H #ifndef MAEMODEVICECONFIGURATIONS_H
#define MAEMODEVICECONFIGURATIONS_H #define MAEMODEVICECONFIGURATIONS_H
#include <coreplugin/ssh/sshconnection.h>
#include <QtCore/QList> #include <QtCore/QList>
#include <QtCore/QObject> #include <QtCore/QObject>
#include <QtCore/QString> #include <QtCore/QString>
@@ -52,22 +54,16 @@ class MaemoDeviceConfig
{ {
public: public:
enum DeviceType { Physical, Simulator }; enum DeviceType { Physical, Simulator };
enum AuthType { Password, Key };
MaemoDeviceConfig(); MaemoDeviceConfig();
MaemoDeviceConfig(const QString &name, DeviceType type); MaemoDeviceConfig(const QString &name, DeviceType type);
MaemoDeviceConfig(const QSettings &settings, quint64 &nextId); MaemoDeviceConfig(const QSettings &settings, quint64 &nextId);
void save(QSettings &settings) const; void save(QSettings &settings) const;
bool isValid() const; bool isValid() const;
Core::SshServerInfo server;
QString name; QString name;
DeviceType type; DeviceType type;
QString host;
int sshPort;
int gdbServerPort; int gdbServerPort;
QString uname;
AuthType authentication;
QString pwd;
QString keyFile;
int timeout;
quint64 internalId; quint64 internalId;
private: private:

View File

@@ -36,7 +36,6 @@
#include "maemosettingspage.h" #include "maemosettingspage.h"
#include "maemotoolchain.h" #include "maemotoolchain.h"
#include "maemorunconfiguration.h" #include "maemorunconfiguration.h"
#include "ne7sshobject.h"
#include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/coreconstants.h> #include <coreplugin/coreconstants.h>
@@ -83,8 +82,6 @@ MaemoManager::MaemoManager()
pluginManager->addObject(m_runConfigurationFactory); pluginManager->addObject(m_runConfigurationFactory);
pluginManager->addObject(m_packageCreationFactory); pluginManager->addObject(m_packageCreationFactory);
pluginManager->addObject(m_settingsPage); pluginManager->addObject(m_settingsPage);
Ne7SshObject::instance();
} }
MaemoManager::~MaemoManager() MaemoManager::~MaemoManager()
@@ -96,7 +93,6 @@ MaemoManager::~MaemoManager()
pluginManager->removeObject(m_packageCreationFactory); pluginManager->removeObject(m_packageCreationFactory);
pluginManager->removeObject(m_settingsPage); pluginManager->removeObject(m_settingsPage);
Ne7SshObject::removeInstance();
m_instance = 0; m_instance = 0;
} }

View File

@@ -123,7 +123,7 @@ void AbstractMaemoRunControl::startDeployment(bool forDebugging)
emit finished(); emit finished();
} else { } else {
m_deployables.clear(); m_deployables.clear();
if (m_runConfig->currentlyNeedsDeployment(m_devConfig.host)) { if (m_runConfig->currentlyNeedsDeployment(m_devConfig.server.host)) {
m_deployables.append(Deployable(packageFileName(), m_deployables.append(Deployable(packageFileName(),
QFileInfo(executableOnHost()).canonicalPath(), QFileInfo(executableOnHost()).canonicalPath(),
&MaemoRunConfiguration::wasDeployed)); &MaemoRunConfiguration::wasDeployed));
@@ -132,7 +132,7 @@ void AbstractMaemoRunControl::startDeployment(bool forDebugging)
m_needsInstall = false; m_needsInstall = false;
} }
if (forDebugging if (forDebugging
&& m_runConfig->debuggingHelpersNeedDeployment(m_devConfig.host)) { && m_runConfig->debuggingHelpersNeedDeployment(m_devConfig.server.host)) {
const QFileInfo &info(m_runConfig->dumperLib()); const QFileInfo &info(m_runConfig->dumperLib());
m_deployables.append(Deployable(info.fileName(), info.canonicalPath(), m_deployables.append(Deployable(info.fileName(), info.canonicalPath(),
&MaemoRunConfiguration::debuggingHelpersDeployed)); &MaemoRunConfiguration::debuggingHelpersDeployed));
@@ -148,7 +148,7 @@ void AbstractMaemoRunControl::deploy()
->addTask(m_progress.future(), tr("Deploying"), ->addTask(m_progress.future(), tr("Deploying"),
QLatin1String("Maemo.Deploy")); QLatin1String("Maemo.Deploy"));
if (!m_deployables.isEmpty()) { if (!m_deployables.isEmpty()) {
QList<SshDeploySpec> deploySpecs; QList<Core::SftpTransferInfo> deploySpecs;
QStringList files; QStringList files;
foreach (const Deployable &deployable, m_deployables) { foreach (const Deployable &deployable, m_deployables) {
const QString srcFilePath const QString srcFilePath
@@ -156,10 +156,11 @@ void AbstractMaemoRunControl::deploy()
const QString tgtFilePath const QString tgtFilePath
= remoteDir() % QDir::separator() % deployable.fileName; = remoteDir() % QDir::separator() % deployable.fileName;
files << srcFilePath; files << srcFilePath;
deploySpecs << SshDeploySpec(srcFilePath, tgtFilePath); deploySpecs << Core::SftpTransferInfo(srcFilePath,
tgtFilePath.toUtf8(), Core::SftpTransferInfo::Upload);
} }
emit appendMessage(this, tr("Files to deploy: %1.").arg(files.join(" ")), false); emit appendMessage(this, tr("Files to deploy: %1.").arg(files.join(" ")), false);
m_sshDeployer.reset(new MaemoSshDeployer(m_devConfig, deploySpecs)); m_sshDeployer.reset(new MaemoSshDeployer(m_devConfig.server, deploySpecs));
connect(m_sshDeployer.data(), SIGNAL(finished()), connect(m_sshDeployer.data(), SIGNAL(finished()),
this, SLOT(handleDeployThreadFinished())); this, SLOT(handleDeployThreadFinished()));
connect(m_sshDeployer.data(), SIGNAL(fileCopied(QString)), connect(m_sshDeployer.data(), SIGNAL(fileCopied(QString)),
@@ -177,7 +178,7 @@ void AbstractMaemoRunControl::deploy()
void AbstractMaemoRunControl::handleFileCopied() void AbstractMaemoRunControl::handleFileCopied()
{ {
Deployable deployable = m_deployables.takeFirst(); Deployable deployable = m_deployables.takeFirst();
(m_runConfig->*deployable.updateTimestamp)(m_devConfig.host); (m_runConfig->*deployable.updateTimestamp)(m_devConfig.server.host);
m_progress.setProgressValue(m_progress.progressValue() + 1); m_progress.setProgressValue(m_progress.progressValue() + 1);
} }
@@ -208,7 +209,7 @@ bool AbstractMaemoRunControl::isCleaning() const
void AbstractMaemoRunControl::startExecution() void AbstractMaemoRunControl::startExecution()
{ {
m_sshRunner.reset(new MaemoSshRunner(m_devConfig, remoteCall())); m_sshRunner.reset(new MaemoSshRunner(m_devConfig.server, remoteCall()));
connect(m_sshRunner.data(), SIGNAL(finished()), connect(m_sshRunner.data(), SIGNAL(finished()),
this, SLOT(handleRunThreadFinished())); this, SLOT(handleRunThreadFinished()));
connect(m_sshRunner.data(), SIGNAL(remoteOutput(QString)), connect(m_sshRunner.data(), SIGNAL(remoteOutput(QString)),
@@ -246,7 +247,7 @@ void AbstractMaemoRunControl::killRemoteProcesses(const QStringList &apps,
remoteCall.remove(remoteCall.count() - 1, 1); // Get rid of trailing semicolon. remoteCall.remove(remoteCall.count() - 1, 1); // Get rid of trailing semicolon.
QScopedPointer<MaemoSshRunner> &runner QScopedPointer<MaemoSshRunner> &runner
= initialCleanup ? m_initialCleaner : m_sshStopper; = initialCleanup ? m_initialCleaner : m_sshStopper;
runner.reset(new MaemoSshRunner(m_devConfig, remoteCall)); runner.reset(new MaemoSshRunner(m_devConfig.server, remoteCall));
if (initialCleanup) if (initialCleanup)
connect(runner.data(), SIGNAL(finished()), connect(runner.data(), SIGNAL(finished()),
this, SLOT(handleInitialCleanupFinished())); this, SLOT(handleInitialCleanupFinished()));
@@ -296,7 +297,6 @@ void AbstractMaemoRunControl::handleRunThreadFinished()
const QString AbstractMaemoRunControl::executableOnHost() const const QString AbstractMaemoRunControl::executableOnHost() const
{ {
qDebug("runconfig->executable: %s", qPrintable(m_runConfig->executable()));
return m_runConfig->executable(); return m_runConfig->executable();
} }
@@ -307,7 +307,7 @@ const QString AbstractMaemoRunControl::executableFileName() const
const QString AbstractMaemoRunControl::remoteDir() const const QString AbstractMaemoRunControl::remoteDir() const
{ {
return homeDirOnDevice(m_devConfig.uname); return homeDirOnDevice(m_devConfig.server.uname);
} }
QString AbstractMaemoRunControl::remoteSudo() const QString AbstractMaemoRunControl::remoteSudo() const
@@ -384,7 +384,7 @@ MaemoDebugRunControl::MaemoDebugRunControl(RunConfiguration *runConfiguration)
m_startParams->startMode = Debugger::StartRemote; m_startParams->startMode = Debugger::StartRemote;
m_startParams->executable = executableOnHost(); m_startParams->executable = executableOnHost();
m_startParams->remoteChannel m_startParams->remoteChannel
= m_devConfig.host % QLatin1Char(':') % gdbServerPort(); = m_devConfig.server.host % QLatin1Char(':') % gdbServerPort();
m_startParams->remoteArchitecture = QLatin1String("arm"); m_startParams->remoteArchitecture = QLatin1String("arm");
m_startParams->sysRoot = m_runConfig->sysRoot(); m_startParams->sysRoot = m_runConfig->sysRoot();
m_startParams->toolChainType = ToolChain::GCC_MAEMO; m_startParams->toolChainType = ToolChain::GCC_MAEMO;

View File

@@ -190,31 +190,31 @@ void MaemoSettingsWidget::display(const MaemoDeviceConfig &devConfig)
otherConfig = &m_lastConfigHW; otherConfig = &m_lastConfigHW;
m_ui->simulatorButton->setChecked(true); m_ui->simulatorButton->setChecked(true);
} }
otherConfig->authentication = devConfig.authentication; otherConfig->server.authType = devConfig.server.authType;
otherConfig->timeout = devConfig.timeout; otherConfig->server.timeout = devConfig.server.timeout;
otherConfig->pwd = devConfig.pwd; otherConfig->server.pwd = devConfig.server.pwd;
otherConfig->keyFile = devConfig.keyFile; otherConfig->server.privateKeyFile = devConfig.server.privateKeyFile;
if (devConfig.authentication == MaemoDeviceConfig::Password) if (devConfig.server.authType == Core::SshServerInfo::AuthByPwd)
m_ui->passwordButton->setChecked(true); m_ui->passwordButton->setChecked(true);
else else
m_ui->keyButton->setChecked(true); m_ui->keyButton->setChecked(true);
m_ui->detailsWidget->setEnabled(true); m_ui->detailsWidget->setEnabled(true);
m_nameValidator->setDisplayName(devConfig.name); m_nameValidator->setDisplayName(devConfig.name);
m_ui->timeoutSpinBox->setValue(devConfig.timeout); m_ui->timeoutSpinBox->setValue(devConfig.server.timeout);
fillInValues(); fillInValues();
} }
void MaemoSettingsWidget::fillInValues() void MaemoSettingsWidget::fillInValues()
{ {
m_ui->nameLineEdit->setText(currentConfig().name); m_ui->nameLineEdit->setText(currentConfig().name);
m_ui->hostLineEdit->setText(currentConfig().host); m_ui->hostLineEdit->setText(currentConfig().server.host);
m_ui->sshPortSpinBox->setValue(currentConfig().sshPort); m_ui->sshPortSpinBox->setValue(currentConfig().server.port);
m_ui->gdbServerPortSpinBox->setValue(currentConfig().gdbServerPort); m_ui->gdbServerPortSpinBox->setValue(currentConfig().gdbServerPort);
m_ui->timeoutSpinBox->setValue(currentConfig().timeout); m_ui->timeoutSpinBox->setValue(currentConfig().server.timeout);
m_ui->userLineEdit->setText(currentConfig().uname); m_ui->userLineEdit->setText(currentConfig().server.uname);
m_ui->pwdLineEdit->setText(currentConfig().pwd); m_ui->pwdLineEdit->setText(currentConfig().server.pwd);
m_ui->keyFileLineEdit->setPath(currentConfig().keyFile); m_ui->keyFileLineEdit->setPath(currentConfig().server.privateKeyFile);
const bool isSimulator const bool isSimulator
= currentConfig().type == MaemoDeviceConfig::Simulator; = currentConfig().type == MaemoDeviceConfig::Simulator;
@@ -269,9 +269,8 @@ void MaemoSettingsWidget::deviceTypeChanged()
void MaemoSettingsWidget::authenticationTypeChanged() void MaemoSettingsWidget::authenticationTypeChanged()
{ {
const bool usePassword = m_ui->passwordButton->isChecked(); const bool usePassword = m_ui->passwordButton->isChecked();
currentConfig().authentication = usePassword currentConfig().server.authType
? MaemoDeviceConfig::Password = usePassword ? Core::SshServerInfo::AuthByPwd : Core::SshServerInfo::AuthByKey;
: MaemoDeviceConfig::Key;
m_ui->pwdLineEdit->setEnabled(usePassword); m_ui->pwdLineEdit->setEnabled(usePassword);
m_ui->passwordLabel->setEnabled(usePassword); m_ui->passwordLabel->setEnabled(usePassword);
m_ui->keyFileLineEdit->setEnabled(!usePassword); m_ui->keyFileLineEdit->setEnabled(!usePassword);
@@ -280,12 +279,12 @@ void MaemoSettingsWidget::authenticationTypeChanged()
void MaemoSettingsWidget::hostNameEditingFinished() void MaemoSettingsWidget::hostNameEditingFinished()
{ {
currentConfig().host = m_ui->hostLineEdit->text(); currentConfig().server.host = m_ui->hostLineEdit->text();
} }
void MaemoSettingsWidget::sshPortEditingFinished() void MaemoSettingsWidget::sshPortEditingFinished()
{ {
currentConfig().sshPort = m_ui->sshPortSpinBox->value(); currentConfig().server.port = m_ui->sshPortSpinBox->value();
} }
void MaemoSettingsWidget::gdbServerPortEditingFinished() void MaemoSettingsWidget::gdbServerPortEditingFinished()
@@ -295,22 +294,22 @@ void MaemoSettingsWidget::gdbServerPortEditingFinished()
void MaemoSettingsWidget::timeoutEditingFinished() void MaemoSettingsWidget::timeoutEditingFinished()
{ {
currentConfig().timeout = m_ui->timeoutSpinBox->value(); currentConfig().server.timeout = m_ui->timeoutSpinBox->value();
} }
void MaemoSettingsWidget::userNameEditingFinished() void MaemoSettingsWidget::userNameEditingFinished()
{ {
currentConfig().uname = m_ui->userLineEdit->text(); currentConfig().server.uname = m_ui->userLineEdit->text();
} }
void MaemoSettingsWidget::passwordEditingFinished() void MaemoSettingsWidget::passwordEditingFinished()
{ {
currentConfig().pwd = m_ui->pwdLineEdit->text(); currentConfig().server.pwd = m_ui->pwdLineEdit->text();
} }
void MaemoSettingsWidget::keyFileEditingFinished() void MaemoSettingsWidget::keyFileEditingFinished()
{ {
currentConfig().keyFile = m_ui->keyFileLineEdit->path(); currentConfig().server.privateKeyFile = m_ui->keyFileLineEdit->path();
} }
void MaemoSettingsWidget::testConfig() void MaemoSettingsWidget::testConfig()
@@ -322,18 +321,9 @@ void MaemoSettingsWidget::testConfig()
void MaemoSettingsWidget::showGenerateSshKeyDialog() void MaemoSettingsWidget::showGenerateSshKeyDialog()
{ {
MaemoSshConfigDialog dialog(this); MaemoSshConfigDialog dialog(this);
connect(&dialog, SIGNAL(publicKeyGenerated(QString)), this,
SLOT(setPublicKey(QString)));
connect(&dialog, SIGNAL(privateKeyGenerated(QString)), this,
SLOT(setPrivateKey(QString)));
dialog.exec(); dialog.exec();
} }
void MaemoSettingsWidget::setPublicKey(const QString &path)
{
m_publicKeyFileName = path;
}
void MaemoSettingsWidget::setPrivateKey(const QString &path) void MaemoSettingsWidget::setPrivateKey(const QString &path)
{ {
m_ui->keyFileLineEdit->setPath(path); m_ui->keyFileLineEdit->setPath(path);
@@ -345,30 +335,29 @@ void MaemoSettingsWidget::deployKey()
if (m_keyDeployer) if (m_keyDeployer)
return; return;
if (!QFileInfo(m_publicKeyFileName).exists()) { const QString &dir
const QString &dir = QFileInfo(currentConfig().keyFile).path(); = QFileInfo(currentConfig().server.privateKeyFile).path();
m_publicKeyFileName = QFileDialog::getOpenFileName(this, QString publicKeyFileName = QFileDialog::getOpenFileName(this,
tr("Choose public key file"), dir, tr("Choose public key file"), dir,
tr("Public Key Files(*.pub);;All Files (*)")); tr("Public Key Files(*.pub);;All Files (*)"));
} if (publicKeyFileName.isEmpty())
if (m_publicKeyFileName.isEmpty())
return; return;
QFile keyFile(m_publicKeyFileName);
QFile keyFile(publicKeyFileName);
QByteArray key; QByteArray key;
const bool keyFileAccessible = keyFile.open(QIODevice::ReadOnly); const bool keyFileAccessible = keyFile.open(QIODevice::ReadOnly);
if (keyFileAccessible) if (keyFileAccessible)
key = keyFile.readAll(); key = keyFile.readAll();
if (!keyFileAccessible || keyFile.error() != QFile::NoError) { if (!keyFileAccessible || keyFile.error() != QFile::NoError) {
QMessageBox::critical(this, tr("Deployment Failed"), QMessageBox::critical(this, tr("Deployment Failed"),
tr("Could not read public key file '%1'.").arg(m_publicKeyFileName)); tr("Could not read public key file '%1'.").arg(publicKeyFileName));
return; return;
} }
m_ui->deployKeyButton->disconnect(); m_ui->deployKeyButton->disconnect();
const QString command = QLatin1String("test -d .ssh || mkdir .ssh && echo '") const QString command = QLatin1String("test -d .ssh || mkdir .ssh && echo '")
+ key + QLatin1String("' >> .ssh/authorized_keys"); + key + QLatin1String("' >> .ssh/authorized_keys");
m_keyDeployer = new MaemoSshRunner(currentConfig(), command); m_keyDeployer = new MaemoSshRunner(currentConfig().server, command);
connect(m_keyDeployer, SIGNAL(finished()), connect(m_keyDeployer, SIGNAL(finished()),
this, SLOT(handleDeployThreadFinished())); this, SLOT(handleDeployThreadFinished()));
m_ui->deployKeyButton->setText(tr("Stop deploying")); m_ui->deployKeyButton->setText(tr("Stop deploying"));
@@ -399,7 +388,7 @@ void MaemoSettingsWidget::stopDeploying()
m_keyDeployer->stop(); m_keyDeployer->stop();
delete m_keyDeployer; delete m_keyDeployer;
m_keyDeployer = 0; m_keyDeployer = 0;
m_ui->deployKeyButton->setText(tr("Deploy Key ...")); m_ui->deployKeyButton->setText(tr("Deploy Public Key ..."));
connect(m_ui->deployKeyButton, SIGNAL(clicked()), this, SLOT(deployKey())); connect(m_ui->deployKeyButton, SIGNAL(clicked()), this, SLOT(deployKey()));
} }
} }

View File

@@ -82,7 +82,6 @@ private slots:
void testConfig(); void testConfig();
void showGenerateSshKeyDialog(); void showGenerateSshKeyDialog();
void setPublicKey(const QString &path);
void setPrivateKey(const QString &path); void setPrivateKey(const QString &path);
// For key deploying. // For key deploying.
@@ -104,7 +103,6 @@ private:
MaemoDeviceConfig m_lastConfigSim; MaemoDeviceConfig m_lastConfigSim;
NameValidator * const m_nameValidator; NameValidator * const m_nameValidator;
MaemoSshRunner *m_keyDeployer; MaemoSshRunner *m_keyDeployer;
QString m_publicKeyFileName;
}; };
} // namespace Internal } // namespace Internal

View File

@@ -6,7 +6,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>563</width> <width>596</width>
<height>336</height> <height>336</height>
</rect> </rect>
</property> </property>
@@ -335,7 +335,7 @@
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="text"> <property name="text">
<string>Deploy public Key ...</string> <string>Deploy Public Key ...</string>
</property> </property>
</widget> </widget>
</item> </item>

View File

@@ -35,23 +35,23 @@
#include "maemosshconfigdialog.h" #include "maemosshconfigdialog.h"
#include "maemodeviceconfigurations.h" #include "maemodeviceconfigurations.h"
#include "ne7sshobject.h"
#include <ne7ssh.h> #include <coreplugin/ssh/sshkeygenerator.h>
#include <QtCore/QDir> #include <QtCore/QDir>
#include <QtNetwork/QHostInfo>
#include <QtGui/QApplication> #include <QtGui/QApplication>
#include <QtGui/QDesktopServices> #include <QtGui/QDesktopServices>
#include <QtGui/QFileDialog> #include <QtGui/QFileDialog>
#include <QtGui/QMessageBox>
#include <QtNetwork/QHostInfo>
using namespace Qt4ProjectManager::Internal; using namespace Qt4ProjectManager::Internal;
MaemoSshConfigDialog::MaemoSshConfigDialog(QWidget *parent) MaemoSshConfigDialog::MaemoSshConfigDialog(QWidget *parent)
: QDialog(parent) : QDialog(parent)
, home(QDesktopServices::storageLocation(QDesktopServices::HomeLocation)) , home(QDesktopServices::storageLocation(QDesktopServices::HomeLocation))
, m_keyGenerator(new Core::SshKeyGenerator)
{ {
m_ui.setupUi(this); m_ui.setupUi(this);
@@ -75,26 +75,22 @@ void MaemoSshConfigDialog::slotToggled()
void MaemoSshConfigDialog::generateSshKey() void MaemoSshConfigDialog::generateSshKey()
{ {
algorithm = m_ui.rsa->isChecked() ? "rsa" : "dsa"; const Core::SshKeyGenerator::KeyType keyType = m_ui.rsa->isChecked()
tmpKey = QDir::tempPath().append(QLatin1Char('/') + algorithm).toUtf8(); ? Core::SshKeyGenerator::Rsa
: Core::SshKeyGenerator::Dsa;
QByteArray userId = QString(home.mid(home.lastIndexOf(QLatin1Char('/')) + 1) QByteArray userId = QString(home.mid(home.lastIndexOf(QLatin1Char('/')) + 1)
+ QLatin1Char('@') + QHostInfo::localHostName()).toUtf8(); + QLatin1Char('@') + QHostInfo::localHostName()).toUtf8();
QFile::remove(tmpKey);
QFile::remove(tmpKey + ".pub");
QApplication::setOverrideCursor(Qt::BusyCursor); QApplication::setOverrideCursor(Qt::BusyCursor);
QSharedPointer<ne7ssh> ssh = Ne7SshObject::instance()->get(); if (m_keyGenerator->generateKeys(keyType, userId,
if (ssh->generateKeyPair(algorithm, userId, tmpKey, tmpKey + ".pub",
m_ui.comboBox->currentText().toUShort())) { m_ui.comboBox->currentText().toUShort())) {
QFile file(tmpKey + ".pub"); m_ui.plainTextEdit->setPlainText(m_keyGenerator->publicKey());
if (file.open(QIODevice::ReadOnly))
m_ui.plainTextEdit->setPlainText(file.readAll());
m_ui.savePublicKey->setEnabled(true); m_ui.savePublicKey->setEnabled(true);
m_ui.savePrivateKey->setEnabled(true); m_ui.savePrivateKey->setEnabled(true);
} else { } else {
m_ui.plainTextEdit->setPlainText(tr("Could not create SSH key pair.")); m_ui.plainTextEdit->setPlainText(m_keyGenerator->error());
} }
QApplication::restoreOverrideCursor(); QApplication::restoreOverrideCursor();
@@ -102,18 +98,12 @@ void MaemoSshConfigDialog::generateSshKey()
void MaemoSshConfigDialog::savePublicKey() void MaemoSshConfigDialog::savePublicKey()
{ {
checkSshDir(); saveKey(true);
copyFile(QFileDialog::getSaveFileName(this, tr("Choose folder to save "
"public key file"), home + QString::fromLatin1("/.ssh/id_%1.pub")
.arg(algorithm.constData())), true);
} }
void MaemoSshConfigDialog::savePrivateKey() void MaemoSshConfigDialog::savePrivateKey()
{ {
checkSshDir(); saveKey(false);
copyFile(QFileDialog::getSaveFileName(this, tr("Choose folder to save "
"private key file"), home + QString::fromLatin1("/.ssh/id_%1")
.arg(algorithm.constData())), false);
} }
void MaemoSshConfigDialog::checkSshDir() void MaemoSshConfigDialog::checkSshDir()
@@ -123,15 +113,31 @@ void MaemoSshConfigDialog::checkSshDir()
dir.mkpath(home + QString::fromLatin1("/.ssh")); dir.mkpath(home + QString::fromLatin1("/.ssh"));
} }
void MaemoSshConfigDialog::copyFile(const QString &file, bool pubKey) void MaemoSshConfigDialog::saveKey(bool publicKey)
{ {
if (!file.isEmpty()) { checkSshDir();
if (!QFile::exists(file) || QFile::remove(file)) { const QString suggestedTypeSuffix =
QFile(tmpKey + (pubKey ? ".pub" : "")).copy(file); m_keyGenerator->type() == Core::SshKeyGenerator::Rsa ? "rsa" : "dsa";
if (pubKey) const QString suggestedName = home + QString::fromLatin1("/.ssh/id_%1%2")
emit publicKeyGenerated(file); .arg(suggestedTypeSuffix).arg(publicKey ? ".pub" : "");
else const QString dlgTitle
emit privateKeyGenerated(file); = publicKey ? tr("Save public key file") : tr("Save private key file");
} const QString fileName
= QFileDialog::getSaveFileName(this, dlgTitle, suggestedName);
if (fileName.isEmpty())
return;
QFile file(fileName);
const bool canOpen = file.open(QIODevice::WriteOnly);
if (canOpen)
file.write(publicKey
? m_keyGenerator->publicKey().toUtf8()
: m_keyGenerator->privateKey().toUtf8());
if (!canOpen || file.error() != QFile::NoError) {
QMessageBox::critical(this, tr("Error writing file"),
tr("Could not write file '%1':\n %2")
.arg(fileName, file.errorString()));
} else if (!publicKey) {
emit privateKeyGenerated(fileName);
} }
} }

View File

@@ -37,8 +37,13 @@
#include "ui_maemosshconfigdialog.h" #include "ui_maemosshconfigdialog.h"
#include <QtCore/QScopedPointer>
#include <QtGui/QDialog> #include <QtGui/QDialog>
namespace Core {
class SshKeyGenerator;
}
namespace Qt4ProjectManager { namespace Qt4ProjectManager {
namespace Internal { namespace Internal {
@@ -50,7 +55,6 @@ public:
~MaemoSshConfigDialog(); ~MaemoSshConfigDialog();
signals: signals:
void publicKeyGenerated(const QString &path);
void privateKeyGenerated(const QString &path); void privateKeyGenerated(const QString &path);
private slots: private slots:
@@ -61,12 +65,11 @@ private slots:
private: private:
void checkSshDir(); void checkSshDir();
void copyFile(const QString &file, bool pubKey); void saveKey(bool publicKey);
private: private:
QString home; QString home;
QByteArray tmpKey; QScopedPointer<Core::SshKeyGenerator> m_keyGenerator;
QByteArray algorithm;
Ui::MaemoSshConfigDialog m_ui; Ui::MaemoSshConfigDialog m_ui;
}; };

View File

@@ -1,262 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Creator.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "maemosshconnection.h"
#include "maemodeviceconfigurations.h"
#include "ne7sshobject.h"
#include <ne7ssh.h>
#include <QtCore/QFileInfo>
#include <QtCore/QScopedPointer>
#include <QtCore/QStringBuilder>
#include <cstdio>
#include <cstring>
namespace Qt4ProjectManager {
namespace Internal {
namespace {
char *alloc(size_t n)
{
return new char[n];
}
}
// TODO: Which encoding to use for file names? Unicode? Latin1? ASCII?
MaemoSshConnection::MaemoSshConnection(const MaemoDeviceConfig &devConf,
bool shell)
: ssh(Ne7SshObject::instance()->get()),
m_channel(-1),
m_stopRequested(false)
{
const QString *authString;
int (ne7ssh::*connFunc)(const char *, int, const char *, const char *, bool, int);
if (devConf.authentication == MaemoDeviceConfig::Password) {
authString = &devConf.pwd;
connFunc = &ne7ssh::connectWithPassword;
} else {
authString = &devConf.keyFile;
connFunc = &ne7ssh::connectWithKey;
}
m_channel = (ssh.data()->*connFunc)(devConf.host.toLatin1(), devConf.sshPort,
devConf.uname.toAscii(), authString->toLatin1(), shell, devConf.timeout);
if (m_channel == -1)
throw MaemoSshException(tr("Could not connect to host"));
}
MaemoSshConnection::~MaemoSshConnection()
{
qDebug("%s", Q_FUNC_INFO);
ssh->close(m_channel);
}
const char *MaemoSshConnection::lastError()
{
return ssh->errors()->pop(channel());
}
void MaemoSshConnection::stop()
{
m_stopRequested = true;
}
MaemoInteractiveSshConnection::MaemoInteractiveSshConnection(const MaemoDeviceConfig &devConf)
: MaemoSshConnection(devConf, true)
{
strcpy(m_prompt, devConf.uname == QLatin1String("root") ? "# " : "$ ");
if (!ssh->waitFor(channel(), m_prompt, devConf.timeout)) {
QScopedPointer<char, QScopedPointerArrayDeleter<char> >
shellString(ssh->readAndReset(channel(), alloc));
if (!shellString.data()) {
const QString error
= tr("Could not start remote shell: %1").arg(lastError());
throw MaemoSshException(error);
} else {
const int length = strlen(shellString.data());
strcpy(m_prompt, shellString.data() + length - qMin(2, length));
}
}
}
MaemoInteractiveSshConnection::~MaemoInteractiveSshConnection()
{
ssh->send("exit\n", channel());
ssh->waitFor(channel(), m_prompt, 1);
}
void MaemoInteractiveSshConnection::runCommand(const QString &command)
{
/*
* We don't have access to the remote process management, so we
* try to track the lifetime of the process by adding a second command
* that prints a rare character. When it occurs for the second time (the
* first one is the echo from the remote terminal), we assume the
* process has finished. If anyone actually prints this special character
* in their application, they are out of luck.
*/
const QString endMarker(QChar(0x13a0));
const int endMarkerLen = strlen(endMarker.toUtf8());
const QString finalCommand
= command + QLatin1String(";echo ") + endMarker + QLatin1Char('\n');
if (!ssh->send(finalCommand.toUtf8().data(), channel())) {
throw MaemoSshException(tr("Error running command: %1")
.arg(lastError()));
}
int endMarkerCount = 0;
do {
ssh->waitFor(channel(), endMarker.toUtf8(), 1); // TODO: Hack net7 to get rid of busy loop.
const char * const error = lastError();
if (error)
throw MaemoSshException(tr("SSH error: %1").arg(error));
QScopedPointer<char, QScopedPointerArrayDeleter<char> >
output(ssh->readAndReset(channel(), alloc));
/*
* The output the user should see is everything after the first
* and before the last occurrence of our marker string.
*/
if (output.data()) {
const char *firstCharToEmit;
int charsToEmitCount;
const char * const endMarkerPos
= strstr(output.data(), endMarker.toUtf8());
if (endMarkerPos) {
if (endMarkerCount++ == 0) {
emit remoteOutput(QLatin1String("========== Remote output starts now. ==========\n"));
firstCharToEmit = endMarkerPos + endMarkerLen + 1;
const char * const endMarkerPos2
= strstr(firstCharToEmit, endMarker.toUtf8());
if (endMarkerPos2) {
++ endMarkerCount;
charsToEmitCount = endMarkerPos2 - firstCharToEmit;
} else {
charsToEmitCount = -1;
}
} else {
firstCharToEmit = output.data();
charsToEmitCount = endMarkerPos - output.data();
}
} else {
if (endMarkerCount == 0) {
charsToEmitCount = 0;
} else {
firstCharToEmit = output.data();
charsToEmitCount = -1;
}
}
if (charsToEmitCount != 0)
emit remoteOutput(QString::fromUtf8(firstCharToEmit, charsToEmitCount));
}
} while (endMarkerCount < 2 && !stopRequested());
emit remoteOutput(QLatin1String("========== Remote output ends now. ==========\n"));
}
MaemoInteractiveSshConnection::Ptr MaemoInteractiveSshConnection::create(const MaemoDeviceConfig &devConf)
{
return Ptr(new MaemoInteractiveSshConnection(devConf));
}
MaemoSftpConnection::MaemoSftpConnection(const MaemoDeviceConfig &devConf)
: MaemoSshConnection(devConf, false),
sftp(new Ne7SftpSubsystem)
{
if (!ssh->initSftp(*sftp, channel()) || !sftp->setTimeout(devConf.timeout))
throw MaemoSshException(tr("Error setting up SFTP subsystem: %1")
.arg(lastError()));
}
MaemoSftpConnection::~MaemoSftpConnection()
{
}
class FileManager
{
public:
FileManager(const QString &filePath)
: m_file(fopen(filePath.toLatin1().data(), "rb")) {}
~FileManager() { if (m_file) fclose(m_file); }
FILE *file() const { return m_file; }
private:
FILE * const m_file;
};
void MaemoSftpConnection::transferFiles(const QList<SshDeploySpec> &deploySpecs)
{
for (int i = 0; i < deploySpecs.count() && !stopRequested(); ++i) {
const SshDeploySpec &deploySpec = deploySpecs.at(i);
const QString &curSrcFile = deploySpec.srcFilePath();
FileManager fileMgr(curSrcFile);
if (!fileMgr.file())
throw MaemoSshException(tr("Could not open file '%1'").arg(curSrcFile));
const QString &curTgtFile = deploySpec.tgtFilePath();
// TODO: Is the mkdir() method recursive? If not, we have to
// introduce a recursive version ourselves.
if (deploySpec.mkdir()) {
const QString &dir = QFileInfo(curTgtFile).path();
sftp->mkdir(dir.toLatin1().data());
}
qDebug("Deploying file %s to %s.", qPrintable(curSrcFile), qPrintable(curTgtFile));
if (!sftp->put(fileMgr.file(), curTgtFile.toLatin1().data())) {
const QString &error = tr("Could not copy local file '%1' "
"to remote file '%2': %3").arg(curSrcFile, curTgtFile)
.arg(lastError());
throw MaemoSshException(error);
}
emit fileCopied(curSrcFile);
}
}
MaemoSftpConnection::Ptr MaemoSftpConnection::create(const MaemoDeviceConfig &devConf)
{
return Ptr(new MaemoSftpConnection(devConf));
}
} // namespace Internal
} // namespace Qt4ProjectManager

View File

@@ -1,152 +0,0 @@
/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the Qt Creator.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef MAEMOSSHCONNECTION_H
#define MAEMOSSHCONNECTION_H
#include <QtCore/QObject>
#include <QtCore/QScopedPointer>
#include <QtCore/QSharedPointer>
#include <QtCore/QString>
class ne7ssh;
class Ne7SftpSubsystem;
namespace Qt4ProjectManager {
namespace Internal {
class MaemoDeviceConfig;
class MaemoSshException
{
public:
MaemoSshException(const QString &error) : m_error(error) {}
const QString &error() const { return m_error; }
private:
const QString m_error;
};
class MaemoSshConnection : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(MaemoSshConnection)
public:
typedef QSharedPointer<MaemoSshConnection> Ptr;
void stop();
virtual ~MaemoSshConnection();
protected:
MaemoSshConnection(const MaemoDeviceConfig &devConf, bool shell);
int channel() const { return m_channel; }
bool stopRequested() const {return m_stopRequested; }
const char *lastError();
QSharedPointer<ne7ssh> ssh;
private:
int m_channel;
volatile bool m_stopRequested;
};
class MaemoInteractiveSshConnection : public MaemoSshConnection
{
Q_OBJECT
Q_DISABLE_COPY(MaemoInteractiveSshConnection)
public:
typedef QSharedPointer<MaemoInteractiveSshConnection> Ptr;
static Ptr create(const MaemoDeviceConfig &devConf);
void runCommand(const QString &command);
virtual ~MaemoInteractiveSshConnection();
signals:
void remoteOutput(const QString &output);
private:
MaemoInteractiveSshConnection(const MaemoDeviceConfig &devConf);
char m_prompt[3];
};
class SshDeploySpec
{
public:
SshDeploySpec(const QString &srcFilePath, const QString &tgtFilePath,
bool mkdir = false)
: m_srcFilePath(srcFilePath), m_tgtFilePath(tgtFilePath), m_mkdir(mkdir)
{
}
QString srcFilePath() const { return m_srcFilePath; }
QString tgtFilePath() const { return m_tgtFilePath; }
bool mkdir() const { return m_mkdir; }
private:
QString m_srcFilePath;
QString m_tgtFilePath;
bool m_mkdir;
};
class MaemoSftpConnection : public MaemoSshConnection
{
Q_OBJECT
Q_DISABLE_COPY(MaemoSftpConnection)
public:
typedef QSharedPointer<MaemoSftpConnection> Ptr;
static Ptr create(const MaemoDeviceConfig &devConf);
void transferFiles(const QList<SshDeploySpec> &deploySpecs);
virtual ~MaemoSftpConnection();
signals:
void fileCopied(const QString &filePath);
private:
MaemoSftpConnection(const MaemoDeviceConfig &devConf);
QScopedPointer<Ne7SftpSubsystem> sftp;
};
} // namespace Internal
} // namespace Qt4ProjectManager
#endif // MAEMOSSHCONNECTION_H

View File

@@ -41,86 +41,164 @@
#include "maemosshthread.h" #include "maemosshthread.h"
#include <exception>
namespace Qt4ProjectManager { namespace Qt4ProjectManager {
namespace Internal { namespace Internal {
MaemoSshThread::MaemoSshThread(const MaemoDeviceConfig &devConf) template <class SshConnection> MaemoSshThread<SshConnection>::MaemoSshThread(const Core::SshServerInfo &server)
: m_stopRequested(false), m_devConf(devConf) : m_server(server), m_stopRequested(false)
{ {
} }
MaemoSshThread::~MaemoSshThread() template <class SshConnection> MaemoSshThread<SshConnection>::~MaemoSshThread()
{ {
stop(); stop();
wait(); wait();
} }
void MaemoSshThread::run() template <class SshConnection> void MaemoSshThread<SshConnection>::run()
{ {
try { if (m_stopRequested)
if (!m_stopRequested) return;
runInternal();
} catch (const MaemoSshException &e) { if (!runInternal())
m_error = e.error(); m_error = m_connection->error();
} catch (const std::exception &e) {
// Should in theory not be necessary, but Net7 leaks Botan exceptions.
m_error = tr("Error in cryptography backend: %1").arg(QLatin1String(e.what()));
}
} }
void MaemoSshThread::stop() template<class SshConnection> void MaemoSshThread<SshConnection>::stop()
{ {
m_mutex.lock(); m_mutex.lock();
m_stopRequested = true; m_stopRequested = true;
m_waitCond.wakeAll();
const bool hasConnection = !m_connection.isNull(); const bool hasConnection = !m_connection.isNull();
m_mutex.unlock();
if (hasConnection) if (hasConnection)
m_connection->stop(); m_connection->quit();
m_mutex.unlock();
} }
template <class Conn> typename Conn::Ptr MaemoSshThread::createConnection() template<class SshConnection> void MaemoSshThread<SshConnection>::waitForStop()
{ {
typename Conn::Ptr connection = Conn::create(m_devConf); m_mutex.lock();
while (!stopRequested())
m_waitCond.wait(&m_mutex);
m_mutex.unlock();
}
template<class SshConnection> void MaemoSshThread<SshConnection>::createConnection()
{
typename SshConnection::Ptr connection = SshConnection::create(m_server);
m_mutex.lock(); m_mutex.lock();
m_connection = connection; m_connection = connection;
m_mutex.unlock(); m_mutex.unlock();
return connection;
} }
MaemoSshRunner::MaemoSshRunner(const MaemoDeviceConfig &devConf, MaemoSshRunner::MaemoSshRunner(const Core::SshServerInfo &server,
const QString &command) const QString &command)
: MaemoSshThread(devConf), m_command(command) : MaemoSshThread<Core::InteractiveSshConnection>(server),
m_command(command)
{ {
m_prompt = server.uname == QLatin1String("root") ? "#" : "$";
} }
void MaemoSshRunner::runInternal() bool MaemoSshRunner::runInternal()
{ {
MaemoInteractiveSshConnection::Ptr connection createConnection();
= createConnection<MaemoInteractiveSshConnection>(); connect(m_connection.data(), SIGNAL(remoteOutput(QByteArray)),
this, SLOT(handleRemoteOutput(QByteArray)));
m_endMarkerCount = 0;
m_promptEncountered = false;
if (!m_connection->start())
return false;
if (stopRequested()) if (stopRequested())
return true;
waitForStop();
return !m_connection->hasError();
}
void MaemoSshRunner::handleRemoteOutput(const QByteArray &output)
{
// Wait for a prompt before sending the command.
if (!m_promptEncountered) {
if (output.indexOf(m_prompt) != -1) {
m_promptEncountered = true;
/*
* We don't have access to the remote process management, so we
* try to track the lifetime of the process by adding a second command
* that prints a rare character. When it occurs for the second time (the
* first one is the echo from the remote terminal), we assume the
* process has finished. If anyone actually prints this special character
* in their application, they are out of luck.
*/
const QString finalCommand = m_command + QLatin1String(";echo ")
+ QString::fromUtf8(EndMarker) + QLatin1Char('\n');
if (!m_connection->sendInput(finalCommand.toUtf8()))
stop();
}
return; return;
connect(connection.data(), SIGNAL(remoteOutput(QString)),
this, SIGNAL(remoteOutput(QString)));
connection->runCommand(m_command);
} }
MaemoSshDeployer::MaemoSshDeployer(const MaemoDeviceConfig &devConf, /*
const QList<SshDeploySpec> &deploySpecs) * The output the user should see is everything after the first
: MaemoSshThread(devConf), m_deploySpecs(deploySpecs) * and before the last occurrence of our marker string.
* Note: We don't currently handle the case of an incomplete unicode
* character being sent.
*/
int firstCharToEmit;
int charsToEmitCount;
const int endMarkerPos = output.indexOf(EndMarker);
if (endMarkerPos != -1) {
if (m_endMarkerCount++ == 0) {
firstCharToEmit = endMarkerPos + EndMarker.count() + 1;
int endMarkerPos2
= output.indexOf(EndMarker, firstCharToEmit);
if (endMarkerPos2 != -1) {
++ m_endMarkerCount;
charsToEmitCount = endMarkerPos2 - firstCharToEmit;
} else {
charsToEmitCount = -1;
}
} else {
firstCharToEmit = 0;
charsToEmitCount = endMarkerPos;
}
} else {
if (m_endMarkerCount == 0) {
charsToEmitCount = 0;
} else {
firstCharToEmit = 0;
charsToEmitCount = -1;
}
}
if (charsToEmitCount != 0)
emit remoteOutput(QString::fromUtf8(output.data() + firstCharToEmit,
charsToEmitCount));
if (m_endMarkerCount == 2)
stop();
}
const QByteArray MaemoSshRunner::EndMarker(QString(QChar(0x13a0)).toUtf8());
MaemoSshDeployer::MaemoSshDeployer(const Core::SshServerInfo &server,
const QList<Core::SftpTransferInfo> &deploySpecs)
: MaemoSshThread<Core::SftpConnection>(server),
m_deploySpecs(deploySpecs)
{ {
} }
void MaemoSshDeployer::runInternal() bool MaemoSshDeployer::runInternal()
{ {
MaemoSftpConnection::Ptr connection createConnection();
= createConnection<MaemoSftpConnection>(); if (!m_connection->start())
return false;
if (stopRequested()) if (stopRequested())
return; return true;
connect(connection.data(), SIGNAL(fileCopied(QString)),
connect(m_connection.data(), SIGNAL(fileCopied(QString)),
this, SIGNAL(fileCopied(QString))); this, SIGNAL(fileCopied(QString)));
connection->transferFiles(m_deploySpecs); return m_connection->transferFiles(m_deploySpecs);
} }
} // namespace Internal } // namespace Internal

View File

@@ -43,18 +43,20 @@
#define MAEMOSSHTHREAD_H #define MAEMOSSHTHREAD_H
#include "maemodeviceconfigurations.h" #include "maemodeviceconfigurations.h"
#include "maemosshconnection.h"
#include <coreplugin/ssh/sshconnection.h>
#include <QtCore/QByteArray>
#include <QtCore/QList> #include <QtCore/QList>
#include <QtCore/QMutex> #include <QtCore/QMutex>
#include <QtCore/QThread> #include <QtCore/QThread>
#include <QtCore/QWaitCondition>
namespace Qt4ProjectManager { namespace Qt4ProjectManager {
namespace Internal { namespace Internal {
class MaemoSshThread : public QThread template<class SshConnection> class MaemoSshThread : public QThread
{ {
Q_OBJECT
Q_DISABLE_COPY(MaemoSshThread) Q_DISABLE_COPY(MaemoSshThread)
public: public:
QString error() const { return m_error; } QString error() const { return m_error; }
@@ -64,53 +66,62 @@ public:
~MaemoSshThread(); ~MaemoSshThread();
protected: protected:
MaemoSshThread(const MaemoDeviceConfig &devConf); MaemoSshThread(const Core::SshServerInfo &server);
template <class Conn> typename Conn::Ptr createConnection(); void createConnection();
bool stopRequested() const { return m_stopRequested; } bool stopRequested() const { return m_stopRequested; }
void waitForStop();
typename SshConnection::Ptr m_connection;
private: private:
virtual void runInternal() = 0; virtual bool runInternal() = 0;
const Core::SshServerInfo m_server;
bool m_stopRequested; bool m_stopRequested;
QString m_error; QString m_error;
QMutex m_mutex; QMutex m_mutex;
const MaemoDeviceConfig m_devConf; QWaitCondition m_waitCond;
MaemoSshConnection::Ptr m_connection;
}; };
class MaemoSshRunner : public MaemoSshThread class MaemoSshRunner : public MaemoSshThread<Core::InteractiveSshConnection>
{ {
Q_OBJECT Q_OBJECT
Q_DISABLE_COPY(MaemoSshRunner) Q_DISABLE_COPY(MaemoSshRunner)
public: public:
MaemoSshRunner(const MaemoDeviceConfig &devConf, const QString &command); MaemoSshRunner(const Core::SshServerInfo &server, const QString &command);
signals: signals:
void remoteOutput(const QString &output); void remoteOutput(const QString &output);
private: private:
virtual void runInternal(); virtual bool runInternal();
Q_SLOT void handleRemoteOutput(const QByteArray &output);
static const QByteArray EndMarker;
const QString m_command; const QString m_command;
const char *m_prompt;
int m_endMarkerCount;
bool m_promptEncountered;
}; };
class MaemoSshDeployer : public MaemoSshThread class MaemoSshDeployer : public MaemoSshThread<Core::SftpConnection>
{ {
Q_OBJECT Q_OBJECT
Q_DISABLE_COPY(MaemoSshDeployer) Q_DISABLE_COPY(MaemoSshDeployer)
public: public:
MaemoSshDeployer(const MaemoDeviceConfig &devConf, MaemoSshDeployer(const Core::SshServerInfo &server,
const QList<SshDeploySpec> &deploySpecs); const QList<Core::SftpTransferInfo> &deploySpecs);
signals: signals:
void fileCopied(const QString &filePath); void fileCopied(const QString &filePath);
private: private:
virtual void runInternal(); virtual bool runInternal();
const QList<SshDeploySpec> m_deploySpecs; const QList<Core::SftpTransferInfo> m_deploySpecs;
}; };
} // namespace Internal } // namespace Internal

View File

@@ -1,7 +1,3 @@
INCLUDEPATH += $$PWD/../../../libs/3rdparty/botan/build
INCLUDEPATH += $$PWD/../../../libs/3rdparty/net7ssh/src
LIBS += -l$$qtLibraryTarget(Net7ssh) -l$$qtLibraryTarget(Botan)
HEADERS += \ HEADERS += \
$$PWD/maemoconfigtestdialog.h \ $$PWD/maemoconfigtestdialog.h \
$$PWD/maemoconstants.h \ $$PWD/maemoconstants.h \
@@ -14,12 +10,10 @@ HEADERS += \
$$PWD/maemosettingspage.h \ $$PWD/maemosettingspage.h \
$$PWD/maemosettingswidget.h \ $$PWD/maemosettingswidget.h \
$$PWD/maemosshconfigdialog.h \ $$PWD/maemosshconfigdialog.h \
$$PWD/maemosshconnection.h \
$$PWD/maemosshthread.h \ $$PWD/maemosshthread.h \
$$PWD/maemotoolchain.h \ $$PWD/maemotoolchain.h \
$$PWD/maemopackagecreationstep.h \ $$PWD/maemopackagecreationstep.h \
$$PWD/maemopackagecreationfactory.h \ $$PWD/maemopackagecreationfactory.h \
$$PWD/ne7sshobject.h \
$$PWD/maemopackagecreationwidget.h \ $$PWD/maemopackagecreationwidget.h \
$$PWD/maemopackagecontents.h $$PWD/maemopackagecontents.h
@@ -34,12 +28,10 @@ SOURCES += \
$$PWD/maemosettingspage.cpp \ $$PWD/maemosettingspage.cpp \
$$PWD/maemosettingswidget.cpp \ $$PWD/maemosettingswidget.cpp \
$$PWD/maemosshconfigdialog.cpp \ $$PWD/maemosshconfigdialog.cpp \
$$PWD/maemosshconnection.cpp \
$$PWD/maemosshthread.cpp \ $$PWD/maemosshthread.cpp \
$$PWD/maemotoolchain.cpp \ $$PWD/maemotoolchain.cpp \
$$PWD/maemopackagecreationstep.cpp \ $$PWD/maemopackagecreationstep.cpp \
$$PWD/maemopackagecreationfactory.cpp \ $$PWD/maemopackagecreationfactory.cpp \
$$PWD/ne7sshobject.cpp \
$$PWD/maemopackagecreationwidget.cpp \ $$PWD/maemopackagecreationwidget.cpp \
$$PWD/maemopackagecontents.cpp $$PWD/maemopackagecontents.cpp