Mercurial > gftp.yaz
diff lib/sshv2.c @ 1:8b1883341c6f
Initial revision
author | masneyb |
---|---|
date | Mon, 05 Aug 2002 19:46:57 +0000 |
parents | |
children | 83090328581e |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lib/sshv2.c Mon Aug 05 19:46:57 2002 +0000 @@ -0,0 +1,1998 @@ +/*****************************************************************************/ +/* sshv2.c - functions that will use the sshv2 protocol */ +/* Copyright (C) 1998-2002 Brian Masney <masneyb@gftp.org> */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; 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; 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; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA */ +/*****************************************************************************/ + +#include "gftp.h" + +#define SSH_MAX_HANDLE_SIZE 256 +#define SSH_MAX_STRING_SIZE 34000 + +typedef struct sshv2_attribs_tag +{ + gint32 flags; + gint64 size; + gint32 uid; + gint32 gid; + gint32 perm; + gint32 atime; + gint32 mtime; +} sshv2_attribs; + +typedef struct sshv2_message_tag +{ + gint32 length; + char command; + char *buffer, + *pos, + *end; +} sshv2_message; + +typedef struct sshv2_params_tag +{ + char handle[SSH_MAX_HANDLE_SIZE + 4]; /* We'll encode the ID in here too */ + int handle_len, + dont_log_status : 1; /* For uploading files */ + sshv2_message message; + + gint32 id, + count; +#ifdef G_HAVE_GINT64 + gint64 offset; +#else + gint32 offset; +#endif + char *read_buffer; +} sshv2_params; + + +static void sshv2_destroy ( gftp_request * request ); +static int sshv2_connect ( gftp_request * request ); +static void sshv2_disconnect ( gftp_request * request ); +static gint32 sshv2_buffer_get_int32 ( gftp_request * request, + sshv2_message * buffer, + int expected_response ); +static char * sshv2_buffer_get_string ( gftp_request * request, + sshv2_message * buffer ); +static int sshv2_send_command ( gftp_request * request, + char type, + char *command, + gint32 len ); +static int sshv2_read_response ( gftp_request * request, + sshv2_message * message, + FILE * fd ); +static void sshv2_message_free ( sshv2_message * message ); +static void sshv2_log_command ( gftp_request * request, + gftp_logging_level level, + char type, + char *message, + size_t length ); +static int sshv2_end_transfer ( gftp_request * request ); +static int sshv2_list_files ( gftp_request * request ); +static int sshv2_get_next_file ( gftp_request * request, + gftp_file * fle, + FILE * fd ); +static int sshv2_chdir ( gftp_request * request, + const char *directory ); +static int sshv2_getcwd ( gftp_request * request ); +static int sshv2_rmdir ( gftp_request * request, + const char *directory ); +static int sshv2_rmfile ( gftp_request * request, + const char *file); +static int sshv2_chmod ( gftp_request * request, + const char *file, + int mode ); +static int sshv2_mkdir ( gftp_request * request, + const char *newdir ); +static int sshv2_rename ( gftp_request * request, + const char *oldname, + const char *newname ); +static int sshv2_set_file_time ( gftp_request * request, + const char *file, + time_t datetime ); +static off_t sshv2_get_file_size ( gftp_request * request, + const char *file ); +static long sshv2_get_file ( gftp_request * request, + const char *filename, + FILE * fd, + off_t startsize ); +static int sshv2_put_file ( gftp_request * request, + const char *filename, + FILE * fd, + off_t startsize, + off_t totalsize ); +static size_t sshv2_get_next_file_chunk ( gftp_request * request, + char *buf, + size_t size ); +static size_t sshv2_put_next_file_chunk ( gftp_request * request, + char *buf, + size_t size ); + +#define SSH_MY_VERSION 3 + +#define SSH_FXP_INIT 1 +#define SSH_FXP_VERSION 2 +#define SSH_FXP_OPEN 3 +#define SSH_FXP_CLOSE 4 +#define SSH_FXP_READ 5 +#define SSH_FXP_WRITE 6 +#define SSH_FXP_LSTAT 7 +#define SSH_FXP_FSTAT 8 +#define SSH_FXP_SETSTAT 9 +#define SSH_FXP_FSETSTAT 10 +#define SSH_FXP_OPENDIR 11 +#define SSH_FXP_READDIR 12 +#define SSH_FXP_REMOVE 13 +#define SSH_FXP_MKDIR 14 +#define SSH_FXP_RMDIR 15 +#define SSH_FXP_REALPATH 16 +#define SSH_FXP_STAT 17 +#define SSH_FXP_RENAME 18 +#define SSH_FXP_STATUS 101 +#define SSH_FXP_HANDLE 102 +#define SSH_FXP_DATA 103 +#define SSH_FXP_NAME 104 +#define SSH_FXP_ATTRS 105 +#define SSH_FXP_EXTENDED 200 +#define SSH_FXP_EXTENDED_REPLY 201 + +#define SSH_FILEXFER_ATTR_SIZE 0x00000001 +#define SSH_FILEXFER_ATTR_UIDGID 0x00000002 +#define SSH_FILEXFER_ATTR_PERMISSIONS 0x00000004 +#define SSH_FILEXFER_ATTR_ACMODTIME 0x00000008 +#define SSH_FILEXFER_ATTR_EXTENDED 0x80000000 + +#define SSH_FXF_READ 0x00000001 +#define SSH_FXF_WRITE 0x00000002 +#define SSH_FXF_APPEND 0x00000004 +#define SSH_FXF_CREAT 0x00000008 +#define SSH_FXF_TRUNC 0x00000010 +#define SSH_FXF_EXCL 0x00000020 + +#define SSH_FX_OK 0 +#define SSH_FX_EOF 1 +#define SSH_FX_NO_SUCH_FILE 2 +#define SSH_FX_PERMISSION_DENIED 3 +#define SSH_FX_FAILURE 4 +#define SSH_FX_BAD_MESSAGE 5 +#define SSH_FX_NO_CONNECTION 6 +#define SSH_FX_CONNECTION_LOST 7 +#define SSH_FX_OP_UNSUPPORTED 8 + + +void +sshv2_init (gftp_request * request) +{ + sshv2_params * params; + + g_return_if_fail (request != NULL); + + request->protonum = GFTP_SSHV2_NUM; + request->init = sshv2_init; + request->destroy = sshv2_destroy; + request->connect = sshv2_connect; + request->disconnect = sshv2_disconnect; + request->get_file = sshv2_get_file; + request->put_file = sshv2_put_file; + request->transfer_file = NULL; + request->get_next_file_chunk = sshv2_get_next_file_chunk; + request->put_next_file_chunk = sshv2_put_next_file_chunk; + request->end_transfer = sshv2_end_transfer; + request->list_files = sshv2_list_files; + request->get_next_file = sshv2_get_next_file; + request->set_data_type = NULL; + request->get_file_size = sshv2_get_file_size; + request->chdir = sshv2_chdir; + request->rmdir = sshv2_rmdir; + request->rmfile = sshv2_rmfile; + request->mkdir = sshv2_mkdir; + request->rename = sshv2_rename; + request->chmod = sshv2_chmod; + request->set_file_time = sshv2_set_file_time; + request->site = NULL; + request->parse_url = NULL; + request->url_prefix = "ssh2"; + request->protocol_name = "SSH2"; + request->need_hostport = 1; + request->need_userpass = ssh_need_userpass; + request->use_cache = 1; + request->use_threads = 1; + request->always_connected = 0; + request->protocol_data = g_malloc0 (sizeof (sshv2_params)); + gftp_set_config_options (request); + + params = request->protocol_data; + params->id = 1; +} + + +static void +sshv2_destroy (gftp_request * request) +{ + g_return_if_fail (request != NULL); + g_return_if_fail (request->protonum == GFTP_SSHV2_NUM); + + g_free (request->protocol_data); + request->protocol_data = NULL; +} + + +static int +sshv2_connect (gftp_request * request) +{ + char **args, *tempstr, pts_name[20], *p1, p2, *exepath, port[6]; + int version, fdm, fds, s[2]; + sshv2_message message; + const gchar *errstr; + pid_t child; + + g_return_val_if_fail (request != NULL, -2); + g_return_val_if_fail (request->protonum == GFTP_SSHV2_NUM, -2); + g_return_val_if_fail (request->hostname != NULL, -2); + + if (request->sockfd != NULL) + return (0); + + request->logging_function (gftp_logging_misc, request->user_data, + _("Opening SSH connection to %s\n"), + request->hostname); + + /* Ugh!! We don't get a login banner from sftp-server, and if we are + using ssh-agent to cache a users password, then we won't receive + any initial text from the server, and we'll block. So I just send a + xsftp server banner over. I hope this works on most Unices */ + + if (request->sftpserv_path == NULL || + *request->sftpserv_path == '\0') + { + p1 = ""; + p2 = ' '; + } + else + { + p1 = request->sftpserv_path; + p2 = '/'; + } + + *port = '\0'; + exepath = g_strdup_printf ("echo -n xsftp ; %s%csftp-server", p1, p2); + args = make_ssh_exec_args (request, exepath, sshv2_use_sftp_subsys, port); + + if (ssh_use_askpass || sshv2_use_sftp_subsys) + { + fdm = fds = 0; + if (socketpair (AF_LOCAL, SOCK_STREAM, 0, s) < 0) + { + request->logging_function (gftp_logging_error, request->user_data, + _("Cannot create a socket pair: %s\n"), + g_strerror (errno)); + return (-2); + } + } + else + { + s[0] = s[1] = 0; + if ((fdm = ptym_open (pts_name)) < 0) + { + request->logging_function (gftp_logging_error, request->user_data, + _("Cannot open master pty %s: %s\n"), pts_name, + g_strerror (errno)); + return (-2); + } + } + + if ((child = fork ()) == 0) + { + setsid (); + if (ssh_use_askpass || sshv2_use_sftp_subsys) + { + close (s[0]); + fds = s[1]; + } + else + { + if ((fds = ptys_open (fdm, pts_name)) < 0) + { + printf ("Cannot open slave pts %s: %s\n", pts_name, + g_strerror (errno)); + return (-1); + } + close (fdm); + } + + tty_raw (fds); + dup2 (fds, 0); + dup2 (fds, 1); + dup2 (fds, 2); + if (!ssh_use_askpass && fds > 2) + close (fds); + execvp (ssh_prog_name != NULL && *ssh_prog_name != '\0' ? + ssh_prog_name : "ssh", args); + + tempstr = _("Error: Cannot execute ssh: "); + write (1, tempstr, strlen (tempstr)); + errstr = g_strerror (errno); + write (1, errstr, strlen (errstr)); + write (1, "\n", 1); + return (-1); + } + else if (child > 0) + { + if (ssh_use_askpass || sshv2_use_sftp_subsys) + { + close (s[1]); + fdm = s[0]; + } + tty_raw (fdm); + if (!sshv2_use_sftp_subsys) + { + tempstr = ssh_start_login_sequence (request, fdm); + if (!tempstr || + !(strlen (tempstr) > 4 && strcmp (tempstr + strlen (tempstr) - 5, + "xsftp") == 0)) + { + request->logging_function (gftp_logging_error, request->user_data, + _("Error: Received wrong init string from server\n")); + g_free (args); + g_free (exepath); + return (-2); + } + g_free (tempstr); + } + g_free (args); + g_free (exepath); + + request->sockfd = request->datafd = fdopen (fdm, "rb"); + request->sockfd_write = fdopen (fdm, "wb"); + + version = htonl (SSH_MY_VERSION); + if (sshv2_send_command (request, SSH_FXP_INIT, (char *) &version, 4) < 0) + return (-2); + + memset (&message, 0, sizeof (message)); + if (sshv2_read_response (request, &message, NULL) != SSH_FXP_VERSION) + { + request->logging_function (gftp_logging_error, request->user_data, + _("Received wrong response from server, disconnecting\n")); + sshv2_message_free (&message); + gftp_disconnect (request); + return (-2); + } + sshv2_message_free (&message); + + request->logging_function (gftp_logging_misc, request->user_data, + _("Successfully logged into SSH server %s\n"), + request->hostname); + } + else + { + request->logging_function (gftp_logging_error, request->user_data, + _("Cannot fork another process: %s\n"), + g_strerror (errno)); + g_free (args); + return (-1); + } + + sshv2_getcwd (request); + if (request->sockfd == NULL) + return (-2); + + return (0); +} + + +static void +sshv2_disconnect (gftp_request * request) +{ + sshv2_params * params; + + g_return_if_fail (request != NULL); + g_return_if_fail (request->protonum == GFTP_SSHV2_NUM); + + params = request->protocol_data; + + if (request->sockfd != NULL) + { + request->logging_function (gftp_logging_misc, request->user_data, + _("Disconnecting from site %s\n"), + request->hostname); + fclose (request->sockfd); + request->sockfd = request->datafd = request->sockfd_write = NULL; + } + + if (params->message.buffer != NULL) + sshv2_message_free (¶ms->message); +} + + +static gint32 +sshv2_buffer_get_int32 (gftp_request * request, sshv2_message * message, + int expected_response) +{ + gint32 ret; + + if (message->end - message->pos < 4) + { + request->logging_function (gftp_logging_error, request->user_data, + _("Received wrong response from server, disconnecting\n")); + sshv2_message_free (message); + gftp_disconnect (request); + return (-2); + } + + memcpy (&ret, message->pos, 4); + ret = ntohl (ret); + message->pos += 4; + + if (expected_response > 0 && ret != expected_response) + { + request->logging_function (gftp_logging_error, request->user_data, + _("Received wrong response from server, disconnecting\n")); + sshv2_message_free (message); + gftp_disconnect (request); + return (-2); + } + + return (ret); +} + + +static char * +sshv2_buffer_get_string (gftp_request * request, sshv2_message * message) +{ + char *string; + gint32 len; + + if ((len = sshv2_buffer_get_int32 (request, message, -1)) < 0) + return (NULL); + + if (len > SSH_MAX_STRING_SIZE || (message->end - message->pos < len)) + { + request->logging_function (gftp_logging_error, request->user_data, + _("Received wrong response from server, disconnecting\n")); + sshv2_message_free (message); + gftp_disconnect (request); + return (NULL); + } + + string = g_malloc (len + 1); + memcpy (string, message->pos, len); + string[len] = '\0'; + message->pos += len; + return (string); +} + + +static int +sshv2_send_command (gftp_request * request, char type, char *command, + gint32 len) +{ + char buf[34000]; + ssize_t wrote; + gint32 clen; + + if (len > 33995) + { + request->logging_function (gftp_logging_error, request->user_data, + _("Error: Message size %d too big\n"), len); + gftp_disconnect (request); + return (-1); + } + + clen = htonl (len + 1); + memcpy (buf, &clen, 4); + buf[4] = type; + memcpy (&buf[5], command, len); + buf[len + 5] = '\0'; + +#ifdef DEBUG + printf ("\rSending: "); + for (wrote=0; wrote<len + 5; wrote++) + printf ("%x ", buf[wrote]); + printf ("\n"); +#endif + + sshv2_log_command (request, gftp_logging_send, type, buf + 5, len); + + wrote = fwrite (buf, len + 5, 1, request->sockfd_write); + if (ferror (request->sockfd_write)) + { + request->logging_function (gftp_logging_error, request->user_data, + _("Error: Could not write to socket: %s\n"), + g_strerror (errno)); + gftp_disconnect (request); + return (-1); + } + + fflush (request->sockfd_write); + if (ferror (request->sockfd_write)) + { + request->logging_function (gftp_logging_error, request->user_data, + _("Error: Could not write to socket: %s\n"), + g_strerror (errno)); + gftp_disconnect (request); + return (-1); + } + return 0; +} + + +static int +sshv2_read_response (gftp_request * request, sshv2_message * message, + FILE * fd) +{ + ssize_t numread; + char buf[5]; + + if (fd == NULL) + fd = request->sockfd; + + numread = fread (buf, 5, 1, fd); + if (ferror (fd)) + { + request->logging_function (gftp_logging_error, request->user_data, + _("Error: Could not read from socket: %s\n"), + g_strerror (errno)); + gftp_disconnect (request); + return (-1); + } + +#ifdef DEBUG + printf ("\rReceived: "); + for (numread=0; numread<5; numread++) + printf ("%x ", buf[numread]); + fflush (stdout); +#endif + + memcpy (&message->length, buf, 4); + message->length = ntohl (message->length); + if (message->length > 34000) + { + request->logging_function (gftp_logging_error, request->user_data, + _("Error: Message size %d too big from server\n"), + message->length); + memset (message, 0, sizeof (*message)); + gftp_disconnect (request); + return (-1); + } + message->command = buf[4]; + message->buffer = g_malloc (message->length); + + message->pos = message->buffer; + message->end = message->buffer + message->length - 1; + + numread = fread (message->buffer, message->length - 1, 1, fd); + if (ferror (fd)) + { + request->logging_function (gftp_logging_error, request->user_data, + _("Error: Could not read from socket: %s\n"), + g_strerror (errno)); + gftp_disconnect (request); + return (-1); + } + message->buffer[message->length - 1] = '\0'; + +#ifdef DEBUG + printf ("\rReceived: "); + for (numread=0; numread<message->length - 1; numread++) + printf ("%x ", message->buffer[numread]); + printf ("\n"); +#endif + + sshv2_log_command (request, gftp_logging_recv, message->command, + message->buffer, message->length); + + return (message->command); +} + + +static void +sshv2_message_free (sshv2_message * message) +{ + if (message->buffer) + g_free (message->buffer); + memset (message, 0, sizeof (*message)); +} + + +static void +sshv2_log_command (gftp_request * request, gftp_logging_level level, + char type, char *message, size_t length) +{ + gint32 id, num, attr, stattype; + char *descr, *pos, oldchar; + sshv2_params * params; + + params = request->protocol_data; + memcpy (&id, message, 4); + id = ntohl (id); + switch (type) + { + case SSH_FXP_INIT: + request->logging_function (level, request->user_data, + _("%d: Protocol Initialization\n"), id); + break; + case SSH_FXP_VERSION: + memcpy (&num, message, 4); + num = ntohl (num); + request->logging_function (level, request->user_data, + _("%d: Protocol version %d\n"), id, num); + break; + case SSH_FXP_OPEN: + memcpy (&num, message + 4, 4); + num = ntohl (num); + pos = message + 12 + num - 1; + oldchar = *pos; + *pos = '\0'; + request->logging_function (level, request->user_data, + _("%d: Open %s\n"), id, message + 8); + *pos = oldchar; + break; + case SSH_FXP_CLOSE: + request->logging_function (level, request->user_data, + _("%d: Close\n"), id); + case SSH_FXP_READ: + case SSH_FXP_WRITE: + break; + case SSH_FXP_OPENDIR: + request->logging_function (level, request->user_data, + _("%d: Open Directory %s\n"), id, + message + 8); + break; + case SSH_FXP_READDIR: + request->logging_function (level, request->user_data, + _("%d: Read Directory\n"), id); + break; + case SSH_FXP_REMOVE: + request->logging_function (level, request->user_data, + _("%d: Remove file %s\n"), id, + message + 8); + break; + case SSH_FXP_MKDIR: + request->logging_function (level, request->user_data, + _("%d: Make directory %s\n"), id, + message + 8); + break; + case SSH_FXP_RMDIR: + request->logging_function (level, request->user_data, + _("%d: Remove directory %s\n"), id, + message + 8); + break; + case SSH_FXP_REALPATH: + request->logging_function (level, request->user_data, + _("%d: Realpath %s\n"), id, + message + 8); + break; + case SSH_FXP_ATTRS: + request->logging_function (level, request->user_data, + _("%d: File attributes\n"), id); + break; + case SSH_FXP_STAT: + request->logging_function (level, request->user_data, + _("%d: Stat %s\n"), id, + message + 8); + break; + case SSH_FXP_SETSTAT: + memcpy (&num, message + 4, 4); + num = ntohl (num); + pos = message + 12 + num - 1; + oldchar = *pos; + *pos = '\0'; + memcpy (&stattype, message + 8 + num, 4); + stattype = ntohl (stattype); + memcpy (&attr, message + 12 + num, 4); + attr = ntohl (attr); + switch (stattype) + { + case SSH_FILEXFER_ATTR_PERMISSIONS: + request->logging_function (level, request->user_data, + _("%d: Chmod %s %o\n"), id, + message + 8, attr); + break; + case SSH_FILEXFER_ATTR_ACMODTIME: + request->logging_function (level, request->user_data, + _("%d: Utime %s %d\n"), id, + message + 8, attr); + } + *pos = oldchar; + break; + case SSH_FXP_STATUS: + if (params->dont_log_status) + break; + memcpy (&num, message + 4, 4); + num = ntohl (num); + switch (num) + { + case SSH_FX_OK: + descr = _("OK"); + break; + case SSH_FX_EOF: + descr = _("EOF"); + break; + case SSH_FX_NO_SUCH_FILE: + descr = _("No such file or directory"); + break; + case SSH_FX_PERMISSION_DENIED: + descr = _("Permission denied"); + break; + case SSH_FX_FAILURE: + descr = _("Failure"); + break; + case SSH_FX_BAD_MESSAGE: + descr = _("Bad message"); + break; + case SSH_FX_NO_CONNECTION: + descr = _("No connection"); + break; + case SSH_FX_CONNECTION_LOST: + descr = _("Connection lost"); + break; + case SSH_FX_OP_UNSUPPORTED: + descr = _("Operation unsupported"); + break; + default: + descr = _("Unknown message returned from server"); + break; + } + request->logging_function (level, request->user_data, + "%d: %s\n", id, descr); + break; + case SSH_FXP_HANDLE: + request->logging_function (level, request->user_data, + "%d: File handle\n", id); + break; + case SSH_FXP_DATA: + break; + case SSH_FXP_NAME: + memcpy (&num, message + 4, 4); + num = ntohl (num); + request->logging_function (level, request->user_data, + "%d: Filenames (%d entries)\n", id, + num); + break; + default: + request->logging_function (level, request->user_data, + "Command: %x\n", type); + } +} + + +static int +sshv2_end_transfer (gftp_request * request) +{ + sshv2_params * params; + sshv2_message message; + gint32 len; + + g_return_val_if_fail (request != NULL, -2); + g_return_val_if_fail (request->protonum == GFTP_SSHV2_NUM, -2); + g_return_val_if_fail (request->datafd != NULL, -2); + + params = request->protocol_data; + if (params->message.buffer != NULL) + { + sshv2_message_free (¶ms->message); + params->count = 0; + } + + if (params->handle_len > 0) + { + len = htonl (params->id++); + memcpy (params->handle, &len, 4); + + if (sshv2_send_command (request, SSH_FXP_CLOSE, params->handle, + params->handle_len) < 0) + return (-2); + + memset (&message, 0, sizeof (message)); + if (sshv2_read_response (request, &message, NULL) != SSH_FXP_STATUS) + { + request->logging_function (gftp_logging_error, request->user_data, + _("Received wrong response from server, disconnecting\n")); + sshv2_message_free (&message); + gftp_disconnect (request); + return (-2); + } + sshv2_message_free (&message); + params->handle_len = 0; + } + + if (params->read_buffer != NULL) + { + g_free (params->read_buffer); + params->read_buffer = NULL; + } + + return (0); +} + + +static int +sshv2_list_files (gftp_request * request) +{ + sshv2_params * params; + sshv2_message message; + char *tempstr; + gint32 len; + + g_return_val_if_fail (request != NULL, -2); + g_return_val_if_fail (request->protonum == GFTP_SSHV2_NUM, -2); + g_return_val_if_fail (request->sockfd != NULL, -2); + + params = request->protocol_data; + + request->logging_function (gftp_logging_misc, request->user_data, + _("Retrieving directory listing...\n")); + + tempstr = g_malloc (strlen (request->directory) + 9); + + len = htonl (params->id++); + memcpy (tempstr, &len, 4); + + len = htonl (strlen (request->directory)); + memcpy (tempstr + 4, &len, 4); + strcpy (tempstr + 8, request->directory); + if (sshv2_send_command (request, SSH_FXP_OPENDIR, tempstr, + strlen (request->directory) + 8) < 0) + { + g_free (tempstr); + return (-2); + } + g_free (tempstr); + + memset (&message, 0, sizeof (message)); + if (sshv2_read_response (request, &message, NULL) != SSH_FXP_HANDLE) + { + request->logging_function (gftp_logging_error, request->user_data, + _("Received wrong response from server, disconnecting\n")); + sshv2_message_free (&message); + gftp_disconnect (request); + return (-2); + } + + if (message.length - 4 > SSH_MAX_HANDLE_SIZE) + { + request->logging_function (gftp_logging_error, request->user_data, + _("Error: Message size %d too big from server\n"), + message.length - 4); + sshv2_message_free (&message); + gftp_disconnect (request); + return (-1); + + } + + memset (params->handle, 0, 4); + memcpy (params->handle + 4, message.buffer + 4, message.length - 5); + params->handle_len = message.length - 1; + sshv2_message_free (&message); + params->count = 0; + return (0); +} + + +static int +sshv2_get_next_file (gftp_request * request, gftp_file * fle, FILE * fd) +{ + gint32 len, attrs, longnamelen; + int ret, i, count, retsize; + sshv2_params *params; + char *longname; + + g_return_val_if_fail (request != NULL, -2); + g_return_val_if_fail (request->protonum == GFTP_SSHV2_NUM, -2); + g_return_val_if_fail (fle != NULL, -2); + + params = request->protocol_data; + + if (request->last_dir_entry) + { + g_free (request->last_dir_entry); + request->last_dir_entry = NULL; + request->last_dir_entry_len = 0; + } + retsize = 0; + + if (params->count > 0) + ret = SSH_FXP_NAME; + else + { + if (!request->cached) + { + if (params->message.buffer != NULL) + sshv2_message_free (¶ms->message); + + len = htonl (params->id++); + memcpy (params->handle, &len, 4); + + if (sshv2_send_command (request, SSH_FXP_READDIR, params->handle, + params->handle_len) < 0) + return (-2); + } + + if ((ret = sshv2_read_response (request, ¶ms->message, fd)) < 0) + return (-2); + + if (!request->cached) + { + request->last_dir_entry = g_malloc (params->message.length + 4); + len = htonl (params->message.length); + memcpy (request->last_dir_entry, &len, 4); + request->last_dir_entry[4] = params->message.command; + memcpy (request->last_dir_entry + 5, params->message.buffer, + params->message.length - 1); + request->last_dir_entry_len = params->message.length + 4; + } + + if (ret == SSH_FXP_NAME) + { + params->message.pos = params->message.buffer + 4; + if ((params->count = sshv2_buffer_get_int32 (request, + ¶ms->message, -1)) < 0) + return (-2); + } + } + + if (ret == SSH_FXP_NAME) + { + if ((len = sshv2_buffer_get_int32 (request, ¶ms->message, -1)) < 0 || + params->message.pos + len > params->message.end) + return (-2); + + params->message.pos += len; + + if ((longnamelen = sshv2_buffer_get_int32 (request, + ¶ms->message, -1)) < 0 || + params->message.pos + longnamelen > params->message.end) + return (-2); + + longname = params->message.pos; + params->message.pos += longnamelen; + + if ((attrs = sshv2_buffer_get_int32 (request, ¶ms->message, -1)) < 0) + return (-2); + + if (attrs & SSH_FILEXFER_ATTR_SIZE) + { + params->message.pos += 8; + if (params->message.pos > params->message.end) + { + request->logging_function (gftp_logging_error, request->user_data, + _("Received wrong response from server, disconnecting\n")); + sshv2_message_free (¶ms->message); + gftp_disconnect (request); + return (-2); + } + } + + if (attrs & SSH_FILEXFER_ATTR_UIDGID) + { + params->message.pos += 8; + if (params->message.pos > params->message.end) + { + request->logging_function (gftp_logging_error, request->user_data, + _("Received wrong response from server, disconnecting\n")); + sshv2_message_free (¶ms->message); + gftp_disconnect (request); + return (-2); + } + } + + if (attrs & SSH_FILEXFER_ATTR_PERMISSIONS) + { + params->message.pos += 4; + if (params->message.pos > params->message.end) + { + request->logging_function (gftp_logging_error, request->user_data, + _("Received wrong response from server, disconnecting\n")); + sshv2_message_free (¶ms->message); + gftp_disconnect (request); + return (-2); + } + } + + if (attrs & SSH_FILEXFER_ATTR_ACMODTIME) + { + params->message.pos += 8; + if (params->message.pos > params->message.end) + { + request->logging_function (gftp_logging_error, request->user_data, + _("Received wrong response from server, disconnecting\n")); + sshv2_message_free (¶ms->message); + gftp_disconnect (request); + return (-2); + } + } + + if (attrs & SSH_FILEXFER_ATTR_EXTENDED) + { + if ((count = sshv2_buffer_get_int32 (request, + ¶ms->message, -1)) < 0) + return (-2); + + for (i=0; i<count; i++) + { + if ((len = sshv2_buffer_get_int32 (request, + ¶ms->message, -1)) < 0 || + params->message.pos + len + 4 > params->message.end) + return (-2); + + params->message.pos += len + 4; + + if ((len = sshv2_buffer_get_int32 (request, + ¶ms->message, -1)) < 0 || + params->message.pos + len + 4 > params->message.end) + return (-2); + + params->message.pos += len + 4; + } + } + + longname[longnamelen] = '\0'; + + /* The commercial SSH2 puts a / and * after some entries */ + if (longname[longnamelen - 1] == '*') + longname[--longnamelen] = '\0'; + if (longname[longnamelen - 1] == '/') + longname[--longnamelen] = '\0'; + + if (gftp_parse_ls (longname, fle) != 0) + { + gftp_file_destroy (fle); + return (-2); + } + retsize = strlen (longname); + + params->count--; + } + else if (ret == SSH_FXP_STATUS) + { + sshv2_message_free (¶ms->message); + return (0); + } + else + { + request->logging_function (gftp_logging_error, request->user_data, + _("Received wrong response from server, disconnecting\n")); + sshv2_message_free (¶ms->message); + gftp_disconnect (request); + return (-2); + } + + return (retsize); +} + + +static int +sshv2_chdir (gftp_request * request, const char *directory) +{ + sshv2_message message; + sshv2_params * params; + char *tempstr, *dir; + gint32 num; + size_t len; + + g_return_val_if_fail (request != NULL, -2); + g_return_val_if_fail (request->protonum == GFTP_SSHV2_NUM, -2); + g_return_val_if_fail (request->datafd != NULL, -2); + + params = request->protocol_data; + if (request->directory != directory) + { + if (*directory == '/') + { + len = strlen (directory) + 8; + tempstr = g_malloc (len + 1); + strcpy (tempstr + 8, directory); + } + else + { + len = strlen (directory) + strlen (request->directory) + 9; + tempstr = g_malloc (len + 1); + strcpy (tempstr + 8, request->directory); + strcat (tempstr + 8, "/"); + strcat (tempstr + 8, directory); + } + + num = htonl (params->id++); + memcpy (tempstr, &num, 4); + + num = htonl (len - 8); + memcpy (tempstr + 4, &num, 4); + if (sshv2_send_command (request, SSH_FXP_REALPATH, tempstr, len) < 0) + { + g_free (tempstr); + return (-2); + } + g_free (tempstr); + + memset (&message, 0, sizeof (message)); + if (sshv2_read_response (request, &message, NULL) != SSH_FXP_NAME) + { + request->logging_function (gftp_logging_error, request->user_data, + _("Received wrong response from server, disconnecting\n")); + sshv2_message_free (&message); + gftp_disconnect (request); + return (-2); + } + + message.pos += 4; + if (sshv2_buffer_get_int32 (request, &message, 1) != 1) + return (-2); + + if ((dir = sshv2_buffer_get_string (request, &message)) == NULL) + return (-2); + + if (request->directory) + g_free (request->directory); + request->directory = dir; + sshv2_message_free (&message); + } + + return (0); +} + + +static int +sshv2_getcwd (gftp_request * request) +{ + sshv2_message message; + sshv2_params * params; + char *tempstr, *dir; + gint32 num; + size_t len; + + g_return_val_if_fail (request != NULL, -2); + g_return_val_if_fail (request->protonum == GFTP_SSHV2_NUM, -2); + g_return_val_if_fail (request->datafd != NULL, -2); + + if (request->directory == NULL || *request->directory == '\0') + dir = "."; + else + dir = request->directory; + + params = request->protocol_data; + len = strlen (dir); + tempstr = g_malloc (len + 9); + strcpy (tempstr + 8, dir); + + num = htonl (params->id++); + memcpy (tempstr, &num, 4); + + num = htonl (len); + memcpy (tempstr + 4, &num, 4); + if (sshv2_send_command (request, SSH_FXP_REALPATH, tempstr, len + 8) < 0) + { + g_free (tempstr); + return (-2); + } + + g_free (tempstr); + if (request->directory) + { + g_free (request->directory); + request->directory = NULL; + } + + memset (&message, 0, sizeof (message)); + if (sshv2_read_response (request, &message, NULL) != SSH_FXP_NAME) + { + request->logging_function (gftp_logging_error, request->user_data, + _("Received wrong response from server, disconnecting\n")); + sshv2_message_free (&message); + gftp_disconnect (request); + return (-2); + } + + message.pos += 4; + if (sshv2_buffer_get_int32 (request, &message, 1) < 0) + return (-2); + + if ((request->directory = sshv2_buffer_get_string (request, &message)) == NULL) + return (-2); + sshv2_message_free (&message); + return (0); +} + + +static int +sshv2_rmdir (gftp_request * request, const char *directory) +{ + sshv2_params * params; + sshv2_message message; + char *tempstr; + gint32 num; + size_t len; + + g_return_val_if_fail (request != NULL, -2); + g_return_val_if_fail (request->protonum == GFTP_SSHV2_NUM, -2); + g_return_val_if_fail (request->datafd != NULL, -2); + g_return_val_if_fail (directory != NULL, -2); + + params = request->protocol_data; + + if (*directory == '/') + { + len = strlen (directory) + 8; + tempstr = g_malloc (len + 1); + strcpy (tempstr + 8, directory); + } + else + { + len = strlen (directory) + strlen (request->directory) + 9; + tempstr = g_malloc (len + 1); + strcpy (tempstr + 8, request->directory); + strcat (tempstr + 8, "/"); + strcat (tempstr + 8, directory); + } + + num = htonl (params->id++); + memcpy (tempstr, &num, 4); + + num = htonl (len - 8); + memcpy (tempstr + 4, &num, 4); + + if (sshv2_send_command (request, SSH_FXP_RMDIR, tempstr, len) < 0) + { + g_free (tempstr); + return (-2); + } + g_free (tempstr); + + memset (&message, 0, sizeof (message)); + if (sshv2_read_response (request, &message, NULL) < 0) + return (-2); + + message.pos += 4; + if (sshv2_buffer_get_int32 (request, &message, SSH_FX_OK) < 0) + return (-2); + + sshv2_message_free (&message); + + return (0); +} + + +static int +sshv2_rmfile (gftp_request * request, const char *file) +{ + sshv2_params * params; + sshv2_message message; + char *tempstr; + gint32 num; + size_t len; + + g_return_val_if_fail (request != NULL, -2); + g_return_val_if_fail (request->protonum == GFTP_SSHV2_NUM, -2); + g_return_val_if_fail (request->datafd != NULL, -2); + g_return_val_if_fail (file != NULL, -2); + + params = request->protocol_data; + + if (*file == '/') + { + len = strlen (file) + 8; + tempstr = g_malloc (len + 1); + strcpy (tempstr + 8, file); + } + else + { + len = strlen (file) + strlen (request->directory) + 9; + tempstr = g_malloc (len + 1); + strcpy (tempstr + 8, request->directory); + strcat (tempstr + 8, "/"); + strcat (tempstr + 8, file); + } + + num = htonl (params->id++); + memcpy (tempstr, &num, 4); + + num = htonl (len - 8); + memcpy (tempstr + 4, &num, 4); + + if (sshv2_send_command (request, SSH_FXP_REMOVE, tempstr, len) < 0) + { + g_free (tempstr); + return (-2); + } + g_free (tempstr); + + memset (&message, 0, sizeof (message)); + if (sshv2_read_response (request, &message, NULL) < 0) + return (-2); + + message.pos += 4; + if (sshv2_buffer_get_int32 (request, &message, SSH_FX_OK) < 0) + return (-2); + + sshv2_message_free (&message); + + return (0); +} + + +static int +sshv2_chmod (gftp_request * request, const char *file, int mode) +{ + char *tempstr, buf[10]; + sshv2_params * params; + sshv2_message message; + gint32 num; + size_t len; + + g_return_val_if_fail (request != NULL, -2); + g_return_val_if_fail (request->protonum == GFTP_SSHV2_NUM, -2); + g_return_val_if_fail (request->datafd != NULL, -2); + g_return_val_if_fail (file != NULL, -2); + + params = request->protocol_data; + + if (*file == '/') + { + len = strlen (file) + 16; + tempstr = g_malloc (len + 1); + strcpy (tempstr + 8, file); + } + else + { + len = strlen (file) + strlen (request->directory) + 17; + tempstr = g_malloc (len + 1); + strcpy (tempstr + 8, request->directory); + strcat (tempstr + 8, "/"); + strcat (tempstr + 8, file); + } + + num = htonl (params->id++); + memcpy (tempstr, &num, 4); + + num = htonl (len - 16); + memcpy (tempstr + 4, &num, 4); + + num = htonl (SSH_FILEXFER_ATTR_PERMISSIONS); + memcpy (tempstr + len - 8, &num, 4); + + g_snprintf (buf, sizeof (buf), "%d", mode); + num = htonl (strtol (buf, NULL, 8)); + memcpy (tempstr + len - 4, &num, 4); + + if (sshv2_send_command (request, SSH_FXP_SETSTAT, tempstr, len) < 0) + { + g_free (tempstr); + return (-2); + } + g_free (tempstr); + + memset (&message, 0, sizeof (message)); + if (sshv2_read_response (request, &message, NULL) < 0) + return (-2); + + message.pos += 4; + if (sshv2_buffer_get_int32 (request, &message, SSH_FX_OK) < 0) + return (-2); + + sshv2_message_free (&message); + + return (0); +} + + +static int +sshv2_mkdir (gftp_request * request, const char *newdir) +{ + sshv2_params * params; + sshv2_message message; + char *tempstr; + gint32 num; + size_t len; + + g_return_val_if_fail (request != NULL, -2); + g_return_val_if_fail (request->protonum == GFTP_SSHV2_NUM, -2); + g_return_val_if_fail (request->datafd != NULL, -2); + g_return_val_if_fail (newdir != NULL, -2); + + params = request->protocol_data; + + if (*newdir == '/') + { + len = strlen (newdir) + 12; + tempstr = g_malloc (len + 1); + strcpy (tempstr + 8, newdir); + } + else + { + len = strlen (newdir) + strlen (request->directory) + 13; + tempstr = g_malloc (len + 1); + strcpy (tempstr + 8, request->directory); + strcat (tempstr + 8, "/"); + strcat (tempstr + 8, newdir); + } + + num = htonl (params->id++); + memcpy (tempstr, &num, 4); + + num = htonl (len - 12); + memcpy (tempstr + 4, &num, 4); + memset (tempstr + len - 4, 0, 4); /* attributes */ + + if (sshv2_send_command (request, SSH_FXP_MKDIR, tempstr, len) < 0) + { + g_free (tempstr); + return (-2); + } + g_free (tempstr); + + memset (&message, 0, sizeof (message)); + if (sshv2_read_response (request, &message, NULL) < 0) + return (-2); + + message.pos += 4; + if (sshv2_buffer_get_int32 (request, &message, SSH_FX_OK) < 0) + return (-2); + + sshv2_message_free (&message); + + return (0); +} + + +static int +sshv2_rename (gftp_request * request, const char *oldname, const char *newname) +{ + char *tempstr, *oldstr, *newstr; + sshv2_params * params; + sshv2_message message; + gint32 num; + size_t oldlen, newlen; + + g_return_val_if_fail (request != NULL, -2); + g_return_val_if_fail (request->protonum == GFTP_SSHV2_NUM, -2); + g_return_val_if_fail (request->datafd != NULL, -2); + g_return_val_if_fail (oldname != NULL, -2); + g_return_val_if_fail (newname != NULL, -2); + + params = request->protocol_data; + + if (*oldname == '/') + { + oldlen = strlen (oldname); + oldstr = g_strconcat (oldname, NULL); + } + else + { + oldlen = strlen (request->directory) + strlen (oldname) + 1; + oldstr = g_strconcat (request->directory, "/", oldname, NULL); + } + + if (*newname == '/') + { + newlen = strlen (newname); + newstr = g_strconcat (newname, NULL); + } + else + { + newlen = strlen (request->directory) + strlen (newname) + 1; + newstr = g_strconcat (request->directory, "/", newname, NULL); + } + + tempstr = g_malloc (oldlen + newlen + 13); + num = htonl (params->id++); + memcpy (tempstr, &num, 4); + + num = htonl (oldlen); + memcpy (tempstr + 4, &num, 4); + strcpy (tempstr + 8, oldstr); + + num = htonl (newlen); + memcpy (tempstr + 8 + oldlen, &num, 4); + strcpy (tempstr + 12 + oldlen, newstr); + + if (sshv2_send_command (request, SSH_FXP_RENAME, tempstr, oldlen + newlen + 12) < 0) + { + g_free (tempstr); + return (-2); + } + g_free (tempstr); + + memset (&message, 0, sizeof (message)); + if (sshv2_read_response (request, &message, NULL) < 0) + return (-2); + + message.pos += 4; + if (sshv2_buffer_get_int32 (request, &message, SSH_FX_OK) < 0) + return (-2); + + sshv2_message_free (&message); + + return (0); +} + + +static int +sshv2_set_file_time (gftp_request * request, const char *file, time_t datetime) +{ + sshv2_params * params; + sshv2_message message; + char *tempstr; + gint32 num; + size_t len; + + g_return_val_if_fail (request != NULL, -2); + g_return_val_if_fail (request->protonum == GFTP_SSHV2_NUM, -2); + g_return_val_if_fail (request->datafd != NULL, -2); + g_return_val_if_fail (file != NULL, -2); + + params = request->protocol_data; + + if (*file == '/') + { + len = strlen (file) + 20; + tempstr = g_malloc (len + 1); + strcpy (tempstr + 8, file); + } + else + { + len = strlen (file) + strlen (request->directory) + 21; + tempstr = g_malloc (len + 1); + strcpy (tempstr + 8, request->directory); + strcat (tempstr + 8, "/"); + strcat (tempstr + 8, file); + } + + num = htonl (params->id++); + memcpy (tempstr, &num, 4); + + num = htonl (len - 20); + memcpy (tempstr + 4, &num, 4); + + num = htonl (SSH_FILEXFER_ATTR_ACMODTIME); + memcpy (tempstr + len - 12, &num, 4); + + num = htonl (datetime); + memcpy (tempstr + len - 8, &num, 4); + + num = htonl (datetime); + memcpy (tempstr + len - 4, &num, 4); + + if (sshv2_send_command (request, SSH_FXP_SETSTAT, tempstr, len) < 0) + { + g_free (tempstr); + return (-2); + } + g_free (tempstr); + + memset (&message, 0, sizeof (message)); + if (sshv2_read_response (request, &message, NULL) < 0) + return (-2); + + message.pos += 4; + if (sshv2_buffer_get_int32 (request, &message, SSH_FX_OK) < 0) + return (-2); + + sshv2_message_free (&message); + + return (0); +} + + +static off_t +sshv2_get_file_size (gftp_request * request, const char *file) +{ + gint32 len, highnum, lownum, attrs, num; + sshv2_params * params; + char *tempstr; +#ifdef G_HAVE_GINT64 + gint64 ret; +#endif + + g_return_val_if_fail (request != NULL, -2); + g_return_val_if_fail (request->protonum == GFTP_SSHV2_NUM, -2); + g_return_val_if_fail (request->datafd != NULL, -2); + g_return_val_if_fail (file != NULL, -2); + + params = request->protocol_data; + + if (*file == '/') + { + len = strlen (file); + tempstr = g_malloc (len + 9); + strcpy (tempstr + 8, file); + } + else + { + len = strlen (file) + strlen (request->directory) + 1; + tempstr = g_malloc (len + 9); + strcpy (tempstr + 8, request->directory); + strcat (tempstr + 8, "/"); + strcat (tempstr + 8, file); + } + + num = htonl (params->id++); + memcpy (tempstr, &num, 4); + + num = htonl (len); + memcpy (tempstr + 4, &num, 4); + + if (sshv2_send_command (request, SSH_FXP_STAT, tempstr, len + 8) < 0) + { + g_free (tempstr); + return (-2); + } + g_free (tempstr); + + memset (¶ms->message, 0, sizeof (params->message)); + if (sshv2_read_response (request, ¶ms->message, NULL) != SSH_FXP_ATTRS) + { + request->logging_function (gftp_logging_error, request->user_data, + _("Received wrong response from server, disconnecting\n")); + sshv2_message_free (¶ms->message); + gftp_disconnect (request); + return (-2); + } + + if (params->message.length < 5) + return (-2); + params->message.pos += 4; + + if ((attrs = sshv2_buffer_get_int32 (request, ¶ms->message, -1)) < 0) + return (-2); + + if (attrs & SSH_FILEXFER_ATTR_SIZE) + { + if ((highnum = sshv2_buffer_get_int32 (request, ¶ms->message, -1)) < 0) + return (-2); + + if ((lownum = sshv2_buffer_get_int32 (request, ¶ms->message, -1)) < 0) + return (-2); + + sshv2_message_free (¶ms->message); + +#if G_HAVE_GINT64 + ret = (gint64) lownum | ((gint64) highnum >> 32); + return (ret); +#else + return (lownum); +#endif + } + + sshv2_message_free (¶ms->message); + + return (0); + +} + + +static long +sshv2_get_file (gftp_request * request, const char *file, FILE * fd, + off_t startsize) +{ + sshv2_params * params; + sshv2_message message; + char *tempstr; + size_t stlen; + gint32 num; + + g_return_val_if_fail (request != NULL, -2); + g_return_val_if_fail (request->protonum == GFTP_SSHV2_NUM, -2); + g_return_val_if_fail (request->sockfd != NULL, -2); + /* fd ignored for this protocol */ + + params = request->protocol_data; + params->offset = startsize; + + if (*file == '/') + { + stlen = strlen (file); + tempstr = g_malloc (stlen + 16); + strcpy (tempstr + 8, file); + } + else + { + stlen = strlen (file) + strlen (request->directory) + 1; + tempstr = g_malloc (stlen + 16); + strcpy (tempstr + 8, request->directory); + strcat (tempstr + 8, "/"); + strcat (tempstr + 8, file); + } + + num = htonl (params->id++); + memcpy (tempstr, &num, 4); + + num = htonl (stlen); + memcpy (tempstr + 4, &num, 4); + + num = htonl (SSH_FXF_READ); + memcpy (tempstr + 8 + stlen, &num, 4); + + memset (tempstr + 12 + stlen, 0, 4); + + if (sshv2_send_command (request, SSH_FXP_OPEN, tempstr, stlen + 16) < 0) + { + g_free (tempstr); + return (-2); + } + + g_free (tempstr); + memset (&message, 0, sizeof (message)); + if (sshv2_read_response (request, &message, NULL) != SSH_FXP_HANDLE) + { + request->logging_function (gftp_logging_error, request->user_data, + _("Received wrong response from server, disconnecting\n")); + sshv2_message_free (&message); + gftp_disconnect (request); + return (-2); + } + + if (message.length - 4 > SSH_MAX_HANDLE_SIZE) + { + request->logging_function (gftp_logging_error, request->user_data, + _("Error: Message size %d too big from server\n"), + message.length - 4); + sshv2_message_free (&message); + gftp_disconnect (request); + return (-1); + + } + + memset (params->handle, 0, 4); + memcpy (params->handle + 4, message.buffer+ 4, message.length - 5); + params->handle_len = message.length - 1; + sshv2_message_free (&message); + + return (sshv2_get_file_size (request, file)); +} + + +static int +sshv2_put_file (gftp_request * request, const char *file, FILE * fd, + off_t startsize, off_t totalsize) +{ + sshv2_params * params; + sshv2_message message; + char *tempstr; + size_t stlen; + gint32 num; + + g_return_val_if_fail (request != NULL, -2); + g_return_val_if_fail (request->protonum == GFTP_SSHV2_NUM, -2); + g_return_val_if_fail (request->sockfd != NULL, -2); + /* fd ignored for this protocol */ + + params = request->protocol_data; + params->offset = 0; + + if (*file == '/') + { + stlen = strlen (file); + tempstr = g_malloc (stlen + 16); + strcpy (tempstr + 8, file); + } + else + { + stlen = strlen (file) + strlen (request->directory) + 1; + tempstr = g_malloc (stlen + 16); + strcpy (tempstr + 8, request->directory); + strcat (tempstr + 8, "/"); + strcat (tempstr + 8, file); + } + + num = htonl (params->id++); + memcpy (tempstr, &num, 4); + + num = htonl (stlen); + memcpy (tempstr + 4, &num, 4); + + if (startsize > 0) + num = htonl (SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_APPEND); + else + num = htonl (SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_TRUNC); + memcpy (tempstr + 8 + stlen, &num, 4); + + memset (tempstr + 12 + stlen, 0, 4); + + if (sshv2_send_command (request, SSH_FXP_OPEN, tempstr, stlen + 16) < 0) + { + g_free (tempstr); + return (-2); + } + + g_free (tempstr); + memset (&message, 0, sizeof (message)); + if (sshv2_read_response (request, &message, NULL) != SSH_FXP_HANDLE) + { + request->logging_function (gftp_logging_error, request->user_data, + _("Received wrong response from server, disconnecting\n")); + sshv2_message_free (&message); + gftp_disconnect (request); + return (-2); + } + + if (message.length - 4 > SSH_MAX_HANDLE_SIZE) + { + request->logging_function (gftp_logging_error, request->user_data, + _("Error: Message size %d too big from server\n"), + message.length - 4); + sshv2_message_free (&message); + gftp_disconnect (request); + return (-1); + + } + + memset (params->handle, 0, 4); + memcpy (params->handle + 4, message.buffer+ 4, message.length - 5); + params->handle_len = message.length - 1; + sshv2_message_free (&message); + + return (0); +} + + +static size_t +sshv2_get_next_file_chunk (gftp_request * request, char *buf, size_t size) +{ + sshv2_params * params; + sshv2_message message; + gint32 num; + +#ifdef G_HAVE_GINT64 + gint64 offset; +#else + gint32 offset; +#endif + + g_return_val_if_fail (request != NULL, -2); + g_return_val_if_fail (request->protonum == GFTP_SSHV2_NUM, -2); + g_return_val_if_fail (request->sockfd != NULL, -2); + g_return_val_if_fail (buf != NULL, -2); + + params = request->protocol_data; + + if (params->read_buffer == NULL) + { + params->read_buffer = g_malloc (params->handle_len + 12); + num = htonl (params->handle_len); + memcpy (params->read_buffer, params->handle, params->handle_len); + } + + num = htonl (params->id++); + memcpy (params->read_buffer, &num, 4); + +#ifdef G_HAVE_GINT64 + offset = hton64 (params->offset); + memcpy (params->read_buffer + params->handle_len, &offset, 8); +#else + memset (params->read_buffer + params->handle_len, 0, 4); + offset = htonl (params->offset); + memcpy (params->read_buffer + params->handle_len + 4, &offset, 4); +#endif + + num = htonl (size); + memcpy (params->read_buffer + params->handle_len + 8, &num, 4); + + if (sshv2_send_command (request, SSH_FXP_READ, params->read_buffer, params->handle_len + 12) < 0) + return (-2); + + memset (&message, 0, sizeof (message)); + if (sshv2_read_response (request, &message, NULL) != SSH_FXP_DATA) + { + message.pos += 4; + if ((num = sshv2_buffer_get_int32 (request, &message, SSH_FX_OK)) < 0) + return (-2); + sshv2_message_free (&message); + if (num != SSH_FX_EOF) + { + request->logging_function (gftp_logging_error, request->user_data, + _("Received wrong response from server, disconnecting\n")); + gftp_disconnect (request); + return (-2); + } + return (0); + } + + memcpy (&num, message.buffer + 4, 4); + num = ntohl (num); + if (num > size) + { + request->logging_function (gftp_logging_error, request->user_data, + _("Error: Message size %d too big from server\n"), + num); + sshv2_message_free (&message); + gftp_disconnect (request); + return (-1); + + } + + memcpy (buf, message.buffer + 8, num); + sshv2_message_free (&message); + params->offset += num; + return (num); +} + + +static size_t +sshv2_put_next_file_chunk (gftp_request * request, char *buf, size_t size) +{ + sshv2_params * params; + sshv2_message message; + char tempstr[32768]; + gint32 num; + int ret; + +#ifdef G_HAVE_GINT64 + gint64 offset; +#else + gint32 offset; +#endif + + g_return_val_if_fail (request != NULL, -2); + g_return_val_if_fail (request->protonum == GFTP_SSHV2_NUM, -2); + g_return_val_if_fail (request->sockfd != NULL, -2); + g_return_val_if_fail (buf != NULL, -2); + g_return_val_if_fail (size <= 32500, -2); + + params = request->protocol_data; + + num = htonl (params->handle_len); + memcpy (tempstr, params->handle, params->handle_len); + + num = htonl (params->id++); + memcpy (tempstr, &num, 4); + +#ifdef G_HAVE_GINT64 + offset = hton64 (params->offset); + memcpy (tempstr + params->handle_len, &offset, 8); +#else + memset (tempstr + params->handle_len, 0, 4); + offset = htonl (params->offset); + memcpy (tempstr + params->handle_len + 4, &offset, 4); +#endif + + num = htonl (size); + memcpy (tempstr + params->handle_len + 8, &num, 4); + memcpy (tempstr + params->handle_len + 12, buf, size); + + if (sshv2_send_command (request, SSH_FXP_WRITE, tempstr, params->handle_len + size + 12) < 0) + { + g_free (tempstr); + return (-2); + } + + memset (&message, 0, sizeof (message)); + params->dont_log_status = 1; + ret = sshv2_read_response (request, &message, NULL); + params->dont_log_status = 0; + if (ret != SSH_FXP_STATUS) + { + request->logging_function (gftp_logging_error, request->user_data, + _("Received wrong response from server, disconnecting\n")); + sshv2_message_free (&message); + gftp_disconnect (request); + return (-2); + } + + message.pos += 4; + if ((num = sshv2_buffer_get_int32 (request, &message, SSH_FX_OK)) < 0) + return (-2); + sshv2_message_free (&message); + + if (num == SSH_FX_EOF) + return (0); + else if (num != SSH_FX_OK) + return (-1); + + params->offset += size; + return (size); +} +