├── .gitignore ├── LICENSE ├── README ├── demos ├── demos.pro ├── filetoprocess │ ├── filetoprocess.cpp │ ├── filetoprocess.h │ ├── filetoprocess.pro │ └── main.cpp ├── processtofile │ ├── main.cpp │ ├── processtofile.cpp │ ├── processtofile.h │ └── processtofile.pro ├── runprocess │ ├── main.cpp │ ├── runprocess.cpp │ ├── runprocess.h │ └── runprocess.pro └── runprocessgui │ ├── main.cpp │ ├── runprocessgui.cpp │ ├── runprocessgui.h │ ├── runprocessgui.pro │ └── runprocessgui.ui ├── libssh.pri ├── libsshqt.pri ├── libsshqtgui.pri ├── src ├── libsshqtchannel.cpp ├── libsshqtchannel.h ├── libsshqtclient.cpp ├── libsshqtclient.h ├── libsshqtdebug.h ├── libsshqtprocess.cpp ├── libsshqtprocess.h ├── libsshqtquestionconsole.cpp ├── libsshqtquestionconsole.h ├── libsshqtquestiondialog.cpp ├── libsshqtquestiondialog.h └── libsshqtquestiondialog.ui └── test ├── test.cpp └── test.pro /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.moc 3 | moc_* 4 | ui_* 5 | Makefile 6 | *.pro.user 7 | *.kate-swp 8 | *.autosave 9 | 10 | libsshqt-runprocess 11 | libsshqt-processtofile 12 | libsshqt-filetoprocess 13 | libsshqt-runprocessgui 14 | 15 | libsshqt-test 16 | libsshqt-test-config 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Copyright (c) 2012 Arto Karppinen 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining 5 | a copy of this software and associated documentation files (the 6 | "Software"), to deal in the Software without restriction, including 7 | without limitation the rights to use, copy, modify, merge, publish, 8 | distribute, sublicense, and/or sell copies of the Software, and to 9 | permit persons to whom the Software is furnished to do so, subject to 10 | the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | libsshqt, The libssh Qt Wrapper 5 | ================================= 6 | 7 | - First, libsshqt is not a SSH implementation, it only wraps libssh functions 8 | and data structures to Qt classes. 9 | 10 | - Currently only starting remote processes is supported. It should be easy to 11 | add support for other features later. 12 | 13 | - libsshqt does not attempt to hide the fact that you are using libssh, you can 14 | still access libssh directly if you need to. 15 | 16 | - Older versions of libssh have bugs in asynchronous mode, which may cause 17 | problems. 18 | 19 | 20 | 21 | Supported environment 22 | ======================= 23 | 24 | I have tested libsshqt in OpenSUSE 12.1, with libssh 0.5.1 and Qt 4.7.4. 25 | 26 | I do however recommend that you use at least libssh version 0.5.2, because 27 | asynchronous mode is somewhat incomplete in older versions, and may block in 28 | some libssh functions. 29 | 30 | I don't see any reason why libsshqt should not work in other environments than 31 | Linux, but patches are welcome if you find problems. 32 | 33 | 34 | 35 | Author 36 | ======== 37 | 38 | Arto Karppinen 39 | 40 | 41 | 42 | How to use libsshqt 43 | ===================== 44 | 45 | Currently libsshqt isn't a library, it is just couple of wrapper classes which 46 | you are supposed to build directly into your application. 47 | 48 | 1. Extract libsshqt: 49 | 50 | tar xvf libsshqt.tar 51 | 52 | 2. Include libsshqt.pri in your QMake project file: 53 | 54 | include(./libsshqt/libsshqt.pri) 55 | 56 | ...or just copy the sources to your source directory. 57 | 58 | 59 | 60 | Debugging libsshqt 61 | ==================== 62 | 63 | You can enable debugging in libsshqt by setting the environment variable 64 | LIBSSHQT_DEBUG or by enabling it programmatically by calling 65 | LibsshQtClient::setDebug(). 66 | 67 | Setting LIBSSHQT_DEBUG variable in a shell: 68 | 69 | export LIBSSHQT_DEBUG=1 70 | 71 | Enabling debugging programmatically: 72 | 73 | LibsshQtClient *client = new LibsshQtClient(); 74 | client->setDebug(true); 75 | 76 | 77 | 78 | Examples 79 | ========== 80 | 81 | Check ./demos directory for inspiration. In particular ./demos/runprocess and 82 | ./demos/runprocessgui should be insightful. 83 | 84 | 85 | 86 | TODO 87 | ====== 88 | 89 | - Turn libsshqt into a Qt library. So that you can easily use it in QMake 90 | projects. 91 | 92 | - Create pkg-config files. So that people who don’t like QMake can use it 93 | easily. 94 | 95 | - D-pointerify all classes. So that the binary API can be stable. 96 | 97 | 98 | 99 | libsshqt Design 100 | ================= 101 | 102 | libssh contains support for an asynchronous mode, in this mode libssh functions 103 | return SSH_AGAIN return code when the function would otherwise block the 104 | execution of the application and wait for data. 105 | 106 | LibsshQtClient enables asynchronous mode on the ssh_session which it wraps and 107 | implements a state machine that calls and re-calls libssh functions when 108 | they return SSH_AGAIN. Once a libssh function returns successfully, the 109 | state is changed to the next state, which matches the next libssh function that 110 | needs to be called and the process is repeated. This behavior is implemented 111 | in the processState() function. 112 | 113 | LibsshQtClient uses QSocketNotifier to monitor the socket used by libssh and 114 | will call processState() function whenever the socket is either readable or 115 | writable. If the connection is in open state, LibsshQtClient sends 116 | doProcessState signal, which causes child objects to process their state in the 117 | same way that LibsshQtClient uses. 118 | 119 | If processState() needs to be executed manually, for example because user 120 | called connectToHost(), a QTimer is used to activate the function once 121 | Qt's main loop is in idle state. 122 | 123 | The result is that libsshqt classes will always call processState() from Qt's 124 | main loop and always process only one state at a time and should call libssh 125 | functions only when neccessary. This means that libsshqt should behave very 126 | nicely and should not cause any delays or excessive CPU usage. 127 | -------------------------------------------------------------------------------- /demos/demos.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = subdirs 2 | SUBDIRS = runprocess processtofile filetoprocess runprocessgui 3 | -------------------------------------------------------------------------------- /demos/filetoprocess/filetoprocess.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "filetoprocess.h" 8 | #include "libsshqtquestionconsole.h" 9 | 10 | void FileToProcess::fileToProcess() 11 | { 12 | QStringList args = qApp->arguments(); 13 | 14 | if ( args.count() != 4) { 15 | qDebug() << "Usage:" 16 | << qPrintable(args.at(0)) 17 | <<"SSH_URL FILE COMMAND"; 18 | qDebug() << "Example:" 19 | << qPrintable(args.at(0)) 20 | << QString("ssh://user@hostname:port/") 21 | << QString("input-file") 22 | << QString("cat"); 23 | qApp->quit(); 24 | return; 25 | } 26 | 27 | QUrl url(args.at(1)); 28 | client = new LibsshQtClient(this); 29 | client->setUrl(url); 30 | client->useDefaultAuths(); 31 | client->connectToHost(); 32 | 33 | process = client->runCommand(args.at(3)); 34 | 35 | file = new QFile(this); 36 | file->setFileName(args.at(2)); 37 | 38 | if ( ! file->open(QIODevice::ReadOnly)) { 39 | qDebug() << "Could not open file:" << file->fileName(); 40 | qApp->exit(-1); 41 | return; 42 | } 43 | 44 | new LibsshQtQuestionConsole(client); 45 | 46 | connect(client, SIGNAL(allAuthsFailed()), qApp, SLOT(quit())); 47 | connect(client, SIGNAL(error()), qApp, SLOT(quit())); 48 | connect(client, SIGNAL(closed()), qApp, SLOT(quit())); 49 | 50 | connect(process, SIGNAL(opened()), this, SLOT(sendData())); 51 | connect(process, SIGNAL(bytesWritten(qint64)), this, SLOT(sendData())); 52 | connect(process, SIGNAL(finished(int)), this, SLOT(endCheck())); 53 | connect(process, SIGNAL(closed()), qApp, SLOT(quit())); 54 | connect(process, SIGNAL(error()), this, SLOT(handleProcessError())); 55 | 56 | connect(file, SIGNAL(readyRead()), this, SLOT(sendData())); 57 | connect(file, SIGNAL(readChannelFinished()), this, SLOT(endCheck())); 58 | } 59 | 60 | void FileToProcess::handleProcessError() 61 | { 62 | if ( ! process->isClientError()) { 63 | qDebug() << "Process error:" 64 | << qPrintable(client->errorCodeAndMessage()); 65 | qApp->quit(); 66 | } 67 | } 68 | 69 | void FileToProcess::sendData() 70 | { 71 | const int size = 1024 * 4; 72 | 73 | if ( process->isOpen() && 74 | process->bytesToWrite() <= size && 75 | file->isOpen() && 76 | file->atEnd() == false ) { 77 | 78 | QByteArray data = file->read(size); 79 | if ( ! process->write(data)) { 80 | qDebug() << "Could not write data to process"; 81 | qApp->exit(-1); 82 | } else { 83 | qDebug() << "Sent" << data.size() << "bytes to process"; 84 | } 85 | } 86 | 87 | if ( file->atEnd()) { 88 | process->sendEof(); 89 | } 90 | } 91 | 92 | void FileToProcess::endCheck() 93 | { 94 | if ( ! process->isOpen()) { 95 | qApp->exit(); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /demos/filetoprocess/filetoprocess.h: -------------------------------------------------------------------------------- 1 | #ifndef FILETOPROCESS_H 2 | #define FILETOPROCESS_H 3 | 4 | #include 5 | #include 6 | 7 | #include "libsshqtclient.h" 8 | #include "libsshqtprocess.h" 9 | 10 | class FileToProcess : public QObject 11 | { 12 | Q_OBJECT 13 | 14 | public slots: 15 | void fileToProcess(); 16 | void handleProcessError(); 17 | void sendData(); 18 | void endCheck(); 19 | 20 | private: 21 | LibsshQtClient *client; 22 | LibsshQtProcess *process; 23 | QFile *file; 24 | }; 25 | 26 | #endif // FILETOPROCESS_H 27 | -------------------------------------------------------------------------------- /demos/filetoprocess/filetoprocess.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2012-02-09T14:13:53 4 | # 5 | #------------------------------------------------- 6 | 7 | QT += core 8 | 9 | QT -= gui 10 | 11 | TARGET = libsshqt-filetoprocess 12 | CONFIG += console 13 | CONFIG -= app_bundle 14 | 15 | TEMPLATE = app 16 | 17 | include( ../../libsshqt.pri ) 18 | include( ../../libssh.pri ) 19 | 20 | SOURCES += main.cpp \ 21 | filetoprocess.cpp 22 | 23 | HEADERS += \ 24 | filetoprocess.h 25 | 26 | 27 | -------------------------------------------------------------------------------- /demos/filetoprocess/main.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #include "filetoprocess.h" 6 | 7 | int main(int argc, char *argv[]) 8 | { 9 | QCoreApplication a(argc, argv); 10 | FileToProcess *filetoprocess = new FileToProcess; 11 | QTimer::singleShot(0, filetoprocess, SLOT(fileToProcess())); 12 | int ret = a.exec(); 13 | delete filetoprocess; 14 | return ret; 15 | } 16 | -------------------------------------------------------------------------------- /demos/processtofile/main.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #include "processtofile.h" 6 | 7 | int main(int argc, char *argv[]) 8 | { 9 | QCoreApplication a(argc, argv); 10 | ProcessToFile *processtofile = new ProcessToFile; 11 | QTimer::singleShot(0, processtofile, SLOT(processToFile())); 12 | int ret = a.exec(); 13 | delete processtofile; 14 | return ret; 15 | } 16 | -------------------------------------------------------------------------------- /demos/processtofile/processtofile.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "processtofile.h" 7 | #include "libsshqtquestionconsole.h" 8 | 9 | void ProcessToFile::processToFile() 10 | { 11 | QStringList args = qApp->arguments(); 12 | 13 | if ( args.count() != 4) { 14 | qDebug() << "Usage:" 15 | << qPrintable(args.at(0)) 16 | <<"SSH_URL COMMAND FILE"; 17 | qDebug() << "Example:" 18 | << qPrintable(args.at(0)) 19 | << QString("ssh://user@hostname:port/") 20 | << QString("ls") 21 | << QString("ls-output"); 22 | qApp->quit(); 23 | return; 24 | } 25 | 26 | QUrl url(args.at(1)); 27 | client = new LibsshQtClient(this); 28 | client->setUrl(url); 29 | client->useDefaultAuths(); 30 | client->connectToHost(); 31 | 32 | process = client->runCommand(args.at(2)); 33 | process->setStdoutBehaviour(LibsshQtProcess::OutputManual); 34 | 35 | file = new QFile(this); 36 | file->setFileName(args.at(3)); 37 | connect(file, SIGNAL(bytesWritten(qint64)), this, SLOT(endcheck())); 38 | 39 | if ( ! file->open(QIODevice::ReadWrite | QIODevice::Truncate)) { 40 | qDebug() << "Could not open file:" << file->fileName(); 41 | qApp->exit(-1); 42 | return; 43 | } 44 | 45 | new LibsshQtQuestionConsole(client); 46 | 47 | connect(client, SIGNAL(allAuthsFailed()), qApp, SLOT(quit())); 48 | connect(client, SIGNAL(error()), qApp, SLOT(quit())); 49 | connect(client, SIGNAL(closed()), qApp, SLOT(quit())); 50 | 51 | connect(process, SIGNAL(readyRead()), this, SLOT(copydata())); 52 | connect(process, SIGNAL(closed()), this, SLOT(endcheck())); 53 | connect(process, SIGNAL(error()), this, SLOT(handleProcessError())); 54 | } 55 | 56 | void ProcessToFile::handleProcessError() 57 | { 58 | if ( ! process->isClientError()) { 59 | qDebug() << "Process error:" 60 | << qPrintable(client->errorCodeAndMessage()); 61 | qApp->quit(); 62 | } 63 | } 64 | 65 | void ProcessToFile::copydata() 66 | { 67 | QByteArray data = process->read(process->bytesAvailable()); 68 | if ( file->write(data) != data.size()) { 69 | qDebug() << "Data write error"; 70 | qApp->exit(-1); 71 | } else { 72 | qDebug() << "Wrote" << data.size() << "bytes to file"; 73 | } 74 | } 75 | 76 | void ProcessToFile::endcheck() 77 | { 78 | if ( file->bytesToWrite() == 0 && 79 | process->bytesAvailable() == 0 ) { 80 | file->close(); 81 | qApp->exit(); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /demos/processtofile/processtofile.h: -------------------------------------------------------------------------------- 1 | #ifndef PROCESSTOFILE_H 2 | #define PROCESSTOFILE_H 3 | 4 | #include 5 | #include 6 | 7 | #include "libsshqtclient.h" 8 | #include "libsshqtprocess.h" 9 | 10 | class ProcessToFile : public QObject 11 | { 12 | Q_OBJECT 13 | 14 | public slots: 15 | void processToFile(); 16 | void handleProcessError(); 17 | void copydata(); 18 | void endcheck(); 19 | 20 | private: 21 | LibsshQtClient *client; 22 | LibsshQtProcess *process; 23 | QFile *file; 24 | }; 25 | 26 | #endif // PROCESSTOFILE_H 27 | -------------------------------------------------------------------------------- /demos/processtofile/processtofile.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2012-02-09T12:30:54 4 | # 5 | #------------------------------------------------- 6 | 7 | QT += core 8 | 9 | QT -= gui 10 | 11 | TARGET = libsshqt-processtofile 12 | CONFIG += console 13 | CONFIG -= app_bundle 14 | 15 | TEMPLATE = app 16 | 17 | include( ../../libsshqt.pri ) 18 | include( ../../libssh.pri ) 19 | 20 | SOURCES += main.cpp \ 21 | processtofile.cpp 22 | 23 | HEADERS += \ 24 | processtofile.h 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /demos/runprocess/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "runprocess.h" 4 | 5 | int main(int argc, char *argv[]) 6 | { 7 | QCoreApplication a(argc, argv); 8 | RunProcess *runprocess = new RunProcess; 9 | QTimer::singleShot(0, runprocess, SLOT(runProcess())); 10 | int ret = a.exec(); 11 | delete runprocess; 12 | return ret; 13 | } 14 | -------------------------------------------------------------------------------- /demos/runprocess/runprocess.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "runprocess.h" 7 | #include "libsshqtquestionconsole.h" 8 | 9 | void RunProcess::runProcess() 10 | { 11 | QStringList args = qApp->arguments(); 12 | 13 | if ( args.count() != 3) { 14 | qDebug() << "Usage:" 15 | << qPrintable(args.at(0)) 16 | <<"SSH_URL COMMAND"; 17 | qDebug() << "Example:" 18 | << qPrintable(args.at(0)) 19 | << QString("ssh://user@hostname:port/") 20 | << QString("ls"); 21 | qApp->quit(); 22 | return; 23 | } 24 | 25 | QUrl url(args.at(1)); 26 | 27 | client = new LibsshQtClient(this); 28 | client->setUrl(url); 29 | client->useDefaultAuths(); 30 | client->connectToHost(); 31 | 32 | new LibsshQtQuestionConsole(client); 33 | 34 | process = client->runCommand(args.at(2)); 35 | 36 | connect(client, SIGNAL(allAuthsFailed()), qApp, SLOT(quit())); 37 | connect(client, SIGNAL(error()), qApp, SLOT(quit())); 38 | connect(client, SIGNAL(closed()), qApp, SLOT(quit())); 39 | 40 | connect(process, SIGNAL(closed()), qApp, SLOT(quit())); 41 | connect(process, SIGNAL(error()), this, SLOT(handleProcessError())); 42 | } 43 | 44 | void RunProcess::handleProcessError() 45 | { 46 | if ( ! process->isClientError()) { 47 | qDebug() << "Process error:" 48 | << qPrintable(client->errorCodeAndMessage()); 49 | qApp->quit(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /demos/runprocess/runprocess.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNPROCESS_H 2 | #define RUNPROCESS_H 3 | 4 | #include 5 | #include "libsshqtclient.h" 6 | #include "libsshqtprocess.h" 7 | 8 | class RunProcess : public QObject 9 | { 10 | Q_OBJECT 11 | 12 | public slots: 13 | void runProcess(); 14 | void handleProcessError(); 15 | 16 | private: 17 | LibsshQtClient *client; 18 | LibsshQtProcess *process; 19 | }; 20 | 21 | #endif // RUNPROCESS_H 22 | -------------------------------------------------------------------------------- /demos/runprocess/runprocess.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2012-01-26T00:29:15 4 | # 5 | #------------------------------------------------- 6 | 7 | QT += core 8 | 9 | QT -= gui 10 | 11 | TARGET = libsshqt-runprocess 12 | CONFIG += console 13 | CONFIG -= app_bundle 14 | 15 | TEMPLATE = app 16 | 17 | include( ../../libsshqt.pri ) 18 | include( ../../libssh.pri ) 19 | 20 | SOURCES += main.cpp \ 21 | runprocess.cpp 22 | 23 | HEADERS += \ 24 | runprocess.h 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /demos/runprocessgui/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "runprocessgui.h" 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | QApplication a(argc, argv); 7 | RunProcessGui w; 8 | w.show(); 9 | return a.exec(); 10 | } 11 | -------------------------------------------------------------------------------- /demos/runprocessgui/runprocessgui.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | 5 | #include "runprocessgui.h" 6 | #include "ui_runprocessgui.h" 7 | 8 | RunProcessGui::RunProcessGui(QWidget *parent) : 9 | QMainWindow(parent), 10 | ui(new Ui::runprocessgui), 11 | client(new LibsshQtClient(this)), 12 | process(0), 13 | gui(new LibsshQtQuestionDialog(this)) 14 | { 15 | ui->setupUi(this); 16 | client->setDebug(true); 17 | gui->setClient(client); 18 | 19 | qApp->setOrganizationName("libsshqt"); 20 | qApp->setApplicationName("runprocessqui"); 21 | 22 | readSettings(); 23 | } 24 | 25 | RunProcessGui::~RunProcessGui() 26 | { 27 | saveSettings(); 28 | delete ui; 29 | } 30 | 31 | void RunProcessGui::readSettings() 32 | { 33 | QSettings settings; 34 | 35 | ui->username_edit->setText(settings.value("username").toString()); 36 | ui->hostname_edit->setText(settings.value("hostname").toString()); 37 | ui->port_edit->setText(settings.value("port").toString()); 38 | ui->command_edit->setText(settings.value("command").toString()); 39 | 40 | ui->auth_none->setChecked(settings.value("auth_none").toBool()); 41 | ui->auth_pubkey->setChecked(settings.value("auth_pubkey").toBool()); 42 | ui->auth_password->setChecked(settings.value("auth_password").toBool()); 43 | ui->auth_kbi->setChecked(settings.value("auth_kbi").toBool()); 44 | } 45 | 46 | void RunProcessGui::saveSettings() 47 | { 48 | QSettings settings; 49 | 50 | settings.setValue("username", ui->username_edit->text()); 51 | settings.setValue("hostname", ui->hostname_edit->text()); 52 | settings.setValue("port", ui->port_edit->text()); 53 | settings.setValue("command", ui->command_edit->text()); 54 | 55 | settings.setValue("auth_none", ui->auth_none->isChecked()); 56 | settings.setValue("auth_pubkey", ui->auth_pubkey->isChecked()); 57 | settings.setValue("auth_password", ui->auth_password->isChecked()); 58 | settings.setValue("auth_kbi", ui->auth_kbi->isChecked()); 59 | } 60 | 61 | void RunProcessGui::on_run_button_clicked() 62 | { 63 | saveSettings(); 64 | 65 | if ( process ) { 66 | process->closeChannel(); 67 | process->deleteLater(); 68 | process = 0; 69 | } 70 | 71 | client->disconnectFromHost(); 72 | ui->output_edit->clear(); 73 | 74 | //client->useAuthDefaults(); 75 | client->useNoneAuth(ui->auth_none->isChecked()); 76 | client->useAutoKeyAuth(ui->auth_pubkey->isChecked()); 77 | client->usePasswordAuth(ui->auth_password->isChecked()); 78 | client->useKbiAuth(ui->auth_kbi->isChecked()); 79 | 80 | client->setUsername(ui->username_edit->text()); 81 | client->setHostname(ui->hostname_edit->text()); 82 | client->setPort(ui->port_edit->text().toInt()); 83 | client->connectToHost(); 84 | 85 | process = client->runCommand(ui->command_edit->text()); 86 | process->setStdoutBehaviour(LibsshQtProcess::OutputManual); 87 | process->setStderrBehaviour(LibsshQtProcess::OutputManual); 88 | 89 | connect(process, SIGNAL(opened()), this, SLOT(handleProcessOpened())); 90 | connect(process, SIGNAL(closed()), this, SLOT(handleProcessClosed())); 91 | connect(process, SIGNAL(error()), this, SLOT(handleProcessError())); 92 | connect(process, SIGNAL(finished(int)), this, SLOT(handleProcessFinished(int))); 93 | 94 | LibsshQtProcessStderr *stderr = process->stderr(); 95 | connect(process, SIGNAL(readyRead()), this, SLOT(readProcessStdout())); 96 | connect(stderr, SIGNAL(readyRead()), this, SLOT(readProcessStderr())); 97 | } 98 | 99 | void RunProcessGui::handleProcessOpened() 100 | { 101 | statusBar()->showMessage("Running: " + process->command()); 102 | } 103 | 104 | void RunProcessGui::handleProcessClosed() 105 | { 106 | statusBar()->showMessage("Process stopped"); 107 | } 108 | 109 | void RunProcessGui::handleProcessError() 110 | { 111 | statusBar()->showMessage("Error: " + process->errorCodeAndMessage()); 112 | } 113 | 114 | void RunProcessGui::handleProcessFinished(int exit_code) 115 | { 116 | statusBar()->showMessage("Process finished with exit code " + exit_code); 117 | } 118 | 119 | void RunProcessGui::readProcessStdout() 120 | { 121 | while ( process->canReadLine()) { 122 | QString line = process->readLine(); 123 | 124 | if ( line.endsWith('\n')) { 125 | line.remove(line.length() - 1, 1); 126 | } 127 | 128 | ui->output_edit->appendPlainText(line); 129 | } 130 | } 131 | 132 | void RunProcessGui::readProcessStderr() 133 | { 134 | while ( process->stderr()->canReadLine()) { 135 | QString line = process->stderr()->readLine(); 136 | 137 | if ( line.endsWith('\n')) { 138 | line.remove(line.length() - 1, 1); 139 | } 140 | 141 | ui->output_edit->appendHtml( 142 | QString("%1") 143 | .arg(Qt::convertFromPlainText(line))); 144 | } 145 | } 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | -------------------------------------------------------------------------------- /demos/runprocessgui/runprocessgui.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNPROCESSGUI_H 2 | #define RUNPROCESSGUI_H 3 | 4 | #include 5 | 6 | #include "libsshqtclient.h" 7 | #include "libsshqtprocess.h" 8 | #include "libsshqtquestiondialog.h" 9 | 10 | namespace Ui { 11 | class runprocessgui; 12 | } 13 | 14 | class RunProcessGui : public QMainWindow 15 | { 16 | Q_OBJECT 17 | 18 | public: 19 | explicit RunProcessGui(QWidget *parent = 0); 20 | ~RunProcessGui(); 21 | 22 | void readSettings(); 23 | void saveSettings(); 24 | 25 | private slots: 26 | void on_run_button_clicked(); 27 | 28 | void handleProcessOpened(); 29 | void handleProcessClosed(); 30 | void handleProcessError(); 31 | void handleProcessFinished(int exit_code); 32 | 33 | void readProcessStdout(); 34 | void readProcessStderr(); 35 | 36 | private: 37 | Ui::runprocessgui *ui; 38 | LibsshQtClient *client; 39 | LibsshQtProcess *process; 40 | LibsshQtQuestionDialog *gui; 41 | }; 42 | 43 | #endif // RUNPROCESSGUI_H 44 | -------------------------------------------------------------------------------- /demos/runprocessgui/runprocessgui.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2012-02-18T12:43:07 4 | # 5 | #------------------------------------------------- 6 | 7 | QT += core gui 8 | 9 | TARGET = libsshqt-runprocessgui 10 | TEMPLATE = app 11 | 12 | include( ../../libsshqtgui.pri ) 13 | include( ../../libssh.pri ) 14 | 15 | SOURCES += main.cpp\ 16 | runprocessgui.cpp 17 | 18 | HEADERS += runprocessgui.h 19 | 20 | FORMS += runprocessgui.ui 21 | -------------------------------------------------------------------------------- /demos/runprocessgui/runprocessgui.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | runprocessgui 4 | 5 | 6 | 7 | 0 8 | 0 9 | 1087 10 | 564 11 | 12 | 13 | 14 | runprocessgui 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 0 25 | 0 26 | 27 | 28 | 29 | 30 | 300 31 | 0 32 | 33 | 34 | 35 | Host Options 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | Username 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | Hostname 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | Port 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | Command 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | Authentication options 89 | 90 | 91 | 92 | 93 | 94 | SSH None method 95 | 96 | 97 | 98 | 99 | 100 | 101 | Automatic public key authentication 102 | 103 | 104 | 105 | 106 | 107 | 108 | SSH Password method 109 | 110 | 111 | 112 | 113 | 114 | 115 | SSH Keyboard Interactive method 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 200 127 | 0 128 | 129 | 130 | 131 | Run Command 132 | 133 | 134 | 135 | 136 | 137 | 138 | Qt::Vertical 139 | 140 | 141 | 142 | 20 143 | 40 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 0 155 | 0 156 | 157 | 158 | 159 | Command Output 160 | 161 | 162 | 163 | 164 | 165 | 166 | Monospace 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 0 183 | 0 184 | 1087 185 | 21 186 | 187 | 188 | 189 | 190 | 191 | TopToolBarArea 192 | 193 | 194 | false 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | -------------------------------------------------------------------------------- /libssh.pri: -------------------------------------------------------------------------------- 1 | CONFIG += link_pkgconfig 2 | PKGCONFIG += libssh 3 | -------------------------------------------------------------------------------- /libsshqt.pri: -------------------------------------------------------------------------------- 1 | HEADERS += $$PWD/src/libsshqtchannel.h 2 | HEADERS += $$PWD/src/libsshqtclient.h 3 | HEADERS += $$PWD/src/libsshqtprocess.h 4 | HEADERS += $$PWD/src/libsshqtquestionconsole.h 5 | 6 | SOURCES += $$PWD/src/libsshqtchannel.cpp 7 | SOURCES += $$PWD/src/libsshqtclient.cpp 8 | SOURCES += $$PWD/src/libsshqtprocess.cpp 9 | SOURCES += $$PWD/src/libsshqtquestionconsole.cpp 10 | 11 | INCLUDEPATH += $$PWD/src 12 | -------------------------------------------------------------------------------- /libsshqtgui.pri: -------------------------------------------------------------------------------- 1 | include(libsshqt.pri) 2 | HEADERS += $$PWD/src/libsshqtquestiondialog.h 3 | SOURCES += $$PWD/src/libsshqtquestiondialog.cpp 4 | FORMS += $$PWD/src/libsshqtquestiondialog.ui 5 | -------------------------------------------------------------------------------- /src/libsshqtchannel.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "libsshqtchannel.h" 9 | #include "libsshqtclient.h" 10 | #include "libsshqtdebug.h" 11 | 12 | const char *LibsshQtChannel::enumToString(const EofState flag) 13 | { 14 | return staticMetaObject.enumerator( 15 | staticMetaObject.indexOfEnumerator("EofState")) 16 | .valueToKey(flag); 17 | } 18 | 19 | LibsshQtChannel::LibsshQtChannel(bool is_stderr, 20 | LibsshQtClient *client, 21 | QObject *parent) : 22 | QIODevice(parent), 23 | debug_prefix_(LibsshQt::debugPrefix(this)), 24 | debug_output_(client->isDebugEnabled()), 25 | client_(client), 26 | channel_(0), 27 | is_stderr_(is_stderr), 28 | eof_state_(EofNotSent), 29 | buffer_size_(1024 * 16), 30 | write_size_(1024 * 16) 31 | { 32 | connect(client_, SIGNAL(debugChanged()), 33 | this, SLOT(handleDebugChanged())); 34 | } 35 | 36 | /*! 37 | Is the channel at end? 38 | 39 | @par Channel is at end if 40 | - IODevice is closed 41 | - Read buffer is empty and more data won't be forthcoming 42 | */ 43 | bool LibsshQtChannel::atEnd() const 44 | { 45 | // Channel is NULL 46 | // IODevice is closed 47 | // Buffer is empty AND ( Channel closed OR Channel is EOF ) 48 | return channel_ == 0 || 49 | isOpen() == false || 50 | ( read_buffer_.isEmpty() && 51 | ( ssh_channel_is_open(channel_) == false || 52 | ssh_channel_poll(channel_, is_stderr_) == SSH_EOF )); 53 | } 54 | 55 | qint64 LibsshQtChannel::bytesAvailable() const 56 | { 57 | return read_buffer_.size() + QIODevice::bytesAvailable(); 58 | } 59 | 60 | qint64 LibsshQtChannel::bytesToWrite() const 61 | { 62 | return write_buffer_.size(); 63 | } 64 | 65 | bool LibsshQtChannel::isSequential() 66 | { 67 | return true; 68 | } 69 | 70 | /*! 71 | Is a line available for reading? 72 | 73 | @par A line available for reading if: 74 | - Newline is found in read buffer 75 | - Read buffer is full 76 | - Data is available in read buffer and more data won't be forthcoming 77 | */ 78 | bool LibsshQtChannel::canReadLine() const 79 | { 80 | // Newline in buffer 81 | // Buffer is full 82 | // Data in buffer and (closed or Channel NULL or EOF ) 83 | return QIODevice::canReadLine() || 84 | read_buffer_.contains('\n') || 85 | read_buffer_.size() >= buffer_size_ || 86 | ( read_buffer_.isEmpty() == false && 87 | ( isOpen() == false || 88 | channel_ == 0 || 89 | ssh_channel_is_open(channel_) == false || 90 | ssh_channel_poll(channel_, is_stderr_) == SSH_EOF )); 91 | } 92 | 93 | qint64 LibsshQtChannel::readData(char *data, qint64 maxlen) 94 | { 95 | queueCheckIo(); 96 | 97 | qint64 copy_len = maxlen; 98 | int data_size = read_buffer_.size(); 99 | 100 | if ( copy_len > data_size ) { 101 | copy_len = data_size; 102 | } 103 | 104 | memcpy(data, read_buffer_.constData(), copy_len); 105 | read_buffer_.remove(0, copy_len); 106 | return copy_len; 107 | } 108 | 109 | qint64 LibsshQtChannel::writeData(const char *data, qint64 len) 110 | { 111 | if ( openMode() == ReadOnly ) { 112 | LIBSSHQT_CRITICAL("Cannot write to channel because" << 113 | "ReadOnly flag is set"); 114 | return 0; 115 | 116 | } else if ( eof_state_ != EofNotSent ) { 117 | LIBSSHQT_CRITICAL("Cannot write to channel because EOF state is" << 118 | eof_state_); 119 | return 0; 120 | 121 | } else { 122 | client_->enableWritableNotifier(); 123 | write_buffer_.reserve(write_size_); 124 | write_buffer_.append(data, len); 125 | return len; 126 | } 127 | } 128 | 129 | void LibsshQtChannel::checkIo() 130 | { 131 | if ( ! channel_ ) return; 132 | 133 | bool emit_ready_read = false; 134 | bool emit_bytes_written = false; 135 | 136 | int read_size = 0; 137 | int read_available = ssh_channel_poll(channel_, is_stderr_); 138 | if ( read_available > 0 ) { 139 | 140 | // Dont read more than buffer_size_ specifies. 141 | int max_read = buffer_size_ - read_buffer_.size(); 142 | if ( read_available > max_read ) { 143 | read_available = max_read; 144 | } 145 | 146 | if ( read_available > 0 ) { 147 | char data[read_available + 1]; 148 | data[read_available] = 0; 149 | 150 | read_size = ssh_channel_read_nonblocking(channel_, 151 | data, 152 | read_available, 153 | is_stderr_); 154 | Q_ASSERT(read_size >= 0); 155 | 156 | read_buffer_.reserve(buffer_size_); 157 | read_buffer_.append(data, read_size); 158 | 159 | LIBSSHQT_DEBUG("Read:" << read_size << 160 | " Data in buffer:" << read_buffer_.size() << 161 | " Readable from channel:" << 162 | ssh_channel_poll(channel_, is_stderr_)); 163 | if ( read_size > 0 ) { 164 | emit_ready_read = true; 165 | } 166 | } 167 | } 168 | 169 | int written = 0; 170 | int writable = 0; 171 | if ( openMode() != ReadOnly ) { 172 | 173 | writable = write_buffer_.size(); 174 | if ( writable > write_size_ ) { 175 | writable = write_size_; 176 | } 177 | 178 | if ( writable > 0 ) { 179 | written = ssh_channel_write(channel_, 180 | write_buffer_.constData(), 181 | writable); 182 | Q_ASSERT(written >= 0); 183 | write_buffer_.remove(0, written); 184 | 185 | LIBSSHQT_DEBUG("Wrote" << written << "bytes to channel"); 186 | if ( written > 0 ) { 187 | emit_bytes_written = true; 188 | } 189 | } 190 | 191 | // Write more data once the socket is ready 192 | if ( write_buffer_.size() > 0 ) { 193 | client_->enableWritableNotifier(); 194 | } 195 | } 196 | 197 | // Send EOF once all data has been written to channel 198 | if ( eof_state_ == EofQueued && write_buffer_.size() == 0 ) { 199 | LIBSSHQT_DEBUG("Sending EOF to channel"); 200 | ssh_channel_send_eof(channel_); 201 | eof_state_ = EofSent; 202 | } 203 | 204 | // Emit signals here, so that somebody wont call closeChannel() while 205 | // when we are reading from it. 206 | if ( emit_ready_read ) { 207 | emit readyRead(); 208 | } 209 | if ( emit_bytes_written ) { 210 | emit bytesWritten(written); 211 | } 212 | } 213 | 214 | /*! 215 | Get parent LibsshQtClient object. 216 | */ 217 | LibsshQtClient *LibsshQtChannel::client() 218 | { 219 | return client_; 220 | } 221 | 222 | /*! 223 | Set maximum amount of data that LibsshQtChannel will write to the channel 224 | in one go. 225 | */ 226 | void LibsshQtChannel::setWriteSize(int write_size) 227 | { 228 | static const int min_size = 4096; 229 | Q_ASSERT( write_size >= min_size ); 230 | 231 | if ( write_size_ >= min_size ) { 232 | write_size_ = write_size; 233 | } else { 234 | write_size_ = min_size; 235 | } 236 | } 237 | 238 | /*! 239 | Set read buffer size. 240 | */ 241 | void LibsshQtChannel::setReadBufferSize(int buffer_size) 242 | { 243 | static const int min_size = 4096; 244 | Q_ASSERT( buffer_size >= min_size ); 245 | 246 | if ( buffer_size >= min_size ) { 247 | buffer_size_ = buffer_size; 248 | } else { 249 | buffer_size_ = min_size; 250 | } 251 | } 252 | 253 | int LibsshQtChannel::getBufferSize() 254 | { 255 | return buffer_size_; 256 | } 257 | 258 | int LibsshQtChannel::getWriteSize() 259 | { 260 | return write_size_; 261 | } 262 | 263 | /*! 264 | Send EOF to the channel once write buffer has been written to the channel. 265 | */ 266 | void LibsshQtChannel::sendEof() 267 | { 268 | if ( eof_state_ == EofNotSent ) { 269 | LIBSSHQT_DEBUG("EOF queued"); 270 | eof_state_ = EofQueued; 271 | } 272 | } 273 | 274 | LibsshQtChannel::EofState LibsshQtChannel::eofState() 275 | { 276 | return eof_state_; 277 | } 278 | 279 | /*! 280 | Did the error happen in LibsshQtClient instead of LibsshQtChannel? 281 | 282 | LibsshQtChannel inherists errors from LibsshQtClient, so that if client 283 | fails because of an error, then LibsshQtChannel will also fail. 284 | 285 | You can use this function to check if the error actually happend in 286 | LibsshQtClient instead of LibsshQtChannel. 287 | */ 288 | bool LibsshQtChannel::isClientError() const 289 | { 290 | return client_->state() == LibsshQtClient::StateError; 291 | } 292 | 293 | /*! 294 | Get error code and message from libssh. 295 | */ 296 | QString LibsshQtChannel::errorCodeAndMessage() const 297 | { 298 | return QString("%1 (%2 %3)") 299 | .arg(errorMessage()) 300 | .arg(tr("libssh error code")) 301 | .arg(errorCode()); 302 | } 303 | 304 | /*! 305 | Get error message from libssh. 306 | */ 307 | QString LibsshQtChannel::errorMessage() const 308 | { 309 | if ( client_->state() == LibsshQtClient::StateError ) { 310 | return client_->errorMessage(); 311 | } else if ( channel_ ) { 312 | return QString(ssh_get_error(channel_)); 313 | } else { 314 | return QString(); 315 | } 316 | } 317 | 318 | /*! 319 | Get error code from libssh. 320 | */ 321 | int LibsshQtChannel::errorCode() const 322 | { 323 | if ( client_->state() == LibsshQtClient::StateError ) { 324 | return client_->errorCode(); 325 | } else if ( channel_ ) { 326 | return ssh_get_error_code(channel_); 327 | } else { 328 | return 0; 329 | } 330 | } 331 | 332 | void LibsshQtChannel::handleDebugChanged() 333 | { 334 | debug_output_ = client_->isDebugEnabled(); 335 | } 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | -------------------------------------------------------------------------------- /src/libsshqtchannel.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBSSHQTCHANNEL_H 2 | #define LIBSSHQTCHANNEL_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | class LibsshQtClient; 11 | 12 | /*! 13 | 14 | LibsshQtChannel 15 | 16 | */ 17 | class LibsshQtChannel : public QIODevice 18 | { 19 | Q_OBJECT 20 | 21 | public: 22 | Q_FLAGS(EofState) 23 | enum EofState 24 | { 25 | EofNotSent, 26 | EofQueued, 27 | EofSent 28 | }; 29 | 30 | static const char *enumToString(const EofState flag); 31 | 32 | explicit LibsshQtChannel(bool is_stderr, 33 | LibsshQtClient *client, 34 | QObject *parent); 35 | 36 | 37 | // QIODevice API 38 | public: 39 | virtual bool atEnd() const; 40 | qint64 bytesAvailable() const; 41 | qint64 bytesToWrite() const; 42 | bool isSequential(); 43 | bool canReadLine() const; 44 | protected: 45 | qint64 readData(char *data, qint64 maxlen); 46 | qint64 writeData(const char *data, qint64 len); 47 | 48 | // LibsshQtChannel Api 49 | public: 50 | LibsshQtClient *client(); 51 | 52 | void setWriteSize(int write_size); 53 | void setReadBufferSize(int buffer_size); 54 | int getWriteSize(); 55 | int getBufferSize(); 56 | 57 | void sendEof(); 58 | EofState eofState(); 59 | 60 | // Error handling 61 | bool isClientError() const; 62 | QString errorCodeAndMessage() const; 63 | QString errorMessage() const; 64 | int errorCode() const; 65 | 66 | protected: 67 | void checkIo(); 68 | virtual void queueCheckIo() = 0; 69 | 70 | private slots: 71 | void handleDebugChanged(); 72 | 73 | protected: 74 | QString debug_prefix_; 75 | bool debug_output_; 76 | LibsshQtClient *client_; 77 | 78 | ssh_channel channel_; 79 | bool is_stderr_; 80 | EofState eof_state_; 81 | 82 | int buffer_size_; 83 | int write_size_; 84 | QByteArray read_buffer_; 85 | QByteArray write_buffer_; 86 | }; 87 | 88 | 89 | // Include before "libsshqt.h" if you want to use these operators 90 | #ifdef QDEBUG_H 91 | 92 | inline QDebug operator<<(QDebug dbg, const LibsshQtChannel::EofState flag) 93 | { 94 | dbg << LibsshQtChannel::enumToString(flag); 95 | return dbg; 96 | } 97 | 98 | #endif 99 | 100 | #endif // LIBSSHQTCHANNEL_H 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /src/libsshqtclient.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "libsshqtclient.h" 9 | #include "libsshqtprocess.h" 10 | #include "libsshqtdebug.h" 11 | 12 | 13 | LibsshQtClient::LibsshQtClient(QObject *parent) : 14 | QObject(parent), 15 | debug_output_(false), 16 | session_(0), 17 | state_(StateClosed), 18 | process_state_running_(false), 19 | enable_writable_nofifier_(false), 20 | port_(22), 21 | read_notifier_(0), 22 | write_notifier_(0), 23 | unknown_host_type_(HostKnown), 24 | password_set_(false) 25 | { 26 | debug_prefix_ = LibsshQt::debugPrefix(this); 27 | 28 | if ( QProcessEnvironment::systemEnvironment().contains("LIBSSHQT_DEBUG")) { 29 | debug_output_ = true; 30 | LIBSSHQT_DEBUG("Constructor"); 31 | } 32 | 33 | timer_.setSingleShot(true); 34 | timer_.setInterval(0); 35 | connect(&timer_, SIGNAL(timeout()), this, SLOT(processStateGuard())); 36 | 37 | if (debug_output_) { 38 | setVerbosity(LogProtocol); 39 | } else { 40 | setVerbosity(LogDisable); 41 | } 42 | 43 | // Ensure that connections are always properly closed 44 | connect(qApp, SIGNAL(aboutToQuit()), this, SLOT(disconnectFromHost())); 45 | } 46 | 47 | LibsshQtClient::~LibsshQtClient() 48 | { 49 | LIBSSHQT_DEBUG("Destructor"); 50 | 51 | disconnectFromHost(); 52 | if ( session_ ) { 53 | ssh_free(session_); 54 | session_ = 0; 55 | } 56 | } 57 | 58 | const char *LibsshQtClient::enumToString(const LogVerbosity value) 59 | { 60 | return staticMetaObject.enumerator( 61 | staticMetaObject.indexOfEnumerator("LogVerbosity")) 62 | .valueToKey(value); 63 | } 64 | 65 | const char *LibsshQtClient::enumToString(const State value) 66 | { 67 | return staticMetaObject.enumerator( 68 | staticMetaObject.indexOfEnumerator("State")) 69 | .valueToKey(value); 70 | } 71 | 72 | const char *LibsshQtClient::enumToString(const HostState value) 73 | { 74 | return staticMetaObject.enumerator( 75 | staticMetaObject.indexOfEnumerator("HostState")) 76 | .valueToKey(value); 77 | } 78 | 79 | const char *LibsshQtClient::enumToString(const AuthMehodFlag value) 80 | { 81 | return staticMetaObject.enumerator( 82 | staticMetaObject.indexOfEnumerator("AuthMehodFlag")) 83 | .valueToKey(value); 84 | } 85 | 86 | const char *LibsshQtClient::enumToString(const UseAuthFlag value) 87 | { 88 | return staticMetaObject.enumerator( 89 | staticMetaObject.indexOfEnumerator("UseAuthFlag")) 90 | .valueToKey(value); 91 | } 92 | 93 | QString LibsshQtClient::flagsToString(const AuthMethods flags) 94 | { 95 | QStringList list; 96 | 97 | int val = flags; 98 | if ( val == AuthMethodUnknown ) { 99 | list << enumToString(AuthMethodUnknown); 100 | 101 | } else { 102 | 103 | if ( flags.testFlag(AuthMethodNone)) { 104 | list << enumToString(AuthMethodNone); 105 | } 106 | 107 | if ( flags.testFlag(AuthMethodPassword)) { 108 | list << enumToString(AuthMethodPassword); 109 | } 110 | 111 | if ( flags.testFlag(AuthMethodPublicKey)) { 112 | list << enumToString(AuthMethodPublicKey); 113 | } 114 | 115 | if ( flags.testFlag(AuthMethodHostBased)) { 116 | list << enumToString(AuthMethodHostBased); 117 | 118 | } 119 | 120 | if ( flags.testFlag(AuthMethodKbi)) { 121 | list << enumToString(AuthMethodKbi); 122 | } 123 | } 124 | 125 | return QString("AuthMethods( %1 )").arg(list.join(", ")); 126 | } 127 | 128 | QString LibsshQtClient::flagsToString(const UseAuths flags) 129 | { 130 | QStringList list; 131 | 132 | if ( flags == UseAuthEmpty ) { 133 | list << enumToString(UseAuthEmpty); 134 | 135 | } else { 136 | 137 | if ( flags.testFlag(UseAuthNone)) { 138 | list << enumToString(UseAuthNone); 139 | } 140 | 141 | if ( flags.testFlag(UseAuthAutoPubKey)) { 142 | list << enumToString(UseAuthAutoPubKey); 143 | } 144 | 145 | if ( flags.testFlag(UseAuthPassword)) { 146 | list << enumToString(UseAuthPassword); 147 | } 148 | 149 | if ( flags.testFlag(UseAuthKbi)) { 150 | list << enumToString(UseAuthKbi); 151 | 152 | } 153 | } 154 | 155 | return QString("UseAuths( %1 )").arg(list.join(", ")); 156 | } 157 | 158 | /*! 159 | Enable or disable debug messages. 160 | 161 | If logging is enabled libssh log level is set to LogProtocol and 162 | LIBSSHQT debug messages are enabled. 163 | */ 164 | void LibsshQtClient::setDebug(bool enabled) 165 | { 166 | if ( enabled ) { 167 | debug_output_ = true; 168 | LIBSSHQT_DEBUG("Enabling debug messages"); 169 | setVerbosity(LogProtocol); 170 | 171 | } else { 172 | LIBSSHQT_DEBUG("Disabling debug messages"); 173 | debug_output_ = false; 174 | setVerbosity(LogDisable); 175 | } 176 | 177 | emit debugChanged(); 178 | } 179 | 180 | void LibsshQtClient::setUsername(QString username) 181 | { 182 | Q_ASSERT( state_ == StateClosed ); 183 | 184 | if ( state_ == StateClosed ) { 185 | username_ = username; 186 | } else { 187 | LIBSSHQT_CRITICAL("Cannot set AAAAAAA when state is" << state_); 188 | } 189 | } 190 | 191 | void LibsshQtClient::setHostname(QString hostname) 192 | { 193 | Q_ASSERT( state_ == StateClosed ); 194 | 195 | if ( state_ == StateClosed ) { 196 | hostname_ = hostname; 197 | } else { 198 | LIBSSHQT_CRITICAL("Cannot set AAAAAAA when state is" << state_); 199 | } 200 | } 201 | 202 | void LibsshQtClient::setPort(quint16 port) 203 | { 204 | Q_ASSERT( state_ == StateClosed ); 205 | 206 | if ( state_ == StateClosed ) { 207 | port_ = port; 208 | } else { 209 | LIBSSHQT_CRITICAL("Cannot set AAAAAAA when state is" << state_); 210 | } 211 | } 212 | 213 | /*! 214 | Set libssh logging level. 215 | */ 216 | void LibsshQtClient::setVerbosity(LogVerbosity loglevel) 217 | { 218 | log_verbosity_ = loglevel; 219 | if ( session_ ) { 220 | int tmp_verbosity = log_verbosity_; 221 | setLibsshOption(SSH_OPTIONS_LOG_VERBOSITY, "SSH_OPTIONS_LOG_VERBOSITY", 222 | &tmp_verbosity, enumToString(log_verbosity_)); 223 | } 224 | } 225 | 226 | bool LibsshQtClient::isDebugEnabled() const 227 | { 228 | return debug_output_; 229 | } 230 | 231 | QString LibsshQtClient::username() const 232 | { 233 | return username_; 234 | } 235 | 236 | QString LibsshQtClient::hostname() const 237 | { 238 | return hostname_; 239 | } 240 | 241 | quint16 LibsshQtClient::port() const 242 | { 243 | return port_; 244 | } 245 | 246 | QUrl LibsshQtClient::url() const 247 | { 248 | QUrl url; 249 | url.setHost(hostname_); 250 | url.setPort(port_); 251 | url.setUserName(username_); 252 | url.setScheme("ssh"); 253 | return url; 254 | } 255 | 256 | /*! 257 | Set hostname, port and username options from QUrl 258 | */ 259 | void LibsshQtClient::setUrl(const QUrl &url) 260 | { 261 | if ( url.scheme().toLower() == "ssh" ) { 262 | LIBSSHQT_DEBUG("Setting options from URL:" << url); 263 | 264 | if ( url.port() > -1 ) { 265 | setPort(url.port()); 266 | } else { 267 | setPort(22); 268 | } 269 | 270 | setHostname(url.host()); 271 | setUsername(url.userName()); 272 | 273 | } else { 274 | LIBSSHQT_CRITICAL("Not SSH URL:" << url); 275 | } 276 | } 277 | 278 | /*! 279 | Returns true if connection is successfully connected and authenticated. 280 | */ 281 | bool LibsshQtClient::isOpen() 282 | { 283 | return state_ == StateOpened; 284 | } 285 | 286 | /*! 287 | Open connection to the host. 288 | */ 289 | void LibsshQtClient::connectToHost() 290 | { 291 | if ( state_ == StateClosed ) { 292 | setState(StateInit); 293 | timer_.start(); 294 | } 295 | } 296 | 297 | /*! 298 | Set hostname and open connection to the host. 299 | */ 300 | void LibsshQtClient::connectToHost(const QString hostName) 301 | { 302 | if ( state_ == StateClosed ) { 303 | setHostname(hostName); 304 | setPort(22); 305 | connectToHost(); 306 | } 307 | } 308 | 309 | /*! 310 | Set hostname and port and open connection to the host. 311 | */ 312 | void LibsshQtClient::connectToHost(const QString hostName, unsigned int port) 313 | { 314 | if ( state_ == StateClosed ) { 315 | setHostname(hostName); 316 | setPort(port); 317 | connectToHost(); 318 | } 319 | } 320 | 321 | /*! 322 | Close connection to host. 323 | */ 324 | void LibsshQtClient::disconnectFromHost() 325 | { 326 | if ( state_ != StateClosed && 327 | state_ != StateClosing ) { 328 | 329 | // Prevent recursion 330 | setState(StateClosing); 331 | 332 | // Child libsshqt objects must handle this and release all libssh 333 | // resources 334 | emit doCleanup(); 335 | 336 | destroyNotifiers(); 337 | 338 | ssh_disconnect(session_); 339 | ssh_free(session_); 340 | session_ = 0; 341 | 342 | setState(StateClosed); 343 | } 344 | } 345 | 346 | /*! 347 | Enable or disable one or more authentications. 348 | */ 349 | void LibsshQtClient::useAuth(UseAuths auths, bool enabled) 350 | { 351 | if ( enabled ) { 352 | use_auths_ |= auths; 353 | if ( state_ == StateAuthChoose || state_ == StateAuthAllFailed ) { 354 | setState(StateAuthContinue); 355 | timer_.start(); 356 | } 357 | 358 | } else { 359 | use_auths_ &= ~auths; 360 | } 361 | } 362 | 363 | /*! 364 | This enables all authentications you would normally like to use. 365 | */ 366 | void LibsshQtClient::useDefaultAuths() 367 | { 368 | UseAuths a(UseAuthNone | UseAuthAutoPubKey | UseAuthPassword | UseAuthKbi); 369 | useAuth(a, true); 370 | } 371 | 372 | /*! 373 | Enable or disable the use of 'None' SSH authentication. 374 | */ 375 | void LibsshQtClient::useNoneAuth(bool enabled) 376 | { 377 | useAuth(UseAuthNone, enabled); 378 | } 379 | 380 | /*! 381 | Enable or disable the use of automatic public key authentication. 382 | 383 | This includes keys stored in ssh-agent and in ~/.ssh/ directory. 384 | */ 385 | void LibsshQtClient::useAutoKeyAuth(bool enabled) 386 | { 387 | useAuth(UseAuthAutoPubKey, enabled); 388 | } 389 | 390 | /*! 391 | Enable or disable the use of password based SSH authentication. 392 | */ 393 | void LibsshQtClient::usePasswordAuth(bool enabled) 394 | { 395 | useAuth(UseAuthPassword, enabled); 396 | } 397 | 398 | /*! 399 | Enable or disable the use of Keyboard Interactive SSH authentication. 400 | */ 401 | void LibsshQtClient::useKbiAuth(bool enabled) 402 | { 403 | useAuth(UseAuthKbi, enabled); 404 | } 405 | 406 | /*! 407 | Set password for use in password authentication. 408 | */ 409 | void LibsshQtClient::setPassword(QString password) 410 | { 411 | password_set_ = true; 412 | password_ = password; 413 | 414 | if ( state_ == StateAuthNeedPassword ) { 415 | setState(StateAuthPassword); 416 | timer_.start(); 417 | } 418 | } 419 | 420 | /*! 421 | Get list of Keyboard Interactive questions sent by the server. 422 | */ 423 | QList LibsshQtClient::kbiQuestions() 424 | { 425 | Q_ASSERT( state_ == StateAuthKbiQuestions ); 426 | if ( state_ == StateAuthKbiQuestions ) { 427 | 428 | QList questions; 429 | QString instruction = ssh_userauth_kbdint_getinstruction(session_); 430 | 431 | int len = ssh_userauth_kbdint_getnprompts(session_); 432 | LIBSSHQT_DEBUG("Number of KBI questions:" << len); 433 | 434 | for ( int i = 0; i < len; i++) { 435 | 436 | char echo = 0; 437 | const char *prompt = 0; 438 | 439 | prompt = ssh_userauth_kbdint_getprompt(session_, i, &echo); 440 | Q_ASSERT( prompt ); 441 | 442 | KbiQuestion kbi_question; 443 | kbi_question.instruction = instruction; 444 | kbi_question.question = QString(prompt); 445 | kbi_question.showAnswer = echo != 0; 446 | 447 | LIBSSHQT_DEBUG("KBI Question:" << kbi_question); 448 | questions << kbi_question; 449 | } 450 | 451 | Q_ASSERT( questions.count() > 0 ); 452 | return questions; 453 | 454 | } else { 455 | LIBSSHQT_CRITICAL("Cannot get KBI questions because state is" << 456 | state_); 457 | return QList(); 458 | } 459 | } 460 | 461 | /*! 462 | Set answers to Keyboard Interactive questions. 463 | */ 464 | void LibsshQtClient::setKbiAnswers(QStringList answers) 465 | { 466 | Q_ASSERT( state_ == StateAuthKbiQuestions ); 467 | if ( state_ == StateAuthKbiQuestions ) { 468 | LIBSSHQT_CRITICAL("Setting KBI answers"); 469 | 470 | int i = 0; 471 | foreach ( QString answer, answers ) { 472 | QByteArray utf8 = answer.toUtf8(); 473 | ssh_userauth_kbdint_setanswer(session_, i, utf8.constData()); 474 | } 475 | 476 | setState(StateAuthKbi); 477 | timer_.start(); 478 | 479 | } else { 480 | LIBSSHQT_CRITICAL("Cannot set KBI answers because state is" << state_); 481 | } 482 | } 483 | 484 | /*! 485 | Get supported authentication methods. 486 | */ 487 | LibsshQtClient::AuthMethods LibsshQtClient::supportedAuthMethods() 488 | { 489 | return AuthMethods(ssh_userauth_list(session_, 0)); 490 | } 491 | 492 | /*! 493 | Get all enabled authentication methods. 494 | */ 495 | LibsshQtClient::UseAuths LibsshQtClient::enabledAuths() 496 | { 497 | return use_auths_; 498 | } 499 | 500 | /*! 501 | Get list of authention methods that have been attempted unsuccesfully. 502 | */ 503 | LibsshQtClient::UseAuths LibsshQtClient::failedAuths() 504 | { 505 | return failed_auths_; 506 | } 507 | 508 | /*! 509 | Run a command 510 | */ 511 | LibsshQtProcess *LibsshQtClient::runCommand(QString command) 512 | { 513 | LibsshQtProcess *process = new LibsshQtProcess(this); 514 | process->setCommand(command); 515 | process->openChannel(); 516 | return process; 517 | } 518 | 519 | LibsshQtClient::HostState LibsshQtClient::unknownHostType() 520 | { 521 | return unknown_host_type_; 522 | } 523 | 524 | /*! 525 | Get a message to show to the user why the host is unknown. 526 | */ 527 | QString LibsshQtClient::unknownHostMessage() 528 | { 529 | switch ( unknown_host_type_ ) { 530 | 531 | case HostKnown: 532 | return QString() + 533 | "This host is known."; 534 | 535 | case HostUnknown: 536 | case HostKnownHostsFileMissing: 537 | return QString() + 538 | "This host is unknown."; 539 | 540 | case HostKeyChanged: 541 | return QString() + 542 | "WARNING: The public key sent by this host does not match the " + 543 | "expected value. A third party may be attempting to " + 544 | "impersonate the host."; 545 | 546 | case HostKeyTypeChanged: 547 | return QString() + 548 | "WARNING: The public key type sent by this host does not " + 549 | "match the expected value. A third party may be attempting " + 550 | "to impersonate the host."; 551 | } 552 | 553 | Q_ASSERT_X(false, __func__, "Case was not handled properly"); 554 | return QString(); 555 | } 556 | 557 | /*! 558 | Get MD5 hexadecimal hash of the servers public key. 559 | */ 560 | QString LibsshQtClient::hostPublicKeyHash() 561 | { 562 | QString string; 563 | int hash_len = 0; 564 | unsigned char *hash = NULL; 565 | char *hexa = NULL; 566 | 567 | hash_len = ssh_get_pubkey_hash(session_, &hash); 568 | hexa = ssh_get_hexa(hash, hash_len); 569 | 570 | string = QString(hexa); 571 | 572 | free(hexa); 573 | free(hash); 574 | 575 | return string; 576 | } 577 | 578 | /*! 579 | Add current host to the known hosts file. 580 | */ 581 | bool LibsshQtClient::markCurrentHostKnown() 582 | { 583 | int rc = ssh_write_knownhost(session_); 584 | 585 | switch ( rc ) { 586 | case SSH_OK: 587 | LIBSSHQT_DEBUG("Added current host to known host list"); 588 | setState(StateIsKnown); 589 | timer_.start(); 590 | return true; 591 | 592 | case SSH_ERROR: 593 | LIBSSHQT_DEBUG("Could not add current host to known host list"); 594 | return false; 595 | 596 | default: 597 | LIBSSHQT_CRITICAL("Unknown result code" << rc << 598 | "received from ssh_write_knownhost()"); 599 | return false; 600 | } 601 | } 602 | 603 | /*! 604 | Get error code and message from libssh. 605 | */ 606 | QString LibsshQtClient::errorCodeAndMessage() const 607 | { 608 | return QString("%1 (%2 %3)") 609 | .arg(errorMessage()) 610 | .arg(tr("libssh error code")) 611 | .arg(errorCode()); 612 | } 613 | 614 | /*! 615 | Get error message from libssh. 616 | */ 617 | QString LibsshQtClient::errorMessage() const 618 | { 619 | return QString(ssh_get_error(session_)); 620 | } 621 | 622 | /*! 623 | Get error code from libssh. 624 | */ 625 | int LibsshQtClient::errorCode() const 626 | { 627 | return ssh_get_error_code(session_); 628 | } 629 | 630 | LibsshQtClient::State LibsshQtClient::state() const 631 | { 632 | return state_; 633 | } 634 | 635 | ssh_session LibsshQtClient::sshSession() 636 | { 637 | return session_; 638 | } 639 | 640 | void LibsshQtClient::enableWritableNotifier() 641 | { 642 | if ( process_state_running_ ) { 643 | enable_writable_nofifier_ = true; 644 | } else if ( write_notifier_ ) { 645 | write_notifier_->setEnabled(true); 646 | } 647 | } 648 | 649 | /*! 650 | Change session state and send appropriate signals. 651 | */ 652 | void LibsshQtClient::setState(State state) 653 | { 654 | if ( state_ == state ) { 655 | LIBSSHQT_DEBUG("State is already" << state); 656 | return; 657 | } 658 | 659 | LIBSSHQT_DEBUG("Changing state to" << state); 660 | state_ = state; 661 | 662 | if ( state_ == StateError ) { 663 | destroyNotifiers(); 664 | } 665 | 666 | // Emit signals 667 | switch ( state_ ) { 668 | case StateClosed: emit closed(); break; 669 | case StateClosing: break; 670 | case StateInit: break; 671 | case StateConnecting: break; 672 | case StateIsKnown: break; 673 | case StateUnknownHost: emit unknownHost(); break; 674 | case StateAuthChoose: emit chooseAuth(); break; 675 | case StateAuthContinue: break; 676 | case StateAuthNone: break; 677 | case StateAuthAutoPubkey: break; 678 | case StateAuthPassword: break; 679 | case StateAuthNeedPassword: emit needPassword(); break; 680 | case StateAuthKbi: break; 681 | case StateAuthKbiQuestions: emit needKbiAnswers(); break; 682 | case StateAuthAllFailed: emit allAuthsFailed(); break; 683 | case StateOpened: emit opened(); break; 684 | case StateError: emit error(); break; 685 | } 686 | } 687 | 688 | /*! 689 | Choose next authentication method to try. 690 | 691 | if authentication methods have not been chosen or all chosen authentication 692 | methods have failed, switch state to StateChooseAuth or StateAuthFailed, 693 | respectively. 694 | */ 695 | void LibsshQtClient::tryNextAuth() 696 | { 697 | UseAuths failed_auth = UseAuthEmpty; 698 | 699 | // Detect failed authentication methods 700 | switch ( state_ ) { 701 | case StateClosed: 702 | case StateClosing: 703 | case StateInit: 704 | case StateConnecting: 705 | case StateIsKnown: 706 | case StateUnknownHost: 707 | case StateAuthChoose: 708 | case StateAuthContinue: 709 | case StateAuthNeedPassword: 710 | case StateAuthKbiQuestions: 711 | case StateAuthAllFailed: 712 | case StateOpened: 713 | case StateError: 714 | break; 715 | 716 | case StateAuthNone: 717 | failed_auth = UseAuthNone; 718 | break; 719 | 720 | case StateAuthAutoPubkey: 721 | failed_auth = UseAuthAutoPubKey; 722 | break; 723 | 724 | case StateAuthPassword: 725 | failed_auth = UseAuthPassword; 726 | break; 727 | 728 | case StateAuthKbi: 729 | failed_auth = UseAuthKbi; 730 | break; 731 | } 732 | 733 | if ( failed_auth != UseAuthEmpty ) { 734 | failed_auths_ |= failed_auth; 735 | State old_state = state_; 736 | emit authFailed(failed_auth); 737 | 738 | // User might close, or otherwise manipulate, the LibsshQtClient when an 739 | // auth fails, so make sure that the state has not been changed. 740 | if ( state_ != old_state ) { 741 | return; 742 | } 743 | } 744 | 745 | LIBSSHQT_DEBUG("Enabled auths:" << use_auths_); 746 | LIBSSHQT_DEBUG("Failed auths:" << failed_auths_); 747 | 748 | // Choose next state for LibsshQtClient 749 | if ( use_auths_ == UseAuthEmpty && failed_auths_ == UseAuthEmpty ) { 750 | setState(StateAuthChoose); 751 | 752 | } else if ( use_auths_ == UseAuthEmpty ) { 753 | setState(StateAuthAllFailed); 754 | 755 | } else if ( use_auths_ & UseAuthNone ) { 756 | use_auths_ &= ~UseAuthNone; 757 | setState(StateAuthNone); 758 | timer_.start(); 759 | 760 | } else if ( use_auths_ & UseAuthAutoPubKey ) { 761 | use_auths_ &= ~UseAuthAutoPubKey; 762 | setState(StateAuthAutoPubkey); 763 | timer_.start(); 764 | 765 | } else if ( use_auths_ & UseAuthPassword ) { 766 | use_auths_ &= ~UseAuthPassword; 767 | setState(StateAuthPassword); 768 | timer_.start(); 769 | 770 | } else if ( use_auths_ & UseAuthKbi ) { 771 | use_auths_ &= ~UseAuthKbi; 772 | setState(StateAuthKbi); 773 | timer_.start(); 774 | } 775 | } 776 | 777 | void LibsshQtClient::setUpNotifiers() 778 | { 779 | if ( ! read_notifier_ ) { 780 | socket_t socket = ssh_get_fd(session_); 781 | 782 | LIBSSHQT_DEBUG("Setting up read notifier for socket" << socket); 783 | read_notifier_ = new QSocketNotifier(socket, 784 | QSocketNotifier::Read, 785 | this); 786 | read_notifier_->setEnabled(true); 787 | connect(read_notifier_, SIGNAL(activated(int)), 788 | this, SLOT(handleSocketReadable(int))); 789 | } 790 | 791 | if ( ! write_notifier_ ) { 792 | socket_t socket = ssh_get_fd(session_); 793 | 794 | LIBSSHQT_DEBUG("Setting up write notifier for socket" << socket); 795 | write_notifier_ = new QSocketNotifier(socket, 796 | QSocketNotifier::Write, 797 | this); 798 | write_notifier_->setEnabled(true); 799 | connect(write_notifier_, SIGNAL(activated(int)), 800 | this, SLOT(handleSocketWritable(int))); 801 | } 802 | } 803 | 804 | void LibsshQtClient::destroyNotifiers() 805 | { 806 | if ( read_notifier_ ) { 807 | read_notifier_->disconnect(this); 808 | read_notifier_->setEnabled(false); 809 | read_notifier_->deleteLater(); 810 | read_notifier_ = 0; 811 | } 812 | 813 | if ( write_notifier_ ) { 814 | write_notifier_->disconnect(this); 815 | write_notifier_->setEnabled(false); 816 | write_notifier_->deleteLater(); 817 | write_notifier_ = 0; 818 | } 819 | } 820 | 821 | void LibsshQtClient::handleSocketReadable(int socket) 822 | { 823 | Q_UNUSED( socket ); 824 | 825 | read_notifier_->setEnabled(false); 826 | processStateGuard(); 827 | if ( read_notifier_ ) { 828 | read_notifier_->setEnabled(true); 829 | } 830 | } 831 | 832 | void LibsshQtClient::handleSocketWritable(int socket) 833 | { 834 | Q_UNUSED( socket ); 835 | 836 | enable_writable_nofifier_ = false; 837 | write_notifier_->setEnabled(false); 838 | processStateGuard(); 839 | } 840 | 841 | void LibsshQtClient::processStateGuard() 842 | { 843 | Q_ASSERT( ! process_state_running_ ); 844 | if ( process_state_running_ ) return; 845 | 846 | process_state_running_ = true; 847 | processState(); 848 | process_state_running_ = false; 849 | 850 | if ( write_notifier_ && enable_writable_nofifier_ ) { 851 | write_notifier_->setEnabled(true); 852 | } 853 | } 854 | 855 | void LibsshQtClient::processState() 856 | { 857 | switch ( state_ ) { 858 | 859 | case StateClosed: 860 | case StateClosing: 861 | case StateUnknownHost: 862 | case StateAuthChoose: 863 | case StateAuthNeedPassword: 864 | case StateAuthKbiQuestions: 865 | case StateAuthAllFailed: 866 | case StateError: 867 | return; 868 | 869 | case StateInit: 870 | { 871 | Q_ASSERT( session_ == 0 ); 872 | 873 | session_ = ssh_new(); 874 | if ( ! session_ ) { 875 | LIBSSHQT_FATAL("Could not create SSH session"); 876 | } 877 | ssh_set_blocking(session_, 0); 878 | 879 | unsigned int tmp_port = port_; 880 | int tmp_verbosity = log_verbosity_; 881 | 882 | if (setLibsshOption(SSH_OPTIONS_LOG_VERBOSITY, 883 | "SSH_OPTIONS_LOG_VERBOSITY", 884 | &tmp_verbosity, enumToString(log_verbosity_)) && 885 | 886 | setLibsshOption(SSH_OPTIONS_USER, "SSH_OPTIONS_USER", 887 | qPrintable(username_), username_) && 888 | 889 | setLibsshOption(SSH_OPTIONS_HOST, "SSH_OPTIONS_HOST", 890 | qPrintable(hostname_), hostname_) && 891 | 892 | setLibsshOption(SSH_OPTIONS_PORT, "SSH_OPTIONS_PORT", 893 | &tmp_port, QString::number(port_))) 894 | { 895 | setState(StateConnecting); 896 | timer_.start(); 897 | return; 898 | } 899 | 900 | return; 901 | } break; 902 | 903 | case StateConnecting: 904 | { 905 | int rc = ssh_connect(session_); 906 | if ( rc != SSH_ERROR && 907 | ( read_notifier_ == 0 || write_notifier_ == 0 )) { 908 | setUpNotifiers(); 909 | } 910 | 911 | switch ( rc ) { 912 | case SSH_AGAIN: 913 | enableWritableNotifier(); 914 | return; 915 | 916 | case SSH_ERROR: 917 | LIBSSHQT_DEBUG("Channel open error:" << errorCodeAndMessage()); 918 | setState(StateError); 919 | return; 920 | 921 | case SSH_OK: 922 | setState(StateIsKnown); 923 | timer_.start(); 924 | return; 925 | 926 | default: 927 | LIBSSHQT_CRITICAL("Unknown result code" << rc << 928 | "received from ssh_connect()"); 929 | return; 930 | } 931 | } break; 932 | 933 | case StateIsKnown: 934 | { 935 | int known = ssh_is_server_known(session_); 936 | 937 | switch ( known ) { 938 | case SSH_SERVER_ERROR: 939 | setState(StateError); 940 | return; 941 | 942 | case SSH_SERVER_NOT_KNOWN: 943 | case SSH_SERVER_KNOWN_CHANGED: 944 | case SSH_SERVER_FOUND_OTHER: 945 | case SSH_SERVER_FILE_NOT_FOUND: 946 | unknown_host_type_ = static_cast< HostState >( known ); 947 | LIBSSHQT_DEBUG("Setting unknown host state to" << 948 | unknown_host_type_); 949 | setState(StateUnknownHost); 950 | return; 951 | 952 | case SSH_SERVER_KNOWN_OK: 953 | unknown_host_type_ = HostKnown; 954 | tryNextAuth(); 955 | return; 956 | 957 | default: 958 | LIBSSHQT_CRITICAL("Unknown result code" << known << 959 | "received from ssh_is_server_known()"); 960 | return; 961 | } 962 | } break; 963 | 964 | case StateAuthContinue: { 965 | tryNextAuth(); 966 | return; 967 | } break; 968 | 969 | case StateAuthNone: 970 | { 971 | int rc = ssh_userauth_none(session_, 0); 972 | handleAuthResponse(rc, "ssh_userauth_none", UseAuthNone); 973 | return; 974 | } break; 975 | 976 | case StateAuthAutoPubkey: 977 | { 978 | int rc = ssh_userauth_autopubkey(session_, 0); 979 | handleAuthResponse(rc, "ssh_userauth_autopubkey", UseAuthAutoPubKey); 980 | return; 981 | } break; 982 | 983 | case StateAuthPassword: 984 | { 985 | int status = ssh_get_status(session_); 986 | if ( status == SSH_CLOSED || 987 | status == SSH_CLOSED_ERROR) { 988 | setState(StateError); 989 | return; 990 | 991 | } else if ( ! password_set_ ) { 992 | setState(StateAuthNeedPassword); 993 | return; 994 | 995 | } else { 996 | QByteArray utf8pw = password_.toUtf8(); 997 | int rc = ssh_userauth_password(session_, 0, utf8pw.constData()); 998 | 999 | if ( rc != SSH_AUTH_AGAIN ) { 1000 | password_set_ = false; 1001 | password_.clear(); 1002 | } 1003 | 1004 | handleAuthResponse(rc, "ssh_userauth_password", UseAuthPassword); 1005 | return; 1006 | } 1007 | } break; 1008 | 1009 | case StateAuthKbi: { 1010 | int rc = ssh_userauth_kbdint(session_, 0, 0); 1011 | if ( rc == SSH_AUTH_INFO ) { 1012 | 1013 | // Sometimes SSH_AUTH_INFO is returned even though there are no 1014 | // KBI questions available, in that case, continue as if 1015 | // SSH_AUTH_AGAIN was returned. 1016 | if ( ssh_userauth_kbdint_getnprompts(session_) <= 0 ) { 1017 | enableWritableNotifier(); 1018 | 1019 | } else { 1020 | setState(StateAuthKbiQuestions); 1021 | } 1022 | 1023 | } else { 1024 | handleAuthResponse(rc, "ssh_userauth_kbdint", UseAuthKbi); 1025 | } 1026 | return; 1027 | } 1028 | 1029 | case StateOpened: 1030 | { 1031 | int status = ssh_get_status(session_); 1032 | if ( status == SSH_CLOSED || 1033 | status == SSH_CLOSED_ERROR) { 1034 | setState(StateError); 1035 | return; 1036 | 1037 | } else { 1038 | // Activate processState() function on all children so that they can 1039 | // process their events and read and write IO. 1040 | emit doProcessState(); 1041 | return; 1042 | } 1043 | } break; 1044 | 1045 | } // End switch 1046 | 1047 | Q_ASSERT_X(false, __func__, "Case was not handled properly"); 1048 | } 1049 | 1050 | void LibsshQtClient::handleAuthResponse(int rc, 1051 | const char *func, 1052 | UseAuthFlag auth) 1053 | { 1054 | switch ( rc ) { 1055 | case SSH_AUTH_AGAIN: 1056 | enableWritableNotifier(); 1057 | return; 1058 | 1059 | case SSH_AUTH_ERROR: 1060 | LIBSSHQT_DEBUG("Authentication error:" << auth << 1061 | errorCodeAndMessage()); 1062 | setState(StateError); 1063 | return; 1064 | 1065 | case SSH_AUTH_DENIED: 1066 | LIBSSHQT_DEBUG("Authentication denied:" << auth); 1067 | tryNextAuth(); 1068 | return; 1069 | 1070 | case SSH_AUTH_PARTIAL: 1071 | LIBSSHQT_DEBUG("Partial authentication:" << auth); 1072 | tryNextAuth(); 1073 | return; 1074 | 1075 | case SSH_AUTH_SUCCESS: 1076 | LIBSSHQT_DEBUG("Authentication success:" << auth); 1077 | succeeded_auth_ = auth; 1078 | setState(StateOpened); 1079 | timer_.start(); 1080 | return; 1081 | 1082 | default: 1083 | LIBSSHQT_CRITICAL("Unknown result code" << rc << 1084 | "received from" << func); 1085 | return; 1086 | } 1087 | 1088 | Q_ASSERT_X(false, __func__, "Case was not handled properly"); 1089 | } 1090 | 1091 | bool LibsshQtClient::setLibsshOption(enum ssh_options_e type, 1092 | QString type_debug, 1093 | const void *value, 1094 | QString value_debug) 1095 | { 1096 | Q_ASSERT( session_ != 0 ); 1097 | 1098 | if ( state_ == StateError ) { 1099 | LIBSSHQT_CRITICAL("Cannot set option" << type_debug << 1100 | "to" << value_debug << "because state is" << state_); 1101 | return false; 1102 | } 1103 | 1104 | LIBSSHQT_DEBUG("Setting option" << type_debug << "to" << value_debug); 1105 | if ( ssh_options_set(session_, type, value) != 0 ) { 1106 | LIBSSHQT_CRITICAL("Failed to set option " << type_debug << 1107 | "to" << value_debug); 1108 | setState(StateError); 1109 | return false; 1110 | } 1111 | 1112 | return true; 1113 | } 1114 | 1115 | 1116 | 1117 | 1118 | 1119 | 1120 | 1121 | 1122 | 1123 | 1124 | 1125 | 1126 | 1127 | 1128 | 1129 | 1130 | 1131 | 1132 | 1133 | 1134 | 1135 | 1136 | 1137 | -------------------------------------------------------------------------------- /src/libsshqtclient.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBSSHQTCLIENT_H 2 | #define LIBSSHQTCLIENT_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | class QUrl; 11 | class LibsshQtProcess; 12 | 13 | /*! 14 | 15 | LibsshQtClient 16 | 17 | */ 18 | class LibsshQtClient : public QObject 19 | { 20 | Q_OBJECT 21 | 22 | public: 23 | 24 | Q_ENUMS(State) 25 | enum State 26 | { 27 | StateClosed, 28 | StateClosing, 29 | StateInit, 30 | StateConnecting, 31 | StateIsKnown, 32 | StateUnknownHost, 33 | StateAuthChoose, 34 | StateAuthContinue, 35 | StateAuthNone, 36 | StateAuthAutoPubkey, 37 | StateAuthPassword, 38 | StateAuthNeedPassword, 39 | StateAuthKbi, 40 | StateAuthKbiQuestions, 41 | StateAuthAllFailed, 42 | StateOpened, 43 | StateError 44 | }; 45 | 46 | Q_ENUMS(LogVerbosity) 47 | enum LogVerbosity 48 | { 49 | LogDisable = SSH_LOG_NOLOG, 50 | LogRare = SSH_LOG_RARE, 51 | LogProtocol = SSH_LOG_PROTOCOL, 52 | LogPacket = SSH_LOG_PACKET, 53 | LogFunction = SSH_LOG_FUNCTIONS 54 | }; 55 | 56 | Q_ENUMS(HostState) 57 | enum HostState 58 | { 59 | HostKnown = SSH_SERVER_KNOWN_OK, 60 | HostUnknown = SSH_SERVER_NOT_KNOWN, 61 | HostKeyChanged = SSH_SERVER_KNOWN_CHANGED, 62 | HostKeyTypeChanged = SSH_SERVER_FOUND_OTHER, 63 | HostKnownHostsFileMissing = SSH_SERVER_FILE_NOT_FOUND 64 | }; 65 | 66 | Q_FLAGS(AuthMehodFlag) 67 | enum AuthMehodFlag 68 | { 69 | AuthMethodUnknown = SSH_AUTH_METHOD_UNKNOWN, 70 | AuthMethodNone = SSH_AUTH_METHOD_NONE, 71 | AuthMethodPassword = SSH_AUTH_METHOD_PASSWORD, 72 | AuthMethodPublicKey = SSH_AUTH_METHOD_PUBLICKEY, 73 | AuthMethodHostBased = SSH_AUTH_METHOD_HOSTBASED, 74 | AuthMethodKbi = SSH_AUTH_METHOD_INTERACTIVE 75 | }; 76 | Q_DECLARE_FLAGS(AuthMethods, AuthMehodFlag) 77 | 78 | Q_FLAGS(UseAuthFlag) 79 | enum UseAuthFlag 80 | { 81 | UseAuthEmpty = 0, //!< Auth method not chosen 82 | UseAuthNone = 1<<0, //!< SSH None authentication method 83 | UseAuthAutoPubKey = 1<<1, //!< Keys from ~/.ssh and ssh-agent 84 | UseAuthPassword = 1<<2, //!< SSH Password auth method 85 | UseAuthKbi = 1<<3 //!< SSH KBI auth method 86 | }; 87 | Q_DECLARE_FLAGS(UseAuths, UseAuthFlag) 88 | 89 | class KbiQuestion 90 | { 91 | public: 92 | QString instruction; 93 | QString question; 94 | bool showAnswer; 95 | }; 96 | 97 | 98 | explicit LibsshQtClient(QObject *parent = 0); 99 | ~LibsshQtClient(); 100 | 101 | static const char *enumToString(const LogVerbosity value); 102 | static const char *enumToString(const State value); 103 | static const char *enumToString(const HostState value); 104 | static const char *enumToString(const AuthMehodFlag value); 105 | static const char *enumToString(const UseAuthFlag value); 106 | 107 | static QString flagsToString(const AuthMethods flags); 108 | static QString flagsToString(const UseAuths flags); 109 | 110 | 111 | // Options 112 | void setDebug(bool enabled); 113 | void setUsername(QString username); 114 | void setHostname(QString hostname); 115 | void setPort(quint16 port); 116 | void setVerbosity(LogVerbosity loglevel); 117 | void setUrl(const QUrl &url); 118 | 119 | bool isDebugEnabled() const; 120 | QString username() const; 121 | QString hostname() const; 122 | quint16 port() const; 123 | QUrl url() const; 124 | 125 | // Connection 126 | bool isOpen(); 127 | public slots: 128 | void connectToHost(); 129 | void connectToHost(const QString hostName); 130 | void connectToHost(const QString hostName, unsigned int port); 131 | void disconnectFromHost(); 132 | public: 133 | 134 | // Authentication 135 | void useAuth(UseAuths auths, bool enabled); 136 | void useDefaultAuths(); 137 | void useNoneAuth(bool enabled); 138 | void useAutoKeyAuth(bool enabled); 139 | void usePasswordAuth(bool enabled); 140 | void useKbiAuth(bool enabled); 141 | 142 | void setPassword(QString password); 143 | QList kbiQuestions(); 144 | void setKbiAnswers(QStringList answers); 145 | 146 | AuthMethods supportedAuthMethods(); 147 | UseAuths enabledAuths(); 148 | UseAuths failedAuths(); 149 | 150 | // Doing something 151 | LibsshQtProcess *runCommand(QString command); 152 | 153 | // Functions for handling unknown hosts 154 | HostState unknownHostType(); 155 | QString unknownHostMessage(); 156 | QString hostPublicKeyHash(); 157 | bool markCurrentHostKnown(); 158 | 159 | // Errors handling 160 | QString errorCodeAndMessage() const; 161 | QString errorMessage() const; 162 | int errorCode() const; 163 | 164 | State state() const; 165 | ssh_session sshSession(); 166 | void enableWritableNotifier(); 167 | 168 | signals: 169 | void debugChanged(); 170 | void unknownHost(); 171 | void chooseAuth(); 172 | void needPassword(); //!< Use setPassword() to set password 173 | void needKbiAnswers(); //!< Use setKbiAnswers() set answers 174 | void authFailed(int auth); //!< One authentication attempt has failed 175 | void allAuthsFailed(); //!< All authentication attempts have failed 176 | void opened(); 177 | void closed(); 178 | void error(); 179 | 180 | // Signals for LIBSSHQT child objects: 181 | void doProcessState(); //!< All children must process their state 182 | void doCleanup(); //!< All children must release libssh resources 183 | 184 | private: 185 | void setState(State state); 186 | void tryNextAuth(); 187 | void setUpNotifiers(); 188 | void destroyNotifiers(); 189 | void processState(); 190 | void handleAuthResponse(int rc, const char *func, UseAuthFlag auth); 191 | bool setLibsshOption(enum ssh_options_e type, 192 | QString type_debug, 193 | const void *value, 194 | QString value_debug); 195 | 196 | private slots: 197 | void handleSocketReadable(int socket); 198 | void handleSocketWritable(int socket); 199 | void processStateGuard(); 200 | 201 | private: 202 | QString debug_prefix_; 203 | bool debug_output_; 204 | 205 | QTimer timer_; 206 | ssh_session session_; 207 | State state_; 208 | bool process_state_running_; 209 | bool enable_writable_nofifier_; 210 | 211 | LogVerbosity log_verbosity_; 212 | quint16 port_; 213 | QString hostname_; 214 | QString username_; 215 | 216 | QSocketNotifier *read_notifier_; 217 | QSocketNotifier *write_notifier_; 218 | 219 | HostState unknown_host_type_; 220 | QString unknwon_host_key_hex_; 221 | 222 | UseAuths use_auths_; 223 | UseAuths failed_auths_; 224 | UseAuthFlag succeeded_auth_; 225 | 226 | bool password_set_; 227 | QString password_; 228 | }; 229 | 230 | 231 | 232 | // Include before "libsshqt.h" if you want to use these operators 233 | #ifdef QDEBUG_H 234 | 235 | inline QDebug operator<<(QDebug dbg, const LibsshQtClient *client) 236 | { 237 | if ( client->state() == LibsshQtClient::StateError ) { 238 | dbg.nospace() << "LibsshQtClient( " 239 | << LibsshQtClient::enumToString(client->state()) << ", " 240 | << client->errorCode() << ", " 241 | << client->errorMessage() << " )"; 242 | } else { 243 | dbg.nospace() << "LibsshQtClient( " 244 | << LibsshQtClient::enumToString(client->state()) 245 | << " )"; 246 | } 247 | return dbg.space(); 248 | } 249 | 250 | inline QDebug operator<<(QDebug dbg, const LibsshQtClient::State value) 251 | { 252 | dbg << LibsshQtClient::enumToString(value); 253 | return dbg; 254 | } 255 | 256 | inline QDebug operator<<(QDebug dbg, const LibsshQtClient::LogVerbosity value) 257 | { 258 | dbg << LibsshQtClient::enumToString(value); 259 | return dbg; 260 | } 261 | 262 | inline QDebug operator<<(QDebug dbg, const LibsshQtClient::HostState value) 263 | { 264 | dbg << LibsshQtClient::enumToString(value); 265 | return dbg; 266 | } 267 | 268 | inline QDebug operator<<(QDebug dbg, const LibsshQtClient::AuthMehodFlag flag) 269 | { 270 | dbg << LibsshQtClient::enumToString(flag); 271 | return dbg; 272 | } 273 | 274 | inline QDebug operator<<(QDebug dbg, const LibsshQtClient::UseAuthFlag flag) 275 | { 276 | dbg << LibsshQtClient::enumToString(flag); 277 | return dbg; 278 | } 279 | 280 | inline QDebug operator<<(QDebug dbg, const LibsshQtClient::AuthMethods flags) 281 | { 282 | dbg << qPrintable(LibsshQtClient::flagsToString(flags)); 283 | return dbg; 284 | } 285 | 286 | inline QDebug operator<<(QDebug dbg, const LibsshQtClient::UseAuths flags) 287 | { 288 | dbg << qPrintable(LibsshQtClient::flagsToString(flags)); 289 | return dbg; 290 | } 291 | 292 | inline QDebug operator<<(QDebug dbg, const LibsshQtClient::KbiQuestion &question) 293 | { 294 | dbg.nospace() << "KbiQuestion( " 295 | << question.instruction << ", " 296 | << question.question << ", " 297 | << question.showAnswer << " )"; 298 | return dbg.space(); 299 | } 300 | 301 | #endif 302 | 303 | #endif // LIBSSHQTCLIENT_H 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | -------------------------------------------------------------------------------- /src/libsshqtdebug.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBSSHQTDEBUG_H 2 | #define LIBSSHQTDEBUG_H 3 | 4 | #include 5 | #include 6 | 7 | #define LIBSSHQT_DEBUG(_message_) \ 8 | if ( debug_output_ ) { \ 9 | qDebug() << qPrintable(debug_prefix_) << _message_; \ 10 | } 11 | 12 | #define LIBSSHQT_CRITICAL(_message_) \ 13 | qCritical() << qPrintable(debug_prefix_) << _message_; 14 | 15 | #define LIBSSHQT_FATAL(_message_) \ 16 | qFatal("%s Fatal error: %s", qPrintable(debug_prefix_), _message_); 17 | 18 | #define LIBSSHQT_HEXNAME(_object_) \ 19 | qPrintable(LibsshQt::hexAndName(_object_)) 20 | 21 | namespace LibsshQt 22 | { 23 | inline QString hexAndName(QObject *object) 24 | { 25 | quint64 val = reinterpret_cast(object); 26 | QString hex = QString("%1").arg(val, 0, 16); 27 | QString name = object->metaObject()->className(); 28 | return hex.toUpper() + "-" + name; 29 | } 30 | 31 | inline QString debugPrefix(QObject *object) 32 | { 33 | return hexAndName(object) + ":"; 34 | } 35 | } 36 | 37 | #endif // LIBSSHQTDEBUG_H 38 | -------------------------------------------------------------------------------- /src/libsshqtprocess.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "libsshqtprocess.h" 9 | #include "libsshqtclient.h" 10 | #include "libsshqtdebug.h" 11 | 12 | 13 | 14 | //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 15 | // LibsshQtProcess 16 | //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 17 | 18 | LibsshQtProcess::LibsshQtProcess(LibsshQtClient *parent) : 19 | LibsshQtChannel(false, parent, parent), 20 | state_(StateClosed), 21 | exit_code_(-1), 22 | stderr_(new LibsshQtProcessStderr(this)) 23 | { 24 | debug_prefix_ = LibsshQt::debugPrefix(this); 25 | 26 | LIBSSHQT_DEBUG("Constructor"); 27 | 28 | timer_.setSingleShot(true); 29 | timer_.setInterval(0); 30 | 31 | connect(&timer_, SIGNAL(timeout()), this, SLOT(processState())); 32 | connect(parent, SIGNAL(error()), this, SLOT(handleClientError())); 33 | connect(parent, SIGNAL(doProcessState()), this, SLOT(processState())); 34 | connect(parent, SIGNAL(doCleanup()), this, SLOT(closeChannel())); 35 | 36 | setStdoutBehaviour(OutputToQDebug, "Remote stdout:"); 37 | setStderrBehaviour(OutputToQDebug, "Remote stderr:"); 38 | } 39 | 40 | LibsshQtProcess::~LibsshQtProcess() 41 | { 42 | LIBSSHQT_DEBUG("Destructor"); 43 | closeChannel(); 44 | } 45 | 46 | const char *LibsshQtProcess::enumToString(const State value) 47 | { 48 | return staticMetaObject.enumerator( 49 | staticMetaObject.indexOfEnumerator("State")) 50 | .valueToKey(value); 51 | } 52 | 53 | const char *LibsshQtProcess::enumToString(const OutputBehaviour value) 54 | { 55 | return staticMetaObject.enumerator( 56 | staticMetaObject.indexOfEnumerator("OutputBehaviour")) 57 | .valueToKey(value); 58 | } 59 | 60 | void LibsshQtProcess::setCommand(QString command) 61 | { 62 | command_ = command; 63 | LIBSSHQT_DEBUG("Setting command to" << command); 64 | } 65 | 66 | QString LibsshQtProcess::command() const 67 | { 68 | return command_; 69 | } 70 | 71 | int LibsshQtProcess::exitCode() const 72 | { 73 | return exit_code_; 74 | } 75 | 76 | void LibsshQtProcess::setStdoutBehaviour(OutputBehaviour behaviour, 77 | QString prefix) 78 | { 79 | LIBSSHQT_DEBUG("Setting stdout behaviour to" << behaviour); 80 | 81 | stdout_behaviour_ = behaviour; 82 | stdout_output_prefix_ = prefix; 83 | 84 | if ( behaviour == OutputManual ) { 85 | disconnect(this, SIGNAL(readyRead()), 86 | this, SLOT(handleStdoutOutput())); 87 | 88 | } else { 89 | connect(this, SIGNAL(readyRead()), 90 | this, SLOT(handleStdoutOutput()), 91 | Qt::UniqueConnection); 92 | handleStdoutOutput(); 93 | } 94 | } 95 | 96 | void LibsshQtProcess::setStderrBehaviour(OutputBehaviour behaviour, 97 | QString prefix) 98 | { 99 | LIBSSHQT_DEBUG("Setting stderr behaviour to" << behaviour); 100 | 101 | stderr_behaviour_ = behaviour; 102 | stderr_output_prefix_ = prefix; 103 | 104 | if ( behaviour == OutputManual ) { 105 | disconnect(stderr_, SIGNAL(readyRead()), 106 | this, SLOT(handleStderrOutput())); 107 | 108 | } else { 109 | connect(stderr_, SIGNAL(readyRead()), 110 | this, SLOT(handleStderrOutput()), 111 | Qt::UniqueConnection); 112 | handleStderrOutput(); 113 | } 114 | } 115 | 116 | LibsshQtProcessStderr *LibsshQtProcess::stderr() 117 | { 118 | return stderr_; 119 | } 120 | 121 | LibsshQtProcess::State LibsshQtProcess::state() const 122 | { 123 | return state_; 124 | } 125 | 126 | /*! 127 | Same as openChannel() 128 | */ 129 | bool LibsshQtProcess::open(OpenMode ignored) 130 | { 131 | Q_UNUSED( ignored ); 132 | openChannel(); 133 | return true; 134 | } 135 | 136 | /*! 137 | If the process has been successfully opened this function calls sendEof() 138 | otherwise closeChannel() is called. 139 | 140 | In either case you should wait for closed signal before deletin the 141 | LibsshQtProcess. 142 | */ 143 | void LibsshQtProcess::close() 144 | { 145 | if ( state_ == StateOpen ) { 146 | sendEof(); 147 | } else { 148 | closeChannel(); 149 | } 150 | } 151 | 152 | /*! 153 | Open SSH channel and start the process. 154 | */ 155 | void LibsshQtProcess::openChannel() 156 | { 157 | if ( state_ == StateClosed ) { 158 | setState(StateWaitClient); 159 | timer_.start(); 160 | } 161 | } 162 | 163 | /*! 164 | This function closes the SSH channel immediadly and prepares the object for 165 | reuse, any possible data in buffers is discarded. 166 | */ 167 | void LibsshQtProcess::closeChannel() 168 | { 169 | if ( state_ != StateClosed && 170 | state_ != StateClosing ){ 171 | 172 | // Prevent recursion 173 | setState(StateClosing); 174 | 175 | emit readChannelFinished(); 176 | handleStdoutOutput(); 177 | handleStderrOutput(); 178 | 179 | if ( channel_ ) { 180 | if ( ssh_channel_is_open(channel_) != 0 ) { 181 | ssh_channel_close(channel_); 182 | } 183 | 184 | ssh_channel_free(channel_); 185 | channel_ = 0; 186 | } 187 | 188 | QIODevice::close(); 189 | reinterpret_cast< QIODevice* >( stderr_ )->close(); 190 | 191 | read_buffer_.clear(); 192 | write_buffer_.clear(); 193 | stderr_->read_buffer_.clear(); 194 | stderr_->write_buffer_.clear(); 195 | 196 | setState(StateClosed); 197 | } 198 | } 199 | 200 | void LibsshQtProcess::setState(State state) 201 | { 202 | if ( state_ == state ) { 203 | LIBSSHQT_DEBUG("State is already" << state); 204 | return; 205 | } 206 | 207 | LIBSSHQT_DEBUG("Changing state to" << state); 208 | state_ = state; 209 | 210 | switch ( state_ ) { 211 | case StateClosed: emit closed(); break; 212 | case StateClosing: break; 213 | case StateWaitClient: break; 214 | case StateOpening: break; 215 | case StateExec: break; 216 | case StateOpen: emit opened(); break; 217 | case StateError: emit error(); break; 218 | case StateClientError: emit error(); break; 219 | } 220 | } 221 | 222 | void LibsshQtProcess::queueCheckIo() 223 | { 224 | timer_.start(); 225 | } 226 | 227 | void LibsshQtProcess::processState() 228 | { 229 | switch ( state_ ) { 230 | 231 | case StateClosed: 232 | case StateClosing: 233 | case StateError: 234 | case StateClientError: 235 | return; 236 | 237 | case StateWaitClient: 238 | { 239 | if ( client_->state() == LibsshQtClient::StateOpened ) { 240 | setState(StateOpening); 241 | timer_.start(); 242 | } 243 | return; 244 | } break; 245 | 246 | case StateOpening: 247 | { 248 | if ( ! channel_ ) { 249 | ssh_channel channel = ssh_channel_new(client_->sshSession()); 250 | 251 | if ( channel ) { 252 | channel_ = channel; 253 | stderr_->channel_ = channel; 254 | 255 | } else { 256 | LIBSSHQT_FATAL("Could not create SSH channel"); 257 | } 258 | } 259 | 260 | int rc = ssh_channel_open_session(channel_); 261 | 262 | switch ( rc ) { 263 | case SSH_AGAIN: 264 | client_->enableWritableNotifier(); 265 | return; 266 | 267 | case SSH_ERROR: 268 | LIBSSHQT_DEBUG("Channel open error:" << errorCodeAndMessage()); 269 | setState(StateError); 270 | return; 271 | 272 | case SSH_OK: 273 | setState(StateExec); 274 | timer_.start(); 275 | return; 276 | 277 | default: 278 | LIBSSHQT_CRITICAL("Unknown result code" << rc << 279 | "received from ssh_channel_open_session()"); 280 | return; 281 | } 282 | } break; 283 | 284 | case StateExec: 285 | { 286 | int rc = ssh_channel_request_exec(channel_, qPrintable(command_)); 287 | 288 | switch ( rc ) { 289 | case SSH_AGAIN: 290 | client_->enableWritableNotifier(); 291 | return; 292 | 293 | case SSH_ERROR: 294 | LIBSSHQT_DEBUG("Channel exec error:" << errorCodeAndMessage()); 295 | setState(StateError); 296 | return; 297 | 298 | case SSH_OK: 299 | // Set Unbuffered to disable QIODevice buffers. 300 | if ( ! QIODevice::open( ReadWrite | Unbuffered )) { 301 | LIBSSHQT_FATAL("QIODevice::open() failed"); 302 | } 303 | 304 | stderr_->open(); 305 | setState(StateOpen); 306 | timer_.start(); 307 | return; 308 | 309 | default: 310 | LIBSSHQT_CRITICAL("Unknown result code" << rc << 311 | "received from ssh_channel_request_exec()"); 312 | return; 313 | } 314 | } break; 315 | 316 | case StateOpen: 317 | { 318 | checkIo(); 319 | stderr_->checkIo(); 320 | 321 | if ( state_ == StateOpen && 322 | ssh_channel_poll(channel_, false) == SSH_EOF && 323 | ssh_channel_poll(channel_, true) == SSH_EOF ) { 324 | 325 | // EOF state affects atEnd() and canReadLine() behavior, 326 | // so emit readyRead signal so that users can do something about it. 327 | if ( ! read_buffer_.isEmpty()) { 328 | emit readyRead(); 329 | } 330 | if ( ! stderr_->read_buffer_.isEmpty()) { 331 | emit stderr_->readyRead(); 332 | } 333 | 334 | exit_code_ = ssh_channel_get_exit_status(channel_); 335 | 336 | LIBSSHQT_DEBUG("Process channel EOF"); 337 | LIBSSHQT_DEBUG("Command exit code:" << exit_code_); 338 | LIBSSHQT_DEBUG("Data in read buffer:" << read_buffer_.size()); 339 | LIBSSHQT_DEBUG("Data in write buffer:" << write_buffer_.size()); 340 | LIBSSHQT_DEBUG("Data in stderr buffer:" << 341 | stderr_->read_buffer_.size()); 342 | 343 | closeChannel(); 344 | emit finished(exit_code_); 345 | return; 346 | } 347 | 348 | return; 349 | } break; 350 | 351 | } // End switch 352 | 353 | Q_ASSERT_X(false, __func__, "Case was not handled properly"); 354 | } 355 | 356 | void LibsshQtProcess::handleClientError() 357 | { 358 | setState(LibsshQtProcess::StateClientError); 359 | } 360 | 361 | void LibsshQtProcess::handleStdoutOutput() 362 | { 363 | if ( stdout_behaviour_ == OutputManual) return; 364 | 365 | while ( canReadLine()) { 366 | QString line = QString(readLine()); 367 | handleOutput(stdout_behaviour_, stdout_output_prefix_, line); 368 | } 369 | } 370 | 371 | void LibsshQtProcess::handleStderrOutput() 372 | { 373 | if ( stderr_behaviour_ == OutputManual) return; 374 | 375 | Q_ASSERT(stderr_); 376 | while ( stderr_->canReadLine()) { 377 | QString line = QString(stderr()->readLine()); 378 | handleOutput(stderr_behaviour_, stderr_output_prefix_, line); 379 | } 380 | } 381 | 382 | void LibsshQtProcess::handleOutput(OutputBehaviour &behaviour, 383 | QString &prefix, 384 | QString &line) 385 | { 386 | switch ( behaviour ) { 387 | case OutputManual: 388 | case OutputToDevNull: 389 | // Do nothing 390 | return; 391 | 392 | case OutputToQDebug: 393 | if ( line.endsWith('\n')) { 394 | line.remove(line.length() - 1, 1); 395 | } 396 | 397 | if ( prefix.isEmpty()) { 398 | qDebug() << qPrintable(line); 399 | } else { 400 | qDebug() << qPrintable(prefix) 401 | << qPrintable(line); 402 | } 403 | return; 404 | } 405 | } 406 | 407 | 408 | 409 | //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 410 | // LibsshQtProcessStderr 411 | //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 412 | 413 | LibsshQtProcessStderr::LibsshQtProcessStderr(LibsshQtProcess *parent) : 414 | LibsshQtChannel(true, parent->client(), parent) 415 | { 416 | debug_prefix_ = LibsshQt::debugPrefix(this); 417 | } 418 | 419 | void LibsshQtProcessStderr::close() 420 | { 421 | reinterpret_cast(parent())->close(); 422 | } 423 | 424 | bool LibsshQtProcessStderr::open(OpenMode ignored) 425 | { 426 | Q_UNUSED( ignored ); 427 | 428 | // Set Unbuffered to disable QIODevice buffers. 429 | if ( ! QIODevice::open( ReadWrite | Unbuffered )) { 430 | LIBSSHQT_FATAL("QIODevice::open() failed"); 431 | } 432 | return true; 433 | } 434 | 435 | void LibsshQtProcessStderr::queueCheckIo() 436 | { 437 | reinterpret_cast(parent())->queueCheckIo(); 438 | } 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | -------------------------------------------------------------------------------- /src/libsshqtprocess.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBSSHQTPROCESS_H 2 | #define LIBSSHQTPROCESS_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "libsshqtchannel.h" 9 | 10 | class LibsshQtProcessStderr; 11 | 12 | /*! 13 | 14 | LibsshQtProcess 15 | 16 | By default LibsshQtProcess will send all process output to QDebug(), to 17 | change this behaviour see setStderrBehaviour() and setStdoutBehaviour(). 18 | 19 | */ 20 | class LibsshQtProcess : public LibsshQtChannel 21 | { 22 | Q_OBJECT 23 | friend class LibsshQtProcessStderr; 24 | 25 | public: 26 | Q_ENUMS(State) 27 | enum State { 28 | StateClosed, 29 | StateClosing, 30 | StateWaitClient, 31 | StateOpening, 32 | StateExec, 33 | StateOpen, 34 | StateError, 35 | StateClientError 36 | }; 37 | 38 | Q_ENUMS(OutputBehaviour) 39 | enum OutputBehaviour 40 | { 41 | OutputManual, //!< No automatic handling, read output manually 42 | OutputToQDebug, //!< All output is sent to QDebug() 43 | OutputToDevNull //!< All output is completely ignored 44 | }; 45 | 46 | explicit LibsshQtProcess(LibsshQtClient *parent); 47 | ~LibsshQtProcess(); 48 | 49 | static const char *enumToString(const State value); 50 | static const char *enumToString(const OutputBehaviour value); 51 | 52 | void setCommand(QString command); 53 | QString command() const; 54 | int exitCode() const; 55 | 56 | void setStdoutBehaviour(OutputBehaviour behaviour, QString prefix = ""); 57 | void setStderrBehaviour(OutputBehaviour behaviour, QString prefix = ""); 58 | LibsshQtProcessStderr *stderr(); 59 | 60 | State state() const; 61 | 62 | bool open(OpenMode ignored = 0); 63 | void close(); 64 | 65 | public slots: 66 | void openChannel(); 67 | void closeChannel(); 68 | 69 | signals: 70 | void opened(); 71 | void closed(); 72 | void error(); 73 | void finished(int exit_code); 74 | 75 | protected: 76 | void setState(State state); 77 | void queueCheckIo(); 78 | 79 | private slots: 80 | void processState(); 81 | void handleClientError(); 82 | void handleStdoutOutput(); 83 | void handleStderrOutput(); 84 | void handleOutput(OutputBehaviour &behaviour, 85 | QString &prefix, QString &line); 86 | 87 | private: 88 | QTimer timer_; 89 | State state_; 90 | QString command_; 91 | int exit_code_; 92 | 93 | OutputBehaviour stdout_behaviour_; 94 | QString stdout_output_prefix_; 95 | 96 | LibsshQtProcessStderr *stderr_; 97 | OutputBehaviour stderr_behaviour_; 98 | QString stderr_output_prefix_; 99 | }; 100 | 101 | 102 | /*! 103 | 104 | LibsshQtChannelStderr 105 | 106 | */ 107 | class LibsshQtProcessStderr : public LibsshQtChannel 108 | { 109 | Q_OBJECT 110 | friend class LibsshQtProcess; 111 | 112 | public: 113 | explicit LibsshQtProcessStderr(LibsshQtProcess *parent); 114 | void close(); 115 | 116 | protected: 117 | bool open(OpenMode ignored = 0); 118 | void queueCheckIo(); 119 | }; 120 | 121 | 122 | // Include before "libsshqt.h" if you want to use these operators 123 | #ifdef QDEBUG_H 124 | 125 | inline QDebug operator<<(QDebug dbg, const LibsshQtProcess *process) 126 | { 127 | if ( process->state() == LibsshQtProcess::StateError ) { 128 | dbg.nospace() << "LibsshQtProcess( " 129 | << LibsshQtProcess::enumToString(process->state()) << ", " 130 | << process->errorCode() << ", " 131 | << process->errorMessage() << " )"; 132 | } else { 133 | dbg.nospace() << "LibsshQtProcess( " 134 | << LibsshQtProcess::enumToString(process->state()) 135 | << " )"; 136 | } 137 | return dbg.space(); 138 | } 139 | 140 | inline QDebug operator<<(QDebug dbg, const LibsshQtProcess::State value) 141 | { 142 | dbg << LibsshQtProcess::enumToString(value); 143 | return dbg; 144 | } 145 | 146 | inline QDebug operator<<(QDebug dbg, const LibsshQtProcess::OutputBehaviour value) 147 | { 148 | dbg << LibsshQtProcess::enumToString(value); 149 | return dbg; 150 | } 151 | 152 | #endif 153 | 154 | #endif // LIBSSHQTPROCESS_H 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | -------------------------------------------------------------------------------- /src/libsshqtquestionconsole.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | #include "libsshqtquestionconsole.h" 14 | 15 | #include "libsshqtdebug.h" 16 | #include "libsshqtclient.h" 17 | 18 | 19 | LibsshQtQuestionConsole::LibsshQtQuestionConsole(LibsshQtClient *parent) : 20 | QObject(parent), 21 | debug_prefix_(LibsshQt::debugPrefix(this)), 22 | debug_output_(parent->isDebugEnabled()), 23 | client_(parent) 24 | { 25 | LIBSSHQT_DEBUG("Parent client is:" << LIBSSHQT_HEXNAME(parent)); 26 | 27 | connect(client_, SIGNAL(debugChanged()), 28 | this, SLOT(handleDebugChanged())); 29 | connect(client_, SIGNAL(unknownHost()), 30 | this, SLOT(handleUnknownHost())); 31 | connect(client_, SIGNAL(authFailed(int)), 32 | this, SLOT(handleAuthFailed(int))); 33 | connect(client_, SIGNAL(needPassword()), 34 | this, SLOT(handleNeedPassword())); 35 | connect(client_, SIGNAL(needKbiAnswers()), 36 | this, SLOT(handleNeedKbiAnswers())); 37 | connect(client_, SIGNAL(error()), 38 | this, SLOT(handleError())); 39 | 40 | if ( client_->state() == LibsshQtClient::StateUnknownHost ) { 41 | handleUnknownHost(); 42 | 43 | } else if ( client_->state() == LibsshQtClient::StateAuthNeedPassword ) { 44 | handleNeedPassword(); 45 | 46 | } else if ( client_->state() == LibsshQtClient::StateAuthKbiQuestions ) { 47 | handleNeedKbiAnswers(); 48 | 49 | } else if ( client_->state() == LibsshQtClient::StateAuthAllFailed ) { 50 | handleAllAuthsFailed(); 51 | } 52 | } 53 | 54 | void LibsshQtQuestionConsole::handleDebugChanged() 55 | { 56 | debug_output_ = client_->isDebugEnabled(); 57 | } 58 | 59 | void LibsshQtQuestionConsole::handleUnknownHost() 60 | { 61 | LIBSSHQT_DEBUG("Handling unknown host"); 62 | 63 | bool valid_input = false; 64 | while ( valid_input == false ) { 65 | 66 | std::cout << qPrintable(tr("Hostname")) << ": " 67 | << qPrintable(client_->hostname()) << ":" << client_->port() 68 | << std::endl 69 | << qPrintable(tr("Key fingerprint")) << ": " 70 | << qPrintable(client_->hostPublicKeyHash()) 71 | << std::endl 72 | << qPrintable(client_->unknownHostMessage()) << " " 73 | << qPrintable(tr("Do you want to trust this host?")) 74 | << std::endl 75 | << qPrintable(tr("Type Yes or No: ")); 76 | std::cout.flush(); 77 | 78 | QString line = readLine(); 79 | LIBSSHQT_DEBUG("User input:" << line); 80 | 81 | line = line.trimmed(); 82 | QRegExp yes_regex("^yes|y$", Qt::CaseInsensitive, QRegExp::RegExp2); 83 | QRegExp no_regex("^no|n$", Qt::CaseInsensitive, QRegExp::RegExp2); 84 | 85 | if ( yes_regex.exactMatch(line)) { 86 | LIBSSHQT_DEBUG("User accepted host"); 87 | client_->markCurrentHostKnown(); 88 | valid_input = true; 89 | 90 | } else if ( no_regex.exactMatch(line)) { 91 | LIBSSHQT_DEBUG("User refused host"); 92 | client_->disconnectFromHost(); 93 | valid_input = true; 94 | 95 | } else { 96 | std::cout << "Invalid input: " << qPrintable(line) << std::endl; 97 | } 98 | } 99 | } 100 | 101 | void LibsshQtQuestionConsole::handleNeedPassword() 102 | { 103 | LIBSSHQT_DEBUG("Handling password input"); 104 | 105 | std::cout << qPrintable(tr("Type password for")) << " " 106 | << qPrintable(client_->username()) << "@" 107 | << qPrintable(client_->hostname()) << ": "; 108 | std::cout.flush(); 109 | 110 | QString password = readPassword(); 111 | LIBSSHQT_DEBUG("Setting password"); 112 | client_->setPassword(password); 113 | } 114 | 115 | void LibsshQtQuestionConsole::handleNeedKbiAnswers() 116 | { 117 | LIBSSHQT_DEBUG("Handling KBI answer input"); 118 | 119 | QStringList answers; 120 | foreach ( LibsshQtClient::KbiQuestion question, client_->kbiQuestions()) { 121 | 122 | std::cout << qPrintable(tr("Authentication question from")) << " " 123 | << qPrintable(client_->hostname()) << ":" << client_->port() 124 | << std::endl 125 | << qPrintable(question.question); 126 | std::cout.flush(); 127 | 128 | if ( question.showAnswer ) { 129 | answers << readLine(); 130 | } else { 131 | answers << readPassword(); 132 | } 133 | } 134 | 135 | LIBSSHQT_DEBUG("Setting KBI answers"); 136 | client_->setKbiAnswers(answers); 137 | } 138 | 139 | void LibsshQtQuestionConsole::handleAuthFailed(int auth) 140 | { 141 | LibsshQtClient::AuthMethods supported = client_->supportedAuthMethods(); 142 | 143 | if ( auth & LibsshQtClient::UseAuthPassword && 144 | supported & LibsshQtClient::AuthMethodPassword ) { 145 | LIBSSHQT_DEBUG("Retrying:" << LibsshQtClient::AuthMethodPassword); 146 | client_->usePasswordAuth(true); 147 | 148 | } else if ( auth & LibsshQtClient::UseAuthKbi && 149 | supported & LibsshQtClient::AuthMethodKbi ) { 150 | LIBSSHQT_DEBUG("Retrying:" << LibsshQtClient::UseAuthKbi); 151 | client_->useKbiAuth(true); 152 | } 153 | } 154 | 155 | void LibsshQtQuestionConsole::handleAllAuthsFailed() 156 | { 157 | LIBSSHQT_DEBUG("All authentication attempts have failed"); 158 | LIBSSHQT_DEBUG("Supported auth:" << client_->supportedAuthMethods()); 159 | LIBSSHQT_DEBUG("Failed auths:" << client_->failedAuths()); 160 | 161 | std::cerr << qPrintable(tr("Could not authenticate to")) << " " 162 | << qPrintable(client_->hostname()) << ":" 163 | << client_->port() << std::endl; 164 | std::cerr.flush(); 165 | } 166 | 167 | void LibsshQtQuestionConsole::handleError() 168 | { 169 | std::cerr << qPrintable(tr("Error")) << ": " 170 | << qPrintable(client_->errorCodeAndMessage()) 171 | << std::endl; 172 | std::cerr.flush(); 173 | } 174 | 175 | QString LibsshQtQuestionConsole::readLine() 176 | { 177 | char line_array[256]; 178 | std::cin.getline(line_array, sizeof(line_array)); 179 | return QString::fromLocal8Bit(line_array); 180 | } 181 | 182 | QString LibsshQtQuestionConsole::readPassword() 183 | { 184 | struct termios old_atribs, no_echo; 185 | 186 | // Get terminal attributes 187 | tcgetattr(fileno(stdin), &old_atribs); 188 | no_echo = old_atribs; 189 | no_echo.c_lflag &= ~ECHO; 190 | no_echo.c_lflag |= ECHONL; 191 | 192 | // Disable echo 193 | if ( tcsetattr(fileno(stdin), TCSANOW, &no_echo) != 0 ) { 194 | LIBSSHQT_FATAL("Failed to disable terminal echo output"); 195 | } 196 | 197 | // Read line 198 | char line_array[256]; 199 | std::cin.getline(line_array, sizeof(line_array)); 200 | 201 | // Restore echo 202 | if ( tcsetattr(fileno(stdin), TCSANOW, &old_atribs) != 0 ) { 203 | LIBSSHQT_FATAL("Failed to restore terminal echo output"); 204 | } 205 | 206 | return QString::fromLocal8Bit(line_array); 207 | } 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | -------------------------------------------------------------------------------- /src/libsshqtquestionconsole.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBSSHQTQUESTIONCONSOLE_H 2 | #define LIBSSHQTQUESTIONCONSOLE_H 3 | 4 | #include 5 | #include 6 | 7 | #include "libsshqtclient.h" 8 | 9 | /*! 10 | 11 | LibsshQtQuestionConsole - Console user input class for LibsshQtClient 12 | 13 | LibsshQtQuestionConsole implements the normal console user input questions 14 | needed while connecting to a SSH server. Specifically 15 | LibsshQtQuestionConsole handles unknownHost, needPassword and needKbiAnswers 16 | signals emitted by LibsshQtClient and prints the appropriate questions to 17 | stdout and reads user input from stdin. 18 | 19 | If the user types the wrong password then LibsshQtQuestionConsole re-enables 20 | the attempted authentication method and asks the question again. 21 | Specifically if LibsshQtClient emits authFailed signal and the 22 | failed authentication type is either UseAuthPassword or UseAuthKbi and if 23 | the server actually supports the attempted method, then 24 | LibsshQtQuestionConsole will re-enable the failed authentication method. 25 | 26 | If LibsshQtClient emits either allAuthsFailed or error signal then 27 | LibsshQtQuestionConsole prints the correct error message to stderr. 28 | 29 | Because LibsshQtQuestionConsole uses the standard cin.readLine() function 30 | this class will block the execution of the application while waiting for 31 | user input. 32 | 33 | */ 34 | class LibsshQtQuestionConsole : public QObject 35 | { 36 | Q_OBJECT 37 | 38 | public: 39 | explicit LibsshQtQuestionConsole(LibsshQtClient *parent); 40 | 41 | private slots: 42 | void handleDebugChanged(); 43 | void handleUnknownHost(); 44 | void handleNeedPassword(); 45 | void handleNeedKbiAnswers(); 46 | void handleAuthFailed(int auth); 47 | void handleAllAuthsFailed(); 48 | void handleError(); 49 | 50 | private: 51 | QString readLine(); 52 | QString readPassword(); 53 | 54 | private: 55 | QString debug_prefix_; 56 | bool debug_output_; 57 | LibsshQtClient *client_; 58 | }; 59 | 60 | #endif // LIBSSHQTQUESTIONCONSOLE_H 61 | -------------------------------------------------------------------------------- /src/libsshqtquestiondialog.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "libsshqtquestiondialog.h" 9 | #include "ui_libsshqtquestiondialog.h" 10 | 11 | #include "libsshqtdebug.h" 12 | 13 | static const QSize icon_size(64, 64); 14 | static const QSize min_size(450, 150); 15 | static const QSize max_size(640, 480); 16 | 17 | LibsshQtQuestionDialog::LibsshQtQuestionDialog(QWidget *parent) : 18 | QDialog(parent), 19 | debug_output_(false), 20 | ui_(new Ui::LibsshQtQuestionDialog), 21 | client_(0), 22 | state_(StateHidden), 23 | kbi_pos_(0), 24 | show_auths_failed_dlg_(true), 25 | show_error_dlg_(true) 26 | { 27 | debug_prefix_ = LibsshQt::debugPrefix(this); 28 | 29 | ui_->setupUi(this); 30 | ui_->host_widget->hide(); 31 | ui_->auth_widget->hide(); 32 | 33 | setUnknownHostIcon(style()->standardIcon(QStyle::SP_MessageBoxWarning)); 34 | setAuthIcon(style()->standardIcon(QStyle::SP_MessageBoxQuestion)); 35 | } 36 | 37 | LibsshQtQuestionDialog::~LibsshQtQuestionDialog() 38 | { 39 | delete ui_; 40 | } 41 | 42 | const char *LibsshQtQuestionDialog::enumToString(const State value) 43 | { 44 | return staticMetaObject.enumerator( 45 | staticMetaObject.indexOfEnumerator("State")) 46 | .valueToKey(value); 47 | } 48 | 49 | /*! 50 | Set which LibsshQtClient object is handled by LibsshQtQuestionDialog. 51 | */ 52 | void LibsshQtQuestionDialog::setClient(LibsshQtClient *client) 53 | { 54 | if ( client_ ) { 55 | client_->disconnect(this); 56 | } 57 | 58 | hide(); 59 | client_ = client; 60 | debug_output_ = client->isDebugEnabled(); 61 | LIBSSHQT_DEBUG("Client set to:" << 62 | qPrintable(LibsshQt::hexAndName(client))); 63 | 64 | connect(client_, SIGNAL(debugChanged()), 65 | this, SLOT(handleDebugChanged())); 66 | connect(client_, SIGNAL(unknownHost()), 67 | this, SLOT(handleUnknownHost())); 68 | connect(client_, SIGNAL(authFailed(int)), 69 | this, SLOT(handleAuthFailed(int))); 70 | connect(client_, SIGNAL(allAuthsFailed()), 71 | this, SLOT(handleAllAuthsFailed())); 72 | connect(client_, SIGNAL(needPassword()), 73 | this, SLOT(handleNeedPassword())); 74 | connect(client_, SIGNAL(needKbiAnswers()), 75 | this, SLOT(handleNeedKbiAnswers())); 76 | connect(client_, SIGNAL(error()), 77 | this, SLOT(handleError())); 78 | 79 | connect(client_, SIGNAL(closed()), this, SLOT(hide())); 80 | connect(client_, SIGNAL(opened()), this, SLOT(hide())); 81 | connect(client_, SIGNAL(error()), this, SLOT(hide())); 82 | 83 | if ( client->state() == LibsshQtClient::StateUnknownHost ) { 84 | handleUnknownHost(); 85 | 86 | } else if ( client->state() == LibsshQtClient::StateAuthNeedPassword ) { 87 | handleNeedPassword(); 88 | 89 | } else if ( client->state() == LibsshQtClient::StateAuthKbiQuestions ) { 90 | handleNeedKbiAnswers(); 91 | 92 | } else if ( client->state() == LibsshQtClient::StateAuthAllFailed ) { 93 | handleAllAuthsFailed(); 94 | } 95 | } 96 | 97 | /*! 98 | Set which icon is used in "Unknown Host" dialogs. 99 | 100 | Defaults to QStyle::SP_MessageBoxWarning 101 | */ 102 | void LibsshQtQuestionDialog::setUnknownHostIcon(QIcon icon) 103 | { 104 | ui_->host_icon_label->setPixmap(icon.pixmap(icon_size)); 105 | } 106 | 107 | /*! 108 | Set which icon is used in "Authentication" dialogs. 109 | 110 | Defaults to QStyle::SP_MessageBoxQuestion 111 | */ 112 | void LibsshQtQuestionDialog::setAuthIcon(QIcon icon) 113 | { 114 | ui_->auth_icon_label->setPixmap(icon.pixmap(icon_size)); 115 | } 116 | 117 | /*! 118 | Should "Authentication failed" dialog be show when LibsshQtClient emits 119 | allAuthsFailed signal. 120 | */ 121 | void LibsshQtQuestionDialog::setShowAuthsFailedDlg(bool enabled) 122 | { 123 | show_auths_failed_dlg_ = enabled; 124 | } 125 | 126 | /*! 127 | Should "Error" dialog be show when LibsshQtClient emits error signal. 128 | */ 129 | void LibsshQtQuestionDialog::setShowErrorDlg(bool enabled) 130 | { 131 | show_error_dlg_ = enabled; 132 | } 133 | 134 | LibsshQtQuestionDialog::State LibsshQtQuestionDialog::state() const 135 | { 136 | return state_; 137 | } 138 | 139 | void LibsshQtQuestionDialog::done(int code) 140 | { 141 | Q_ASSERT( state_ != StateHidden ); 142 | 143 | if ( state_ == StateHidden ) { 144 | LIBSSHQT_CRITICAL("done() called in state:" << state_); 145 | 146 | } else if ( code == QDialog::Accepted) { 147 | LIBSSHQT_DEBUG("Dialog accepted in state:" << state_); 148 | 149 | switch ( state_ ) { 150 | case StateHidden: 151 | // What are we doing here? 152 | break; 153 | 154 | case StateUnknownHostDlg: 155 | LIBSSHQT_DEBUG("Accepting host"); 156 | client_->markCurrentHostKnown(); 157 | break; 158 | 159 | case StatePasswordAuthDlg: 160 | LIBSSHQT_DEBUG("Setting password"); 161 | client_->setPassword(ui_->auth_line->text()); 162 | break; 163 | 164 | case StateKbiAuthDlg: 165 | kbi_answers_ << ui_->auth_line->text(); 166 | 167 | if ( kbi_pos_ < kbi_questions_.count()) { 168 | QTimer::singleShot(0, this, SLOT(showNextKbiQuestion())); 169 | 170 | } else { 171 | client_->setKbiAnswers(kbi_answers_); 172 | kbi_questions_.clear(); 173 | kbi_answers_.clear(); 174 | kbi_pos_ = 0; 175 | } 176 | break; 177 | } 178 | 179 | } else { 180 | LIBSSHQT_DEBUG("Dialog rejected in state:" << state_); 181 | LIBSSHQT_DEBUG("Closing connection:" << LIBSSHQT_HEXNAME(client_)); 182 | client_->disconnectFromHost(); 183 | } 184 | 185 | QDialog::done(code); 186 | } 187 | 188 | void LibsshQtQuestionDialog::setVisible(bool visible) 189 | { 190 | QDialog::setVisible(visible); 191 | 192 | if ( visible ) { 193 | Q_ASSERT( state_ != StateHidden ); 194 | 195 | } else if ( state_ != StateHidden ) { 196 | 197 | // Clear dialog for reuse 198 | ui_->host_info_label->clear(); 199 | ui_->host_msg_label->clear(); 200 | ui_->host_widget->hide(); 201 | ui_->auth_msg_label->clear(); 202 | ui_->auth_line->clear(); 203 | ui_->auth_widget->hide(); 204 | 205 | setState(StateHidden); 206 | } 207 | } 208 | 209 | void LibsshQtQuestionDialog::handleDebugChanged() 210 | { 211 | debug_output_ = client_->isDebugEnabled(); 212 | } 213 | 214 | void LibsshQtQuestionDialog::handleUnknownHost() 215 | { 216 | Q_ASSERT( state_ == StateHidden ); 217 | LIBSSHQT_DEBUG("Handling unknown host"); 218 | setState(StateUnknownHostDlg); 219 | 220 | QString msg = QString("%1 %2") 221 | .arg(QString(client_->unknownHostMessage()).toHtmlEscaped()) 222 | .arg(tr("Do you want to trust this host?")); 223 | 224 | QString info = QString("
%1: %2:%3
%4: %5") 225 | .arg(QString(tr("Hostname")).toHtmlEscaped()) 226 | .arg(QString(client_->hostname()).toHtmlEscaped()) 227 | .arg(client_->port()) 228 | .arg(QString(tr("Key fingerprint")).toHtmlEscaped()) 229 | .arg(QString(client_->hostPublicKeyHash()).toHtmlEscaped()); 230 | 231 | showHostDlg(msg, info); 232 | } 233 | 234 | void LibsshQtQuestionDialog::handleNeedPassword() 235 | { 236 | Q_ASSERT( state_ == StateHidden ); 237 | LIBSSHQT_DEBUG("Handling password input"); 238 | setState(StatePasswordAuthDlg); 239 | 240 | QString question = QString(tr("Type password for %1@%2")) 241 | .arg(client_->username()) 242 | .arg(client_->hostname()); 243 | showAuthDlg(question); 244 | } 245 | 246 | void LibsshQtQuestionDialog::handleNeedKbiAnswers() 247 | { 248 | Q_ASSERT( state_ == StateHidden ); 249 | LIBSSHQT_DEBUG("Handling KBI answer input"); 250 | setState(StateKbiAuthDlg); 251 | 252 | kbi_pos_ = 0; 253 | kbi_questions_ = client_->kbiQuestions(); 254 | 255 | showNextKbiQuestion(); 256 | } 257 | 258 | void LibsshQtQuestionDialog::showNextKbiQuestion() 259 | { 260 | Q_ASSERT( state_ == StateHidden || state_ == StateKbiAuthDlg ); 261 | Q_ASSERT( kbi_questions_.count() > 0 ); 262 | Q_ASSERT( kbi_pos_ < kbi_questions_.count()); 263 | 264 | if ( kbi_pos_ < kbi_questions_.count()) { 265 | LibsshQtClient::KbiQuestion question = kbi_questions_.at(kbi_pos_); 266 | LIBSSHQT_DEBUG("Showing KBI question" << kbi_pos_ + 1 << "/" << 267 | kbi_questions_.count() << ":" << question); 268 | 269 | QString msg = QString("%1 %2:%3

