Blame examples/libssh_scp.c

Packit Service 31306d
/* libssh_scp.c
Packit Service 31306d
 * Sample implementation of a SCP client
Packit Service 31306d
 */
Packit Service 31306d
Packit Service 31306d
/*
Packit Service 31306d
Copyright 2009 Aris Adamantiadis
Packit Service 31306d
Packit Service 31306d
This file is part of the SSH Library
Packit Service 31306d
Packit Service 31306d
You are free to copy this file, modify it in any way, consider it being public
Packit Service 31306d
domain. This does not apply to the rest of the library though, but it is
Packit Service 31306d
allowed to cut-and-paste working code from this file to any license of
Packit Service 31306d
program.
Packit Service 31306d
 */
Packit Service 31306d
Packit Service 31306d
#include <stdio.h>
Packit Service 31306d
#include <stdlib.h>
Packit Service 31306d
#include <string.h>
Packit Service 31306d
#include <errno.h>
Packit Service 31306d
#include <sys/stat.h>
Packit Service 31306d
Packit Service 31306d
#include <libssh/libssh.h>
Packit Service 31306d
#include "examples_common.h"
Packit Service 31306d
Packit Service 31306d
static char **sources;
Packit Service 31306d
static int nsources;
Packit Service 31306d
static char *destination;
Packit Service 31306d
static int verbosity = 0;
Packit Service 31306d
Packit Service 31306d
struct location {
Packit Service 31306d
    int is_ssh;
Packit Service 31306d
    char *user;
Packit Service 31306d
    char *host;
Packit Service 31306d
    char *path;
Packit Service 31306d
    ssh_session session;
Packit Service 31306d
    ssh_scp scp;
Packit Service 31306d
    FILE *file;
Packit Service 31306d
};
Packit Service 31306d
Packit Service 31306d
enum {
Packit Service 31306d
    READ,
Packit Service 31306d
    WRITE
Packit Service 31306d
};
Packit Service 31306d
Packit Service 31306d
static void usage(const char *argv0) {
Packit Service 31306d
    fprintf(stderr, "Usage : %s [options] [[user@]host1:]file1 ... \n"
Packit Service 31306d
            "                               [[user@]host2:]destination\n"
Packit Service 31306d
            "sample scp client - libssh-%s\n",
Packit Service 31306d
            //      "Options :\n",
Packit Service 31306d
            //      "  -r : use RSA to verify host public key\n",
Packit Service 31306d
            argv0,
Packit Service 31306d
            ssh_version(0));
Packit Service 31306d
    exit(0);
Packit Service 31306d
}
Packit Service 31306d
Packit Service 31306d
static int opts(int argc, char **argv) {
Packit Service 31306d
    int i;
Packit Service 31306d
Packit Service 31306d
    while((i = getopt(argc, argv, "v")) != -1) {
Packit Service 31306d
        switch(i) {
Packit Service 31306d
        case 'v':
Packit Service 31306d
            verbosity++;
Packit Service 31306d
            break;
Packit Service 31306d
        default:
Packit Service 31306d
            fprintf(stderr, "unknown option %c\n", optopt);
Packit Service 31306d
            usage(argv[0]);
Packit Service 31306d
            return -1;
Packit Service 31306d
        }
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    nsources = argc - optind - 1;
Packit Service 31306d
    if (nsources < 1) {
Packit Service 31306d
        usage(argv[0]);
Packit Service 31306d
        return -1;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    sources = malloc((nsources + 1) * sizeof(char *));
Packit Service 31306d
    if (sources == NULL) {
Packit Service 31306d
        return -1;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    for(i = 0; i < nsources; ++i) {
Packit Service 31306d
        sources[i] = argv[optind];
Packit Service 31306d
        optind++;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    sources[i] = NULL;
Packit Service 31306d
    destination = argv[optind];
Packit Service 31306d
    return 0;
Packit Service 31306d
}
Packit Service 31306d
Packit Service 31306d
static void location_free(struct location *loc)
Packit Service 31306d
{
Packit Service 31306d
    if (loc) {
Packit Service 31306d
        if (loc->path) {
Packit Service 31306d
            free(loc->path);
Packit Service 31306d
        }
Packit Service 31306d
        loc->path = NULL;
Packit Service 31306d
        if (loc->is_ssh) {
Packit Service 31306d
            if (loc->host) {
Packit Service 31306d
                free(loc->host);
Packit Service 31306d
            }
Packit Service 31306d
            loc->host = NULL;
Packit Service 31306d
            if (loc->user) {
Packit Service 31306d
                free(loc->user);
Packit Service 31306d
            }
Packit Service 31306d
            loc->user = NULL;
Packit Service 31306d
            if (loc->host) {
Packit Service 31306d
                free(loc->host);
Packit Service 31306d
            }
Packit Service 31306d
            loc->host = NULL;
Packit Service 31306d
        }
Packit Service 31306d
        free(loc);
Packit Service 31306d
    }
Packit Service 31306d
}
Packit Service 31306d
Packit Service 31306d
static struct location *parse_location(char *loc) {
Packit Service 31306d
    struct location *location;
Packit Service 31306d
    char *ptr;
Packit Service 31306d
Packit Service 31306d
    location = malloc(sizeof(struct location));
Packit Service 31306d
    if (location == NULL) {
Packit Service 31306d
        return NULL;
Packit Service 31306d
    }
Packit Service 31306d
    memset(location, 0, sizeof(struct location));
Packit Service 31306d
Packit Service 31306d
    location->host = location->user = NULL;
Packit Service 31306d
    ptr = strchr(loc, ':');
Packit Service 31306d
Packit Service 31306d
    if (ptr != NULL) {
Packit Service 31306d
        location->is_ssh = 1;
Packit Service 31306d
        location->path = strdup(ptr+1);
Packit Service 31306d
        *ptr = '\0';
Packit Service 31306d
        ptr = strchr(loc, '@');
Packit Service 31306d
Packit Service 31306d
        if (ptr != NULL) {
Packit Service 31306d
            location->host = strdup(ptr+1);
Packit Service 31306d
            *ptr = '\0';
Packit Service 31306d
            location->user = strdup(loc);
Packit Service 31306d
        } else {
Packit Service 31306d
            location->host = strdup(loc);
Packit Service 31306d
        }
Packit Service 31306d
    } else {
Packit Service 31306d
        location->is_ssh = 0;
Packit Service 31306d
        location->path = strdup(loc);
Packit Service 31306d
    }
Packit Service 31306d
    return location;
Packit Service 31306d
}
Packit Service 31306d
Packit Service 31306d
static void close_location(struct location *loc) {
Packit Service 31306d
    int rc;
Packit Service 31306d
Packit Service 31306d
    if (loc) {
Packit Service 31306d
        if (loc->is_ssh) {
Packit Service 31306d
            if (loc->scp) {
Packit Service 31306d
                rc = ssh_scp_close(loc->scp);
Packit Service 31306d
                if (rc == SSH_ERROR) {
Packit Service 31306d
                    fprintf(stderr,
Packit Service 31306d
                            "Error closing scp: %s\n",
Packit Service 31306d
                            ssh_get_error(loc->session));
Packit Service 31306d
                }
Packit Service 31306d
                ssh_scp_free(loc->scp);
Packit Service 31306d
                loc->scp = NULL;
Packit Service 31306d
            }
Packit Service 31306d
            if (loc->session) {
Packit Service 31306d
                ssh_disconnect(loc->session);
Packit Service 31306d
                ssh_free(loc->session);
Packit Service 31306d
                loc->session = NULL;
Packit Service 31306d
            }
Packit Service 31306d
        } else {
Packit Service 31306d
            if (loc->file) {
Packit Service 31306d
                fclose(loc->file);
Packit Service 31306d
                loc->file = NULL;
Packit Service 31306d
            }
Packit Service 31306d
        }
Packit Service 31306d
    }
Packit Service 31306d
}
Packit Service 31306d
Packit Service 31306d
static int open_location(struct location *loc, int flag) {
Packit Service 31306d
    if (loc->is_ssh && flag == WRITE) {
Packit Service 31306d
        loc->session = connect_ssh(loc->host, loc->user, verbosity);
Packit Service 31306d
        if (!loc->session) {
Packit Service 31306d
            fprintf(stderr, "Couldn't connect to %s\n", loc->host);
Packit Service 31306d
            return -1;
Packit Service 31306d
        }
Packit Service 31306d
Packit Service 31306d
        loc->scp = ssh_scp_new(loc->session, SSH_SCP_WRITE, loc->path);
Packit Service 31306d
        if (!loc->scp) {
Packit Service 31306d
            fprintf(stderr, "error : %s\n", ssh_get_error(loc->session));
Packit Service 31306d
            ssh_disconnect(loc->session);
Packit Service 31306d
            ssh_free(loc->session);
Packit Service 31306d
            loc->session = NULL;
Packit Service 31306d
            return -1;
Packit Service 31306d
        }
Packit Service 31306d
Packit Service 31306d
        if (ssh_scp_init(loc->scp) == SSH_ERROR) {
Packit Service 31306d
            fprintf(stderr, "error : %s\n", ssh_get_error(loc->session));
Packit Service 31306d
            ssh_scp_free(loc->scp);
Packit Service 31306d
            loc->scp = NULL;
Packit Service 31306d
            ssh_disconnect(loc->session);
Packit Service 31306d
            ssh_free(loc->session);
Packit Service 31306d
            loc->session = NULL;
Packit Service 31306d
            return -1;
Packit Service 31306d
        }
Packit Service 31306d
        return 0;
Packit Service 31306d
    } else if (loc->is_ssh && flag == READ) {
Packit Service 31306d
        loc->session = connect_ssh(loc->host, loc->user, verbosity);
Packit Service 31306d
        if (!loc->session) {
Packit Service 31306d
            fprintf(stderr, "Couldn't connect to %s\n", loc->host);
Packit Service 31306d
            return -1;
Packit Service 31306d
        }
Packit Service 31306d
Packit Service 31306d
        loc->scp = ssh_scp_new(loc->session, SSH_SCP_READ, loc->path);
Packit Service 31306d
        if (!loc->scp) {
Packit Service 31306d
            fprintf(stderr, "error : %s\n", ssh_get_error(loc->session));
Packit Service 31306d
            ssh_disconnect(loc->session);
Packit Service 31306d
            ssh_free(loc->session);
Packit Service 31306d
            loc->session = NULL;
Packit Service 31306d
            return -1;
Packit Service 31306d
        }
Packit Service 31306d
Packit Service 31306d
        if (ssh_scp_init(loc->scp) == SSH_ERROR) {
Packit Service 31306d
            fprintf(stderr, "error : %s\n", ssh_get_error(loc->session));
Packit Service 31306d
            ssh_scp_free(loc->scp);
Packit Service 31306d
            loc->scp = NULL;
Packit Service 31306d
            ssh_disconnect(loc->session);
Packit Service 31306d
            ssh_free(loc->session);
Packit Service 31306d
            loc->session = NULL;
Packit Service 31306d
            return -1;
Packit Service 31306d
        }
Packit Service 31306d
        return 0;
Packit Service 31306d
    } else {
Packit Service 31306d
        loc->file = fopen(loc->path, flag == READ ? "r":"w");
Packit Service 31306d
        if (!loc->file) {
Packit Service 31306d
            if (errno == EISDIR) {
Packit Service 31306d
                if (chdir(loc->path)) {
Packit Service 31306d
                    fprintf(stderr,
Packit Service 31306d
                            "Error changing directory to %s: %s\n",
Packit Service 31306d
                            loc->path, strerror(errno));
Packit Service 31306d
                    return -1;
Packit Service 31306d
                }
Packit Service 31306d
                return 0;
Packit Service 31306d
            }
Packit Service 31306d
            fprintf(stderr,
Packit Service 31306d
                    "Error opening %s: %s\n",
Packit Service 31306d
                    loc->path, strerror(errno));
Packit Service 31306d
            return -1;
Packit Service 31306d
        }
Packit Service 31306d
        return 0;
Packit Service 31306d
    }
Packit Service 31306d
    return -1;
Packit Service 31306d
}
Packit Service 31306d
Packit Service 31306d
/** @brief copies files from source location to destination
Packit Service 31306d
 * @param src source location
Packit Service 31306d
 * @param dest destination location
Packit Service 31306d
 * @param recursive Copy also directories
Packit Service 31306d
 */
Packit Service 31306d
static int do_copy(struct location *src, struct location *dest, int recursive) {
Packit Service 31306d
    size_t size;
Packit Service 31306d
    socket_t fd;
Packit Service 31306d
    struct stat s;
Packit Service 31306d
    int w, r;
Packit Service 31306d
    char buffer[16384];
Packit Service 31306d
    size_t total = 0;
Packit Service 31306d
    mode_t mode;
Packit Service 31306d
    char *filename = NULL;
Packit Service 31306d
Packit Service 31306d
    /* recursive mode doesn't work yet */
Packit Service 31306d
    (void)recursive;
Packit Service 31306d
    /* Get the file name and size*/
Packit Service 31306d
    if (!src->is_ssh) {
Packit Service 31306d
        fd = fileno(src->file);
Packit Service 31306d
        if (fd < 0) {
Packit Service 31306d
            fprintf(stderr,
Packit Service 31306d
                    "Invalid file pointer, error: %s\n",
Packit Service 31306d
                    strerror(errno));
Packit Service 31306d
            return -1;
Packit Service 31306d
        }
Packit Service 31306d
        r = fstat(fd, &s);
Packit Service 31306d
        if (r < 0) {
Packit Service 31306d
            return -1;
Packit Service 31306d
        }
Packit Service 31306d
        size = s.st_size;
Packit Service 31306d
        mode = s.st_mode & ~S_IFMT;
Packit Service 31306d
        filename = ssh_basename(src->path);
Packit Service 31306d
    } else {
Packit Service 31306d
        size = 0;
Packit Service 31306d
        do {
Packit Service 31306d
            r = ssh_scp_pull_request(src->scp);
Packit Service 31306d
            if (r == SSH_SCP_REQUEST_NEWDIR) {
Packit Service 31306d
                ssh_scp_deny_request(src->scp, "Not in recursive mode");
Packit Service 31306d
                continue;
Packit Service 31306d
            }
Packit Service 31306d
            if (r == SSH_SCP_REQUEST_NEWFILE) {
Packit Service 31306d
                size = ssh_scp_request_get_size(src->scp);
Packit Service 31306d
                filename = strdup(ssh_scp_request_get_filename(src->scp));
Packit Service 31306d
                mode = ssh_scp_request_get_permissions(src->scp);
Packit Service 31306d
                //ssh_scp_accept_request(src->scp);
Packit Service 31306d
                break;
Packit Service 31306d
            }
Packit Service 31306d
            if (r == SSH_ERROR) {
Packit Service 31306d
                fprintf(stderr,
Packit Service 31306d
                        "Error: %s\n",
Packit Service 31306d
                        ssh_get_error(src->session));
Packit Service 31306d
                SSH_STRING_FREE_CHAR(filename);
Packit Service 31306d
                return -1;
Packit Service 31306d
            }
Packit Service 31306d
        } while(r != SSH_SCP_REQUEST_NEWFILE);
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    if (dest->is_ssh) {
Packit Service 31306d
        r = ssh_scp_push_file(dest->scp, src->path, size, mode);
Packit Service 31306d
        //  snprintf(buffer, sizeof(buffer), "C0644 %d %s\n", size, src->path);
Packit Service 31306d
        if (r == SSH_ERROR) {
Packit Service 31306d
            fprintf(stderr,
Packit Service 31306d
                    "error: %s\n",
Packit Service 31306d
                    ssh_get_error(dest->session));
Packit Service 31306d
            SSH_STRING_FREE_CHAR(filename);
Packit Service 31306d
            ssh_scp_free(dest->scp);
Packit Service 31306d
            dest->scp = NULL;
Packit Service 31306d
            return -1;
Packit Service 31306d
        }
Packit Service 31306d
    } else {
Packit Service 31306d
        if (!dest->file) {
Packit Service 31306d
            dest->file = fopen(filename, "w");
Packit Service 31306d
            if (!dest->file) {
Packit Service 31306d
                fprintf(stderr,
Packit Service 31306d
                        "Cannot open %s for writing: %s\n",
Packit Service 31306d
                        filename, strerror(errno));
Packit Service 31306d
                if (src->is_ssh) {
Packit Service 31306d
                    ssh_scp_deny_request(src->scp, "Cannot open local file");
Packit Service 31306d
                }
Packit Service 31306d
                SSH_STRING_FREE_CHAR(filename);
Packit Service 31306d
                return -1;
Packit Service 31306d
            }
Packit Service 31306d
        }
Packit Service 31306d
        if (src->is_ssh) {
Packit Service 31306d
            ssh_scp_accept_request(src->scp);
Packit Service 31306d
        }
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    do {
Packit Service 31306d
        if (src->is_ssh) {
Packit Service 31306d
            r = ssh_scp_read(src->scp, buffer, sizeof(buffer));
Packit Service 31306d
            if (r == SSH_ERROR) {
Packit Service 31306d
                fprintf(stderr,
Packit Service 31306d
                        "Error reading scp: %s\n",
Packit Service 31306d
                        ssh_get_error(src->session));
Packit Service 31306d
                SSH_STRING_FREE_CHAR(filename);
Packit Service 31306d
                return -1;
Packit Service 31306d
            }
Packit Service 31306d
Packit Service 31306d
            if (r == 0) {
Packit Service 31306d
                break;
Packit Service 31306d
            }
Packit Service 31306d
        } else {
Packit Service 31306d
            r = fread(buffer, 1, sizeof(buffer), src->file);
Packit Service 31306d
            if (r == 0) {
Packit Service 31306d
                break;
Packit Service 31306d
            }
Packit Service 31306d
Packit Service 31306d
            if (r < 0) {
Packit Service 31306d
                fprintf(stderr,
Packit Service 31306d
                        "Error reading file: %s\n",
Packit Service 31306d
                        strerror(errno));
Packit Service 31306d
                SSH_STRING_FREE_CHAR(filename);
Packit Service 31306d
                return -1;
Packit Service 31306d
            }
Packit Service 31306d
        }
Packit Service 31306d
Packit Service 31306d
        if (dest->is_ssh) {
Packit Service 31306d
            w = ssh_scp_write(dest->scp, buffer, r);
Packit Service 31306d
            if (w == SSH_ERROR) {
Packit Service 31306d
                fprintf(stderr,
Packit Service 31306d
                        "Error writing in scp: %s\n",
Packit Service 31306d
                        ssh_get_error(dest->session));
Packit Service 31306d
                ssh_scp_free(dest->scp);
Packit Service 31306d
                dest->scp = NULL;
Packit Service 31306d
                SSH_STRING_FREE_CHAR(filename);
Packit Service 31306d
                return -1;
Packit Service 31306d
            }
Packit Service 31306d
        } else {
Packit Service 31306d
            w = fwrite(buffer, r, 1, dest->file);
Packit Service 31306d
            if (w <= 0) {
Packit Service 31306d
                fprintf(stderr,
Packit Service 31306d
                        "Error writing in local file: %s\n",
Packit Service 31306d
                        strerror(errno));
Packit Service 31306d
                SSH_STRING_FREE_CHAR(filename);
Packit Service 31306d
                return -1;
Packit Service 31306d
            }
Packit Service 31306d
        }
Packit Service 31306d
        total += r;
Packit Service 31306d
Packit Service 31306d
    } while(total < size);
Packit Service 31306d
Packit Service 31306d
    SSH_STRING_FREE_CHAR(filename);
Packit Service 31306d
    printf("wrote %zu bytes\n", total);
Packit Service 31306d
    return 0;
Packit Service 31306d
}
Packit Service 31306d
Packit Service 31306d
int main(int argc, char **argv) {
Packit Service 31306d
    struct location *dest, *src;
Packit Service 31306d
    int i;
Packit Service 31306d
    int r;
Packit Service 31306d
    if (opts(argc, argv) < 0) {
Packit Service 31306d
        r = EXIT_FAILURE;
Packit Service 31306d
        goto end;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    dest = parse_location(destination);
Packit Service 31306d
    if (dest == NULL) {
Packit Service 31306d
        r = EXIT_FAILURE;
Packit Service 31306d
        goto end;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    if (open_location(dest, WRITE) < 0) {
Packit Service 31306d
        location_free(dest);
Packit Service 31306d
        r = EXIT_FAILURE;
Packit Service 31306d
        goto end;
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    for (i = 0; i < nsources; ++i) {
Packit Service 31306d
        src = parse_location(sources[i]);
Packit Service 31306d
        if (src == NULL) {
Packit Service 31306d
            r = EXIT_FAILURE;
Packit Service 31306d
            goto close_dest;
Packit Service 31306d
        }
Packit Service 31306d
Packit Service 31306d
        if (open_location(src, READ) < 0) {
Packit Service 31306d
            location_free(src);
Packit Service 31306d
            r = EXIT_FAILURE;
Packit Service 31306d
            goto close_dest;
Packit Service 31306d
        }
Packit Service 31306d
Packit Service 31306d
        if (do_copy(src, dest, 0) < 0) {
Packit Service 31306d
            close_location(src);
Packit Service 31306d
            location_free(src);
Packit Service 31306d
            break;
Packit Service 31306d
        }
Packit Service 31306d
Packit Service 31306d
        close_location(src);
Packit Service 31306d
        location_free(src);
Packit Service 31306d
    }
Packit Service 31306d
Packit Service 31306d
    r = 0;
Packit Service 31306d
Packit Service 31306d
close_dest:
Packit Service 31306d
    close_location(dest);
Packit Service 31306d
    location_free(dest);
Packit Service 31306d
end:
Packit Service 31306d
    return r;
Packit Service 31306d
}