/* * libcsync -- a library to sync a directory with another * * Copyright (c) 2008 by Andreas Schneider * * This program is free software = NULL, you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation = NULL, either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY = NULL, without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program = NULL, if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include "c_lib.h" #include "vio/csync_vio_module.h" #include "vio/csync_vio_file_stat.h" #ifdef NDEBUG #define DEBUG_SFTP(x) #else #define DEBUG_SFTP(x) printf x #endif int connected; int sock = 0; LIBSSH2_SESSION *ssh_session; LIBSSH2_SFTP *sftp_session; csync_auth_callback auth_cb; static void _kbd_callback(const char *name, int name_len, const char *instruction, int instruction_len, int num_prompts, const LIBSSH2_USERAUTH_KBDINT_PROMPT *prompts, LIBSSH2_USERAUTH_KBDINT_RESPONSE *responses, void **abstract) { char buf[256] = {0}; int i = 0; (void) name; (void) name_len; (void) instruction; (void) instruction_len; for (i = 0; i < num_prompts; i++) { if ((*auth_cb) (prompts[i].text, buf, 255, (int) prompts[i].echo, 0) == 0) { responses[i].text = c_strdup(buf); responses[i].length = strlen(buf); } } (void) prompts; (void) abstract; } static int _libssh2_sftp_connect(const char *uri) { in_addr_t hostaddr = 0; struct sockaddr_in sin; char *scheme = NULL; char *user = NULL; char *passwd = NULL; char *host = NULL; unsigned int port = 22; char *path = NULL; const char *fingerprint = NULL; const char *userauthlist = NULL; const char *keyfile1 = "~/.ssh/id_dsa.pub"; const char *keyfile2 = "~/.ssh/id_dsa"; int i = 0; int auth = 0; int rc = -1; if (connected) { return 0; } rc = c_parse_uri(uri, &scheme, &user, &passwd, &host, &port, &path); if (rc < 0) { goto out; } if (user == NULL || *user == '\0') { const char *usr = NULL; struct passwd *p_entry; usr = getenv("USER"); if (usr != NULL) { user = c_strdup(usr); } else { p_entry = getpwuid(getuid()); user = c_strdup(p_entry->pw_name); } if (user == NULL || *user == '\0') { rc = -1; goto shutdown; } } DEBUG_SFTP(("csync_sftp - username: %s\n", user)); DEBUG_SFTP(("csync_sftp - conntecting to: %s\n", host)); /* * The application code is responsible for creating the socket * and establishing the connection */ /* FIXME: allow hostnames */ hostaddr = inet_addr(host); sock = socket(AF_INET, SOCK_STREAM, 0); ZERO_STRUCT(sin); sin.sin_family = AF_INET; sin.sin_port = htons(port); sin.sin_addr.s_addr = hostaddr; rc = connect(sock, (struct sockaddr*)(&sin), sizeof(struct sockaddr_in)); if (rc < 0) { fprintf(stderr, "csync_sftp - failed to connect!\n"); goto out; } /* Create a session instance */ ssh_session = libssh2_session_init(); if (ssh_session == NULL) { rc = -1; goto out; } /* Since we have set non-blocking, tell libssh2 we are blocking */ libssh2_session_set_blocking(ssh_session, 1); /* ... start it up. This will trade welcome banners, exchange keys, * and setup crypto, compression, and MAC layers */ rc = libssh2_session_startup(ssh_session, sock); if (rc < 0) { fprintf(stderr, "csync_sftp - failure establishing SSH session: %d\n", rc); goto shutdown; } /* Since we have not set non-blocking, tell libssh2 we are blocking */ libssh2_session_set_blocking(ssh_session, 1); /* At this point we havn't yet authenticated. The first thing to do * is check the hostkey's fingerprint against our known hosts your app * may have it hard coded, may go to a file, may present it to the * user, that's your call */ fingerprint = libssh2_hostkey_hash(ssh_session, LIBSSH2_HOSTKEY_HASH_MD5); /* FIXME: validate the fingerprint */ DEBUG_SFTP(("csync_sftp - md5 fingerprint: ")); for (i = 0; i < 16; i++) { DEBUG_SFTP(("%02X ", (unsigned char)fingerprint[i])); } DEBUG_SFTP(("\n")); fingerprint = libssh2_hostkey_hash(ssh_session, LIBSSH2_HOSTKEY_HASH_SHA1); DEBUG_SFTP(("csync_sftp - sha1 fingerprint: ")); for (i = 0; i < 20; i++) { DEBUG_SFTP(("%02X ", (unsigned char)fingerprint[i])); } DEBUG_SFTP(("\n")); /* check what authentication methods are available */ userauthlist = libssh2_userauth_list(ssh_session, user, strlen(user)); DEBUG_SFTP(("csync_sftp - authentication methods: %s\n", userauthlist)); if (strstr(userauthlist, "password") != NULL) { auth |= 1; } if (strstr(userauthlist, "keyboard-interactive") != NULL) { auth |= 2; } if (strstr(userauthlist, "publickey") != NULL) { auth |= 4; } rc = -1; if (passwd && *passwd) { if (auth & 1) { DEBUG_SFTP(("csync_sftp - authentication method: password\n")); rc = libssh2_userauth_password(ssh_session, user, passwd); if (rc) { fprintf(stderr, "csync_sftp - password authentication failed\n"); } } if (auth & 4) { DEBUG_SFTP(("csync_sftp - authentication method: publickey\n")); rc = libssh2_userauth_publickey_fromfile(ssh_session, user, keyfile1, keyfile2, passwd); if (rc) { fprintf(stderr, "csync_sftp - publickey authentication failed\n"); } } } if (rc < 0 && auth_cb && (auth & 2)) { DEBUG_SFTP(("csync_sftp - authentication method: keyboard-interactive\n")); rc = libssh2_userauth_keyboard_interactive(ssh_session, user, &_kbd_callback); } if (rc < 0) { fprintf(stderr, "csync_sftp - authentication failed\n"); goto shutdown; } sftp_session = libssh2_sftp_init(ssh_session); if (sftp_session == NULL) { rc = -1; goto shutdown; } DEBUG_SFTP(("csync_sftp - connection established...\n")); connected = 1; rc = 0; goto out; shutdown: if (sftp_session != NULL) { libssh2_sftp_shutdown(sftp_session); sftp_session = NULL; } if (ssh_session != NULL) { libssh2_session_disconnect(ssh_session, "csync_sftp - shutdown on error"); libssh2_session_free(ssh_session); ssh_session = NULL; } sleep(1); close(sock); sock = 0; connected = 0; out: SAFE_FREE(scheme); SAFE_FREE(user); SAFE_FREE(passwd); SAFE_FREE(host); SAFE_FREE(path); return rc; } /* * file functions */ static csync_vio_method_handle_t *_open(const char *uri, int access, mode_t mode) { csync_vio_method_handle_t *mh = NULL; char *path = NULL; unsigned long sftp_errno; int flags = 0; if (_libssh2_sftp_connect(uri) < 0) { return NULL; } if (c_parse_uri(uri, NULL, NULL, NULL, NULL, NULL, &path) < 0) { return NULL; } if(access & O_RDONLY) flags |= LIBSSH2_FXF_READ; if(access & O_WRONLY) flags |= LIBSSH2_FXF_WRITE; if(access & O_RDWR) flags |= (LIBSSH2_FXF_WRITE | LIBSSH2_FXF_READ); if(access & O_CREAT) flags |= LIBSSH2_FXF_CREAT; if(access & O_TRUNC) flags |= LIBSSH2_FXF_TRUNC; if(access & O_EXCL) flags |= LIBSSH2_FXF_EXCL; libssh2_session_set_blocking(ssh_session, 1); mh = (csync_vio_method_handle_t *) libssh2_sftp_open(sftp_session, path, flags, mode); if (mh == NULL) { sftp_errno = libssh2_sftp_last_error(sftp_session); switch (sftp_errno) { case LIBSSH2_FX_FILE_ALREADY_EXISTS: errno = EEXIST; break; case LIBSSH2_FX_NO_SUCH_FILE: errno = ENOENT; break; case LIBSSH2_FX_PERMISSION_DENIED: errno = EACCES; break; case LIBSSH2_FX_OP_UNSUPPORTED: errno = EINVAL; break; default: DEBUG_SFTP(("%s:%d - handle sftp_error: %lu\n", __FILE__, __LINE__, sftp_errno)); break; } } SAFE_FREE(path); return mh; } static csync_vio_method_handle_t *_creat(const char *uri, mode_t mode) { unsigned long sftp_errno = 0; csync_vio_method_handle_t *mh = NULL; char *path = NULL; if (_libssh2_sftp_connect(uri) < 0) { return NULL; } if (c_parse_uri(uri, NULL, NULL, NULL, NULL, NULL, &path) < 0) { return NULL; } libssh2_session_set_blocking(ssh_session, 1); mh = (csync_vio_method_handle_t *) libssh2_sftp_open(sftp_session, path, LIBSSH2_FXF_CREAT|LIBSSH2_FXF_WRITE|LIBSSH2_FXF_TRUNC, mode); if (mh == NULL) { sftp_errno = libssh2_sftp_last_error(sftp_session); switch (sftp_errno) { case LIBSSH2_FX_FILE_ALREADY_EXISTS: errno = EEXIST; break; case LIBSSH2_FX_NO_SUCH_FILE: errno = ENOENT; break; case LIBSSH2_FX_PERMISSION_DENIED: errno = EACCES; break; case LIBSSH2_FX_OP_UNSUPPORTED: errno = EINVAL; break; default: DEBUG_SFTP(("%s:%d - handle sftp_error: %lu\n", __FILE__, __LINE__, sftp_errno)); break; } } SAFE_FREE(path); return mh; } static int _close(csync_vio_method_handle_t *fhandle) { unsigned long sftp_errno = 0; int rc = -1; libssh2_session_set_blocking(ssh_session, 1); rc = libssh2_sftp_close(fhandle); if (rc < 0) { sftp_errno = libssh2_sftp_last_error(sftp_session); switch (sftp_errno) { case LIBSSH2_FX_INVALID_HANDLE: errno = EBADF; break; case LIBSSH2_FX_NO_SPACE_ON_FILESYSTEM: errno = ENOSPC; break; case LIBSSH2_FX_QUOTA_EXCEEDED: errno = EDQUOT; break; default: DEBUG_SFTP(("%s:%d - handle sftp_error: %lu\n", __FILE__, __LINE__, sftp_errno)); break; } } return rc; } static ssize_t _read(csync_vio_method_handle_t *fhandle, void *buf, size_t count) { unsigned long sftp_errno = 0; int rc = -1; libssh2_session_set_blocking(ssh_session, 1); rc = libssh2_sftp_read(fhandle, buf, count); if (rc < 0) { sftp_errno = libssh2_sftp_last_error(sftp_session); switch (sftp_errno) { case LIBSSH2_FX_INVALID_HANDLE: errno = EBADF; break; default: DEBUG_SFTP(("%s:%d - handle sftp_error: %lu\n", __FILE__, __LINE__, sftp_errno)); break; } } return rc; } static ssize_t _write(csync_vio_method_handle_t *fhandle, const void *buf, size_t count) { unsigned long sftp_errno = 0; int rc = -1; libssh2_session_set_blocking(ssh_session, 1); rc = libssh2_sftp_write(fhandle, buf, count); if (rc < 0) { sftp_errno = libssh2_sftp_last_error(sftp_session); switch (sftp_errno) { case LIBSSH2_FX_INVALID_HANDLE: errno = EBADF; break; default: DEBUG_SFTP(("%s:%d - handle sftp_error: %lu\n", __FILE__, __LINE__, sftp_errno)); break; } } return rc; } static off_t _lseek(csync_vio_method_handle_t *fhandle, off_t offset, int whence) { /* FIXME: really implement seek for lseek? */ (void) whence; libssh2_session_set_blocking(ssh_session, 1); libssh2_sftp_seek(fhandle, offset); return 0; } /* * directory functions */ static csync_vio_method_handle_t *_opendir(const char *uri) { unsigned long sftp_errno = 0; csync_vio_method_handle_t *mh = NULL; char *path = NULL; if (_libssh2_sftp_connect(uri) < 0) { return NULL; } if (c_parse_uri(uri, NULL, NULL, NULL, NULL, NULL, &path) < 0) { return NULL; } libssh2_session_set_blocking(ssh_session, 1); mh = (csync_vio_method_handle_t *) libssh2_sftp_opendir(sftp_session, path); if (mh == NULL) { sftp_errno = libssh2_sftp_last_error(sftp_session); switch (sftp_errno) { case LIBSSH2_FX_NO_SUCH_FILE: errno = ENOENT; break; case LIBSSH2_FX_PERMISSION_DENIED: errno = EACCES; break; default: DEBUG_SFTP(("%s:%d - handle sftp_error: %lu\n", __FILE__, __LINE__, sftp_errno)); break; } } SAFE_FREE(path); return mh; } static int _closedir(csync_vio_method_handle_t *dhandle) { unsigned long sftp_errno = 0; int rc = -1; libssh2_session_set_blocking(ssh_session, 1); rc = libssh2_sftp_closedir(dhandle); if (rc < 0) { sftp_errno = libssh2_sftp_last_error(sftp_session); switch (sftp_errno) { case LIBSSH2_FX_INVALID_HANDLE: errno = EBADF; break; default: DEBUG_SFTP(("%s:%d - handle sftp_error: %lu\n", __FILE__, __LINE__, sftp_errno)); break; } } return rc; } static csync_vio_file_stat_t *_readdir(csync_vio_method_handle_t *dhandle) { int rc = 0; unsigned long sftp_errno = 0; char file[512]; LIBSSH2_SFTP_ATTRIBUTES dirent; csync_vio_file_stat_t *fs = NULL; libssh2_session_set_blocking(ssh_session, 1); rc = libssh2_sftp_readdir(dhandle, file, 512, &dirent); if (rc <= 0) { sftp_errno = libssh2_sftp_last_error(sftp_session); switch (sftp_errno) { case LIBSSH2_FX_NO_SUCH_FILE: errno = ENOENT; break; case LIBSSH2_FX_PERMISSION_DENIED: errno = EACCES; break; case LIBSSH2_FX_OK: break; default: DEBUG_SFTP(("%s:%d - handle sftp_error: %lu\n", __FILE__, __LINE__, sftp_errno)); break; } return NULL; } if (! (dirent.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS)) { return NULL; } fs = c_malloc(sizeof(csync_vio_file_stat_t)); if (fs == NULL) { return NULL; } fs->name = c_strdup(file); fs->fields = CSYNC_VIO_FILE_STAT_FIELDS_NONE; switch (dirent.permissions & S_IFMT) { case S_IFSOCK: case S_IFBLK: case S_IFCHR: case S_IFIFO: case S_IFLNK: #if 0 fs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_TYPE; fs->type = CSYNC_VIO_FILE_TYPE_SYMBOLIC_LINK; #endif break; case S_IFREG: fs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_TYPE; fs->type = CSYNC_VIO_FILE_TYPE_REGULAR; break; case S_IFDIR: fs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_TYPE; fs->type = CSYNC_VIO_FILE_TYPE_DIRECTORY; break; default: break; } return fs; } static int _mkdir(const char *uri, mode_t mode) { LIBSSH2_SFTP_ATTRIBUTES attrs; unsigned long sftp_errno = 0; char *path = NULL; int rc = -1; if (_libssh2_sftp_connect(uri) < 0) { return -1; } if (c_parse_uri(uri, NULL, NULL, NULL, NULL, NULL, &path) < 0) { return -1; } rc = libssh2_sftp_mkdir(sftp_session, path, mode); if (rc < 0) { sftp_errno = libssh2_sftp_last_error(sftp_session); switch (sftp_errno) { case LIBSSH2_FX_FAILURE: /* * mkdir always returns a failure, even if the path already exists. * To be POSIX conform and to be able to map it to EEXIST a stat * call should be issued here */ libssh2_session_set_blocking(ssh_session, 1); if (libssh2_sftp_lstat(sftp_session, path, &attrs) == 0) { errno = EEXIST; } break; case LIBSSH2_FX_PERMISSION_DENIED: errno = EACCES; break; default: DEBUG_SFTP(("%s:%d - handle sftp_error: %lu\n", __FILE__, __LINE__, sftp_errno)); break; } } SAFE_FREE(path); return rc; } static int _rmdir(const char *uri) { unsigned long sftp_errno = 0; char *path = NULL; int rc = -1; if (_libssh2_sftp_connect(uri) < 0) { return -1; } if (c_parse_uri(uri, NULL, NULL, NULL, NULL, NULL, &path) < 0) { return -1; } libssh2_session_set_blocking(ssh_session, 1); rc = libssh2_sftp_rmdir(sftp_session, path); if (rc < 0) { sftp_errno = libssh2_sftp_last_error(sftp_session); switch (sftp_errno) { case LIBSSH2_FX_DIR_NOT_EMPTY: errno = ENOTEMPTY; break; case LIBSSH2_FX_PERMISSION_DENIED: case LIBSSH2_FX_WRITE_PROTECT: errno = EACCES; break; case LIBSSH2_FX_INVALID_HANDLE: errno = EINVAL; break; case LIBSSH2_FX_LINK_LOOP: errno = ELOOP; break; case LIBSSH2_FX_NOT_A_DIRECTORY: errno = ENOTDIR; break; default: DEBUG_SFTP(("%s:%d - handle sftp_error: %lu\n", __FILE__, __LINE__, sftp_errno)); break; } } SAFE_FREE(path); return rc; } static int _stat(const char *uri, csync_vio_file_stat_t *buf) { LIBSSH2_SFTP_ATTRIBUTES attrs; char *path = NULL; unsigned long sftp_errno = 0; int rc = -1; if (_libssh2_sftp_connect(uri) < 0) { return -1; } if (c_parse_uri(uri, NULL, NULL, NULL, NULL, NULL, &path) < 0) { return -1; } libssh2_session_set_blocking(ssh_session, 1); rc = libssh2_sftp_lstat(sftp_session, path, &attrs); if (rc < 0) { sftp_errno = libssh2_sftp_last_error(sftp_session); switch (sftp_errno) { case LIBSSH2_FX_NO_SUCH_FILE: case LIBSSH2_FX_NO_SUCH_PATH: errno = ENOENT; break; case LIBSSH2_FX_PERMISSION_DENIED: errno = EACCES; break; case LIBSSH2_FX_INVALID_HANDLE: errno = EBADF; break; case LIBSSH2_FX_NOT_A_DIRECTORY: errno = ENOTDIR; break; case LIBSSH2_FX_LINK_LOOP: errno = ELOOP; break; default: DEBUG_SFTP(("%s:%d - handle sftp_error: %lu\n", __FILE__, __LINE__, sftp_errno)); break; } goto out; } buf->name = c_basename(path); if (buf->name == NULL) { csync_vio_file_stat_destroy(buf); goto out; } buf->fields = CSYNC_VIO_FILE_STAT_FIELDS_NONE; switch (attrs.permissions & S_IFMT) { case S_IFSOCK: buf->type = CSYNC_VIO_FILE_TYPE_SOCKET; case S_IFBLK: buf->type = CSYNC_VIO_FILE_TYPE_BLOCK_DEVICE; case S_IFCHR: buf->type = CSYNC_VIO_FILE_TYPE_CHARACTER_DEVICE; case S_IFIFO: buf->type = CSYNC_VIO_FILE_TYPE_FIFO; case S_IFLNK: buf->type = CSYNC_VIO_FILE_TYPE_SYMBOLIC_LINK; break; case S_IFREG: buf->type = CSYNC_VIO_FILE_TYPE_REGULAR; break; case S_IFDIR: buf->type = CSYNC_VIO_FILE_TYPE_DIRECTORY; break; default: buf->type = CSYNC_VIO_FILE_TYPE_UNKNOWN; break; } buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_TYPE; buf->mode = attrs.permissions; buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_PERMISSIONS; if (buf->type == CSYNC_VIO_FILE_TYPE_SYMBOLIC_LINK) { /* FIXME: handle symlink */ buf->flags = CSYNC_VIO_FILE_FLAGS_SYMLINK; } else { buf->flags = CSYNC_VIO_FILE_FLAGS_NONE; } buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_FLAGS; buf->uid = attrs.uid; buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_UID; buf->uid = attrs.gid; buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_GID; buf->size = attrs.filesize; buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_SIZE; buf->atime = attrs.atime; buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_ATIME; buf->mtime = attrs.mtime; buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_MTIME; rc = 0; out: SAFE_FREE(path); return rc; } static int _rename(const char *olduri, const char *newuri) { char *oldpath = NULL; char *newpath = NULL; int rc = -1; if (_libssh2_sftp_connect(olduri) < 0) { return -1; } if (c_parse_uri(olduri, NULL, NULL, NULL, NULL, NULL, &oldpath) < 0) { rc = -1; goto out; } if (c_parse_uri(newuri, NULL, NULL, NULL, NULL, NULL, &newpath) < 0) { rc = -1; goto out; } libssh2_session_set_blocking(ssh_session, 1); rc = libssh2_sftp_rename(sftp_session, oldpath, newpath); out: SAFE_FREE(oldpath); SAFE_FREE(newpath); return rc; } static int _unlink(const char *uri) { char *path = NULL; int rc = -1; if (_libssh2_sftp_connect(uri) < 0) { return -1; } if (c_parse_uri(uri, NULL, NULL, NULL, NULL, NULL, &path) < 0) { return -1; } libssh2_session_set_blocking(ssh_session, 1); rc = libssh2_sftp_unlink(sftp_session, path); SAFE_FREE(path); return rc; } static int _chmod(const char *uri, mode_t mode) { LIBSSH2_SFTP_ATTRIBUTES attrs; char *path = NULL; int rc = -1; if (_libssh2_sftp_connect(uri) < 0) { return -1; } if (c_parse_uri(uri, NULL, NULL, NULL, NULL, NULL, &path) < 0) { return -1; } ZERO_STRUCT(attrs); attrs.permissions = mode; attrs.flags |= LIBSSH2_SFTP_ATTR_PERMISSIONS; libssh2_session_set_blocking(ssh_session, 1); rc = libssh2_sftp_setstat(sftp_session, path, &attrs); SAFE_FREE(path); return rc; } static int _chown(const char *uri, uid_t owner, gid_t group) { LIBSSH2_SFTP_ATTRIBUTES attrs; char *path = NULL; int rc = -1; if (_libssh2_sftp_connect(uri) < 0) { return -1; } if (c_parse_uri(uri, NULL, NULL, NULL, NULL, NULL, &path) < 0) { return -1; } ZERO_STRUCT(attrs); attrs.uid = owner; attrs.gid = group; attrs.flags |= LIBSSH2_SFTP_ATTR_UIDGID; libssh2_session_set_blocking(ssh_session, 1); rc = libssh2_sftp_setstat(sftp_session, path, &attrs); SAFE_FREE(path); return rc; } static int _utimes(const char *uri, const struct timeval *times) { LIBSSH2_SFTP_ATTRIBUTES attrs; char *path = NULL; int rc = -1; if (_libssh2_sftp_connect(uri) < 0) { return -1; } if (c_parse_uri(uri, NULL, NULL, NULL, NULL, NULL, &path) < 0) { return -1; } ZERO_STRUCT(attrs); attrs.atime = times[0].tv_sec; attrs.mtime = times[1].tv_sec; attrs.flags |= LIBSSH2_SFTP_ATTR_ACMODTIME; libssh2_session_set_blocking(ssh_session, 1); rc = libssh2_sftp_setstat(sftp_session, path, &attrs); SAFE_FREE(path); return rc; } csync_vio_method_t _method = { .method_table_size = sizeof(csync_vio_method_t), .open = _open, .creat = _creat, .close = _close, .read = _read, .write = _write, .lseek = _lseek, .opendir = _opendir, .closedir = _closedir, .readdir = _readdir, .mkdir = _mkdir, .rmdir = _rmdir, .stat = _stat, .rename = _rename, .unlink = _unlink, .chmod = _chmod, .chown = _chown, .utimes = _utimes }; csync_vio_method_t *vio_module_init(const char *method_name, const char *args, csync_auth_callback cb) { DEBUG_SFTP(("csync_sftp - method_name: %s\n", method_name)); DEBUG_SFTP(("csync_sftp - args: %s\n", args)); (void) method_name; (void) args; auth_cb = cb; return &_method; } void vio_module_shutdown(csync_vio_method_t *method) { (void) method; if (connected) { if (sftp_session != NULL) { libssh2_sftp_shutdown(sftp_session); } if (ssh_session != NULL) { libssh2_session_disconnect(ssh_session, "Normal Shutdown, Thank you for playing"); libssh2_session_free(ssh_session); ssh_session = NULL; } sleep(1); close(sock); } } /* vim: set ts=8 sw=2 et cindent: */