如何使用 Qt/QNetworkAccessManager 实现 SFTP (C++)

发布于 2024-11-25 08:03:15 字数 169 浏览 0 评论 0原文

我是 Qt 新手,我想为我的软件实现 FTP 和 SFTP 支持。 当我用谷歌搜索时,我发现 Qt 不存在 sftp 库,但使用 QNetworkAccessManager 应该可以。 然后我试图发现如何构建自定义协议或类似的东西,但不知道如何去做。

有人知道我该怎么做吗?

谢谢, 迈克尔

I'm new to Qt and I would like to implement FTP and SFTP support for my software.
As I googled I discovered that there doesn't exist a sftp library for Qt but it should be possible with QNetworkAccessManager.
I tried then to discover on how to build a custom protocol or something like that but didn't figure out how to do it.

Does someone know how I could do that?

Thanks,
Michael

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

忘年祭陌 2024-12-02 08:03:15

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

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

我写了一个关于如何使用 SFTP 上传文件的示例。看看 examples/SecureUploader/

我希望它可能会有所帮助

There is no support for SFTP in Qt SDK but Qt Creator implements SFTP.

I have isolated the library that contains SSH and SFTP and I have created a new project named QSsh in Github. The aim of the project is to provide SSH and SFTP support for any Qt Application.

I have written an example on how to upload a file using SFTP. Take a look at examples/SecureUploader/

I hope it might be helpful

烟柳画桥 2024-12-02 08:03:15

您需要为每个协议定制实现。但我们可以创建一个像 QHttp 这样的类来做到这一点。有几个协议具有相似的语义,但不是全部。所以,如果你想写,请告诉我,我会帮助你。

You need a custom implementation for each protocol. But we can create a class like QHttp which will do that. There are several protocols that has similar semantic, but not all. So, if you want write it, tell me and I help you.

安人多梦 2024-12-02 08:03:15

Qt SDK 中当前没有 SSH 包装器实现。您在这里有 3 个选择:

  1. 使用 IETF RFC 和标准草案(如 RFC4253。这可能不是您要找的。
  2. 直接使用任何 ssh 实现库(例如 openssh/libssh)或编写自己的 Qt/C++ 包装器以供将来重用。任何具有 ssh 需求的相当不错的项目通常都会链接到一个已经建立的 ssh 库并以编程方式使用它。就像 Qt Creator 一样,如果您深入研究它足够长的时间,您会发现用户 Paglian 前面提到的内容。与自行构建相比,依赖库的风险更小,也更面向未来。
  3. 直接在命令行界面使用 openssh 工具,使用 QProcess 就像你一样' d 在 shell 中使用它。如果您正在处理概念验证项目并且不需要任何复杂的 ftp 操作,这是最快的方法,因为围绕 CLI 工具设计一个强大的包装器可能会有点困难。

There's no current SSH wrapper implementation in the Qt SDK. You have 3 choices here:

  1. Roll your own custom SSH/SFTP client implementation using the IETF RFC and standard drafts like RFC4253. This might not be what you're looking for.
  2. Use any of the ssh implementation libraries like openssh/libssh directly or writing your own Qt/C++ wrapper for future-reuse. Any reasonably decent project with ssh needs usually links to a an already established ssh library and uses it programatically. Like Qt Creator does, if you dig inside it long enough you'll find what user Paglian mentioned earlier. Relying on a library is less risky and more future-proof then rolling your own.
  3. Use openssh tooling at the command line interface directly, using QProcess just like you'd use it at the shell. This is is the fastest method if you're working on a proof-of-concept project and don't need any complex ftp operations, as it might get a bit difficult to devise a robust wrapper around the CLI tooling.
对不⑦ 2024-12-02 08:03:15

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

I do this using libssh. Very straight forward.
https://api.libssh.org/stable/libssh_tutor_sftp.html

Don't forget to add your sftp server into known hosts in your system.

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

Example code:

#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;
}

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文