Как реализовать SFTP с помощью Qt/QNetworkAccessManager (C++)

Я новичок в Qt, и я хотел бы реализовать поддержку FTP и SFTP для моего программного обеспечения. Когда я гуглил, я обнаружил, что не существует библиотеки sftp для Qt, но это должно быть возможно с QNetworkAccessManager. Затем я попытался выяснить, как создать собственный протокол или что-то подобное, но не понял, как это сделать.

Кто-нибудь знает, как я мог это сделать?

Спасибо майкл

2 ответа

В Qt SDK нет поддержки SFTP, но Qt Creator реализует SFTP.

Я изолировал библиотеку, которая содержит SSH и SFTP, и я создал новый проект с именем QSsh в Github. Целью проекта является предоставление поддержки SSH и SFTP для любого Приложения Qt.

Я написал пример о том, как загрузить файл с использованием SFTP. Взгляни на examples/SecureUploader/

Я надеюсь, что это может быть полезно

В Qt SDK нет текущей реализации оболочки SSH. Здесь у вас есть 3 варианта:

  1. Разверните свою собственную реализацию клиента SSH/SFTP, используя IETF RFC и стандартные проекты, такие как RFC4253. Возможно, это не то, что вы ищете.
  2. Используйте любую из библиотек реализации ssh, например openssh/libssh, напрямую или напишите свою собственную оболочку Qt/C++ для повторного использования в будущем. Любой достаточно приличный проект с потребностями ssh обычно связан с уже установленной библиотекой ssh ​​и использует ее программно. Как и Qt Creator, если вы достаточно долго копаетесь в нем, вы найдете того пользователя, о котором упоминал ранее Паглиан. Использование библиотеки менее рискованно и более перспективно, чем использование собственной библиотеки.
  3. Используйте инструменты openssh напрямую в интерфейсе командной строки, используя QProcess точно так же, как вы бы использовали его в оболочке. Это самый быстрый метод, если вы работаете над пилотным проектом и не нуждаетесь в каких-либо сложных ftp-операциях, так как может быть немного сложно разработать надежную оболочку для инструментария CLI.

Вам нужна индивидуальная реализация для каждого протокола. Но мы можем создать класс вроде QHttp, который будет это делать. Существует несколько протоколов, имеющих сходную семантику, но не все. Так что, если вы хотите написать это, скажите мне, и я помогу вам.

Я делаю это с помощью libssh. Очень прямо.https://api.libssh.org/stable/libssh_tutor_sftp.html

Не забудьте добавить свой sftp-сервер к известным хостам в вашей системе.

ssh-keyscan -H mysftpserver.com >> ~/.ssh/known_hosts

Пример кода:

#include "sftpuploader.h"
#include <QtDebug>
#include <QFileInfo>
#include <libssh/libssh.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <libssh/sftp.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <QFile>

int verify_knownhost(ssh_session session)
{
    int state, hlen;
    unsigned char *hash = NULL;
    char *hexa;
    char buf[10];

    state = ssh_is_server_known(session);

    hlen = ssh_get_pubkey_hash(session, &hash);
    if (hlen < 0)
        return -1;

    switch (state)
    {
    case SSH_SERVER_KNOWN_OK:
        break; /* ok */

    case SSH_SERVER_KNOWN_CHANGED:
        fprintf(stderr, "Host key for server changed: it is now:\n");
        ssh_print_hexa("Public key hash", hash, hlen);
        fprintf(stderr, "For security reasons, connection will be stopped\n");
        free(hash);
        return -1;

    case SSH_SERVER_FOUND_OTHER:
        fprintf(stderr, "The host key for this server was not found but an other"
                        "type of key exists.\n");
        fprintf(stderr, "An attacker might change the default server key to"
                        "confuse your client into thinking the key does not exist\n");
        free(hash);
        return -1;

    case SSH_SERVER_FILE_NOT_FOUND:
        fprintf(stderr, "Could not find known host file.\n");
        fprintf(stderr, "If you accept the host key here, the file will be"
                        "automatically created.\n");
        /* fallback to SSH_SERVER_NOT_KNOWN behavior */

    case SSH_SERVER_NOT_KNOWN:
        hexa = ssh_get_hexa(hash, hlen);
        fprintf(stderr,"The server is unknown. Do you trust the host key?\n");
        fprintf(stderr, "Public key hash: %s\n", hexa);
        free(hexa);
        if (fgets(buf, sizeof(buf), stdin) == NULL)
        {
            free(hash);
            return -1;
        }
        if (strncasecmp(buf, "yes", 3) != 0)
        {
            free(hash);
            return -1;
        }
        if (ssh_write_knownhost(session) < 0)
        {
            fprintf(stderr, "Error %s\n", strerror(errno));
            free(hash);
            return -1;
        }
        break;

    case SSH_SERVER_ERROR:
        fprintf(stderr, "Error %s", ssh_get_error(session));
        free(hash);
        return -1;
    }

    free(hash);
    return 0;
}

