Search code examples
c++sshscplibssh

How to avoid compressing of image, audio and video files downloaded from remote ssh using libssh scp command in c++?


I am trying to download few files stored on a remote machine. Those files can be either text files, image files (of jpeg,jpg form), video or audio files. Text files are downloaded successfully without issue and I am also able to open it as well in my local machine where I download them.

But image files and other video, audio files do not open after getting downloaded. When I checked, they were not of same size as original file. Like say the original file was of 30kb, but the downloaded file was only of 5kb. And this is the reason that maybe I am not able to open those files locally after downloading?

I am using scp function of libssh. Below is what I am trying:

#include <iostream>
#include <string>
#include <libssh.h>
#include <stdlib.h>
#include <fstream> 
#include <stdio.h>
#include <Windows.h>
#include <fcntl.h>
#include <sstream>
#include <io.h>
using namespace std;

static int fetch_files(ssh_session session){
    int size;
    //char buffer[16384];
    int mode;
    int rc;
    //creating scp session to read from remote host for this file
    ssh_scp scp = ssh_scp_new(session, SSH_SCP_READ | SSH_SCP_RECURSIVE, "/home/snaps.jpg");

    
    //executing the init
    rc = ssh_scp_init(scp);
    
    //pulling from request object
    rc = ssh_scp_pull_request(scp);
    if (rc != SSH_SCP_REQUEST_NEWFILE)
    {
        std::cout << "Error receiving information about file: rc != SSH_SCP_REQUEST_NEWFILE";
        return -3;
    }
    else {
        //trying to download the file from the created scp object
        int t_filesize = ssh_scp_request_get_size(scp);
        cout << "filesize is = " << t_filesize;
        string t_filename = strdup(ssh_scp_request_get_filename(scp));
        cout << "File name read is = " << t_filename;
        int t_filemode = ssh_scp_request_get_permissions(scp);
        cout << "File mode is = " << t_filemode;
        //fprintf(stderr, "Receiving file %s, size %d, permisssions 0%o\n", t_filename, t_filesize, t_filemode);
        FILE *fptr = fopen("file.jpg", "w");
        if (NULL == fptr)
        {
            fprintf(stderr, "Error opening local file: %s, error %s\n", t_filename, strerror(errno));
            ssh_scp_deny_request(scp, "Unable to open local file");

        }
        ssh_scp_accept_request(scp);

        while (rc >= 0)
        {   //reading and storing into buffer to write on local file system
            rc = ssh_scp_read(scp, buffer, sizeof(buffer));
            cout << "Buffer = " << buffer;
            //cout << "RC = " << rc << " and SSH_ERROR is = " << SSH_ERROR;
            if (rc == SSH_ERROR)
            {
                fprintf(stderr, "Error receiving file data: %s\n", ssh_get_error(session));
                fclose(fptr);
                ssh_scp_close(scp);
                ssh_scp_free(scp);
                return rc;
            }
            //fprintf(stderr, "Bytes received = %d\n", rc);
            if (fwrite(buffer, rc, 1, fptr) != t_filesize)
            {
                fprintf(stderr, "Error writing file data: %s\n", strerror(errno));
                fclose(fptr);
                ssh_scp_close(scp);
                ssh_scp_free(scp);
                return rc;
            }
        } 
        cout << "Buffer is = " << buffer;
        fclose(fptr);

    }

How can I resolve it?


Solution

  • if (fwrite(buffer, rc, 1, fptr) != t_filesize)
    

    Should have been

    if (fwrite(buffer, 1, rc, fptr) != rc)
    

    Or

    if (fwrite(buffer, rc, 1, fptr) != 1)
    

    rc and 1 are swapped. You intended to write rc bytes, second parameter is size of each unit with, third is number of units. Neither has anything to do with t_filesize yet.


    while (rc >= 0)
    

    Insufficient, you also need a counter for the total bytes received yet. In the loop increment by rc and break if total >= t_filesize.


    rc = ssh_scp_read(scp, buffer, sizeof(buffer));
    

    Extend to

    rc = ssh_scp_read(scp, buffer, min(sizeof(buffer), t_filesize - total));
    

    So you don't request read of bytes which are not available.


    As for why your files are too small, it's because you run into the faulty error handling mentioned first. You only get the first buffer full (respectively not even that, because the buffer isn't fully used), and the you already return from transfer.