Blame examples/libssh_scp.c

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