bool upload(const QString &localFile,
                          const QString &dest,
                          const QString &host,
                          const QString &username,
                          const QString &passwd)
{
    bool retVal = false;

    QFileInfo info(localFile);

    m_localFilename = info.canonicalFilePath();
    m_remoteFilename = dest + "/" + info.fileName();

    int verbosity = SSH_LOG_PROTOCOL;
    int port = 22;
    int rc;
    sftp_session sftp;
    sftp_file file;
    int access_type;
    int nwritten;
    QByteArray dataToWrite;
    ssh_session my_ssh_session;

    QFile myfile(m_localFilename);

    if(!myfile.exists())
    {
        qDebug() << "SFTPUploader: File doesn't exist " << m_localFilename;
        return retVal;
    }

    my_ssh_session = ssh_new();
    if(my_ssh_session == NULL)
    {
        return retVal;
    }

    ssh_options_set(my_ssh_session, SSH_OPTIONS_HOST, host.toUtf8());
    ssh_options_set(my_ssh_session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
    ssh_options_set(my_ssh_session, SSH_OPTIONS_PORT, &port);

    rc = ssh_connect(my_ssh_session);
    if (rc != SSH_OK)
    {
        qDebug() << "SFTPUploader: Error connecting to localhost: " << ssh_get_error(my_ssh_session);
        ssh_free(my_ssh_session);
        return retVal;
    }
    else
    {
        qDebug() << "SFTPUploader: SSH connected";
    }

    // Verify the server's identity
    // For the source code of verify_knowhost(), check previous example
    if (verify_knownhost(my_ssh_session) < 0)
    {
        ssh_disconnect(my_ssh_session);
        ssh_free(my_ssh_session);
        qDebug() << "SFTPUploader: verify_knownhost failed";
        return retVal;
    }

    rc = ssh_userauth_password(my_ssh_session, username.toUtf8(), passwd.toUtf8());
    if (rc != SSH_AUTH_SUCCESS)
    {
        qDebug() << "SFTPUploader: Error authenticating with password: " << ssh_get_error(my_ssh_session);
        ssh_disconnect(my_ssh_session);
        ssh_free(my_ssh_session);
        return retVal;
    }
    else
    {
        qDebug() << "SFTPUploader: Authentication sucess";
    }

    sftp = sftp_new(my_ssh_session);
    if (sftp == NULL)
    {
        qDebug() << "SFTPUploader: Error allocating SFTP session:" << ssh_get_error(my_ssh_session);
        ssh_disconnect(my_ssh_session);
        ssh_free(my_ssh_session);
        return retVal;
    }

    rc = sftp_init(sftp);
    if (rc != SSH_OK)
    {
        qDebug() << "SFTPUploader: Error initializing SFTP session:", sftp_get_error(sftp);
        sftp_free(sftp);
        ssh_disconnect(my_ssh_session);
        ssh_free(my_ssh_session);
        return retVal;
    }

    access_type = O_WRONLY | O_CREAT | O_TRUNC;
    file = sftp_open(sftp, dest.toUtf8(), access_type, S_IRWXU);
    if (file == NULL)
    {
        qDebug() << "SFTPUploader: Can't open file for writing:", ssh_get_error(my_ssh_session);
        sftp_free(sftp);
        ssh_disconnect(my_ssh_session);
        ssh_free(my_ssh_session);
        return retVal;
    }

    if(myfile.open(QFile::ReadOnly))
    {
        dataToWrite = myfile.readAll();
    }

    nwritten = sftp_write(file, dataToWrite, dataToWrite.size());
    if (nwritten != dataToWrite.size())
    {
        qDebug() << "SFTPUploader: Can't write data to file: ", ssh_get_error(my_ssh_session);
        sftp_close(file);
        sftp_free(sftp);
        ssh_disconnect(my_ssh_session);
        ssh_free(my_ssh_session);
        return retVal;
    }

    rc = sftp_close(file);
    if (rc != SSH_OK)
    {
        qDebug() << "SFTPUploader: Can't close the written file:" << ssh_get_error(my_ssh_session);
        sftp_free(sftp);
        ssh_disconnect(my_ssh_session);
        ssh_free(my_ssh_session);
        return retVal;
    }
    else
    {
        qDebug() << "SFTPUploader: Success";
        retVal = true;
    }
    return retVal;
}

Другие вопросы по тегам