%4") 270 | .arg(QString(tr("Authentication question from")).toHtmlEscaped()) 271 | .arg(QString(client_->hostname()).toHtmlEscaped()) 272 | .arg(client_->port()) 273 | .arg(QString(question.question).toHtmlEscaped()); 274 | if ( ! question.instruction.isEmpty()) { 275 | msg += "

" + QString(question.instruction).toHtmlEscaped(); 276 | } 277 | 278 | kbi_pos_++; 279 | showAuthDlg(msg, question.showAnswer); 280 | 281 | } else { 282 | LIBSSHQT_DEBUG("All KBI questions have been asked already"); 283 | } 284 | } 285 | 286 | void LibsshQtQuestionDialog::handleAuthFailed(int auth) 287 | { 288 | LibsshQtClient::AuthMethods supported = client_->supportedAuthMethods(); 289 | 290 | if ( auth & LibsshQtClient::UseAuthPassword && 291 | supported & LibsshQtClient::AuthMethodPassword ) { 292 | LIBSSHQT_DEBUG("Retrying:" << LibsshQtClient::AuthMethodPassword); 293 | client_->usePasswordAuth(true); 294 | 295 | } else if ( auth & LibsshQtClient::UseAuthKbi && 296 | supported & LibsshQtClient::AuthMethodKbi ) { 297 | LIBSSHQT_DEBUG("Retrying:" << LibsshQtClient::UseAuthKbi); 298 | client_->useKbiAuth(true); 299 | } 300 | } 301 | 302 | void LibsshQtQuestionDialog::handleAllAuthsFailed() 303 | { 304 | LIBSSHQT_DEBUG("All authentication attempts have failed"); 305 | LIBSSHQT_DEBUG("Supported auth:" << client_->supportedAuthMethods()); 306 | LIBSSHQT_DEBUG("Failed auths:" << client_->failedAuths()); 307 | 308 | if ( show_auths_failed_dlg_ ) { 309 | QString message = QString("%1 %2:%3") 310 | .arg(tr("Could not authenticate to")) 311 | .arg(client_->hostname()) 312 | .arg(client_->port()); 313 | showInfoDlg(message, tr("Authentication failed")); 314 | } 315 | } 316 | 317 | void LibsshQtQuestionDialog::handleError() 318 | { 319 | if ( show_error_dlg_ ) { 320 | QString message = QString("%1: %2") 321 | .arg(tr("Error")) 322 | .arg(client_->errorCodeAndMessage()); 323 | showInfoDlg(message, tr("Error")); 324 | } 325 | } 326 | 327 | void LibsshQtQuestionDialog::setState(State state) 328 | { 329 | if ( state_ == state ) { 330 | LIBSSHQT_DEBUG("State is already" << state); 331 | return; 332 | } 333 | 334 | LIBSSHQT_DEBUG("Changing state to" << state); 335 | state_ = state; 336 | } 337 | 338 | void LibsshQtQuestionDialog::showHostDlg(QString message, QString info) 339 | { 340 | setWindowTitle(tr("Unknown Host")); 341 | 342 | ui_->host_widget->show(); 343 | ui_->host_msg_label->setText(message); 344 | ui_->host_info_label->setText(info); 345 | ui_->button_box->setStandardButtons(QDialogButtonBox::Yes | 346 | QDialogButtonBox::No); 347 | 348 | QSize size = sizeHint().boundedTo(max_size).expandedTo(min_size); 349 | setMinimumSize(size); 350 | setMaximumSize(size); 351 | show(); 352 | } 353 | 354 | void LibsshQtQuestionDialog::showAuthDlg(QString message, bool show_answer) 355 | { 356 | setWindowTitle(tr("Authentication")); 357 | 358 | ui_->auth_widget->show(); 359 | ui_->auth_msg_label->setText(message); 360 | ui_->auth_line->clear(); 361 | ui_->button_box->setStandardButtons(QDialogButtonBox::Ok | 362 | QDialogButtonBox::Cancel); 363 | 364 | if ( show_answer ) { 365 | ui_->auth_line->setEchoMode(QLineEdit::Normal); 366 | } else { 367 | ui_->auth_line->setEchoMode(QLineEdit::Password); 368 | } 369 | 370 | QSize size = sizeHint().boundedTo(max_size).expandedTo(min_size); 371 | setMinimumSize(size); 372 | setMaximumSize(size); 373 | show(); 374 | } 375 | 376 | void LibsshQtQuestionDialog::showInfoDlg(QString message, QString title) 377 | { 378 | QMessageBox *msg_box = new QMessageBox(parentWidget()); 379 | msg_box->setWindowModality(windowModality()); 380 | msg_box->setWindowTitle(title); 381 | msg_box->setText(message); 382 | msg_box->setIcon(QMessageBox::Information); 383 | 384 | connect(msg_box, SIGNAL(accepted()), msg_box, SLOT(deleteLater())); 385 | connect(msg_box, SIGNAL(rejected()), msg_box, SLOT(deleteLater())); 386 | 387 | msg_box->show(); 388 | } 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | -------------------------------------------------------------------------------- /src/libsshqtquestiondialog.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBSSHQTQUESTIONDIALOG_H 2 | #define LIBSSHQTQUESTIONDIALOG_H 3 | 4 | #include 5 | #include "libsshqtclient.h" 6 | 7 | namespace Ui { 8 | class LibsshQtQuestionDialog; 9 | } 10 | 11 | /*! 12 | 13 | LibsshQtQuestionDialog - Question and message dialog for LibsshQtClient 14 | 15 | LibsshQtQuestionDialog implements the standard question dialogs needed while 16 | connecting to a SSH server. Specifically LibsshQtQuestionDialog handles 17 | unknownHost, needPassword and needKbiAnswers signals emitted by 18 | LibsshQtClient and displays the appropriate dialog to the user. 19 | 20 | If the user types the wrong password then LibsshQtQuestionDialog re-enables 21 | the attempted authentication method and re-displays the authentication 22 | dialog. Specifically If LibsshQtClient emits authFailed signal and the 23 | failed authentication type is either UseAuthPassword or UseAuthKbi and if 24 | the server actually supports the attempted method, then 25 | LibsshQtQuestionDialog will re-enable the failed authentication method. 26 | 27 | If LibsshQtClient emits allAuthsFailed or error signal then 28 | LibsshQtQuestionDialog displays either "Authentication Failed" or "Error" 29 | messagebox. If you want to display these messages yourself, you can disable 30 | these messageboxes with setShowAuthsFailedDlg() and setShowErrorDlg() 31 | functions. 32 | 33 | If user cancels a dialog, LibsshQtClient::disconnectFromHost() is called. 34 | 35 | Use setClient() function to set which LibsshQtClient object is handled by 36 | LibsshQtQuestionDialog. 37 | 38 | */ 39 | class LibsshQtQuestionDialog : public QDialog 40 | { 41 | Q_OBJECT 42 | 43 | public: 44 | 45 | Q_ENUMS(State) 46 | enum State 47 | { 48 | StateHidden, 49 | StateUnknownHostDlg, 50 | StatePasswordAuthDlg, 51 | StateKbiAuthDlg 52 | }; 53 | 54 | explicit LibsshQtQuestionDialog(QWidget *parent = 0); 55 | ~LibsshQtQuestionDialog(); 56 | 57 | static const char *enumToString(const State value); 58 | 59 | void setClient(LibsshQtClient *client); 60 | void setUnknownHostIcon(QIcon icon); 61 | void setAuthIcon(QIcon icon); 62 | void setShowAuthsFailedDlg(bool enabled); 63 | void setShowErrorDlg(bool enabled); 64 | State state() const; 65 | 66 | void done(int code); 67 | void setVisible(bool visible); 68 | 69 | private slots: 70 | void handleDebugChanged(); 71 | void handleUnknownHost(); 72 | void handleNeedPassword(); 73 | void handleNeedKbiAnswers(); 74 | void showNextKbiQuestion(); 75 | void handleAuthFailed(int auth); 76 | void handleAllAuthsFailed(); 77 | void handleError(); 78 | 79 | private: 80 | void setState(State state); 81 | void showHostDlg(QString message, QString info); 82 | void showAuthDlg(QString message, bool show_answer = false); 83 | void showInfoDlg(QString message, QString title); 84 | 85 | private: 86 | QString debug_prefix_; 87 | bool debug_output_; 88 | 89 | Ui::LibsshQtQuestionDialog *ui_; 90 | LibsshQtClient *client_; 91 | State state_; 92 | 93 | QList kbi_questions_; 94 | QStringList kbi_answers_; 95 | int kbi_pos_; 96 | 97 | bool show_auths_failed_dlg_; 98 | bool show_error_dlg_; 99 | }; 100 | 101 | // Include before "libsshqt.h" if you want to use these operators 102 | #ifdef QDEBUG_H 103 | 104 | inline QDebug operator<<(QDebug dbg, const LibsshQtQuestionDialog *gui) 105 | { 106 | dbg.nospace() << "LibsshQtQuestionDialog( " 107 | << LibsshQtQuestionDialog::enumToString(gui->state()) 108 | << " )"; 109 | return dbg.space(); 110 | } 111 | 112 | inline QDebug operator<<(QDebug dbg, const LibsshQtQuestionDialog::State value) 113 | { 114 | dbg << LibsshQtQuestionDialog::enumToString(value); 115 | return dbg; 116 | } 117 | 118 | #endif 119 | 120 | #endif // LIBSSHQTQUESTIONDIALOG_H 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | -------------------------------------------------------------------------------- /src/libsshqtquestiondialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | LibsshQtQuestionDialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 10 | 11 | 12 | Dialog 13 | 14 | 15 | 16 | 17 | 18 | 19 | 0 20 | 0 21 | 22 | 23 | 24 | 25 | 0 26 | 27 | 28 | 0 29 | 30 | 31 | 0 32 | 33 | 34 | 35 | 36 | 37 | 0 38 | 0 39 | 40 | 41 | 42 | TextLabel 43 | 44 | 45 | Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 0 54 | 0 55 | 56 | 57 | 58 | TextLabel 59 | 60 | 61 | Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop 62 | 63 | 64 | true 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 0 73 | 0 74 | 75 | 76 | 77 | TextLabel 78 | 79 | 80 | Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 0 92 | 0 93 | 94 | 95 | 96 | 97 | 0 98 | 99 | 100 | 0 101 | 102 | 103 | 0 104 | 105 | 106 | 107 | 108 | 109 | 0 110 | 0 111 | 112 | 113 | 114 | TextLabel 115 | 116 | 117 | Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 0 126 | 0 127 | 128 | 129 | 130 | TextLabel 131 | 132 | 133 | Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop 134 | 135 | 136 | true 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | Qt::Horizontal 150 | 151 | 152 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | button_box 162 | accepted() 163 | LibsshQtQuestionDialog 164 | accept() 165 | 166 | 167 | 248 168 | 254 169 | 170 | 171 | 157 172 | 274 173 | 174 | 175 | 176 | 177 | button_box 178 | rejected() 179 | LibsshQtQuestionDialog 180 | reject() 181 | 182 | 183 | 316 184 | 260 185 | 186 | 187 | 286 188 | 274 189 | 190 | 191 | 192 | 193 | 194 | -------------------------------------------------------------------------------- /test/test.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "libsshqtclient.h" 8 | #include "libsshqtprocess.h" 9 | 10 | 11 | 12 | //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 13 | // TestCaseBase 14 | //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 15 | 16 | class TestCaseOpts 17 | { 18 | public: 19 | QEventLoop loop; 20 | QUrl url; 21 | QString password; 22 | }; 23 | 24 | class TestCaseBase : public QObject 25 | { 26 | Q_OBJECT 27 | 28 | public: 29 | TestCaseBase(TestCaseOpts *opts); 30 | void testSuccess(); 31 | void testFailed(); 32 | 33 | public slots: 34 | void handleError(); 35 | 36 | public: 37 | TestCaseOpts * const opts; 38 | LibsshQtClient * const client; 39 | }; 40 | 41 | TestCaseBase::TestCaseBase(TestCaseOpts *opts) : 42 | opts(opts), 43 | client(new LibsshQtClient(this)) 44 | { 45 | //client->setDebug(true); 46 | 47 | connect(client, SIGNAL(error()), 48 | this, SLOT(handleError())); 49 | connect(client, SIGNAL(closed()), 50 | this, SLOT(handleError())); 51 | connect(client, SIGNAL(allAuthsFailed()), 52 | this, SLOT(handleError())); 53 | 54 | client->setUrl(opts->url); 55 | client->usePasswordAuth(true); 56 | client->setPassword(opts->password); 57 | client->connectToHost(); 58 | } 59 | 60 | void TestCaseBase::testSuccess() 61 | { 62 | opts->loop.exit(0); 63 | client->disconnect(this); 64 | } 65 | 66 | void TestCaseBase::testFailed() 67 | { 68 | opts->loop.exit(-1); 69 | client->disconnect(this); 70 | } 71 | 72 | void TestCaseBase::handleError() 73 | { 74 | testFailed(); 75 | } 76 | 77 | 78 | 79 | //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 80 | // TestConnect 81 | //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 82 | 83 | /*! 84 | Test that connecting to the ssh server works. 85 | */ 86 | class TestCaseConnect : public TestCaseBase 87 | { 88 | Q_OBJECT 89 | 90 | public: 91 | TestCaseConnect(TestCaseOpts *opts); 92 | 93 | public slots: 94 | void connectTest(); 95 | }; 96 | 97 | TestCaseConnect::TestCaseConnect(TestCaseOpts *opts) : 98 | TestCaseBase(opts) 99 | { 100 | connect(client, SIGNAL(opened()), 101 | this, SLOT(connectTest())); 102 | } 103 | 104 | void TestCaseConnect::connectTest() 105 | { 106 | testSuccess(); 107 | } 108 | 109 | 110 | 111 | //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 112 | // TestCaseReadline 113 | //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 114 | 115 | /*! 116 | Test that readline() and canReadLine() functions work properly. 117 | */ 118 | class TestCaseReadline : public TestCaseBase 119 | { 120 | Q_OBJECT 121 | 122 | public: 123 | TestCaseReadline(TestCaseOpts *opts); 124 | 125 | public slots: 126 | void readLine(); 127 | void finished(); 128 | 129 | public: 130 | LibsshQtProcess *process; 131 | QIODevice *readdev; 132 | QStringList expected; 133 | int line_pos; 134 | }; 135 | 136 | TestCaseReadline::TestCaseReadline(TestCaseOpts *opts) : 137 | TestCaseBase(opts), 138 | process(0), 139 | readdev(0), 140 | line_pos(0) 141 | { 142 | expected << "first" << "second" << "last"; 143 | } 144 | 145 | void TestCaseReadline::readLine() 146 | { 147 | while ( readdev->canReadLine()) { 148 | QString line = QString(readdev->readLine()); 149 | if ( line.endsWith('\n')) { 150 | line.remove(line.length() - 1, 1); 151 | } 152 | 153 | if ( line == expected.at(line_pos)) { 154 | qDebug() << "Readline:" << line; 155 | line_pos++; 156 | 157 | } else { 158 | qDebug() << "Invalid line:" << line; 159 | testFailed(); 160 | return; 161 | } 162 | } 163 | } 164 | 165 | void TestCaseReadline::finished() 166 | { 167 | if ( line_pos == expected.count()) { 168 | testSuccess(); 169 | } else { 170 | qDebug() << "Invalid number of lines read:" << line_pos; 171 | testFailed(); 172 | } 173 | } 174 | 175 | 176 | 177 | //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 178 | // TestCaseReadlineStdout 179 | //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 180 | 181 | /*! 182 | Test that readline() and canReadLine() functions work properly with STDOUT. 183 | */ 184 | class TestCaseReadlineStdout : public TestCaseReadline 185 | { 186 | Q_OBJECT 187 | 188 | public: 189 | TestCaseReadlineStdout(TestCaseOpts *opts); 190 | }; 191 | 192 | TestCaseReadlineStdout::TestCaseReadlineStdout(TestCaseOpts *opts) : 193 | TestCaseReadline(opts) 194 | { 195 | QString command = QString("echo -en %1").arg(expected.join("\\\\n")); 196 | qDebug("Command: %s", qPrintable(command)); 197 | 198 | process = client->runCommand(command); 199 | process->setStdoutBehaviour(LibsshQtProcess::OutputManual); 200 | readdev = process; 201 | 202 | connect(process, SIGNAL(finished(int)), 203 | this, SLOT(finished())); 204 | connect(process, SIGNAL(readyRead()), 205 | this, SLOT(readLine())); 206 | } 207 | 208 | 209 | 210 | //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 211 | // TestCaseReadlineStderr 212 | //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 213 | 214 | /*! 215 | Test that readline() and canReadLine() functions work properly with STDERR. 216 | */ 217 | class TestCaseReadlineStderr : public TestCaseReadline 218 | { 219 | Q_OBJECT 220 | 221 | public: 222 | TestCaseReadlineStderr(TestCaseOpts *opts); 223 | }; 224 | 225 | TestCaseReadlineStderr::TestCaseReadlineStderr(TestCaseOpts *opts) : 226 | TestCaseReadline(opts) 227 | { 228 | QString command = QString("echo -en %1 1>&2").arg(expected.join("\\\\n")); 229 | qDebug("Command: %s", qPrintable(command)); 230 | 231 | process = client->runCommand(command); 232 | process->setStderrBehaviour(LibsshQtProcess::OutputManual); 233 | readdev = process->stderr(); 234 | 235 | connect(process, SIGNAL(finished(int)), 236 | this, SLOT(finished())); 237 | connect(readdev, SIGNAL(readyRead()), 238 | this, SLOT(readLine())); 239 | } 240 | 241 | 242 | 243 | //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 244 | // TestCaseIO 245 | //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 246 | 247 | /*! 248 | Test that every uint16 value can be written and read without error. 249 | Basically this test checks for data corruption. 250 | */ 251 | class TestCaseIO : public TestCaseBase 252 | { 253 | Q_OBJECT 254 | 255 | public: 256 | TestCaseIO(TestCaseOpts *opts); 257 | quint16 posToValue(int pos); 258 | 259 | public slots: 260 | void writeData(); 261 | void readData(); 262 | 263 | public: 264 | QIODevice *writedev; 265 | QIODevice *readdev; 266 | 267 | int error_count; 268 | int max_uint16; 269 | int write_pos; 270 | int read_pos; 271 | }; 272 | 273 | TestCaseIO::TestCaseIO(TestCaseOpts *opts) : 274 | TestCaseBase(opts), 275 | writedev(0), 276 | readdev(0), 277 | error_count(0), 278 | max_uint16(0xFFFF), 279 | write_pos(0), 280 | read_pos(0) 281 | { 282 | } 283 | 284 | quint16 TestCaseIO::posToValue(int pos) 285 | { 286 | int val = pos % ( max_uint16 + 1 ); 287 | //qDebug() << "Max:" << max << " Pos:" << pos << " Val:" << val; 288 | return val; 289 | } 290 | 291 | void TestCaseIO::writeData() 292 | { 293 | if ( writedev->bytesToWrite() == 0 && write_pos <= max_uint16 + 1 ) { 294 | quint16 val = posToValue(write_pos); 295 | QByteArray data(reinterpret_cast< const char* >( &val ), sizeof(val)); 296 | writedev->write(data); 297 | write_pos++; 298 | } 299 | } 300 | 301 | void TestCaseIO::readData() 302 | { 303 | while ( readdev->bytesAvailable() >= 2 && read_pos <= max_uint16 + 1 ) { 304 | 305 | quint16 read_val = 0; 306 | readdev->read(reinterpret_cast< char* >( &read_val ), sizeof(quint16)); 307 | 308 | quint16 correct_val = posToValue(read_pos); 309 | if ( read_val != correct_val ) { 310 | error_count++; 311 | qDebug() << "Position:" << read_pos 312 | << " Read:" << read_val 313 | << " Expected:" << correct_val 314 | << " Diff:" << read_val - correct_val; 315 | } 316 | read_pos++; 317 | } 318 | 319 | if ( read_pos > max_uint16 ) { 320 | qDebug() << "Found" << error_count << "errors"; 321 | if ( error_count == 0 ) { 322 | testSuccess(); 323 | } else { 324 | testFailed(); 325 | } 326 | } 327 | } 328 | 329 | 330 | 331 | //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 332 | // TestCaseIOStdout 333 | //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 334 | 335 | /*! 336 | Test that every uint16 value can be written and read without error 337 | from STDOUT. 338 | */ 339 | class TestCaseIOStdout : public TestCaseIO 340 | { 341 | Q_OBJECT 342 | 343 | public: 344 | TestCaseIOStdout(TestCaseOpts *opts); 345 | }; 346 | 347 | TestCaseIOStdout::TestCaseIOStdout(TestCaseOpts *opts) : 348 | TestCaseIO(opts) 349 | { 350 | LibsshQtProcess *process = client->runCommand("cat"); 351 | process->setStdoutBehaviour(LibsshQtProcess::OutputManual); 352 | 353 | readdev = process; 354 | writedev = process; 355 | 356 | connect(process, SIGNAL(opened()), 357 | this, SLOT(writeData())); 358 | connect(process, SIGNAL(bytesWritten(qint64)), 359 | this, SLOT(writeData())); 360 | connect(process, SIGNAL(readyRead()), 361 | this, SLOT(readData())); 362 | } 363 | 364 | 365 | 366 | //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 367 | // TestCaseIOStderr 368 | //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 369 | 370 | /*! 371 | Test that every uint16 value can be written and read without error 372 | from STDERR. 373 | */ 374 | class TestCaseIOStderr : public TestCaseIO 375 | { 376 | Q_OBJECT 377 | 378 | public: 379 | TestCaseIOStderr(TestCaseOpts *opts); 380 | }; 381 | 382 | TestCaseIOStderr::TestCaseIOStderr(TestCaseOpts *opts) : 383 | TestCaseIO(opts) 384 | { 385 | LibsshQtProcess *process = client->runCommand("cat 1>&2"); 386 | process->setStderrBehaviour(LibsshQtProcess::OutputManual); 387 | 388 | readdev = process->stderr(); 389 | writedev = process; 390 | 391 | connect(process, SIGNAL(opened()), 392 | this, SLOT(writeData())); 393 | connect(process, SIGNAL(bytesWritten(qint64)), 394 | this, SLOT(writeData())); 395 | connect(readdev, SIGNAL(readyRead()), 396 | this, SLOT(readData())); 397 | } 398 | 399 | 400 | 401 | //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 402 | // Test 403 | //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 404 | 405 | class Test : public QObject 406 | { 407 | Q_OBJECT 408 | 409 | public: 410 | Test(); 411 | 412 | private Q_SLOTS: 413 | void testConnect(); 414 | void testReadlineStdin(); 415 | void testReadlineStderr(); 416 | void testIoStdout(); 417 | void testIoStderr(); 418 | 419 | private: 420 | TestCaseOpts opts; 421 | }; 422 | 423 | Test::Test() 424 | { 425 | const char conf_file[] = "./libsshqt-test-config"; 426 | const QString conf_help = QString( 427 | "Please create configuration file:\n" 428 | "\n" 429 | " %1\n" 430 | "\n" 431 | "with the contents:\n" 432 | "\n" 433 | " ssh://user@hostname:port/\n" 434 | " password\n") 435 | .arg(conf_file); 436 | 437 | QFile file; 438 | file.setFileName(conf_file); 439 | if ( ! file.open(QIODevice::ReadOnly)) { 440 | qFatal("\nError: Could not open test configuration file: %s\n\n%s", 441 | conf_file, qPrintable(conf_help)); 442 | } 443 | 444 | QStringList config = QString(file.readAll()).split('\n'); 445 | if ( ! config.length() == 2 ) { 446 | qFatal("\nError: Could not read SSH url and password from: %s\n\n%s", 447 | conf_file, qPrintable(conf_help)); 448 | } 449 | 450 | opts.url = config.at(0); 451 | opts.password = config.at(1); 452 | } 453 | 454 | void Test::testConnect() 455 | { 456 | TestCaseConnect testcase(&opts); 457 | QVERIFY2(opts.loop.exec() == 0, "Could not connect to the SSH server"); 458 | } 459 | 460 | void Test::testReadlineStdin() 461 | { 462 | TestCaseReadlineStdout testcase(&opts); 463 | QVERIFY2(opts.loop.exec() == 0, "Could not read lines correctly from STDOUT"); 464 | } 465 | 466 | void Test::testReadlineStderr() 467 | { 468 | TestCaseReadlineStderr testcase(&opts); 469 | QVERIFY2(opts.loop.exec() == 0, "Could not read lines correctly from STDERR"); 470 | } 471 | 472 | void Test::testIoStdout() 473 | { 474 | TestCaseIOStdout testcase(&opts); 475 | QVERIFY2(opts.loop.exec() == 0, "Data corruption in STDOUT stream"); 476 | } 477 | 478 | void Test::testIoStderr() 479 | { 480 | TestCaseIOStderr testcase(&opts); 481 | QVERIFY2(opts.loop.exec() == 0, "Data corruption in STDERR stream"); 482 | } 483 | 484 | QTEST_MAIN(Test); 485 | 486 | #include "test.moc" 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | -------------------------------------------------------------------------------- /test/test.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2012-03-12T13:26:00 4 | # 5 | #------------------------------------------------- 6 | 7 | QT += testlib 8 | 9 | QT -= gui 10 | 11 | TARGET = libsshqt-test 12 | CONFIG += console 13 | CONFIG -= app_bundle 14 | 15 | TEMPLATE = app 16 | 17 | 18 | SOURCES += test.cpp 19 | DEFINES += SRCDIR=\\\"$$PWD/\\\" 20 | 21 | include( ../libsshqt.pri ) 22 | include( ../libssh.pri ) 23 | --------------------------------------------------------------------------------