如何在Qt/QNetworkAccessManager(C++)中实现SFTP

13

我是Qt的新手,希望为我的软件实现FTP和SFTP支持。通过谷歌搜索,我发现Qt没有SFTP库,但使用QNetworkAccessManager应该是可能的。然后我尝试了解如何构建自定义协议或类似的内容,但没有弄清楚如何实现。

有人知道我该怎么做吗?

谢谢, 迈克尔


你的目标操作系统是什么,你想如何使用SFTP? - Soheil Armin
我可以建议您依赖于libssh自行实现它。 - JuanDeLosMuertos
4个回答

13

Qt SDK不支持SFTP,但Qt Creator实现了SFTP。

我已经分离了包含SSH和SFTP的库,并在Github上创建了一个名为QSsh的新项目。该项目的目标是为任何Qt应用程序提供SSH和SFTP支持。

我写了一个示例,介绍如何使用SFTP上传文件。请查看examples/SecureUploader/

希望这对你有所帮助。


你好,你能帮我一下,在Qt中如何安装这个库吗? - GeneCode

1

Qt SDK中没有当前的SSH包装器实现。您有三个选择:

  1. 使用IETF RFC和标准草案(如RFC4253)自己编写自定义SSH / SFTP客户端实现。这可能不是您想要的。
  2. 直接使用任何ssh实现库,例如openssh / libssh,或编写自己的Qt / C ++包装器以供将来重用。任何具有ssh需求的合理不错的项目通常都链接到已经建立的ssh库并在程序中使用它。就像Qt Creator一样,如果您足够深入地挖掘,您会发现用户Paglian之前提到的内容。依赖库比自己编写更安全,并且更具未来性。
  3. 直接在命令行界面上使用openssh工具,使用QProcess就像您在shell上使用它一样。如果您正在开发概念验证项目并且不需要进行任何复杂的ftp操作,则这是最快的方法,因为在CLI工具周围设计健壮的包装器可能会有点困难。

1

每个协议都需要自定义实现。但是我们可以创建一个像QHttp这样的类来完成这项工作。有几种协议具有类似的语义,但并非全部都是如此。因此,如果您想编写它,请告诉我,我会帮助您。


不知道我是否很快就有空闲时间,但也许我会尝试一下 :) 感谢您的回答。 - Michael Weibel

1
我使用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;
}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接