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 += .
INCLUDEPATH += $$PWD $$PWD/../../botan $$PWD/../../botan/build
LIBS += -l$$qtLibraryTarget(Botan)
include(../net7ssh_dependencies.pri)
win32 {
LIBS += -lWs2_32

View File

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

View File

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

View File

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

View File

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

View File

@@ -42,22 +42,26 @@
#ifndef NE7SSHOBJECT_H
#define NE7SSHOBJECT_H
#include <coreplugin/core_global.h>
#include <QtCore/QMutex>
#include <QtCore/QSharedPointer>
#include <QtCore/QWeakPointer>
class ne7ssh;
namespace Qt4ProjectManager {
namespace Core {
namespace Internal {
class Ne7SshObject
{
public:
typedef QSharedPointer<ne7ssh> Ptr;
static Ne7SshObject *instance();
static void removeInstance();
QSharedPointer<ne7ssh> get();
Ptr get();
private:
Ne7SshObject();
@@ -71,6 +75,6 @@ private:
};
} // namespace Internal
} // namespace Qt4ProjectManager
} // namespace Core
#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' "
"|cut -d ' ' -f 2,3 |sed 's/~.*//g'");
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)),
this, SLOT(processSshOutput(QString)));
connect(m_deviceTester, SIGNAL(finished()),
@@ -126,12 +126,12 @@ void MaemoConfigTestDialog::handleTestThreadFinished()
output.append(tr("\nDid you start Qemu?"));
} else {
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);
if (!m_qtVersionOk) {
m_ui->errorLabel->setText(tr("Qt version mismatch! Expected Qt on device: "
"4.6.2 or later."));
}
stopConfigTest();
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -35,23 +35,23 @@
#include "maemosshconfigdialog.h"
#include "maemodeviceconfigurations.h"
#include "ne7sshobject.h"
#include <ne7ssh.h>
#include <coreplugin/ssh/sshkeygenerator.h>
#include <QtCore/QDir>
#include <QtNetwork/QHostInfo>
#include <QtGui/QApplication>
#include <QtGui/QDesktopServices>
#include <QtGui/QFileDialog>
#include <QtGui/QMessageBox>
#include <QtNetwork/QHostInfo>
using namespace Qt4ProjectManager::Internal;
MaemoSshConfigDialog::MaemoSshConfigDialog(QWidget *parent)
: QDialog(parent)
, home(QDesktopServices::storageLocation(QDesktopServices::HomeLocation))
, m_keyGenerator(new Core::SshKeyGenerator)
{
m_ui.setupUi(this);
@@ -75,26 +75,22 @@ void MaemoSshConfigDialog::slotToggled()
void MaemoSshConfigDialog::generateSshKey()
{
algorithm = m_ui.rsa->isChecked() ? "rsa" : "dsa";
tmpKey = QDir::tempPath().append(QLatin1Char('/') + algorithm).toUtf8();
const Core::SshKeyGenerator::KeyType keyType = m_ui.rsa->isChecked()
? Core::SshKeyGenerator::Rsa
: Core::SshKeyGenerator::Dsa;
QByteArray userId = QString(home.mid(home.lastIndexOf(QLatin1Char('/')) + 1)
+ QLatin1Char('@') + QHostInfo::localHostName()).toUtf8();
QFile::remove(tmpKey);
QFile::remove(tmpKey + ".pub");
QApplication::setOverrideCursor(Qt::BusyCursor);
QSharedPointer<ne7ssh> ssh = Ne7SshObject::instance()->get();
if (ssh->generateKeyPair(algorithm, userId, tmpKey, tmpKey + ".pub",
m_ui.comboBox->currentText().toUShort())) {
QFile file(tmpKey + ".pub");
if (file.open(QIODevice::ReadOnly))
m_ui.plainTextEdit->setPlainText(file.readAll());
if (m_keyGenerator->generateKeys(keyType, userId,
m_ui.comboBox->currentText().toUShort())) {
m_ui.plainTextEdit->setPlainText(m_keyGenerator->publicKey());
m_ui.savePublicKey->setEnabled(true);
m_ui.savePrivateKey->setEnabled(true);
} else {
m_ui.plainTextEdit->setPlainText(tr("Could not create SSH key pair."));
m_ui.plainTextEdit->setPlainText(m_keyGenerator->error());
}
QApplication::restoreOverrideCursor();
@@ -102,18 +98,12 @@ void MaemoSshConfigDialog::generateSshKey()
void MaemoSshConfigDialog::savePublicKey()
{
checkSshDir();
copyFile(QFileDialog::getSaveFileName(this, tr("Choose folder to save "
"public key file"), home + QString::fromLatin1("/.ssh/id_%1.pub")
.arg(algorithm.constData())), true);
saveKey(true);
}
void MaemoSshConfigDialog::savePrivateKey()
{
checkSshDir();
copyFile(QFileDialog::getSaveFileName(this, tr("Choose folder to save "
"private key file"), home + QString::fromLatin1("/.ssh/id_%1")
.arg(algorithm.constData())), false);
saveKey(false);
}
void MaemoSshConfigDialog::checkSshDir()
@@ -123,15 +113,31 @@ void MaemoSshConfigDialog::checkSshDir()
dir.mkpath(home + QString::fromLatin1("/.ssh"));
}
void MaemoSshConfigDialog::copyFile(const QString &file, bool pubKey)
void MaemoSshConfigDialog::saveKey(bool publicKey)
{
if (!file.isEmpty()) {
if (!QFile::exists(file) || QFile::remove(file)) {
QFile(tmpKey + (pubKey ? ".pub" : "")).copy(file);
if (pubKey)
emit publicKeyGenerated(file);
else
emit privateKeyGenerated(file);
}
checkSshDir();
const QString suggestedTypeSuffix =
m_keyGenerator->type() == Core::SshKeyGenerator::Rsa ? "rsa" : "dsa";
const QString suggestedName = home + QString::fromLatin1("/.ssh/id_%1%2")
.arg(suggestedTypeSuffix).arg(publicKey ? ".pub" : "");
const QString dlgTitle
= 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 <QtCore/QScopedPointer>
#include <QtGui/QDialog>
namespace Core {
class SshKeyGenerator;
}
namespace Qt4ProjectManager {
namespace Internal {
@@ -46,11 +51,10 @@ class MaemoSshConfigDialog : public QDialog
{
Q_OBJECT
public:
MaemoSshConfigDialog(QWidget *parent = 0);
~MaemoSshConfigDialog();
MaemoSshConfigDialog(QWidget *parent = 0);
~MaemoSshConfigDialog();
signals:
void publicKeyGenerated(const QString &path);
void privateKeyGenerated(const QString &path);
private slots:
@@ -61,12 +65,11 @@ private slots:
private:
void checkSshDir();
void copyFile(const QString &file, bool pubKey);
void saveKey(bool publicKey);
private:
QString home;
QByteArray tmpKey;
QByteArray algorithm;
QScopedPointer<Core::SshKeyGenerator> m_keyGenerator;
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 <exception>
namespace Qt4ProjectManager {
namespace Internal {
MaemoSshThread::MaemoSshThread(const MaemoDeviceConfig &devConf)
: m_stopRequested(false), m_devConf(devConf)
template <class SshConnection> MaemoSshThread<SshConnection>::MaemoSshThread(const Core::SshServerInfo &server)
: m_server(server), m_stopRequested(false)
{
}
MaemoSshThread::~MaemoSshThread()
template <class SshConnection> MaemoSshThread<SshConnection>::~MaemoSshThread()
{
stop();
wait();
}
void MaemoSshThread::run()
template <class SshConnection> void MaemoSshThread<SshConnection>::run()
{
try {
if (!m_stopRequested)
runInternal();
} catch (const MaemoSshException &e) {
m_error = e.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()));
}
if (m_stopRequested)
return;
if (!runInternal())
m_error = m_connection->error();
}
void MaemoSshThread::stop()
template<class SshConnection> void MaemoSshThread<SshConnection>::stop()
{
m_mutex.lock();
m_stopRequested = true;
m_waitCond.wakeAll();
const bool hasConnection = !m_connection.isNull();
m_mutex.unlock();
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_connection = connection;
m_mutex.unlock();
return connection;
}
MaemoSshRunner::MaemoSshRunner(const MaemoDeviceConfig &devConf,
MaemoSshRunner::MaemoSshRunner(const Core::SshServerInfo &server,
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<MaemoInteractiveSshConnection>();
createConnection();
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())
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;
connect(connection.data(), SIGNAL(remoteOutput(QString)),
this, SIGNAL(remoteOutput(QString)));
connection->runCommand(m_command);
}
/*
* The output the user should see is everything after the first
* 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();
}
MaemoSshDeployer::MaemoSshDeployer(const MaemoDeviceConfig &devConf,
const QList<SshDeploySpec> &deploySpecs)
: MaemoSshThread(devConf), m_deploySpecs(deploySpecs)
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<MaemoSftpConnection>();
createConnection();
if (!m_connection->start())
return false;
if (stopRequested())
return;
connect(connection.data(), SIGNAL(fileCopied(QString)),
return true;
connect(m_connection.data(), SIGNAL(fileCopied(QString)),
this, SIGNAL(fileCopied(QString)));
connection->transferFiles(m_deploySpecs);
return m_connection->transferFiles(m_deploySpecs);
}
} // namespace Internal

View File

@@ -43,18 +43,20 @@
#define MAEMOSSHTHREAD_H
#include "maemodeviceconfigurations.h"
#include "maemosshconnection.h"
#include <coreplugin/ssh/sshconnection.h>
#include <QtCore/QByteArray>
#include <QtCore/QList>
#include <QtCore/QMutex>
#include <QtCore/QThread>
#include <QtCore/QWaitCondition>
namespace Qt4ProjectManager {
namespace Internal {
class MaemoSshThread : public QThread
template<class SshConnection> class MaemoSshThread : public QThread
{
Q_OBJECT
Q_DISABLE_COPY(MaemoSshThread)
public:
QString error() const { return m_error; }
@@ -64,53 +66,62 @@ public:
~MaemoSshThread();
protected:
MaemoSshThread(const MaemoDeviceConfig &devConf);
template <class Conn> typename Conn::Ptr createConnection();
MaemoSshThread(const Core::SshServerInfo &server);
void createConnection();
bool stopRequested() const { return m_stopRequested; }
void waitForStop();
typename SshConnection::Ptr m_connection;
private:
virtual void runInternal() = 0;
virtual bool runInternal() = 0;
const Core::SshServerInfo m_server;
bool m_stopRequested;
QString m_error;
QMutex m_mutex;
const MaemoDeviceConfig m_devConf;
MaemoSshConnection::Ptr m_connection;
QWaitCondition m_waitCond;
};
class MaemoSshRunner : public MaemoSshThread
class MaemoSshRunner : public MaemoSshThread<Core::InteractiveSshConnection>
{
Q_OBJECT
Q_DISABLE_COPY(MaemoSshRunner)
public:
MaemoSshRunner(const MaemoDeviceConfig &devConf, const QString &command);
MaemoSshRunner(const Core::SshServerInfo &server, const QString &command);
signals:
void remoteOutput(const QString &output);
private:
virtual void runInternal();
virtual bool runInternal();
Q_SLOT void handleRemoteOutput(const QByteArray &output);
static const QByteArray EndMarker;
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_DISABLE_COPY(MaemoSshDeployer)
public:
MaemoSshDeployer(const MaemoDeviceConfig &devConf,
const QList<SshDeploySpec> &deploySpecs);
MaemoSshDeployer(const Core::SshServerInfo &server,
const QList<Core::SftpTransferInfo> &deploySpecs);
signals:
void fileCopied(const QString &filePath);
private:
virtual void runInternal();
virtual bool runInternal();
const QList<SshDeploySpec> m_deploySpecs;
const QList<Core::SftpTransferInfo> m_deploySpecs;
};
} // 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 += \
$$PWD/maemoconfigtestdialog.h \
$$PWD/maemoconstants.h \
@@ -14,12 +10,10 @@ HEADERS += \
$$PWD/maemosettingspage.h \
$$PWD/maemosettingswidget.h \
$$PWD/maemosshconfigdialog.h \
$$PWD/maemosshconnection.h \
$$PWD/maemosshthread.h \
$$PWD/maemotoolchain.h \
$$PWD/maemopackagecreationstep.h \
$$PWD/maemopackagecreationfactory.h \
$$PWD/ne7sshobject.h \
$$PWD/maemopackagecreationwidget.h \
$$PWD/maemopackagecontents.h
@@ -34,12 +28,10 @@ SOURCES += \
$$PWD/maemosettingspage.cpp \
$$PWD/maemosettingswidget.cpp \
$$PWD/maemosshconfigdialog.cpp \
$$PWD/maemosshconnection.cpp \
$$PWD/maemosshthread.cpp \
$$PWD/maemotoolchain.cpp \
$$PWD/maemopackagecreationstep.cpp \
$$PWD/maemopackagecreationfactory.cpp \
$$PWD/ne7sshobject.cpp \
$$PWD/maemopackagecreationwidget.cpp \
$$PWD/maemopackagecontents.cpp