diff options
author | Andreas Schneider <asn@cynapses.org> | 2010-09-06 14:28:38 +0200 |
---|---|---|
committer | Andreas Schneider <asn@cynapses.org> | 2010-09-06 14:28:38 +0200 |
commit | f7842e3a4b9acea2126ff725f993c299aef0e6db (patch) | |
tree | 18239f819a5edbcfc7f2961c48f3f9297314ef22 /libssh/sftp.c | |
parent | 38421403d2dc45636e597f2a909daa6ae31976de (diff) | |
download | libssh-f7842e3a4b9acea2126ff725f993c299aef0e6db.tar.gz libssh-f7842e3a4b9acea2126ff725f993c299aef0e6db.tar.xz libssh-f7842e3a4b9acea2126ff725f993c299aef0e6db.zip |
misc: Rename libssh/ to src/
Diffstat (limited to 'libssh/sftp.c')
-rw-r--r-- | libssh/sftp.c | 3207 |
1 files changed, 0 insertions, 3207 deletions
diff --git a/libssh/sftp.c b/libssh/sftp.c deleted file mode 100644 index e5c91af..0000000 --- a/libssh/sftp.c +++ /dev/null @@ -1,3207 +0,0 @@ -/* - * sftp.c - Secure FTP functions - * - * This file is part of the SSH Library - * - * Copyright (c) 2005-2008 by Aris Adamantiadis - * Copyright (c) 2008-2009 by Andreas Schneider <mail@cynapses.org> - * - * The SSH Library is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; either version 2.1 of the License, or (at your - * option) any later version. - * - * The SSH Library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public - * License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with the SSH Library; see the file COPYING. If not, write to - * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, - * MA 02111-1307, USA. - */ - -/* This file contains code written by Nick Zitzmann */ - -#include <errno.h> -#include <ctype.h> -#include <fcntl.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <stdio.h> -#include <sys/types.h> -#include <sys/stat.h> - -#ifndef _WIN32 -#include <arpa/inet.h> -#else -#define S_IFSOCK 0140000 -#define S_IFLNK 0120000 - -#ifdef _MSC_VER -#define S_IFBLK 0060000 -#define S_IFIFO 0010000 -#endif -#endif - -#include "libssh/priv.h" -#include "libssh/ssh2.h" -#include "libssh/sftp.h" -#include "libssh/buffer.h" -#include "libssh/channels.h" -#include "libssh/session.h" -#include "libssh/misc.h" - -#ifdef WITH_SFTP - -#define sftp_enter_function() _enter_function(sftp->channel->session) -#define sftp_leave_function() _leave_function(sftp->channel->session) - -struct sftp_ext_struct { - unsigned int count; - char **name; - char **data; -}; - -/* functions */ -static int sftp_enqueue(sftp_session session, sftp_message msg); -static void sftp_message_free(sftp_message msg); -static void sftp_set_error(sftp_session sftp, int errnum); -static void status_msg_free(sftp_status_message status); - -static sftp_ext sftp_ext_new(void) { - sftp_ext ext; - - ext = malloc(sizeof(struct sftp_ext_struct)); - if (ext == NULL) { - return NULL; - } - ZERO_STRUCTP(ext); - - return ext; -} - -static void sftp_ext_free(sftp_ext ext) { - unsigned int i; - - if (ext == NULL) { - return; - } - - if (ext->count) { - for (i = 0; i < ext->count; i++) { - SAFE_FREE(ext->name[i]); - SAFE_FREE(ext->data[i]); - } - SAFE_FREE(ext->name); - SAFE_FREE(ext->data); - } - - SAFE_FREE(ext); -} - -sftp_session sftp_new(ssh_session session){ - sftp_session sftp; - - if (session == NULL) { - return NULL; - } - enter_function(); - - sftp = malloc(sizeof(struct sftp_session_struct)); - if (sftp == NULL) { - ssh_set_error_oom(session); - leave_function(); - return NULL; - } - ZERO_STRUCTP(sftp); - - sftp->ext = sftp_ext_new(); - if (sftp->ext == NULL) { - ssh_set_error_oom(session); - SAFE_FREE(sftp); - leave_function(); - return NULL; - } - - sftp->session = session; - sftp->channel = ssh_channel_new(session); - if (sftp->channel == NULL) { - SAFE_FREE(sftp); - leave_function(); - return NULL; - } - - if (ssh_channel_open_session(sftp->channel)) { - ssh_channel_free(sftp->channel); - SAFE_FREE(sftp); - leave_function(); - return NULL; - } - - if (ssh_channel_request_sftp(sftp->channel)) { - sftp_free(sftp); - leave_function(); - return NULL; - } - - leave_function(); - return sftp; -} - -#ifdef WITH_SERVER -sftp_session sftp_server_new(ssh_session session, ssh_channel chan){ - sftp_session sftp = NULL; - - sftp = malloc(sizeof(struct sftp_session_struct)); - if (sftp == NULL) { - ssh_set_error_oom(session); - return NULL; - } - ZERO_STRUCTP(sftp); - - sftp->session = session; - sftp->channel = chan; - - return sftp; -} - -int sftp_server_init(sftp_session sftp){ - ssh_session session = sftp->session; - sftp_packet packet = NULL; - ssh_buffer reply = NULL; - uint32_t version; - - sftp_enter_function(); - - packet = sftp_packet_read(sftp); - if (packet == NULL) { - sftp_leave_function(); - return -1; - } - - if (packet->type != SSH_FXP_INIT) { - ssh_set_error(session, SSH_FATAL, - "Packet read of type %d instead of SSH_FXP_INIT", - packet->type); - - sftp_packet_free(packet); - sftp_leave_function(); - return -1; - } - - ssh_log(session, SSH_LOG_PACKET, "Received SSH_FXP_INIT"); - - buffer_get_u32(packet->payload, &version); - version = ntohl(version); - ssh_log(session, SSH_LOG_PACKET, "Client version: %d", version); - sftp->client_version = version; - - sftp_packet_free(packet); - - reply = ssh_buffer_new(); - if (reply == NULL) { - ssh_set_error_oom(session); - sftp_leave_function(); - return -1; - } - - if (buffer_add_u32(reply, ntohl(LIBSFTP_VERSION)) < 0) { - ssh_set_error_oom(session); - ssh_buffer_free(reply); - sftp_leave_function(); - return -1; - } - - if (sftp_packet_write(sftp, SSH_FXP_VERSION, reply) < 0) { - ssh_buffer_free(reply); - sftp_leave_function(); - return -1; - } - ssh_buffer_free(reply); - - ssh_log(session, SSH_LOG_RARE, "Server version sent"); - - if (version > LIBSFTP_VERSION) { - sftp->version = LIBSFTP_VERSION; - } else { - sftp->version=version; - } - - sftp_leave_function(); - return 0; -} -#endif /* WITH_SERVER */ - -void sftp_free(sftp_session sftp){ - sftp_request_queue ptr; - - if (sftp == NULL) { - return; - } - - ssh_channel_send_eof(sftp->channel); - ptr = sftp->queue; - while(ptr) { - sftp_request_queue old; - sftp_message_free(ptr->message); - old = ptr->next; - SAFE_FREE(ptr); - ptr = old; - } - - ssh_channel_free(sftp->channel); - sftp_ext_free(sftp->ext); - ZERO_STRUCTP(sftp); - - SAFE_FREE(sftp); -} - -int sftp_packet_write(sftp_session sftp, uint8_t type, ssh_buffer payload){ - int size; - - if (buffer_prepend_data(payload, &type, sizeof(uint8_t)) < 0) { - ssh_set_error_oom(sftp->session); - return -1; - } - - size = htonl(ssh_buffer_get_len(payload)); - if (buffer_prepend_data(payload, &size, sizeof(uint32_t)) < 0) { - ssh_set_error_oom(sftp->session); - return -1; - } - - size = ssh_channel_write(sftp->channel, ssh_buffer_get_begin(payload), - ssh_buffer_get_len(payload)); - if (size < 0) { - return -1; - } else if((uint32_t) size != ssh_buffer_get_len(payload)) { - ssh_log(sftp->session, SSH_LOG_PACKET, - "Had to write %d bytes, wrote only %d", - ssh_buffer_get_len(payload), - size); - } - - return size; -} - -sftp_packet sftp_packet_read(sftp_session sftp) { - sftp_packet packet = NULL; - uint32_t size; - - sftp_enter_function(); - - packet = malloc(sizeof(struct sftp_packet_struct)); - if (packet == NULL) { - ssh_set_error_oom(sftp->session); - return NULL; - } - packet->sftp = sftp; - packet->payload = ssh_buffer_new(); - if (packet->payload == NULL) { - ssh_set_error_oom(sftp->session); - SAFE_FREE(packet); - return NULL; - } - - if (channel_read_buffer(sftp->channel, packet->payload, 4, 0) <= 0) { - ssh_buffer_free(packet->payload); - SAFE_FREE(packet); - sftp_leave_function(); - return NULL; - } - - if (buffer_get_u32(packet->payload, &size) != sizeof(uint32_t)) { - ssh_set_error(sftp->session, SSH_FATAL, "Short sftp packet!"); - ssh_buffer_free(packet->payload); - SAFE_FREE(packet); - sftp_leave_function(); - return NULL; - } - - size = ntohl(size); - if (channel_read_buffer(sftp->channel, packet->payload, 1, 0) <= 0) { - /* TODO: check if there are cases where an error needs to be set here */ - ssh_buffer_free(packet->payload); - SAFE_FREE(packet); - sftp_leave_function(); - return NULL; - } - - buffer_get_u8(packet->payload, &packet->type); - if (size > 1) { - if (channel_read_buffer(sftp->channel, packet->payload, size - 1, 0) <= 0) { - /* TODO: check if there are cases where an error needs to be set here */ - ssh_buffer_free(packet->payload); - SAFE_FREE(packet); - sftp_leave_function(); - return NULL; - } - } - - sftp_leave_function(); - return packet; -} - -static void sftp_set_error(sftp_session sftp, int errnum) { - if (sftp != NULL) { - sftp->errnum = errnum; - } -} - -/* Get the last sftp error */ -int sftp_get_error(sftp_session sftp) { - if (sftp == NULL) { - return -1; - } - - return sftp->errnum; -} - -static sftp_message sftp_message_new(sftp_session sftp){ - sftp_message msg = NULL; - - sftp_enter_function(); - - msg = malloc(sizeof(struct sftp_message_struct)); - if (msg == NULL) { - ssh_set_error_oom(sftp->session); - return NULL; - } - ZERO_STRUCTP(msg); - - msg->payload = ssh_buffer_new(); - if (msg->payload == NULL) { - ssh_set_error_oom(sftp->session); - SAFE_FREE(msg); - return NULL; - } - msg->sftp = sftp; - - sftp_leave_function(); - return msg; -} - -static void sftp_message_free(sftp_message msg) { - sftp_session sftp; - - if (msg == NULL) { - return; - } - - sftp = msg->sftp; - sftp_enter_function(); - - ssh_buffer_free(msg->payload); - SAFE_FREE(msg); - - sftp_leave_function(); -} - -static sftp_message sftp_get_message(sftp_packet packet) { - sftp_session sftp = packet->sftp; - sftp_message msg = NULL; - - sftp_enter_function(); - - msg = sftp_message_new(sftp); - if (msg == NULL) { - sftp_leave_function(); - return NULL; - } - - msg->sftp = packet->sftp; - msg->packet_type = packet->type; - - if ((packet->type != SSH_FXP_STATUS) && (packet->type!=SSH_FXP_HANDLE) && - (packet->type != SSH_FXP_DATA) && (packet->type != SSH_FXP_ATTRS) && - (packet->type != SSH_FXP_NAME) && (packet->type != SSH_FXP_EXTENDED_REPLY)) { - ssh_set_error(packet->sftp->session, SSH_FATAL, - "Unknown packet type %d", packet->type); - sftp_message_free(msg); - sftp_leave_function(); - return NULL; - } - - if (buffer_get_u32(packet->payload, &msg->id) != sizeof(uint32_t)) { - ssh_set_error(packet->sftp->session, SSH_FATAL, - "Invalid packet %d: no ID", packet->type); - sftp_message_free(msg); - sftp_leave_function(); - return NULL; - } - - ssh_log(packet->sftp->session, SSH_LOG_PACKET, - "Packet with id %d type %d", - msg->id, - msg->packet_type); - - if (buffer_add_data(msg->payload, buffer_get_rest(packet->payload), - buffer_get_rest_len(packet->payload)) < 0) { - ssh_set_error_oom(sftp->session); - sftp_message_free(msg); - sftp_leave_function(); - return NULL; - } - - sftp_leave_function(); - return msg; -} - -static int sftp_read_and_dispatch(sftp_session sftp) { - sftp_packet packet = NULL; - sftp_message msg = NULL; - - sftp_enter_function(); - - packet = sftp_packet_read(sftp); - if (packet == NULL) { - sftp_leave_function(); - return -1; /* something nasty happened reading the packet */ - } - - msg = sftp_get_message(packet); - sftp_packet_free(packet); - if (msg == NULL) { - sftp_leave_function(); - return -1; - } - - if (sftp_enqueue(sftp, msg) < 0) { - sftp_message_free(msg); - sftp_leave_function(); - return -1; - } - - sftp_leave_function(); - return 0; -} - -void sftp_packet_free(sftp_packet packet) { - if (packet == NULL) { - return; - } - - ssh_buffer_free(packet->payload); - free(packet); -} - -/* Initialize the sftp session with the server. */ -int sftp_init(sftp_session sftp) { - sftp_packet packet = NULL; - ssh_buffer buffer = NULL; - ssh_string ext_name_s = NULL; - ssh_string ext_data_s = NULL; - char *ext_name = NULL; - char *ext_data = NULL; - uint32_t version = htonl(LIBSFTP_VERSION); - - sftp_enter_function(); - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(sftp->session); - sftp_leave_function(); - return -1; - } - - if (buffer_add_u32(buffer, version) < 0) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - sftp_leave_function(); - return -1; - } - if (sftp_packet_write(sftp, SSH_FXP_INIT, buffer) < 0) { - ssh_buffer_free(buffer); - sftp_leave_function(); - return -1; - } - ssh_buffer_free(buffer); - - packet = sftp_packet_read(sftp); - if (packet == NULL) { - sftp_leave_function(); - return -1; - } - - if (packet->type != SSH_FXP_VERSION) { - ssh_set_error(sftp->session, SSH_FATAL, - "Received a %d messages instead of SSH_FXP_VERSION", packet->type); - sftp_packet_free(packet); - sftp_leave_function(); - return -1; - } - - /* TODO: are we sure there are 4 bytes ready? */ - buffer_get_u32(packet->payload, &version); - version = ntohl(version); - ssh_log(sftp->session, SSH_LOG_RARE, - "SFTP server version %d", - version); - - ext_name_s = buffer_get_ssh_string(packet->payload); - while (ext_name_s != NULL) { - int count = sftp->ext->count; - char **tmp; - - ext_data_s = buffer_get_ssh_string(packet->payload); - if (ext_data_s == NULL) { - ssh_string_free(ext_name_s); - break; - } - - ext_name = ssh_string_to_char(ext_name_s); - ext_data = ssh_string_to_char(ext_data_s); - if (ext_name == NULL || ext_data == NULL) { - ssh_set_error_oom(sftp->session); - SAFE_FREE(ext_name); - SAFE_FREE(ext_data); - ssh_string_free(ext_name_s); - ssh_string_free(ext_data_s); - return -1; - } - ssh_log(sftp->session, SSH_LOG_RARE, - "SFTP server extension: %s, version: %s", - ext_name, ext_data); - - count++; - tmp = realloc(sftp->ext->name, count * sizeof(char *)); - if (tmp == NULL) { - ssh_set_error_oom(sftp->session); - SAFE_FREE(ext_name); - SAFE_FREE(ext_data); - ssh_string_free(ext_name_s); - ssh_string_free(ext_data_s); - return -1; - } - tmp[count - 1] = ext_name; - sftp->ext->name = tmp; - - tmp = realloc(sftp->ext->data, count * sizeof(char *)); - if (tmp == NULL) { - ssh_set_error_oom(sftp->session); - SAFE_FREE(ext_name); - SAFE_FREE(ext_data); - ssh_string_free(ext_name_s); - ssh_string_free(ext_data_s); - return -1; - } - tmp[count - 1] = ext_data; - sftp->ext->data = tmp; - - sftp->ext->count = count; - - ssh_string_free(ext_name_s); - ssh_string_free(ext_data_s); - - ext_name_s = buffer_get_ssh_string(packet->payload); - } - - sftp_packet_free(packet); - - sftp->version = sftp->server_version = version; - - sftp_leave_function(); - - return 0; -} - -unsigned int sftp_extensions_get_count(sftp_session sftp) { - if (sftp == NULL || sftp->ext == NULL) { - return 0; - } - - return sftp->ext->count; -} - -const char *sftp_extensions_get_name(sftp_session sftp, unsigned int idx) { - if (sftp == NULL) - return NULL; - if (sftp->ext == NULL || sftp->ext->name == NULL) { - ssh_set_error_invalid(sftp->session, __FUNCTION__); - return NULL; - } - - if (idx > sftp->ext->count) { - ssh_set_error_invalid(sftp->session, __FUNCTION__); - return NULL; - } - - return sftp->ext->name[idx]; -} - -const char *sftp_extensions_get_data(sftp_session sftp, unsigned int idx) { - if (sftp == NULL) - return NULL; - if (sftp->ext == NULL || sftp->ext->name == NULL) { - ssh_set_error_invalid(sftp->session, __FUNCTION__); - return NULL; - } - - if (idx > sftp->ext->count) { - ssh_set_error_invalid(sftp->session, __FUNCTION__); - return NULL; - } - - return sftp->ext->data[idx]; -} - -int sftp_extension_supported(sftp_session sftp, const char *name, - const char *data) { - int i, n; - - n = sftp_extensions_get_count(sftp); - for (i = 0; i < n; i++) { - if (strcmp(sftp_extensions_get_name(sftp, i), name) == 0 && - strcmp(sftp_extensions_get_data(sftp, i), data) == 0) { - return 1; - } - } - - return 0; -} - -static sftp_request_queue request_queue_new(sftp_message msg) { - sftp_request_queue queue = NULL; - - queue = malloc(sizeof(struct sftp_request_queue_struct)); - if (queue == NULL) { - ssh_set_error_oom(msg->sftp->session); - return NULL; - } - ZERO_STRUCTP(queue); - - queue->message = msg; - - return queue; -} - -static void request_queue_free(sftp_request_queue queue) { - if (queue == NULL) { - return; - } - - ZERO_STRUCTP(queue); - SAFE_FREE(queue); -} - -static int sftp_enqueue(sftp_session sftp, sftp_message msg) { - sftp_request_queue queue = NULL; - sftp_request_queue ptr; - - queue = request_queue_new(msg); - if (queue == NULL) { - return -1; - } - - ssh_log(sftp->session, SSH_LOG_PACKET, - "Queued msg type %d id %d", - msg->id, msg->packet_type); - - if(sftp->queue == NULL) { - sftp->queue = queue; - } else { - ptr = sftp->queue; - while(ptr->next) { - ptr=ptr->next; /* find end of linked list */ - } - ptr->next = queue; /* add it on bottom */ - } - - return 0; -} - -/* - * Pulls of a message from the queue based on the ID. - * Returns NULL if no message has been found. - */ -static sftp_message sftp_dequeue(sftp_session sftp, uint32_t id){ - sftp_request_queue prev = NULL; - sftp_request_queue queue; - sftp_message msg; - - if(sftp->queue == NULL) { - return NULL; - } - - queue = sftp->queue; - while (queue) { - if(queue->message->id == id) { - /* remove from queue */ - if (prev == NULL) { - sftp->queue = queue->next; - } else { - prev->next = queue->next; - } - msg = queue->message; - request_queue_free(queue); - ssh_log(sftp->session, SSH_LOG_PACKET, - "Dequeued msg id %d type %d", - msg->id, - msg->packet_type); - return msg; - } - prev = queue; - queue = queue->next; - } - - return NULL; -} - -/* - * Assigns a new SFTP ID for new requests and assures there is no collision - * between them. - * Returns a new ID ready to use in a request - */ -static inline uint32_t sftp_get_new_id(sftp_session session) { - return ++session->id_counter; -} - -static sftp_status_message parse_status_msg(sftp_message msg){ - sftp_status_message status; - - if (msg->packet_type != SSH_FXP_STATUS) { - ssh_set_error(msg->sftp->session, SSH_FATAL, - "Not a ssh_fxp_status message passed in!"); - return NULL; - } - - status = malloc(sizeof(struct sftp_status_message_struct)); - if (status == NULL) { - ssh_set_error_oom(msg->sftp->session); - return NULL; - } - ZERO_STRUCTP(status); - - status->id = msg->id; - if (buffer_get_u32(msg->payload,&status->status) != 4){ - SAFE_FREE(status); - ssh_set_error(msg->sftp->session, SSH_FATAL, - "Invalid SSH_FXP_STATUS message"); - return NULL; - } - status->error = buffer_get_ssh_string(msg->payload); - status->lang = buffer_get_ssh_string(msg->payload); - if(status->error == NULL || status->lang == NULL){ - if(msg->sftp->version >=3){ - /* These are mandatory from version 3 */ - ssh_string_free(status->error); - /* status->lang never get allocated if something failed */ - SAFE_FREE(status); - ssh_set_error(msg->sftp->session, SSH_FATAL, - "Invalid SSH_FXP_STATUS message"); - return NULL; - } - } - - status->status = ntohl(status->status); - if(status->error) - status->errormsg = ssh_string_to_char(status->error); - else - status->errormsg = strdup("No error message in packet"); - if(status->lang) - status->langmsg = ssh_string_to_char(status->lang); - else - status->langmsg = strdup(""); - if (status->errormsg == NULL || status->langmsg == NULL) { - ssh_set_error_oom(msg->sftp->session); - status_msg_free(status); - return NULL; - } - - return status; -} - -static void status_msg_free(sftp_status_message status){ - if (status == NULL) { - return; - } - - ssh_string_free(status->error); - ssh_string_free(status->lang); - SAFE_FREE(status->errormsg); - SAFE_FREE(status->langmsg); - SAFE_FREE(status); -} - -static sftp_file parse_handle_msg(sftp_message msg){ - sftp_file file; - - if(msg->packet_type != SSH_FXP_HANDLE) { - ssh_set_error(msg->sftp->session, SSH_FATAL, - "Not a ssh_fxp_handle message passed in!"); - return NULL; - } - - file = malloc(sizeof(struct sftp_file_struct)); - if (file == NULL) { - ssh_set_error_oom(msg->sftp->session); - return NULL; - } - ZERO_STRUCTP(file); - - file->handle = buffer_get_ssh_string(msg->payload); - if (file->handle == NULL) { - ssh_set_error(msg->sftp->session, SSH_FATAL, - "Invalid SSH_FXP_HANDLE message"); - SAFE_FREE(file); - return NULL; - } - - file->sftp = msg->sftp; - file->offset = 0; - file->eof = 0; - - return file; -} - -/* Open a directory */ -sftp_dir sftp_opendir(sftp_session sftp, const char *path){ - sftp_message msg = NULL; - sftp_file file = NULL; - sftp_dir dir = NULL; - sftp_status_message status; - ssh_string path_s; - ssh_buffer payload; - uint32_t id; - - payload = ssh_buffer_new(); - if (payload == NULL) { - ssh_set_error_oom(sftp->session); - return NULL; - } - - path_s = ssh_string_from_char(path); - if (path_s == NULL) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(payload); - return NULL; - } - - id = sftp_get_new_id(sftp); - if (buffer_add_u32(payload, id) < 0 || - buffer_add_ssh_string(payload, path_s) < 0) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(payload); - ssh_string_free(path_s); - return NULL; - } - ssh_string_free(path_s); - - if (sftp_packet_write(sftp, SSH_FXP_OPENDIR, payload) < 0) { - ssh_buffer_free(payload); - return NULL; - } - ssh_buffer_free(payload); - - while (msg == NULL) { - if (sftp_read_and_dispatch(sftp) < 0) { - /* something nasty has happened */ - return NULL; - } - msg = sftp_dequeue(sftp, id); - } - - switch (msg->packet_type) { - case SSH_FXP_STATUS: - status = parse_status_msg(msg); - sftp_message_free(msg); - if (status == NULL) { - return NULL; - } - sftp_set_error(sftp, status->status); - ssh_set_error(sftp->session, SSH_REQUEST_DENIED, - "SFTP server: %s", status->errormsg); - status_msg_free(status); - return NULL; - case SSH_FXP_HANDLE: - file = parse_handle_msg(msg); - sftp_message_free(msg); - if (file != NULL) { - dir = malloc(sizeof(struct sftp_dir_struct)); - if (dir == NULL) { - ssh_set_error_oom(sftp->session); - return NULL; - } - ZERO_STRUCTP(dir); - - dir->sftp = sftp; - dir->name = strdup(path); - if (dir->name == NULL) { - SAFE_FREE(dir); - SAFE_FREE(file); - return NULL; - } - dir->handle = file->handle; - SAFE_FREE(file); - } - return dir; - default: - ssh_set_error(sftp->session, SSH_FATAL, - "Received message %d during opendir!", msg->packet_type); - sftp_message_free(msg); - } - - return NULL; -} - -/* - * Parse the attributes from a payload from some messages. It is coded on - * baselines from the protocol version 4. - * This code is more or less dead but maybe we need it in future. - */ -static sftp_attributes sftp_parse_attr_4(sftp_session sftp, ssh_buffer buf, - int expectnames) { - sftp_attributes attr; - ssh_string owner = NULL; - ssh_string group = NULL; - uint32_t flags = 0; - int ok = 0; - - /* unused member variable */ - (void) expectnames; - - attr = malloc(sizeof(struct sftp_attributes_struct)); - if (attr == NULL) { - ssh_set_error_oom(sftp->session); - return NULL; - } - ZERO_STRUCTP(attr); - - /* This isn't really a loop, but it is like a try..catch.. */ - do { - if (buffer_get_u32(buf, &flags) != 4) { - break; - } - - flags = ntohl(flags); - attr->flags = flags; - - if (flags & SSH_FILEXFER_ATTR_SIZE) { - if (buffer_get_u64(buf, &attr->size) != 8) { - break; - } - attr->size = ntohll(attr->size); - } - - if (flags & SSH_FILEXFER_ATTR_OWNERGROUP) { - if((owner = buffer_get_ssh_string(buf)) == NULL || - (attr->owner = ssh_string_to_char(owner)) == NULL) { - break; - } - if ((group = buffer_get_ssh_string(buf)) == NULL || - (attr->group = ssh_string_to_char(group)) == NULL) { - break; - } - } - - if (flags & SSH_FILEXFER_ATTR_PERMISSIONS) { - if (buffer_get_u32(buf, &attr->permissions) != 4) { - break; - } - attr->permissions = ntohl(attr->permissions); - - /* FIXME on windows! */ - switch (attr->permissions & S_IFMT) { - case S_IFSOCK: - case S_IFBLK: - case S_IFCHR: - case S_IFIFO: - attr->type = SSH_FILEXFER_TYPE_SPECIAL; - break; - case S_IFLNK: - attr->type = SSH_FILEXFER_TYPE_SYMLINK; - break; - case S_IFREG: - attr->type = SSH_FILEXFER_TYPE_REGULAR; - break; - case S_IFDIR: - attr->type = SSH_FILEXFER_TYPE_DIRECTORY; - break; - default: - attr->type = SSH_FILEXFER_TYPE_UNKNOWN; - break; - } - } - - if (flags & SSH_FILEXFER_ATTR_ACCESSTIME) { - if (buffer_get_u64(buf, &attr->atime64) != 8) { - break; - } - attr->atime64 = ntohll(attr->atime64); - } - - if (flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) { - if (buffer_get_u32(buf, &attr->atime_nseconds) != 4) { - break; - } - attr->atime_nseconds = ntohl(attr->atime_nseconds); - } - - if (flags & SSH_FILEXFER_ATTR_CREATETIME) { - if (buffer_get_u64(buf, &attr->createtime) != 8) { - break; - } - attr->createtime = ntohll(attr->createtime); - } - - if (flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) { - if (buffer_get_u32(buf, &attr->createtime_nseconds) != 4) { - break; - } - attr->createtime_nseconds = ntohl(attr->createtime_nseconds); - } - - if (flags & SSH_FILEXFER_ATTR_MODIFYTIME) { - if (buffer_get_u64(buf, &attr->mtime64) != 8) { - break; - } - attr->mtime64 = ntohll(attr->mtime64); - } - - if (flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES) { - if (buffer_get_u32(buf, &attr->mtime_nseconds) != 4) { - break; - } - attr->mtime_nseconds = ntohl(attr->mtime_nseconds); - } - - if (flags & SSH_FILEXFER_ATTR_ACL) { - if ((attr->acl = buffer_get_ssh_string(buf)) == NULL) { - break; - } - } - - if (flags & SSH_FILEXFER_ATTR_EXTENDED) { - if (buffer_get_u32(buf,&attr->extended_count) != 4) { - break; - } - attr->extended_count = ntohl(attr->extended_count); - - while(attr->extended_count && - (attr->extended_type = buffer_get_ssh_string(buf)) && - (attr->extended_data = buffer_get_ssh_string(buf))){ - attr->extended_count--; - } - - if (attr->extended_count) { - break; - } - } - ok = 1; - } while (0); - - if (ok == 0) { - /* break issued somewhere */ - ssh_string_free(owner); - ssh_string_free(group); - ssh_string_free(attr->acl); - ssh_string_free(attr->extended_type); - ssh_string_free(attr->extended_data); - SAFE_FREE(attr->owner); - SAFE_FREE(attr->group); - SAFE_FREE(attr); - - ssh_set_error(sftp->session, SSH_FATAL, "Invalid ATTR structure"); - - return NULL; - } - - return attr; -} - -enum sftp_longname_field_e { - SFTP_LONGNAME_PERM = 0, - SFTP_LONGNAME_FIXME, - SFTP_LONGNAME_OWNER, - SFTP_LONGNAME_GROUP, - SFTP_LONGNAME_SIZE, - SFTP_LONGNAME_DATE, - SFTP_LONGNAME_TIME, - SFTP_LONGNAME_NAME, -}; - -static char *sftp_parse_longname(const char *longname, - enum sftp_longname_field_e longname_field) { - const char *p, *q; - size_t len, field = 0; - char *x; - - p = longname; - /* Find the beginning of the field which is specified by sftp_longanme_field_e. */ - while(field != longname_field) { - if(isspace(*p)) { - field++; - p++; - while(*p && isspace(*p)) { - p++; - } - } else { - p++; - } - } - - q = p; - while (! isspace(*q)) { - q++; - } - - /* There is no strndup on windows */ - len = q - p + 1; - x = malloc(len); - if (x == NULL) { - return NULL; - } - - snprintf(x, len, "%s", p); - - return x; -} - -/* sftp version 0-3 code. It is different from the v4 */ -/* maybe a paste of the draft is better than the code */ -/* - uint32 flags - uint64 size present only if flag SSH_FILEXFER_ATTR_SIZE - uint32 uid present only if flag SSH_FILEXFER_ATTR_UIDGID - uint32 gid present only if flag SSH_FILEXFER_ATTR_UIDGID - uint32 permissions present only if flag SSH_FILEXFER_ATTR_PERMISSIONS - uint32 atime present only if flag SSH_FILEXFER_ACMODTIME - uint32 mtime present only if flag SSH_FILEXFER_ACMODTIME - uint32 extended_count present only if flag SSH_FILEXFER_ATTR_EXTENDED - string extended_type - string extended_data - ... more extended data (extended_type - extended_data pairs), - so that number of pairs equals extended_count */ -static sftp_attributes sftp_parse_attr_3(sftp_session sftp, ssh_buffer buf, - int expectname) { - ssh_string longname = NULL; - ssh_string name = NULL; - sftp_attributes attr; - uint32_t flags = 0; - int ok = 0; - - attr = malloc(sizeof(struct sftp_attributes_struct)); - if (attr == NULL) { - ssh_set_error_oom(sftp->session); - return NULL; - } - ZERO_STRUCTP(attr); - - /* This isn't really a loop, but it is like a try..catch.. */ - do { - if (expectname) { - if ((name = buffer_get_ssh_string(buf)) == NULL || - (attr->name = ssh_string_to_char(name)) == NULL) { - break; - } - ssh_string_free(name); - - ssh_log(sftp->session, SSH_LOG_RARE, "Name: %s", attr->name); - - if ((longname=buffer_get_ssh_string(buf)) == NULL || - (attr->longname=ssh_string_to_char(longname)) == NULL) { - break; - } - ssh_string_free(longname); - - /* Set owner and group if we talk to openssh and have the longname */ - if (ssh_get_openssh_version(sftp->session)) { - attr->owner = sftp_parse_longname(attr->longname, SFTP_LONGNAME_OWNER); - if (attr->owner == NULL) { - break; - } - - attr->group = sftp_parse_longname(attr->longname, SFTP_LONGNAME_GROUP); - if (attr->group == NULL) { - break; - } - } - } - - if (buffer_get_u32(buf, &flags) != sizeof(uint32_t)) { - break; - } - flags = ntohl(flags); - attr->flags = flags; - ssh_log(sftp->session, SSH_LOG_RARE, - "Flags: %.8lx\n", (long unsigned int) flags); - - if (flags & SSH_FILEXFER_ATTR_SIZE) { - if(buffer_get_u64(buf, &attr->size) != sizeof(uint64_t)) { - break; - } - attr->size = ntohll(attr->size); - ssh_log(sftp->session, SSH_LOG_RARE, - "Size: %llu\n", - (long long unsigned int) attr->size); - } - - if (flags & SSH_FILEXFER_ATTR_UIDGID) { - if (buffer_get_u32(buf, &attr->uid) != sizeof(uint32_t)) { - break; - } - if (buffer_get_u32(buf, &attr->gid) != sizeof(uint32_t)) { - break; - } - attr->uid = ntohl(attr->uid); - attr->gid = ntohl(attr->gid); - } - - if (flags & SSH_FILEXFER_ATTR_PERMISSIONS) { - if (buffer_get_u32(buf, &attr->permissions) != sizeof(uint32_t)) { - break; - } - attr->permissions = ntohl(attr->permissions); - - switch (attr->permissions & S_IFMT) { - case S_IFSOCK: - case S_IFBLK: - case S_IFCHR: - case S_IFIFO: - attr->type = SSH_FILEXFER_TYPE_SPECIAL; - break; - case S_IFLNK: - attr->type = SSH_FILEXFER_TYPE_SYMLINK; - break; - case S_IFREG: - attr->type = SSH_FILEXFER_TYPE_REGULAR; - break; - case S_IFDIR: - attr->type = SSH_FILEXFER_TYPE_DIRECTORY; - break; - default: - attr->type = SSH_FILEXFER_TYPE_UNKNOWN; - break; - } - } - - if (flags & SSH_FILEXFER_ATTR_ACMODTIME) { - if (buffer_get_u32(buf, &attr->atime) != sizeof(uint32_t)) { - break; - } - attr->atime = ntohl(attr->atime); - if (buffer_get_u32(buf, &attr->mtime) != sizeof(uint32_t)) { - break; - } - attr->mtime = ntohl(attr->mtime); - } - - if (flags & SSH_FILEXFER_ATTR_EXTENDED) { - if (buffer_get_u32(buf, &attr->extended_count) != sizeof(uint32_t)) { - break; - } - - attr->extended_count = ntohl(attr->extended_count); - while (attr->extended_count && - (attr->extended_type = buffer_get_ssh_string(buf)) - && (attr->extended_data = buffer_get_ssh_string(buf))) { - attr->extended_count--; - } - - if (attr->extended_count) { - break; - } - } - ok = 1; - } while (0); - - if (!ok) { - /* break issued somewhere */ - ssh_string_free(name); - ssh_string_free(longname); - ssh_string_free(attr->extended_type); - ssh_string_free(attr->extended_data); - SAFE_FREE(attr->name); - SAFE_FREE(attr->longname); - SAFE_FREE(attr->owner); - SAFE_FREE(attr->group); - SAFE_FREE(attr); - - ssh_set_error(sftp->session, SSH_FATAL, "Invalid ATTR structure"); - - return NULL; - } - - /* everything went smoothly */ - return attr; -} - -/* FIXME is this really needed as a public function? */ -int buffer_add_attributes(ssh_buffer buffer, sftp_attributes attr) { - uint32_t flags = (attr ? attr->flags : 0); - - flags &= (SSH_FILEXFER_ATTR_SIZE | SSH_FILEXFER_ATTR_UIDGID | - SSH_FILEXFER_ATTR_PERMISSIONS | SSH_FILEXFER_ATTR_ACMODTIME); - - if (buffer_add_u32(buffer, htonl(flags)) < 0) { - return -1; - } - - if (attr) { - if (flags & SSH_FILEXFER_ATTR_SIZE) { - if (buffer_add_u64(buffer, htonll(attr->size)) < 0) { - return -1; - } - } - - if (flags & SSH_FILEXFER_ATTR_UIDGID) { - if (buffer_add_u32(buffer,htonl(attr->uid)) < 0 || - buffer_add_u32(buffer,htonl(attr->gid)) < 0) { - return -1; - } - } - - if (flags & SSH_FILEXFER_ATTR_PERMISSIONS) { - if (buffer_add_u32(buffer, htonl(attr->permissions)) < 0) { - return -1; - } - } - - if (flags & SSH_FILEXFER_ATTR_ACMODTIME) { - if (buffer_add_u32(buffer, htonl(attr->atime)) < 0 || - buffer_add_u32(buffer, htonl(attr->mtime)) < 0) { - return -1; - } - } - } - - return 0; -} - - -sftp_attributes sftp_parse_attr(sftp_session session, ssh_buffer buf, - int expectname) { - switch(session->version) { - case 4: - return sftp_parse_attr_4(session, buf, expectname); - case 3: - case 2: - case 1: - case 0: - return sftp_parse_attr_3(session, buf, expectname); - default: - ssh_set_error(session->session, SSH_FATAL, - "Version %d unsupported by client", session->server_version); - return NULL; - } - - return NULL; -} - -/* Get the version of the SFTP protocol supported by the server */ -int sftp_server_version(sftp_session sftp) { - return sftp->server_version; -} - -/* Get a single file attributes structure of a directory. */ -sftp_attributes sftp_readdir(sftp_session sftp, sftp_dir dir) { - sftp_message msg = NULL; - sftp_status_message status; - sftp_attributes attr; - ssh_buffer payload; - uint32_t id; - - if (dir->buffer == NULL) { - payload = ssh_buffer_new(); - if (payload == NULL) { - ssh_set_error_oom(sftp->session); - return NULL; - } - - id = sftp_get_new_id(sftp); - if (buffer_add_u32(payload, id) < 0 || - buffer_add_ssh_string(payload, dir->handle) < 0) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(payload); - return NULL; - } - - if (sftp_packet_write(sftp, SSH_FXP_READDIR, payload) < 0) { - ssh_buffer_free(payload); - return NULL; - } - ssh_buffer_free(payload); - - ssh_log(sftp->session, SSH_LOG_PACKET, - "Sent a ssh_fxp_readdir with id %d", id); - - while (msg == NULL) { - if (sftp_read_and_dispatch(sftp) < 0) { - /* something nasty has happened */ - return NULL; - } - msg = sftp_dequeue(sftp, id); - } - - switch (msg->packet_type){ - case SSH_FXP_STATUS: - status = parse_status_msg(msg); - sftp_message_free(msg); - if (status == NULL) { - return NULL; - } - sftp_set_error(sftp, status->status); - switch (status->status) { - case SSH_FX_EOF: - dir->eof = 1; - status_msg_free(status); - return NULL; - default: - break; - } - - ssh_set_error(sftp->session, SSH_FATAL, - "Unknown error status: %d", status->status); - status_msg_free(status); - - return NULL; - case SSH_FXP_NAME: - buffer_get_u32(msg->payload, &dir->count); - dir->count = ntohl(dir->count); - dir->buffer = msg->payload; - msg->payload = NULL; - sftp_message_free(msg); - break; - default: - ssh_set_error(sftp->session, SSH_FATAL, - "Unsupported message back %d", msg->packet_type); - sftp_message_free(msg); - - return NULL; - } - } - - /* now dir->buffer contains a buffer and dir->count != 0 */ - if (dir->count == 0) { - ssh_set_error(sftp->session, SSH_FATAL, - "Count of files sent by the server is zero, which is invalid, or " - "libsftp bug"); - return NULL; - } - - ssh_log(sftp->session, SSH_LOG_RARE, "Count is %d", dir->count); - - attr = sftp_parse_attr(sftp, dir->buffer, 1); - if (attr == NULL) { - ssh_set_error(sftp->session, SSH_FATAL, - "Couldn't parse the SFTP attributes"); - return NULL; - } - - dir->count--; - if (dir->count == 0) { - ssh_buffer_free(dir->buffer); - dir->buffer = NULL; - } - - return attr; -} - -/* Tell if the directory has reached EOF (End Of File). */ -int sftp_dir_eof(sftp_dir dir) { - return dir->eof; -} - -/* Free a SFTP_ATTRIBUTE handle */ -void sftp_attributes_free(sftp_attributes file){ - if (file == NULL) { - return; - } - - ssh_string_free(file->acl); - ssh_string_free(file->extended_data); - ssh_string_free(file->extended_type); - - SAFE_FREE(file->name); - SAFE_FREE(file->longname); - SAFE_FREE(file->group); - SAFE_FREE(file->owner); - - SAFE_FREE(file); -} - -static int sftp_handle_close(sftp_session sftp, ssh_string handle) { - sftp_status_message status; - sftp_message msg = NULL; - ssh_buffer buffer = NULL; - uint32_t id; - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(sftp->session); - return -1; - } - - id = sftp_get_new_id(sftp); - if (buffer_add_u32(buffer, id) < 0 || - buffer_add_ssh_string(buffer, handle) < 0) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - return -1; - } - if (sftp_packet_write(sftp, SSH_FXP_CLOSE ,buffer) < 0) { - ssh_buffer_free(buffer); - return -1; - } - ssh_buffer_free(buffer); - - while (msg == NULL) { - if (sftp_read_and_dispatch(sftp) < 0) { - /* something nasty has happened */ - return -1; - } - msg = sftp_dequeue(sftp,id); - } - - switch (msg->packet_type) { - case SSH_FXP_STATUS: - status = parse_status_msg(msg); - sftp_message_free(msg); - if(status == NULL) { - return -1; - } - sftp_set_error(sftp, status->status); - switch (status->status) { - case SSH_FX_OK: - status_msg_free(status); - return 0; - break; - default: - break; - } - ssh_set_error(sftp->session, SSH_REQUEST_DENIED, - "SFTP server: %s", status->errormsg); - status_msg_free(status); - return -1; - default: - ssh_set_error(sftp->session, SSH_FATAL, - "Received message %d during sftp_handle_close!", msg->packet_type); - sftp_message_free(msg); - } - - return -1; -} - -/* Close an open file handle. */ -int sftp_close(sftp_file file){ - int err = SSH_NO_ERROR; - - SAFE_FREE(file->name); - if (file->handle){ - err = sftp_handle_close(file->sftp,file->handle); - ssh_string_free(file->handle); - } - /* FIXME: check server response and implement errno */ - SAFE_FREE(file); - - return err; -} - -/* Close an open directory. */ -int sftp_closedir(sftp_dir dir){ - int err = SSH_NO_ERROR; - - SAFE_FREE(dir->name); - if (dir->handle) { - err = sftp_handle_close(dir->sftp, dir->handle); - ssh_string_free(dir->handle); - } - /* FIXME: check server response and implement errno */ - ssh_buffer_free(dir->buffer); - SAFE_FREE(dir); - - return err; -} - -/* Open a file on the server. */ -sftp_file sftp_open(sftp_session sftp, const char *file, int flags, - mode_t mode) { - sftp_message msg = NULL; - sftp_status_message status; - struct sftp_attributes_struct attr; - sftp_file handle; - ssh_string filename; - ssh_buffer buffer; - uint32_t sftp_flags = 0; - uint32_t id; - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(sftp->session); - return NULL; - } - - filename = ssh_string_from_char(file); - if (filename == NULL) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - return NULL; - } - - ZERO_STRUCT(attr); - attr.permissions = mode; - attr.flags = SSH_FILEXFER_ATTR_PERMISSIONS; - - if (flags == O_RDONLY) - sftp_flags |= SSH_FXF_READ; /* if any of the other flag is set, - READ should not be set initialy */ - if (flags & O_WRONLY) - sftp_flags |= SSH_FXF_WRITE; - if (flags & O_RDWR) - sftp_flags |= (SSH_FXF_WRITE | SSH_FXF_READ); - if (flags & O_CREAT) - sftp_flags |= SSH_FXF_CREAT; - if (flags & O_TRUNC) - sftp_flags |= SSH_FXF_TRUNC; - if (flags & O_EXCL) - sftp_flags |= SSH_FXF_EXCL; - ssh_log(sftp->session,SSH_LOG_PACKET,"Opening file %s with sftp flags %x",file,sftp_flags); - id = sftp_get_new_id(sftp); - if (buffer_add_u32(buffer, id) < 0 || - buffer_add_ssh_string(buffer, filename) < 0) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - ssh_string_free(filename); - return NULL; - } - ssh_string_free(filename); - - if (buffer_add_u32(buffer, htonl(sftp_flags)) < 0 || - buffer_add_attributes(buffer, &attr) < 0) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - return NULL; - } - if (sftp_packet_write(sftp, SSH_FXP_OPEN, buffer) < 0) { - ssh_buffer_free(buffer); - return NULL; - } - ssh_buffer_free(buffer); - - while (msg == NULL) { - if (sftp_read_and_dispatch(sftp) < 0) { - /* something nasty has happened */ - return NULL; - } - msg = sftp_dequeue(sftp, id); - } - - switch (msg->packet_type) { - case SSH_FXP_STATUS: - status = parse_status_msg(msg); - sftp_message_free(msg); - if (status == NULL) { - return NULL; - } - sftp_set_error(sftp, status->status); - ssh_set_error(sftp->session, SSH_REQUEST_DENIED, - "SFTP server: %s", status->errormsg); - status_msg_free(status); - - return NULL; - case SSH_FXP_HANDLE: - handle = parse_handle_msg(msg); - sftp_message_free(msg); - return handle; - default: - ssh_set_error(sftp->session, SSH_FATAL, - "Received message %d during open!", msg->packet_type); - sftp_message_free(msg); - } - - return NULL; -} - -void sftp_file_set_nonblocking(sftp_file handle){ - handle->nonblocking=1; -} -void sftp_file_set_blocking(sftp_file handle){ - handle->nonblocking=0; -} - -/* Read from a file using an opened sftp file handle. */ -ssize_t sftp_read(sftp_file handle, void *buf, size_t count) { - sftp_session sftp = handle->sftp; - sftp_message msg = NULL; - sftp_status_message status; - ssh_string datastring; - ssh_buffer buffer; - int id; - - if (handle->eof) { - return 0; - } - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(sftp->session); - return -1; - } - id = sftp_get_new_id(handle->sftp); - if (buffer_add_u32(buffer, id) < 0 || - buffer_add_ssh_string(buffer, handle->handle) < 0 || - buffer_add_u64(buffer, htonll(handle->offset)) < 0 || - buffer_add_u32(buffer,htonl(count)) < 0) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - return -1; - } - if (sftp_packet_write(handle->sftp, SSH_FXP_READ, buffer) < 0) { - ssh_buffer_free(buffer); - return -1; - } - ssh_buffer_free(buffer); - - while (msg == NULL) { - if (handle->nonblocking) { - if (ssh_channel_poll(handle->sftp->channel, 0) == 0) { - /* we cannot block */ - return 0; - } - } - if (sftp_read_and_dispatch(handle->sftp) < 0) { - /* something nasty has happened */ - return -1; - } - msg = sftp_dequeue(handle->sftp, id); - } - - switch (msg->packet_type) { - case SSH_FXP_STATUS: - status = parse_status_msg(msg); - sftp_message_free(msg); - if (status == NULL) { - return -1; - } - sftp_set_error(sftp, status->status); - switch (status->status) { - case SSH_FX_EOF: - handle->eof = 1; - status_msg_free(status); - return 0; - default: - break; - } - ssh_set_error(sftp->session,SSH_REQUEST_DENIED, - "SFTP server: %s", status->errormsg); - status_msg_free(status); - return -1; - case SSH_FXP_DATA: - datastring = buffer_get_ssh_string(msg->payload); - sftp_message_free(msg); - if (datastring == NULL) { - ssh_set_error(sftp->session, SSH_FATAL, - "Received invalid DATA packet from sftp server"); - return -1; - } - - if (ssh_string_len(datastring) > count) { - ssh_set_error(sftp->session, SSH_FATAL, - "Received a too big DATA packet from sftp server: " - "%zu and asked for %zu", - ssh_string_len(datastring), count); - ssh_string_free(datastring); - return -1; - } - count = ssh_string_len(datastring); - handle->offset += count; - memcpy(buf, ssh_string_data(datastring), count); - ssh_string_free(datastring); - return count; - default: - ssh_set_error(sftp->session, SSH_FATAL, - "Received message %d during read!", msg->packet_type); - sftp_message_free(msg); - return -1; - } - - return -1; /* not reached */ -} - -/* Start an asynchronous read from a file using an opened sftp file handle. */ -int sftp_async_read_begin(sftp_file file, uint32_t len){ - sftp_session sftp = file->sftp; - ssh_buffer buffer; - uint32_t id; - - sftp_enter_function(); - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(sftp->session); - return -1; - } - - id = sftp_get_new_id(sftp); - if (buffer_add_u32(buffer, id) < 0 || - buffer_add_ssh_string(buffer, file->handle) < 0 || - buffer_add_u64(buffer, htonll(file->offset)) < 0 || - buffer_add_u32(buffer, htonl(len)) < 0) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - return -1; - } - if (sftp_packet_write(sftp, SSH_FXP_READ, buffer) < 0) { - ssh_buffer_free(buffer); - return -1; - } - ssh_buffer_free(buffer); - - file->offset += len; /* assume we'll read len bytes */ - - sftp_leave_function(); - return id; -} - -/* Wait for an asynchronous read to complete and save the data. */ -int sftp_async_read(sftp_file file, void *data, uint32_t size, uint32_t id){ - sftp_session sftp = file->sftp; - sftp_message msg = NULL; - sftp_status_message status; - ssh_string datastring; - int err = SSH_OK; - uint32_t len; - - sftp_enter_function(); - - if (file->eof) { - sftp_leave_function(); - return 0; - } - - /* handle an existing request */ - while (msg == NULL) { - if (file->nonblocking){ - if (ssh_channel_poll(sftp->channel, 0) == 0) { - /* we cannot block */ - return SSH_AGAIN; - } - } - - if (sftp_read_and_dispatch(sftp) < 0) { - /* something nasty has happened */ - sftp_leave_function(); - return SSH_ERROR; - } - - msg = sftp_dequeue(sftp,id); - } - - switch (msg->packet_type) { - case SSH_FXP_STATUS: - status = parse_status_msg(msg); - sftp_message_free(msg); - if (status == NULL) { - sftp_leave_function(); - return -1; - } - sftp_set_error(sftp, status->status); - if (status->status != SSH_FX_EOF) { - ssh_set_error(sftp->session, SSH_REQUEST_DENIED, - "SFTP server : %s", status->errormsg); - sftp_leave_function(); - err = SSH_ERROR; - } else { - file->eof = 1; - } - status_msg_free(status); - sftp_leave_function(); - return err; - case SSH_FXP_DATA: - datastring = buffer_get_ssh_string(msg->payload); - sftp_message_free(msg); - if (datastring == NULL) { - ssh_set_error(sftp->session, SSH_FATAL, - "Received invalid DATA packet from sftp server"); - sftp_leave_function(); - return SSH_ERROR; - } - if (ssh_string_len(datastring) > size) { - ssh_set_error(sftp->session, SSH_FATAL, - "Received a too big DATA packet from sftp server: " - "%zu and asked for %u", - ssh_string_len(datastring), size); - ssh_string_free(datastring); - sftp_leave_function(); - return SSH_ERROR; - } - len = ssh_string_len(datastring); - //handle->offset+=len; - /* We already have set the offset previously. All we can do is warn that the expected len - * and effective lengths are different */ - memcpy(data, ssh_string_data(datastring), len); - ssh_string_free(datastring); - sftp_leave_function(); - return len; - default: - ssh_set_error(sftp->session,SSH_FATAL,"Received message %d during read!",msg->packet_type); - sftp_message_free(msg); - sftp_leave_function(); - return SSH_ERROR; - } - - sftp_leave_function(); - return SSH_ERROR; -} - -ssize_t sftp_write(sftp_file file, const void *buf, size_t count) { - sftp_session sftp = file->sftp; - sftp_message msg = NULL; - sftp_status_message status; - ssh_string datastring; - ssh_buffer buffer; - uint32_t id; - int len; - int packetlen; - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(sftp->session); - return -1; - } - - datastring = ssh_string_new(count); - if (datastring == NULL) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - return -1; - } - ssh_string_fill(datastring, buf, count); - - id = sftp_get_new_id(file->sftp); - if (buffer_add_u32(buffer, id) < 0 || - buffer_add_ssh_string(buffer, file->handle) < 0 || - buffer_add_u64(buffer, htonll(file->offset)) < 0 || - buffer_add_ssh_string(buffer, datastring) < 0) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - ssh_string_free(datastring); - return -1; - } - ssh_string_free(datastring); - len = sftp_packet_write(file->sftp, SSH_FXP_WRITE, buffer); - packetlen=ssh_buffer_get_len(buffer); - ssh_buffer_free(buffer); - if (len < 0) { - return -1; - } else if (len != packetlen) { - ssh_log(sftp->session, SSH_LOG_PACKET, - "Could not write as much data as expected"); - } - - while (msg == NULL) { - if (sftp_read_and_dispatch(file->sftp) < 0) { - /* something nasty has happened */ - return -1; - } - msg = sftp_dequeue(file->sftp, id); - } - - switch (msg->packet_type) { - case SSH_FXP_STATUS: - status = parse_status_msg(msg); - sftp_message_free(msg); - if (status == NULL) { - return -1; - } - sftp_set_error(sftp, status->status); - switch (status->status) { - case SSH_FX_OK: - file->offset += count; - status_msg_free(status); - return count; - default: - break; - } - ssh_set_error(sftp->session, SSH_REQUEST_DENIED, - "SFTP server: %s", status->errormsg); - file->offset += count; - status_msg_free(status); - return -1; - default: - ssh_set_error(sftp->session, SSH_FATAL, - "Received message %d during write!", msg->packet_type); - sftp_message_free(msg); - return -1; - } - - return -1; /* not reached */ -} - -/* Seek to a specific location in a file. */ -int sftp_seek(sftp_file file, uint32_t new_offset) { - if (file == NULL) { - return -1; - } - - file->offset = new_offset; - - return 0; -} - -int sftp_seek64(sftp_file file, uint64_t new_offset) { - if (file == NULL) { - return -1; - } - - file->offset = new_offset; - - return 0; -} - -/* Report current byte position in file. */ -unsigned long sftp_tell(sftp_file file) { - return (unsigned long)file->offset; -} -/* Report current byte position in file. */ -uint64_t sftp_tell64(sftp_file file) { - return (uint64_t) file->offset; -} - -/* Rewinds the position of the file pointer to the beginning of the file.*/ -void sftp_rewind(sftp_file file) { - file->offset = 0; -} - -/* code written by Nick */ -int sftp_unlink(sftp_session sftp, const char *file) { - sftp_status_message status = NULL; - sftp_message msg = NULL; - ssh_string filename; - ssh_buffer buffer; - uint32_t id; - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(sftp->session); - return -1; - } - - filename = ssh_string_from_char(file); - if (filename == NULL) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - return -1; - } - - id = sftp_get_new_id(sftp); - if (buffer_add_u32(buffer, id) < 0 || - buffer_add_ssh_string(buffer, filename) < 0) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - ssh_string_free(filename); - } - if (sftp_packet_write(sftp, SSH_FXP_REMOVE, buffer) < 0) { - ssh_buffer_free(buffer); - ssh_string_free(filename); - } - ssh_string_free(filename); - ssh_buffer_free(buffer); - - while (msg == NULL) { - if (sftp_read_and_dispatch(sftp)) { - return -1; - } - msg = sftp_dequeue(sftp, id); - } - - if (msg->packet_type == SSH_FXP_STATUS) { - /* by specification, this command's only supposed to return SSH_FXP_STATUS */ - status = parse_status_msg(msg); - sftp_message_free(msg); - if (status == NULL) { - return -1; - } - sftp_set_error(sftp, status->status); - switch (status->status) { - case SSH_FX_OK: - status_msg_free(status); - return 0; - default: - break; - } - - /* - * The status should be SSH_FX_OK if the command was successful, if it - * didn't, then there was an error - */ - ssh_set_error(sftp->session, SSH_REQUEST_DENIED, - "SFTP server: %s", status->errormsg); - status_msg_free(status); - return -1; - } else { - ssh_set_error(sftp->session,SSH_FATAL, - "Received message %d when attempting to remove file", msg->packet_type); - sftp_message_free(msg); - } - - return -1; -} - -/* code written by Nick */ -int sftp_rmdir(sftp_session sftp, const char *directory) { - sftp_status_message status = NULL; - sftp_message msg = NULL; - ssh_string filename; - ssh_buffer buffer; - uint32_t id; - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(sftp->session); - return -1; - } - - filename = ssh_string_from_char(directory); - if (filename == NULL) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - return -1; - } - - id = sftp_get_new_id(sftp); - if (buffer_add_u32(buffer, id) < 0 || - buffer_add_ssh_string(buffer, filename) < 0) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - ssh_string_free(filename); - return -1; - } - if (sftp_packet_write(sftp, SSH_FXP_RMDIR, buffer) < 0) { - ssh_buffer_free(buffer); - ssh_string_free(filename); - return -1; - } - ssh_buffer_free(buffer); - ssh_string_free(filename); - - while (msg == NULL) { - if (sftp_read_and_dispatch(sftp) < 0) { - return -1; - } - msg = sftp_dequeue(sftp, id); - } - - /* By specification, this command returns SSH_FXP_STATUS */ - if (msg->packet_type == SSH_FXP_STATUS) { - status = parse_status_msg(msg); - sftp_message_free(msg); - if (status == NULL) { - return -1; - } - sftp_set_error(sftp, status->status); - switch (status->status) { - case SSH_FX_OK: - status_msg_free(status); - return 0; - break; - default: - break; - } - ssh_set_error(sftp->session, SSH_REQUEST_DENIED, - "SFTP server: %s", status->errormsg); - status_msg_free(status); - return -1; - } else { - ssh_set_error(sftp->session, SSH_FATAL, - "Received message %d when attempting to remove directory", - msg->packet_type); - sftp_message_free(msg); - } - - return -1; -} - -/* Code written by Nick */ -int sftp_mkdir(sftp_session sftp, const char *directory, mode_t mode) { - sftp_status_message status = NULL; - sftp_message msg = NULL; - sftp_attributes errno_attr = NULL; - struct sftp_attributes_struct attr; - ssh_buffer buffer; - ssh_string path; - uint32_t id; - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(sftp->session); - return -1; - } - - path = ssh_string_from_char(directory); - if (path == NULL) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - return -1; - } - - ZERO_STRUCT(attr); - attr.permissions = mode; - attr.flags = SSH_FILEXFER_ATTR_PERMISSIONS; - - id = sftp_get_new_id(sftp); - if (buffer_add_u32(buffer, id) < 0 || - buffer_add_ssh_string(buffer, path) < 0 || - buffer_add_attributes(buffer, &attr) < 0 || - sftp_packet_write(sftp, SSH_FXP_MKDIR, buffer) < 0) { - ssh_buffer_free(buffer); - ssh_string_free(path); - } - ssh_buffer_free(buffer); - ssh_string_free(path); - - while (msg == NULL) { - if (sftp_read_and_dispatch(sftp) < 0) { - return -1; - } - msg = sftp_dequeue(sftp, id); - } - - /* By specification, this command only returns SSH_FXP_STATUS */ - if (msg->packet_type == SSH_FXP_STATUS) { - status = parse_status_msg(msg); - sftp_message_free(msg); - if (status == NULL) { - return -1; - } - sftp_set_error(sftp, status->status); - switch (status->status) { - case SSH_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 is needed here. - */ - errno_attr = sftp_lstat(sftp, directory); - if (errno_attr != NULL) { - SAFE_FREE(errno_attr); - sftp_set_error(sftp, SSH_FX_FILE_ALREADY_EXISTS); - } - break; - case SSH_FX_OK: - status_msg_free(status); - return 0; - break; - default: - break; - } - /* - * The status should be SSH_FX_OK if the command was successful, if it - * didn't, then there was an error - */ - ssh_set_error(sftp->session, SSH_REQUEST_DENIED, - "SFTP server: %s", status->errormsg); - status_msg_free(status); - return -1; - } else { - ssh_set_error(sftp->session, SSH_FATAL, - "Received message %d when attempting to make directory", - msg->packet_type); - sftp_message_free(msg); - } - - return -1; -} - -/* code written by nick */ -int sftp_rename(sftp_session sftp, const char *original, const char *newname) { - sftp_status_message status = NULL; - sftp_message msg = NULL; - ssh_buffer buffer; - ssh_string oldpath; - ssh_string newpath; - uint32_t id; - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(sftp->session); - return -1; - } - - oldpath = ssh_string_from_char(original); - if (oldpath == NULL) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - return -1; - } - - newpath = ssh_string_from_char(newname); - if (newpath == NULL) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - ssh_string_free(oldpath); - return -1; - } - - id = sftp_get_new_id(sftp); - if (buffer_add_u32(buffer, id) < 0 || - buffer_add_ssh_string(buffer, oldpath) < 0 || - buffer_add_ssh_string(buffer, newpath) < 0 || - /* POSIX rename atomically replaces newpath, we should do the same - * only available on >=v4 */ - sftp->version>=4 ? (buffer_add_u32(buffer, SSH_FXF_RENAME_OVERWRITE) < 0):0) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - ssh_string_free(oldpath); - ssh_string_free(newpath); - return -1; - } - if (sftp_packet_write(sftp, SSH_FXP_RENAME, buffer) < 0) { - ssh_buffer_free(buffer); - ssh_string_free(oldpath); - ssh_string_free(newpath); - return -1; - } - ssh_buffer_free(buffer); - ssh_string_free(oldpath); - ssh_string_free(newpath); - - while (msg == NULL) { - if (sftp_read_and_dispatch(sftp) < 0) { - return -1; - } - msg = sftp_dequeue(sftp, id); - } - - /* By specification, this command only returns SSH_FXP_STATUS */ - if (msg->packet_type == SSH_FXP_STATUS) { - status = parse_status_msg(msg); - sftp_message_free(msg); - if (status == NULL) { - return -1; - } - sftp_set_error(sftp, status->status); - switch (status->status) { - case SSH_FX_OK: - status_msg_free(status); - return 0; - default: - break; - } - /* - * Status should be SSH_FX_OK if the command was successful, if it didn't, - * then there was an error - */ - ssh_set_error(sftp->session, SSH_REQUEST_DENIED, - "SFTP server: %s", status->errormsg); - status_msg_free(status); - return -1; - } else { - ssh_set_error(sftp->session, SSH_FATAL, - "Received message %d when attempting to rename", - msg->packet_type); - sftp_message_free(msg); - } - - return -1; -} - -/* Code written by Nick */ -/* Set file attributes on a file, directory or symbolic link. */ -int sftp_setstat(sftp_session sftp, const char *file, sftp_attributes attr) { - uint32_t id; - ssh_buffer buffer; - ssh_string path; - sftp_message msg = NULL; - sftp_status_message status = NULL; - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(sftp->session); - return -1; - } - - path = ssh_string_from_char(file); - if (path == NULL) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - return -1; - } - - id = sftp_get_new_id(sftp); - if (buffer_add_u32(buffer, id) < 0 || - buffer_add_ssh_string(buffer, path) < 0 || - buffer_add_attributes(buffer, attr) < 0) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - ssh_string_free(path); - return -1; - } - if (sftp_packet_write(sftp, SSH_FXP_SETSTAT, buffer) < 0) { - ssh_buffer_free(buffer); - ssh_string_free(path); - return -1; - } - ssh_buffer_free(buffer); - ssh_string_free(path); - - while (msg == NULL) { - if (sftp_read_and_dispatch(sftp) < 0) { - return -1; - } - msg = sftp_dequeue(sftp, id); - } - - /* By specification, this command only returns SSH_FXP_STATUS */ - if (msg->packet_type == SSH_FXP_STATUS) { - status = parse_status_msg(msg); - sftp_message_free(msg); - if (status == NULL) { - return -1; - } - sftp_set_error(sftp, status->status); - switch (status->status) { - case SSH_FX_OK: - status_msg_free(status); - return 0; - default: - break; - } - /* - * The status should be SSH_FX_OK if the command was successful, if it - * didn't, then there was an error - */ - ssh_set_error(sftp->session, SSH_REQUEST_DENIED, - "SFTP server: %s", status->errormsg); - status_msg_free(status); - return -1; - } else { - ssh_set_error(sftp->session, SSH_FATAL, - "Received message %d when attempting to set stats", msg->packet_type); - sftp_message_free(msg); - } - - return -1; -} - -/* Change the file owner and group */ -int sftp_chown(sftp_session sftp, const char *file, uid_t owner, gid_t group) { - struct sftp_attributes_struct attr; - ZERO_STRUCT(attr); - - attr.uid = owner; - attr.gid = group; - - attr.flags = SSH_FILEXFER_ATTR_UIDGID; - - return sftp_setstat(sftp, file, &attr); -} - -/* Change permissions of a file */ -int sftp_chmod(sftp_session sftp, const char *file, mode_t mode) { - struct sftp_attributes_struct attr; - ZERO_STRUCT(attr); - attr.permissions = mode; - attr.flags = SSH_FILEXFER_ATTR_PERMISSIONS; - - return sftp_setstat(sftp, file, &attr); -} - -/* Change the last modification and access time of a file. */ -int sftp_utimes(sftp_session sftp, const char *file, - const struct timeval *times) { - struct sftp_attributes_struct attr; - ZERO_STRUCT(attr); - - attr.atime = times[0].tv_sec; - attr.atime_nseconds = times[0].tv_usec; - - attr.mtime = times[1].tv_sec; - attr.mtime_nseconds = times[1].tv_usec; - - attr.flags |= SSH_FILEXFER_ATTR_ACCESSTIME | SSH_FILEXFER_ATTR_MODIFYTIME | - SSH_FILEXFER_ATTR_SUBSECOND_TIMES; - - return sftp_setstat(sftp, file, &attr); -} - -int sftp_symlink(sftp_session sftp, const char *target, const char *dest) { - sftp_status_message status = NULL; - sftp_message msg = NULL; - ssh_string target_s; - ssh_string dest_s; - ssh_buffer buffer; - uint32_t id; - - if (sftp == NULL) - return -1; - if (target == NULL || dest == NULL) { - ssh_set_error_invalid(sftp->session, __FUNCTION__); - return -1; - } - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(sftp->session); - return -1; - } - - target_s = ssh_string_from_char(target); - if (target_s == NULL) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - return -1; - } - - dest_s = ssh_string_from_char(dest); - if (dest_s == NULL) { - ssh_set_error_oom(sftp->session); - ssh_string_free(target_s); - ssh_buffer_free(buffer); - return -1; - } - - id = sftp_get_new_id(sftp); - if (buffer_add_u32(buffer, id) < 0) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - ssh_string_free(dest_s); - ssh_string_free(target_s); - return -1; - } - if (ssh_get_openssh_version(sftp->session)) { - /* TODO check for version number if they ever fix it. */ - if (buffer_add_ssh_string(buffer, target_s) < 0 || - buffer_add_ssh_string(buffer, dest_s) < 0) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - ssh_string_free(dest_s); - ssh_string_free(target_s); - return -1; - } - } else { - if (buffer_add_ssh_string(buffer, dest_s) < 0 || - buffer_add_ssh_string(buffer, target_s) < 0) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - ssh_string_free(dest_s); - ssh_string_free(target_s); - return -1; - } - } - - if (sftp_packet_write(sftp, SSH_FXP_SYMLINK, buffer) < 0) { - ssh_buffer_free(buffer); - ssh_string_free(dest_s); - ssh_string_free(target_s); - return -1; - } - ssh_buffer_free(buffer); - ssh_string_free(dest_s); - ssh_string_free(target_s); - - while (msg == NULL) { - if (sftp_read_and_dispatch(sftp) < 0) { - return -1; - } - msg = sftp_dequeue(sftp, id); - } - - /* By specification, this command only returns SSH_FXP_STATUS */ - if (msg->packet_type == SSH_FXP_STATUS) { - status = parse_status_msg(msg); - sftp_message_free(msg); - if (status == NULL) { - return -1; - } - sftp_set_error(sftp, status->status); - switch (status->status) { - case SSH_FX_OK: - status_msg_free(status); - return 0; - default: - break; - } - /* - * The status should be SSH_FX_OK if the command was successful, if it - * didn't, then there was an error - */ - ssh_set_error(sftp->session, SSH_REQUEST_DENIED, - "SFTP server: %s", status->errormsg); - status_msg_free(status); - return -1; - } else { - ssh_set_error(sftp->session, SSH_FATAL, - "Received message %d when attempting to set stats", msg->packet_type); - sftp_message_free(msg); - } - - return -1; -} - -char *sftp_readlink(sftp_session sftp, const char *path) { - sftp_status_message status = NULL; - sftp_message msg = NULL; - ssh_string path_s = NULL; - ssh_string link_s = NULL; - ssh_buffer buffer; - char *lnk; - uint32_t ignored; - uint32_t id; - - if (sftp == NULL) - return NULL; - if (path == NULL) { - ssh_set_error_invalid(sftp, __FUNCTION__); - return NULL; - } - if (sftp->version < 3){ - ssh_set_error(sftp,SSH_REQUEST_DENIED,"sftp version %d does not support sftp_readlink",sftp->version); - return NULL; - } - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(sftp->session); - return NULL; - } - - path_s = ssh_string_from_char(path); - if (path_s == NULL) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - return NULL; - } - - id = sftp_get_new_id(sftp); - if (buffer_add_u32(buffer, id) < 0 || - buffer_add_ssh_string(buffer, path_s) < 0) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - ssh_string_free(path_s); - return NULL; - } - if (sftp_packet_write(sftp, SSH_FXP_READLINK, buffer) < 0) { - ssh_buffer_free(buffer); - ssh_string_free(path_s); - return NULL; - } - ssh_buffer_free(buffer); - ssh_string_free(path_s); - - while (msg == NULL) { - if (sftp_read_and_dispatch(sftp) < 0) { - return NULL; - } - msg = sftp_dequeue(sftp, id); - } - - if (msg->packet_type == SSH_FXP_NAME) { - /* we don't care about "count" */ - buffer_get_u32(msg->payload, &ignored); - /* we only care about the file name string */ - link_s = buffer_get_ssh_string(msg->payload); - sftp_message_free(msg); - if (link_s == NULL) { - /* TODO: what error to set here? */ - return NULL; - } - lnk = ssh_string_to_char(link_s); - ssh_string_free(link_s); - - return lnk; - } else if (msg->packet_type == SSH_FXP_STATUS) { /* bad response (error) */ - status = parse_status_msg(msg); - sftp_message_free(msg); - if (status == NULL) { - return NULL; - } - ssh_set_error(sftp->session, SSH_REQUEST_DENIED, - "SFTP server: %s", status->errormsg); - status_msg_free(status); - } else { /* this shouldn't happen */ - ssh_set_error(sftp->session, SSH_FATAL, - "Received message %d when attempting to set stats", msg->packet_type); - sftp_message_free(msg); - } - - return NULL; -} - -static sftp_statvfs_t sftp_parse_statvfs(sftp_session sftp, ssh_buffer buf) { - sftp_statvfs_t statvfs; - uint64_t tmp; - int ok = 0; - - statvfs = malloc(sizeof(struct sftp_statvfs_struct)); - if (statvfs == NULL) { - ssh_set_error_oom(sftp->session); - return NULL; - } - ZERO_STRUCTP(statvfs); - - /* try .. catch */ - do { - /* file system block size */ - if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) { - break; - } - statvfs->f_bsize = ntohll(tmp); - - /* fundamental fs block size */ - if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) { - break; - } - statvfs->f_frsize = ntohll(tmp); - - /* number of blocks (unit f_frsize) */ - if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) { - break; - } - statvfs->f_blocks = ntohll(tmp); - - /* free blocks in file system */ - if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) { - break; - } - statvfs->f_bfree = ntohll(tmp); - - /* free blocks for non-root */ - if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) { - break; - } - statvfs->f_bavail = ntohll(tmp); - - /* total file inodes */ - if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) { - break; - } - statvfs->f_files = ntohll(tmp); - - /* free file inodes */ - if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) { - break; - } - statvfs->f_ffree = ntohll(tmp); - - /* free file inodes for to non-root */ - if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) { - break; - } - statvfs->f_favail = ntohll(tmp); - - /* file system id */ - if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) { - break; - } - statvfs->f_fsid = ntohll(tmp); - - /* bit mask of f_flag values */ - if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) { - break; - } - statvfs->f_flag = ntohll(tmp); - - /* maximum filename length */ - if (buffer_get_u64(buf, &tmp) != sizeof(uint64_t)) { - break; - } - statvfs->f_namemax = ntohll(tmp); - - ok = 1; - } while(0); - - if (!ok) { - SAFE_FREE(statvfs); - ssh_set_error(sftp->session, SSH_FATAL, "Invalid statvfs structure"); - return NULL; - } - - return statvfs; -} - -sftp_statvfs_t sftp_statvfs(sftp_session sftp, const char *path) { - sftp_status_message status = NULL; - sftp_message msg = NULL; - ssh_string pathstr; - ssh_string ext; - ssh_buffer buffer; - uint32_t id; - - if (sftp == NULL) - return NULL; - if (path == NULL) { - ssh_set_error_invalid(sftp->session, __FUNCTION__); - return NULL; - } - if (sftp->version < 3){ - ssh_set_error(sftp,SSH_REQUEST_DENIED,"sftp version %d does not support sftp_statvfs",sftp->version); - return NULL; - } - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(sftp->session); - return NULL; - } - - ext = ssh_string_from_char("statvfs@openssh.com"); - if (ext == NULL) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - return NULL; - } - - pathstr = ssh_string_from_char(path); - if (pathstr == NULL) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - ssh_string_free(ext); - return NULL; - } - - id = sftp_get_new_id(sftp); - if (buffer_add_u32(buffer, id) < 0 || - buffer_add_ssh_string(buffer, ext) < 0 || - buffer_add_ssh_string(buffer, pathstr) < 0) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - ssh_string_free(ext); - ssh_string_free(pathstr); - return NULL; - } - if (sftp_packet_write(sftp, SSH_FXP_EXTENDED, buffer) < 0) { - ssh_buffer_free(buffer); - ssh_string_free(ext); - ssh_string_free(pathstr); - return NULL; - } - ssh_buffer_free(buffer); - ssh_string_free(ext); - ssh_string_free(pathstr); - - while (msg == NULL) { - if (sftp_read_and_dispatch(sftp) < 0) { - return NULL; - } - msg = sftp_dequeue(sftp, id); - } - - if (msg->packet_type == SSH_FXP_EXTENDED_REPLY) { - sftp_statvfs_t buf = sftp_parse_statvfs(sftp, msg->payload); - sftp_message_free(msg); - if (buf == NULL) { - return NULL; - } - - return buf; - } else if (msg->packet_type == SSH_FXP_STATUS) { /* bad response (error) */ - status = parse_status_msg(msg); - sftp_message_free(msg); - if (status == NULL) { - return NULL; - } - ssh_set_error(sftp->session, SSH_REQUEST_DENIED, - "SFTP server: %s", status->errormsg); - status_msg_free(status); - } else { /* this shouldn't happen */ - ssh_set_error(sftp->session, SSH_FATAL, - "Received message %d when attempting to get statvfs", msg->packet_type); - sftp_message_free(msg); - } - - return NULL; -} - -sftp_statvfs_t sftp_fstatvfs(sftp_file file) { - sftp_status_message status = NULL; - sftp_message msg = NULL; - sftp_session sftp; - ssh_string ext; - ssh_buffer buffer; - uint32_t id; - - if (file == NULL) { - return NULL; - } - sftp = file->sftp; - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(sftp->session); - return NULL; - } - - ext = ssh_string_from_char("fstatvfs@openssh.com"); - if (ext == NULL) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - return NULL; - } - - id = sftp_get_new_id(sftp); - if (buffer_add_u32(buffer, id) < 0 || - buffer_add_ssh_string(buffer, ext) < 0 || - buffer_add_ssh_string(buffer, file->handle) < 0) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - ssh_string_free(ext); - return NULL; - } - if (sftp_packet_write(sftp, SSH_FXP_EXTENDED, buffer) < 0) { - ssh_buffer_free(buffer); - ssh_string_free(ext); - return NULL; - } - ssh_buffer_free(buffer); - ssh_string_free(ext); - - while (msg == NULL) { - if (sftp_read_and_dispatch(sftp) < 0) { - return NULL; - } - msg = sftp_dequeue(sftp, id); - } - - if (msg->packet_type == SSH_FXP_EXTENDED_REPLY) { - sftp_statvfs_t buf = sftp_parse_statvfs(sftp, msg->payload); - sftp_message_free(msg); - if (buf == NULL) { - return NULL; - } - - return buf; - } else if (msg->packet_type == SSH_FXP_STATUS) { /* bad response (error) */ - status = parse_status_msg(msg); - sftp_message_free(msg); - if (status == NULL) { - return NULL; - } - ssh_set_error(sftp->session, SSH_REQUEST_DENIED, - "SFTP server: %s", status->errormsg); - status_msg_free(status); - } else { /* this shouldn't happen */ - ssh_set_error(sftp->session, SSH_FATAL, - "Received message %d when attempting to set stats", msg->packet_type); - sftp_message_free(msg); - } - - return NULL; -} - -void sftp_statvfs_free(sftp_statvfs_t statvfs) { - if (statvfs == NULL) { - return; - } - - SAFE_FREE(statvfs); -} - -/* another code written by Nick */ -char *sftp_canonicalize_path(sftp_session sftp, const char *path) { - sftp_status_message status = NULL; - sftp_message msg = NULL; - ssh_string name = NULL; - ssh_string pathstr; - ssh_buffer buffer; - char *cname; - uint32_t ignored; - uint32_t id; - - if (sftp == NULL) - return NULL; - if (path == NULL) { - ssh_set_error_invalid(sftp->session, __FUNCTION__); - return NULL; - } - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(sftp->session); - return NULL; - } - - pathstr = ssh_string_from_char(path); - if (pathstr == NULL) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - return NULL; - } - - id = sftp_get_new_id(sftp); - if (buffer_add_u32(buffer, id) < 0 || - buffer_add_ssh_string(buffer, pathstr) < 0) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - ssh_string_free(pathstr); - return NULL; - } - if (sftp_packet_write(sftp, SSH_FXP_REALPATH, buffer) < 0) { - ssh_buffer_free(buffer); - ssh_string_free(pathstr); - return NULL; - } - ssh_buffer_free(buffer); - ssh_string_free(pathstr); - - while (msg == NULL) { - if (sftp_read_and_dispatch(sftp) < 0) { - return NULL; - } - msg = sftp_dequeue(sftp, id); - } - - if (msg->packet_type == SSH_FXP_NAME) { - /* we don't care about "count" */ - buffer_get_u32(msg->payload, &ignored); - /* we only care about the file name string */ - name = buffer_get_ssh_string(msg->payload); - sftp_message_free(msg); - if (name == NULL) { - /* TODO: error message? */ - return NULL; - } - cname = ssh_string_to_char(name); - ssh_string_free(name); - if (cname == NULL) { - ssh_set_error_oom(sftp->session); - } - return cname; - } else if (msg->packet_type == SSH_FXP_STATUS) { /* bad response (error) */ - status = parse_status_msg(msg); - sftp_message_free(msg); - if (status == NULL) { - return NULL; - } - ssh_set_error(sftp->session, SSH_REQUEST_DENIED, - "SFTP server: %s", status->errormsg); - status_msg_free(status); - } else { /* this shouldn't happen */ - ssh_set_error(sftp->session, SSH_FATAL, - "Received message %d when attempting to set stats", msg->packet_type); - sftp_message_free(msg); - } - - return NULL; -} - -static sftp_attributes sftp_xstat(sftp_session sftp, const char *path, - int param) { - sftp_status_message status = NULL; - sftp_message msg = NULL; - ssh_string pathstr; - ssh_buffer buffer; - uint32_t id; - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(sftp->session); - return NULL; - } - - pathstr = ssh_string_from_char(path); - if (pathstr == NULL) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - return NULL; - } - - id = sftp_get_new_id(sftp); - if (buffer_add_u32(buffer, id) < 0 || - buffer_add_ssh_string(buffer, pathstr) < 0) { - ssh_set_error_oom(sftp->session); - ssh_buffer_free(buffer); - ssh_string_free(pathstr); - return NULL; - } - if (sftp_packet_write(sftp, param, buffer) < 0) { - ssh_buffer_free(buffer); - ssh_string_free(pathstr); - return NULL; - } - ssh_buffer_free(buffer); - ssh_string_free(pathstr); - - while (msg == NULL) { - if (sftp_read_and_dispatch(sftp) < 0) { - return NULL; - } - msg = sftp_dequeue(sftp, id); - } - - if (msg->packet_type == SSH_FXP_ATTRS) { - return sftp_parse_attr(sftp, msg->payload, 0); - } else if (msg->packet_type == SSH_FXP_STATUS) { - status = parse_status_msg(msg); - sftp_message_free(msg); - if (status == NULL) { - return NULL; - } - sftp_set_error(sftp, status->status); - ssh_set_error(sftp->session, SSH_REQUEST_DENIED, - "SFTP server: %s", status->errormsg); - status_msg_free(status); - return NULL; - } - ssh_set_error(sftp->session, SSH_FATAL, - "Received mesg %d during stat()", msg->packet_type); - sftp_message_free(msg); - - return NULL; -} - -sftp_attributes sftp_stat(sftp_session session, const char *path) { - return sftp_xstat(session, path, SSH_FXP_STAT); -} - -sftp_attributes sftp_lstat(sftp_session session, const char *path) { - return sftp_xstat(session, path, SSH_FXP_LSTAT); -} - -sftp_attributes sftp_fstat(sftp_file file) { - sftp_status_message status = NULL; - sftp_message msg = NULL; - ssh_buffer buffer; - uint32_t id; - - buffer = ssh_buffer_new(); - if (buffer == NULL) { - ssh_set_error_oom(file->sftp->session); - return NULL; - } - - id = sftp_get_new_id(file->sftp); - if (buffer_add_u32(buffer, id) < 0 || - buffer_add_ssh_string(buffer, file->handle) < 0) { - ssh_set_error_oom(file->sftp->session); - ssh_buffer_free(buffer); - return NULL; - } - if (sftp_packet_write(file->sftp, SSH_FXP_FSTAT, buffer) < 0) { - ssh_buffer_free(buffer); - return NULL; - } - ssh_buffer_free(buffer); - - while (msg == NULL) { - if (sftp_read_and_dispatch(file->sftp) < 0) { - return NULL; - } - msg = sftp_dequeue(file->sftp, id); - } - - if (msg->packet_type == SSH_FXP_ATTRS){ - return sftp_parse_attr(file->sftp, msg->payload, 0); - } else if (msg->packet_type == SSH_FXP_STATUS) { - status = parse_status_msg(msg); - sftp_message_free(msg); - if (status == NULL) { - return NULL; - } - ssh_set_error(file->sftp->session, SSH_REQUEST_DENIED, - "SFTP server: %s", status->errormsg); - status_msg_free(status); - - return NULL; - } - ssh_set_error(file->sftp->session, SSH_FATAL, - "Received msg %d during fstat()", msg->packet_type); - sftp_message_free(msg); - - return NULL; -} - -#endif /* WITH_SFTP */ -/* vim: set ts=2 sw=2 et cindent: */ |