/*****************************************************************************/
/* sshv2.c - functions that will use the sshv2 protocol */
/* Copyright (C) 1998-2003 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"
static const char cvsid[] = "$Id: sshv2.c,v 1.70 2004/12/31 22:55:23 masneyb Exp $";
#define SSH_MAX_HANDLE_SIZE 256
#define SSH_MAX_STRING_SIZE 34000
static gftp_config_vars config_vars[] =
{
{"", N_("SSH"), gftp_option_type_notebook, NULL, NULL,
GFTP_CVARS_FLAGS_SHOW_BOOKMARK, NULL, GFTP_PORT_GTK, NULL},
{"ssh_prog_name", N_("SSH Prog Name:"),
gftp_option_type_text, "ssh", NULL, 0,
N_("The path to the SSH executable"), GFTP_PORT_ALL, NULL},
{"ssh_extra_params", N_("SSH Extra Params:"),
gftp_option_type_text, NULL, NULL, 0,
N_("Extra parameters to pass to the SSH program"), GFTP_PORT_ALL, NULL},
{"ssh_need_userpass", N_("Need SSH User/Pass"),
gftp_option_type_checkbox, GINT_TO_POINTER(1), NULL,
GFTP_CVARS_FLAGS_SHOW_BOOKMARK,
N_("Require a username/password for SSH connections"), GFTP_PORT_ALL, NULL},
{NULL, NULL, 0, NULL, NULL, 0, NULL, 0, NULL}
};
typedef struct sshv2_message_tag
{
guint32 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 */
*transfer_buffer;
size_t handle_len, transfer_buffer_len;
guint32 id,
count;
sshv2_message message;
unsigned int initialized : 1,
dont_log_status : 1; /* For uploading files */
#ifdef G_HAVE_GINT64
guint64 offset;
#else
guint32 offset;
#endif
} sshv2_params;
#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_READLINK 19
#define SSH_FXP_SYMLINK 20
#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_FILEXFER_TYPE_REGULAR 1
#define SSH_FILEXFER_TYPE_DIRECTORY 2
#define SSH_FILEXFER_TYPE_SYMLINK 3
#define SSH_FILEXFER_TYPE_SPECIAL 4
#define SSH_FILEXFER_TYPE_UNKNOWN 5
#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
#define SSH_LOGIN_BUFSIZE 200
#define SSH_ERROR_BADPASS -1
#define SSH_ERROR_QUESTION -2
static char *
sshv2_initialize_string (gftp_request * request, size_t len)
{
sshv2_params * params;
guint32 num;
char *ret;
params = request->protocol_data;
ret = g_malloc0 (len + 1);
num = htonl (params->id++);
memcpy (ret, &num, 4);
return (ret);
}
static void
sshv2_add_string_to_buf (char *buf, const char *str)
{
guint32 num;
num = htonl (strlen (str));
memcpy (buf, &num, 4);
strcpy (buf + 4, str);
}
static char *
sshv2_initialize_string_with_path (gftp_request * request, const char *path,
size_t *len, char **endpos)
{
char *ret, *tempstr;
size_t pathlen;
if (*path == '/')
{
pathlen = strlen (path);
*len += pathlen + 8;
ret = sshv2_initialize_string (request, *len);
sshv2_add_string_to_buf (ret + 4, path);
}
else
{
tempstr = gftp_build_path (request, request->directory, path, NULL);
pathlen = strlen (tempstr);
*len += pathlen + 8;
ret = sshv2_initialize_string (request, *len);
sshv2_add_string_to_buf (ret + 4, tempstr);
g_free (tempstr);
}
if (endpos != NULL)
*endpos = ret + 8 + pathlen;
return (ret);
}
static void
sshv2_add_exec_args (char **logstr, size_t *logstr_len, char ***args,
size_t *args_len, size_t *args_max, char *first, ...)
{
char tempstr[2048], *curpos, *endpos, save_char;
va_list argp;
int at_end;
va_start (argp, first);
g_vsnprintf (tempstr, sizeof (tempstr), first, argp);
va_end (argp);
*logstr_len += strlen (tempstr);
*logstr = g_realloc (*logstr, *logstr_len + 1);
strcat (*logstr, tempstr);
curpos = tempstr;
while (*curpos == ' ')
curpos++;
save_char = ' ';
at_end = 0;
while (!at_end)
{
if (*curpos == '"')
{
curpos++;
endpos = strchr (curpos + 1, '"');
}
else
endpos = strchr (curpos, ' ');
if (endpos == NULL)
at_end = 1;
else
{
save_char = *endpos;
*endpos = '\0';
}
if (*args_max == *args_len + 1)
{
*args_max += 10;
*args = g_realloc (*args, sizeof (char *) * *args_max);
}
(*args)[(*args_len)++] = g_strdup (curpos);
(*args)[*args_len] = NULL;
if (!at_end)
{
*endpos = save_char;
curpos = endpos + 1;
while (*curpos == ' ')
curpos++;
}
}
}
static char **
sshv2_gen_exec_args (gftp_request * request)
{
size_t logstr_len, args_len, args_cur;
char **args, *tempstr, *logstr;
gftp_lookup_request_option (request, "ssh_prog_name", &tempstr);
if (tempstr == NULL || *tempstr == '\0')
tempstr = "ssh";
args_len = 1;
args_cur = 15;
args = g_malloc (sizeof (char *) * args_cur);
args[0] = g_strdup (tempstr);
logstr = g_strdup (args[0]);
logstr_len = strlen (logstr);
gftp_lookup_request_option (request, "ssh_extra_params", &tempstr);
if (tempstr != NULL && *tempstr != '\0')
sshv2_add_exec_args (&logstr, &logstr_len, &args, &args_len, &args_cur,
"%s", tempstr);
sshv2_add_exec_args (&logstr, &logstr_len, &args, &args_len, &args_cur,
" -e none");
if (request->username && *request->username != '\0')
sshv2_add_exec_args (&logstr, &logstr_len, &args, &args_len, &args_cur,
" -l %s", request->username);
sshv2_add_exec_args (&logstr, &logstr_len, &args, &args_len, &args_cur,
" -p %d", request->port);
sshv2_add_exec_args (&logstr, &logstr_len, &args, &args_len, &args_cur,
" %s -s sftp", request->hostname);
request->logging_function (gftp_logging_misc, request,
_("Running program %s\n"), logstr);
g_free (logstr);
return (args);
}
static int
sshv2_start_login_sequence (gftp_request * request, int fdm, int ptymfd)
{
static char *pwstrs[] = {N_("Enter passphrase for RSA key"),
N_("Enter passphrase for key '"),
N_("Password"),
N_("password"),
NULL};
char *tempstr, *temp1str, *pwstr, *yesstr = "yes\n", *securid_pass;
int wrotepw, ok, maxfd, ret, clear_tempstr, pwidx;
size_t rem, len, diff;
fd_set rset, eset;
ssize_t rd;
rem = len = SSH_LOGIN_BUFSIZE;
diff = 0;
tempstr = g_malloc0 (len + 1);
wrotepw = 0;
ok = 1;
if (gftp_fd_set_sockblocking (request, fdm, 1) == -1)
return (GFTP_ERETRYABLE);
if (gftp_fd_set_sockblocking (request, ptymfd, 1) == -1)
return (GFTP_ERETRYABLE);
if (request->password == NULL)
pwstr = g_strdup ("\n");
else
pwstr = g_strconcat (request->password, "\n", NULL);
FD_ZERO (&rset);
FD_ZERO (&eset);
maxfd = fdm > ptymfd ? fdm : ptymfd;
errno = 0;
while (1)
{
FD_SET (fdm, &rset);
FD_SET (ptymfd, &rset);
FD_SET (fdm, &eset);
FD_SET (ptymfd, &eset);
ret = select (maxfd + 1, &rset, NULL, &eset, NULL);
if (ret < 0 && (errno == EINTR || errno == EAGAIN))
continue;
if (ret < 0)
{
request->logging_function (gftp_logging_error, request,
_("Connection to %s timed out\n"),
request->hostname);
gftp_disconnect (request);
return (GFTP_ERETRYABLE);
}
if (FD_ISSET (fdm, &eset) || FD_ISSET (ptymfd, &eset))
{
request->logging_function (gftp_logging_error, request,
_("Error: Could not read from socket: %s\n"),
g_strerror (errno));
gftp_disconnect (request);
return (GFTP_ERETRYABLE);
}
if (FD_ISSET (fdm, &rset))
{
ok = 1;
break;
}
else if (!FD_ISSET (ptymfd, &rset))
continue;
rd = gftp_fd_read (request, tempstr + diff, rem - 1, ptymfd);
if (rd < 0)
return (rd);
else if (rd == 0)
continue;
tempstr[diff + rd] = '\0';
request->logging_function (gftp_logging_recv, request, "%s", tempstr + diff);
rem -= rd;
diff += rd;
/* See if we are at the enter password prompt... */
for (pwidx = 0; pwstrs[pwidx] != NULL; pwidx++)
{
if (strstr (tempstr, pwstrs[pwidx]) != NULL ||
strstr (tempstr, _(pwstrs[pwidx])) != NULL)
break;
}
if (pwstrs[pwidx] != NULL)
{
clear_tempstr = 1;
if (wrotepw)
{
ok = SSH_ERROR_BADPASS;
break;
}
wrotepw = 1;
if (gftp_fd_write (request, pwstr, strlen (pwstr), ptymfd) < 0)
{
ok = 0;
break;
}
}
else if (strstr (tempstr, "(yes/no)?") != NULL ||
strstr (tempstr, _("(yes/no)?")) != NULL)
{
clear_tempstr = 1;
if (!gftpui_protocol_ask_yes_no (request, request->hostname, tempstr))
{
ok = SSH_ERROR_QUESTION;
break;
}
else
{
if (gftp_fd_write (request, yesstr, strlen (yesstr), ptymfd) < 0)
{
ok = 0;
break;
}
}
}
else if (strstr (tempstr, "Enter PASSCODE:") != NULL ||
strstr (tempstr, _("Enter PASSCODE:")) != NULL)
{
clear_tempstr = 1;
securid_pass = gftpui_protocol_ask_user_input (request,
_("Enter Password"),
_("Enter SecurID Password:"), 0);
if (securid_pass == NULL || *securid_pass == '\0')
{
ok = SSH_ERROR_BADPASS;
break;
}
temp1str = g_strconcat (securid_pass, "\n", NULL);
ret = gftp_fd_write (request, temp1str, strlen (temp1str), ptymfd);
memset (temp1str, 0, strlen (temp1str));
g_free (temp1str);
memset (securid_pass, 0, strlen (securid_pass));
g_free (securid_pass);
if (ret <= 0)
{
ok = 0;
break;
}
}
else if (rem <= 1)
{
len += SSH_LOGIN_BUFSIZE;
rem += SSH_LOGIN_BUFSIZE;
tempstr = g_realloc (tempstr, len);
continue;
}
else
clear_tempstr = 0;
if (clear_tempstr)
{
*tempstr = '\0';
rem = SSH_LOGIN_BUFSIZE;
diff = 0;
}
}
g_free (pwstr);
g_free (tempstr);
if (ok <= 0)
{
request->logging_function (gftp_logging_error, request, "\n");
if (ok == SSH_ERROR_BADPASS)
request->logging_function (gftp_logging_error, request,
_("Error: An incorrect password was entered\n"));
gftp_disconnect (request);
return (GFTP_EFATAL);
}
return (0);
}
static void
sshv2_log_command (gftp_request * request, gftp_logging_level level,
char type, char *message, size_t length)
{
guint32 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_DATA:
case SSH_FXP_READ:
case SSH_FXP_WRITE:
break;
case SSH_FXP_INIT:
request->logging_function (level, request,
_("%d: Protocol Initialization\n"), id);
break;
case SSH_FXP_VERSION:
request->logging_function (level, request,
_("%d: Protocol version %d\n"), id, id);
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,
_("%d: Open %s\n"), id, message + 8);
*pos = oldchar;
break;
case SSH_FXP_CLOSE:
request->logging_function (level, request,
_("%d: Close\n"), id);
case SSH_FXP_OPENDIR:
request->logging_function (level, request,
_("%d: Open Directory %s\n"), id,
message + 8);
break;
case SSH_FXP_READDIR:
request->logging_function (level, request,
_("%d: Read Directory\n"), id);
break;
case SSH_FXP_REMOVE:
request->logging_function (level, request,
_("%d: Remove file %s\n"), id,
message + 8);
break;
case SSH_FXP_MKDIR:
request->logging_function (level, request,
_("%d: Make directory %s\n"), id,
message + 8);
break;
case SSH_FXP_RMDIR:
request->logging_function (level, request,
_("%d: Remove directory %s\n"), id,
message + 8);
break;
case SSH_FXP_REALPATH:
request->logging_function (level, request,
_("%d: Realpath %s\n"), id,
message + 8);
break;
case SSH_FXP_ATTRS:
request->logging_function (level, request,
_("%d: File attributes\n"), id);
break;
case SSH_FXP_STAT:
request->logging_function (level, request,
_("%d: Stat %s\n"), id,
message + 8);
break;
case SSH_FXP_SETSTAT:
memcpy (&num, message + 4, 4);
num = ntohl (num);
memcpy (&stattype, message + 8 + num, 4);
stattype = ntohl (stattype);
memcpy (&attr, message + 12 + num, 4);
attr = ntohl (attr);
pos = message + 8 + num;
oldchar = *pos;
*pos = '\0';
switch (stattype)
{
case SSH_FILEXFER_ATTR_PERMISSIONS:
request->logging_function (level, request,
_("%d: Chmod %s %o\n"), id,
message + 8, attr);
break;
case SSH_FILEXFER_ATTR_ACMODTIME:
request->logging_function (level, request,
_("%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,
"%d: %s\n", id, descr);
break;
case SSH_FXP_HANDLE:
request->logging_function (level, request,
"%d: File handle\n", id);
break;
case SSH_FXP_NAME:
memcpy (&num, message + 4, 4);
num = ntohl (num);
request->logging_function (level, request,
"%d: Filenames (%d entries)\n", id,
num);
break;
default:
request->logging_function (level, request,
"Command: %x\n", type);
}
}
static int
sshv2_send_command (gftp_request * request, char type, char *command,
size_t len)
{
char buf[34000];
guint32 clen;
int ret;
if (len > 33995)
{
request->logging_function (gftp_logging_error, request,
_("Error: Message size %d too big\n"), len);
gftp_disconnect (request);
return (GFTP_EFATAL);
}
clen = htonl (len + 1);
memcpy (buf, &clen, 4);
buf[4] = type;
memcpy (&buf[5], command, len);
buf[len + 5] = '\0';
#ifdef DEBUG
printf ("\rSending to FD %d: ", request->datafd);
for (clen=0; clen<len + 5; clen++)
printf ("%x ", buf[clen] & 0xff);
printf ("\n");
#endif
sshv2_log_command (request, gftp_logging_send, type, buf + 5, len);
if ((ret = gftp_fd_write (request, buf, len + 5, request->datafd)) < 0)
return (ret);
return (0);
}
static int
sshv2_read_response (gftp_request * request, sshv2_message * message,
int fd)
{
char buf[6], error_buffer[255], *pos;
sshv2_params * params;
ssize_t numread;
size_t rem;
params = request->protocol_data;
if (fd <= 0)
fd = request->datafd;
pos = buf;
rem = 5;
while (rem > 0)
{
if ((numread = gftp_fd_read (request, pos, rem, fd)) < 0)
return (numread);
rem -= numread;
pos += numread;
}
buf[5] = '\0';
memcpy (&message->length, buf, 4);
message->length = ntohl (message->length);
if (message->length > 34000)
{
if (params->initialized)
{
request->logging_function (gftp_logging_error, request,
_("Error: Message size %d too big from server\n"),
message->length);
}
else
{
request->logging_function (gftp_logging_error, request,
_("There was an error initializing a SSH connection with the remote server. The error message from the remote server follows:\n"), buf);
request->logging_function (gftp_logging_error, request, "%s", buf);
if (gftp_fd_set_sockblocking (request, fd, 0) == -1)
return (GFTP_EFATAL);
if ((numread = gftp_fd_read (NULL, error_buffer,
sizeof (error_buffer) - 1,
fd)) > 0)
{
error_buffer[numread] = '\0';
request->logging_function (gftp_logging_error, request,
"%s", error_buffer);
}
}
memset (message, 0, sizeof (*message));
gftp_disconnect (request);
return (GFTP_EFATAL);
}
message->command = buf[4];
message->buffer = g_malloc (message->length + 1);
message->pos = message->buffer;
message->end = message->buffer + message->length - 1;
pos = message->buffer;
rem = message->length - 1;
while (rem > 0)
{
if ((numread = gftp_fd_read (request, pos, rem, fd)) < 0)
return (numread);
rem -= numread;
pos += numread;
}
#ifdef DEBUG
printf ("\rReceived message: ");
for (rem=0; rem<message->length; rem++)
printf ("%x ", message->buffer[rem] & 0xff);
printf ("\n");
#endif
message->buffer[message->length] = '\0';
sshv2_log_command (request, gftp_logging_recv, message->command,
message->buffer, message->length);
return (message->command);
}
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 void
sshv2_message_free (sshv2_message * message)
{
if (message->buffer)
g_free (message->buffer);
memset (message, 0, sizeof (*message));
}
static int
sshv2_wrong_response (gftp_request * request, sshv2_message * message)
{
request->logging_function (gftp_logging_error, request,
_("Received wrong response from server, disconnecting\n"));
gftp_disconnect (request);
if (message != NULL)
sshv2_message_free (message);
return (GFTP_EFATAL);
}
static int
sshv2_read_status_response (gftp_request * request, sshv2_message * message,
int fd, int fxp_is_retryable, int fxp_is_wrong)
{
int ret;
memset (message, 0, sizeof (*message));
ret = sshv2_read_response (request, message, -1);
if (ret < 0)
return (ret);
else if (fxp_is_retryable > 0 && ret == fxp_is_retryable)
{
sshv2_message_free (message);
return (GFTP_ERETRYABLE);
}
else if (fxp_is_wrong > 0 && ret != fxp_is_wrong)
return (sshv2_wrong_response (request, message));
return (ret);
}
static int
sshv2_response_return_code (gftp_request * request, sshv2_message * message,
unsigned int ret)
{
switch (ret)
{
case SSH_FX_OK:
case SSH_FX_EOF:
case SSH_FX_NO_SUCH_FILE:
case SSH_FX_PERMISSION_DENIED:
case SSH_FX_FAILURE:
case SSH_FX_OP_UNSUPPORTED:
return (GFTP_ERETRYABLE);
default:
return (sshv2_wrong_response (request, message));
}
}
static int
sshv2_buffer_get_int32 (gftp_request * request, sshv2_message * message,
unsigned int expected_response, int check_response,
guint32 * num)
{
guint32 snum;
if (message->end - message->pos < 4)
return (sshv2_wrong_response (request, message));
memcpy (&snum, message->pos, 4);
snum = ntohl (snum);
message->pos += 4;
if (check_response && snum != expected_response)
return (sshv2_response_return_code (request, message, snum));
if (num != NULL)
*num = snum;
return (0);
}
static int
sshv2_buffer_get_int64 (gftp_request * request, sshv2_message * message,
unsigned int expected_response, int check_response,
#if G_HAVE_GINT64
guint64 * num)
{
guint64 snum;
#else
guint32 * num)
{
guint32 snum;
#endif
guint32 hinum, lonum;
int ret;
if ((ret = sshv2_buffer_get_int32 (request, message, 0, 0, &hinum)) < 0)
return (ret);
if ((ret = sshv2_buffer_get_int32 (request, message, 0, 0, &lonum)) < 0)
return (ret);
#if G_HAVE_GINT64
snum = (gint64) lonum | ((gint64) hinum >> 32);
#else
snum = lonum;
#endif
if (check_response && snum != expected_response)
return (sshv2_response_return_code (request, message, snum));
if (num != NULL)
*num = snum;
return (0);
}
static char *
sshv2_buffer_get_string (gftp_request * request, sshv2_message * message,
int return_string)
{
guint32 len, buflen;
char *string;
if (sshv2_buffer_get_int32 (request, message, 0, 0, &len) < 0)
return (NULL);
buflen = message->end - message->pos;
if (len > SSH_MAX_STRING_SIZE || (buflen < len))
{
sshv2_wrong_response (request, message);
return (NULL);
}
if (return_string)
{
string = g_malloc (len + 1);
memcpy (string, message->pos, len);
string[len] = '\0';
}
else
string = NULL;
message->pos += len;
return (string);
}
static int
sshv2_getcwd (gftp_request * request)
{
sshv2_message message;
char *tempstr, *dir;
size_t len;
int ret;
g_return_val_if_fail (request != NULL, GFTP_EFATAL);
g_return_val_if_fail (request->protonum == GFTP_SSHV2_NUM, GFTP_EFATAL);
if (request->directory == NULL || *request->directory == '\0')
dir = ".";
else
dir = request->directory;
len = strlen (dir) + 8;
tempstr = sshv2_initialize_string (request, len);
sshv2_add_string_to_buf (tempstr + 4, dir);
ret = sshv2_send_command (request, SSH_FXP_REALPATH, tempstr, len);
g_free (tempstr);
if (ret < 0)
return (ret);
if (request->directory)
{
g_free (request->directory);
request->directory = NULL;
}
ret = sshv2_read_status_response (request, &message, -1, SSH_FXP_STATUS,
SSH_FXP_NAME);
if (ret < 0)
return (ret);
message.pos += 4;
if ((ret = sshv2_buffer_get_int32 (request, &message, 1, 1, NULL)) < 0)
return (ret);
if ((request->directory = sshv2_buffer_get_string (request, &message, 1)) == NULL)
return (GFTP_EFATAL);
sshv2_message_free (&message);
return (0);
}
static void
sshv2_free_args (char **args)
{
int i;
for (i=0; args[i] != NULL; i++)
g_free (args[i]);
g_free (args);
}
static int
sshv2_connect (gftp_request * request)
{
struct servent serv_struct;
sshv2_params * params;
sshv2_message message;
int ret, fdm, ptymfd;
guint32 version;
char **args;
pid_t child;
g_return_val_if_fail (request != NULL, GFTP_EFATAL);
g_return_val_if_fail (request->protonum == GFTP_SSHV2_NUM, GFTP_EFATAL);
g_return_val_if_fail (request->hostname != NULL, GFTP_EFATAL);
if (request->datafd > 0)
return (0);
params = request->protocol_data;
request->logging_function (gftp_logging_misc, request,
_("Opening SSH connection to %s\n"),
request->hostname);
if (request->port == 0)
{
if (!r_getservbyname ("ssh", "tcp", &serv_struct, NULL))
{
request->logging_function (gftp_logging_error, request,
_("Cannot look up service name %s/tcp. Please check your services file\n"),
"ssh");
}
else
request->port = ntohs (serv_struct.s_port);
}
args = sshv2_gen_exec_args (request);
child = gftp_exec (request, &fdm, &ptymfd, args);
if (child == 0)
exit (0);
sshv2_free_args (args);
if (child < 0)
return (GFTP_ERETRYABLE);
request->datafd = fdm;
version = htonl (SSH_MY_VERSION);
if ((ret = sshv2_send_command (request, SSH_FXP_INIT, (char *)
&version, 4)) < 0)
return (ret);
ret = sshv2_start_login_sequence (request, fdm, ptymfd);
if (ret < 0)
return (ret);
memset (&message, 0, sizeof (message));
ret = sshv2_read_response (request, &message, -1);
if (ret < 0)
{
sshv2_message_free (&message);
return (ret);
}
else if (ret != SSH_FXP_VERSION)
return (sshv2_wrong_response (request, &message));
sshv2_message_free (&message);
params->initialized = 1;
request->logging_function (gftp_logging_misc, request,
_("Successfully logged into SSH server %s\n"),
request->hostname);
if (sshv2_getcwd (request) < 0)
{
if (request->directory)
g_free (request->directory);
request->directory = g_strdup (".");
if ((ret = sshv2_getcwd (request)) < 0)
{
gftp_disconnect (request);
return (ret);
}
}
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->datafd > 0)
{
request->logging_function (gftp_logging_misc, request,
_("Disconnecting from site %s\n"),
request->hostname);
if (close (request->datafd) < 0)
request->logging_function (gftp_logging_error, request,
_("Error closing file descriptor: %s\n"),
g_strerror (errno));
request->datafd = -1;
}
if (params->message.buffer != NULL)
{
sshv2_message_free (¶ms->message);
params->message.buffer = NULL;
}
}
static int
sshv2_end_transfer (gftp_request * request)
{
sshv2_params * params;
sshv2_message message;
guint32 len;
int ret;
g_return_val_if_fail (request != NULL, GFTP_EFATAL);
g_return_val_if_fail (request->protonum == GFTP_SSHV2_NUM, GFTP_EFATAL);
params = request->protocol_data;
if (params->message.buffer != NULL)
{
sshv2_message_free (¶ms->message);
params->message.buffer = NULL;
params->count = 0;
}
if (params->handle_len > 0)
{
len = htonl (params->id++);
memcpy (params->handle, &len, 4);
if ((ret = sshv2_send_command (request, SSH_FXP_CLOSE, params->handle,
params->handle_len)) < 0)
return (ret);
ret = sshv2_read_status_response (request, &message, -1, 0,
SSH_FXP_STATUS);
if (ret < 0)
return (ret);
sshv2_message_free (&message);
params->handle_len = 0;
}
if (params->transfer_buffer != NULL)
{
g_free (params->transfer_buffer);
params->transfer_buffer = NULL;
}
return (0);
}
static int
sshv2_list_files (gftp_request * request)
{
sshv2_params * params;
sshv2_message message;
char *tempstr;
size_t len;
int ret;
g_return_val_if_fail (request != NULL, GFTP_EFATAL);
g_return_val_if_fail (request->protonum == GFTP_SSHV2_NUM, GFTP_EFATAL);
g_return_val_if_fail (request->datafd > 0, GFTP_EFATAL);
params = request->protocol_data;
request->logging_function (gftp_logging_misc, request,
_("Retrieving directory listing...\n"));
len = strlen (request->directory) + 8;
tempstr = sshv2_initialize_string (request, len);
sshv2_add_string_to_buf (tempstr + 4, request->directory);
ret = sshv2_send_command (request, SSH_FXP_OPENDIR, tempstr, len);
g_free (tempstr);
if (ret < 0)
return (ret);
ret = sshv2_read_status_response (request, &message, -1, SSH_FXP_STATUS,
SSH_FXP_HANDLE);
if (ret < 0)
return (ret);
if (message.length - 4 > SSH_MAX_HANDLE_SIZE)
{
request->logging_function (gftp_logging_error, request,
_("Error: Message size %d too big from server\n"),
message.length - 4);
sshv2_message_free (&message);
gftp_disconnect (request);
return (GFTP_EFATAL);
}
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_decode_file_attributes (gftp_request * request, sshv2_message * message,
gftp_file * fle)
{
guint32 attrs, num, count, i;
int ret;
if ((ret = sshv2_buffer_get_int32 (request, message, 0, 0, &attrs)) < 0)
return (ret);
if (attrs & SSH_FILEXFER_ATTR_SIZE)
{
if ((ret = sshv2_buffer_get_int64 (request, message, 0, 0, &fle->size)) < 0)
return (ret);
}
if (attrs & SSH_FILEXFER_ATTR_UIDGID)
{
if ((ret = sshv2_buffer_get_int32 (request, message, 0, 0, &num)) < 0)
return (ret);
fle->user = g_strdup_printf ("%d", num);
if ((ret = sshv2_buffer_get_int32 (request, message, 0, 0, &num)) < 0)
return (ret);
fle->group = g_strdup_printf ("%d", num);
}
if (attrs & SSH_FILEXFER_ATTR_PERMISSIONS)
{
if ((ret = sshv2_buffer_get_int32 (request, message, 0, 0, &num)) < 0)
return (ret);
fle->st_mode = num;
}
if (attrs & SSH_FILEXFER_ATTR_ACMODTIME)
{
if ((ret = sshv2_buffer_get_int32 (request, message, 0, 0, NULL)) < 0)
return (num);
if ((ret = sshv2_buffer_get_int32 (request, message, 0, 0, &num)) < 0)
return (ret);
fle->datetime = num;
}
if (attrs & SSH_FILEXFER_ATTR_EXTENDED)
{
if ((ret = sshv2_buffer_get_int32 (request, message, 0, 0, &count)) < 0)
return (ret);
for (i=0; i<count; i++)
{
if ((ret = sshv2_buffer_get_int32 (request, message, 0, 0,
&num)) < 0 ||
message->pos + num + 4 > message->end)
return (GFTP_EFATAL);
message->pos += num + 4;
if ((ret = sshv2_buffer_get_int32 (request, message, 0, 0,
&num)) < 0 ||
message->pos + num + 4 > message->end)
return (GFTP_EFATAL);
message->pos += num + 4;
}
}
return (0);
}
static int
sshv2_get_next_file (gftp_request * request, gftp_file * fle, int fd)
{
int ret, retsize, iret;
sshv2_params *params;
char *stpos;
guint32 len;
g_return_val_if_fail (request != NULL, GFTP_EFATAL);
g_return_val_if_fail (request->protonum == GFTP_SSHV2_NUM, GFTP_EFATAL);
g_return_val_if_fail (fle != NULL, GFTP_EFATAL);
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 ((ret = sshv2_send_command (request, SSH_FXP_READDIR,
params->handle,
params->handle_len)) < 0)
return (ret);
}
if ((ret = sshv2_read_response (request, ¶ms->message, fd)) < 0)
return (ret);
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 ((iret = sshv2_buffer_get_int32 (request,
¶ms->message, 0, 0,
¶ms->count)) < 0)
return (iret);
}
}
if (ret == SSH_FXP_NAME)
{
stpos = params->message.pos;
if ((fle->file = sshv2_buffer_get_string (request, ¶ms->message, 1)) == NULL)
return (GFTP_EFATAL);
sshv2_buffer_get_string (request, ¶ms->message, 0);
if ((ret = sshv2_decode_file_attributes (request, ¶ms->message,
fle)) < 0)
{
gftp_file_destroy (fle, 0);
return (ret);
}
retsize = params->message.pos - stpos;
params->count--;
}
else if (ret == SSH_FXP_STATUS)
{
sshv2_message_free (¶ms->message);
return (0);
}
else
return (sshv2_wrong_response (request, ¶ms->message));
return (retsize);
}
static int
sshv2_chdir (gftp_request * request, const char *directory)
{
sshv2_message message;
char *tempstr, *dir;
size_t len;
int ret;
g_return_val_if_fail (request != NULL, GFTP_EFATAL);
g_return_val_if_fail (request->protonum == GFTP_SSHV2_NUM, GFTP_EFATAL);
if (request->directory != directory)
{
len = 0;
tempstr = sshv2_initialize_string_with_path (request, directory,
&len, NULL);
ret = sshv2_send_command (request, SSH_FXP_REALPATH, tempstr, len);
g_free (tempstr);
if (ret < 0)
return (ret);
ret = sshv2_read_status_response (request, &message, -1, SSH_FXP_STATUS,
SSH_FXP_NAME);
if (ret < 0)
return (ret);
message.pos += 4;
if ((ret = sshv2_buffer_get_int32 (request, &message, 1, 1, NULL)) < 0)
return (ret);
if ((dir = sshv2_buffer_get_string (request, &message, 1)) == NULL)
return (GFTP_EFATAL);
if (request->directory)
g_free (request->directory);
request->directory = dir;
sshv2_message_free (&message);
return (0);
}
return (0);
}
static int
sshv2_rmdir (gftp_request * request, const char *directory)
{
sshv2_message message;
char *tempstr;
size_t len;
int ret;
g_return_val_if_fail (request != NULL, GFTP_EFATAL);
g_return_val_if_fail (request->protonum == GFTP_SSHV2_NUM, GFTP_EFATAL);
g_return_val_if_fail (directory != NULL, GFTP_EFATAL);
len = 0;
tempstr = sshv2_initialize_string_with_path (request, directory, &len, NULL);
ret = sshv2_send_command (request, SSH_FXP_RMDIR, tempstr, len);
g_free (tempstr);
if (ret < 0)
return (ret);
memset (&message, 0, sizeof (message));
if ((ret = sshv2_read_response (request, &message, -1)) < 0)
return (ret);
message.pos += 4;
if ((ret = sshv2_buffer_get_int32 (request, &message, SSH_FX_OK, 1, NULL)) < 0)
return (ret);
sshv2_message_free (&message);
return (0);
}
static int
sshv2_rmfile (gftp_request * request, const char *file)
{
sshv2_message message;
char *tempstr;
size_t len;
int ret;
g_return_val_if_fail (request != NULL, GFTP_EFATAL);
g_return_val_if_fail (request->protonum == GFTP_SSHV2_NUM, GFTP_EFATAL);
g_return_val_if_fail (file != NULL, GFTP_EFATAL);
len = 0;
tempstr = sshv2_initialize_string_with_path (request, file, &len, NULL);
ret = sshv2_send_command (request, SSH_FXP_REMOVE, tempstr, len);
g_free (tempstr);
if (ret < 0)
return (ret);
memset (&message, 0, sizeof (message));
if ((ret = sshv2_read_response (request, &message, -1)) < 0)
return (ret);
message.pos += 4;
if ((ret = sshv2_buffer_get_int32 (request, &message, SSH_FX_OK, 1, NULL)) < 0)
return (ret);
sshv2_message_free (&message);
return (0);
}
static int
sshv2_chmod (gftp_request * request, const char *file, mode_t mode)
{
char *tempstr, *endpos;
sshv2_message message;
guint32 num;
size_t len;
int ret;
g_return_val_if_fail (request != NULL, GFTP_EFATAL);
g_return_val_if_fail (request->protonum == GFTP_SSHV2_NUM, GFTP_EFATAL);
g_return_val_if_fail (file != NULL, GFTP_EFATAL);
len = 8; /* For attributes */
tempstr = sshv2_initialize_string_with_path (request, file, &len, &endpos);
num = htonl (SSH_FILEXFER_ATTR_PERMISSIONS);
memcpy (endpos, &num, 4);
num = htonl (mode);
memcpy (endpos + 4, &num, 4);
ret = sshv2_send_command (request, SSH_FXP_SETSTAT, tempstr, len);
g_free (tempstr);
if (ret < 0)
return (ret);
memset (&message, 0, sizeof (message));
if ((ret = sshv2_read_response (request, &message, -1)) < 0)
return (ret);
message.pos += 4;
if ((ret = sshv2_buffer_get_int32 (request, &message, SSH_FX_OK, 1, NULL)) < 0)
return (ret);
sshv2_message_free (&message);
return (0);
}
static int
sshv2_mkdir (gftp_request * request, const char *newdir)
{
sshv2_message message;
char *tempstr;
size_t len;
int ret;
g_return_val_if_fail (request != NULL, GFTP_EFATAL);
g_return_val_if_fail (request->protonum == GFTP_SSHV2_NUM, GFTP_EFATAL);
g_return_val_if_fail (newdir != NULL, GFTP_EFATAL);
len = 4; /* For attributes */
tempstr = sshv2_initialize_string_with_path (request, newdir, &len, NULL);
/* No need to set attributes since all characters of the tempstr buffer is
initialized to 0 */
ret = sshv2_send_command (request, SSH_FXP_MKDIR, tempstr, len);
g_free (tempstr);
if (ret < 0)
return (ret);
memset (&message, 0, sizeof (message));
if ((ret = sshv2_read_response (request, &message, -1)) < 0)
return (ret);
message.pos += 4;
if ((ret = sshv2_buffer_get_int32 (request, &message, SSH_FX_OK, 1, NULL)) < 0)
return (ret);
sshv2_message_free (&message);
return (0);
}
static int
sshv2_rename (gftp_request * request, const char *oldname, const char *newname)
{
char *tempstr, *oldstr, *newstr;
size_t oldlen, newlen, len;
sshv2_message message;
int ret;
g_return_val_if_fail (request != NULL, GFTP_EFATAL);
g_return_val_if_fail (request->protonum == GFTP_SSHV2_NUM, GFTP_EFATAL);
g_return_val_if_fail (oldname != NULL, GFTP_EFATAL);
g_return_val_if_fail (newname != NULL, GFTP_EFATAL);
if (*oldname == '/')
oldstr = g_strdup (oldname);
else
oldstr = gftp_build_path (request, request->directory, oldname, NULL);
if (*newname == '/')
newstr = g_strdup (newname);
else
newstr = gftp_build_path (request, request->directory, newname, NULL);
oldlen = strlen (oldstr);
newlen = strlen (newstr);
len = oldlen + newlen + 12;
tempstr = sshv2_initialize_string (request, len);
sshv2_add_string_to_buf (tempstr + 4, oldstr);
sshv2_add_string_to_buf (tempstr + 8 + oldlen, newstr);
g_free (oldstr);
g_free (newstr);
ret = sshv2_send_command (request, SSH_FXP_RENAME, tempstr, len);
g_free (tempstr);
if (ret < 0)
return (ret);
memset (&message, 0, sizeof (message));
if ((ret = sshv2_read_response (request, &message, -1)) < 0)
return (ret);
message.pos += 4;
if ((ret = sshv2_buffer_get_int32 (request, &message, SSH_FX_OK, 1, NULL)) < 0)
return (ret);
sshv2_message_free (&message);
return (0);
}
static int
sshv2_set_file_time (gftp_request * request, const char *file, time_t datetime)
{
char *tempstr, *endpos;
sshv2_message message;
guint32 num;
size_t len;
int ret;
g_return_val_if_fail (request != NULL, GFTP_EFATAL);
g_return_val_if_fail (request->protonum == GFTP_SSHV2_NUM, GFTP_EFATAL);
g_return_val_if_fail (file != NULL, GFTP_EFATAL);
len = 12; /* For date/time */
tempstr = sshv2_initialize_string_with_path (request, file, &len, &endpos);
num = htonl (SSH_FILEXFER_ATTR_ACMODTIME);
memcpy (endpos, &num, 4);
num = htonl (datetime);
memcpy (endpos + 4, &num, 4);
memcpy (endpos + 8, &num, 4);
ret = sshv2_send_command (request, SSH_FXP_SETSTAT, tempstr, len);
g_free (tempstr);
if (ret < 0)
return (ret);
memset (&message, 0, sizeof (message));
if ((ret = sshv2_read_response (request, &message, -1)) < 0)
return (ret);
message.pos += 4;
if ((ret = sshv2_buffer_get_int32 (request, &message, SSH_FX_OK, 1, NULL)) < 0)
return (ret);
sshv2_message_free (&message);
return (0);
}
static int
sshv2_send_stat_command (gftp_request * request, const char *filename,
gftp_file * fle)
{
sshv2_message message;
char *tempstr;
size_t len;
int ret;
g_return_val_if_fail (request != NULL, GFTP_EFATAL);
g_return_val_if_fail (request->protonum == GFTP_SSHV2_NUM, GFTP_EFATAL);
g_return_val_if_fail (filename != NULL, GFTP_EFATAL);
len = 0;
tempstr = sshv2_initialize_string_with_path (request, filename, &len, NULL);
ret = sshv2_send_command (request, SSH_FXP_STAT, tempstr, len);
g_free (tempstr);
if (ret < 0)
return (ret);
ret = sshv2_read_status_response (request, &message, -1, SSH_FXP_STATUS,
SSH_FXP_ATTRS);
if (ret < 0)
return (ret);
if (message.length < 5)
return (GFTP_EFATAL);
message.pos += 4;
if ((ret = sshv2_decode_file_attributes (request, &message, fle)) < 0)
{
gftp_file_destroy (fle, 0);
return (ret);
}
sshv2_message_free (&message);
return (0);
}
static int
sshv2_stat_filename (gftp_request * request, const char *filename,
mode_t * mode)
{
gftp_file fle;
int ret;
memset (&fle, 0, sizeof (fle));
ret = sshv2_send_stat_command (request, filename, &fle);
if (ret < 0)
return (ret);
*mode = fle.st_mode;
gftp_file_destroy (&fle, 0);
return (0);
}
static off_t
sshv2_get_file_size (gftp_request * request, const char *file)
{
gftp_file fle;
off_t size;
int ret;
memset (&fle, 0, sizeof (fle));
ret = sshv2_send_stat_command (request, file, &fle);
if (ret < 0)
return (ret);
size = fle.size;
gftp_file_destroy (&fle, 0);
return (size);
}
static int
sshv2_open_file (gftp_request * request, const char *file, off_t startsize,
guint32 mode)
{
char *tempstr, *endpos;
sshv2_params * params;
sshv2_message message;
guint32 num;
size_t len;
int ret;
g_return_val_if_fail (request != NULL, GFTP_EFATAL);
g_return_val_if_fail (request->protonum == GFTP_SSHV2_NUM, GFTP_EFATAL);
g_return_val_if_fail (request->datafd > 0, GFTP_EFATAL);
params = request->protocol_data;
params->offset = startsize;
len = 8;
tempstr = sshv2_initialize_string_with_path (request, file, &len, &endpos);
num = htonl (mode);
memcpy (endpos, &num, 4);
ret = sshv2_send_command (request, SSH_FXP_OPEN, tempstr, len);
g_free (tempstr);
if (ret < 0)
return (ret);
ret = sshv2_read_status_response (request, &message, -1, SSH_FXP_STATUS,
SSH_FXP_HANDLE);
if (ret < 0)
return (ret);
if (message.length - 4 > SSH_MAX_HANDLE_SIZE)
{
request->logging_function (gftp_logging_error, request,
_("Error: Message size %d too big from server\n"),
message.length - 4);
sshv2_message_free (&message);
gftp_disconnect (request);
return (GFTP_ERETRYABLE);
}
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 off_t
sshv2_get_file (gftp_request * request, const char *file, int fd,
off_t startsize)
{
int ret;
if ((ret = sshv2_open_file (request, file, startsize, SSH_FXP_OPEN)) < 0)
return (ret);
return (sshv2_get_file_size (request, file));
}
static int
sshv2_put_file (gftp_request * request, const char *file, int fd,
off_t startsize, off_t totalsize)
{
guint32 mode;
int ret;
if (startsize > 0)
mode = SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_APPEND;
else
mode = SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_TRUNC;
if ((ret = sshv2_open_file (request, file, startsize, mode)) < 0)
return (ret);
return (0);
}
#ifdef G_HAVE_GINT64
static gint64
sshv2_hton64 (gint64 val)
{
#if G_BYTE_ORDER != G_BIG_ENDIAN
return (GINT64_TO_BE (val));
#else
return (val);
#endif
}
#endif
static void
sshv2_setup_file_offset (sshv2_params * params, char *buf)
{
guint32 hinum, lownum;
#ifdef G_HAVE_GINT64
gint64 offset;
offset = sshv2_hton64 (params->offset);
lownum = offset >> 32;
hinum = (guint32) offset;
#else
hinum = 0;
lownum = htonl (params->offset);
#endif
memcpy (buf + params->handle_len, &hinum, 4);
memcpy (buf + params->handle_len + 4, &lownum, 4);
}
static ssize_t
sshv2_get_next_file_chunk (gftp_request * request, char *buf, size_t size)
{
sshv2_params * params;
sshv2_message message;
guint32 num;
int ret;
g_return_val_if_fail (request != NULL, GFTP_EFATAL);
g_return_val_if_fail (request->protonum == GFTP_SSHV2_NUM, GFTP_EFATAL);
g_return_val_if_fail (request->datafd > 0, GFTP_EFATAL);
g_return_val_if_fail (buf != NULL, GFTP_EFATAL);
params = request->protocol_data;
if (params->transfer_buffer == NULL)
{
params->transfer_buffer_len = params->handle_len + 12;
params->transfer_buffer = g_malloc (params->transfer_buffer_len);
memcpy (params->transfer_buffer, params->handle, params->handle_len);
}
num = htonl (params->id++);
memcpy (params->transfer_buffer, &num, 4);
sshv2_setup_file_offset (params, params->transfer_buffer);
num = htonl (size);
memcpy (params->transfer_buffer + params->handle_len + 8, &num, 4);
if ((ret = sshv2_send_command (request, SSH_FXP_READ, params->transfer_buffer,
params->handle_len + 12)) < 0)
return (ret);
memset (&message, 0, sizeof (message));
if ((ret = sshv2_read_response (request, &message, -1)) != SSH_FXP_DATA)
{
if (ret < 0)
return (ret);
message.pos += 4;
if ((ret = sshv2_buffer_get_int32 (request, &message, SSH_FX_EOF, 1,
NULL)) < 0)
return (ret);
sshv2_message_free (&message);
return (0);
}
memcpy (&num, message.buffer + 4, 4);
num = ntohl (num);
if (num > size)
{
request->logging_function (gftp_logging_error, request,
_("Error: Message size %d too big from server\n"),
num);
sshv2_message_free (&message);
gftp_disconnect (request);
return (GFTP_ERETRYABLE);
}
memcpy (buf, message.buffer + 8, num);
sshv2_message_free (&message);
params->offset += num;
return (num);
}
static ssize_t
sshv2_put_next_file_chunk (gftp_request * request, char *buf, size_t size)
{
sshv2_params * params;
sshv2_message message;
guint32 num;
int ret;
g_return_val_if_fail (request != NULL, GFTP_EFATAL);
g_return_val_if_fail (request->protonum == GFTP_SSHV2_NUM, GFTP_EFATAL);
g_return_val_if_fail (request->datafd > 0, GFTP_EFATAL);
g_return_val_if_fail (buf != NULL, GFTP_EFATAL);
params = request->protocol_data;
if (params->transfer_buffer == NULL)
{
params->transfer_buffer_len = params->handle_len + size + 12;
params->transfer_buffer = g_malloc (params->transfer_buffer_len);
memcpy (params->transfer_buffer, params->handle, params->handle_len);
}
num = htonl (params->id++);
memcpy (params->transfer_buffer, &num, 4);
sshv2_setup_file_offset (params, params->transfer_buffer);
num = htonl (size);
memcpy (params->transfer_buffer + params->handle_len + 8, &num, 4);
memcpy (params->transfer_buffer + params->handle_len + 12, buf, size);
if ((ret = sshv2_send_command (request, SSH_FXP_WRITE,
params->transfer_buffer,
params->transfer_buffer_len)) < 0)
return (ret);
memset (&message, 0, sizeof (message));
params->dont_log_status = 1;
ret = sshv2_read_response (request, &message, -1);
if (ret < 0)
return (ret);
params->dont_log_status = 0;
if (ret != SSH_FXP_STATUS)
return (sshv2_wrong_response (request, &message));
message.pos += 4;
if ((ret = sshv2_buffer_get_int32 (request, &message, SSH_FX_OK, 1, &num)) < 0)
{
if (num == SSH_FX_EOF)
return (0);
return (ret);
}
sshv2_message_free (&message);
params->offset += size;
return (size);
}
static int
sshv2_set_config_options (gftp_request * request)
{
intptr_t ssh_need_userpass;
gftp_lookup_request_option (request, "ssh_need_userpass", &ssh_need_userpass);
request->need_username = ssh_need_userpass;
request->need_password = ssh_need_userpass;
return (0);
}
static void
sshv2_swap_socks (gftp_request * dest, gftp_request * source)
{
sshv2_params * sparams, * dparams;
sparams = source->protocol_data;
dparams = dest->protocol_data;
dparams->id = sparams->id;
}
void
sshv2_register_module (void)
{
gftp_register_config_vars (config_vars);
}
static void
sshv2_copy_message (sshv2_message * src_message, sshv2_message * dest_message)
{
dest_message->length = src_message->length;
dest_message->command = src_message->command;
dest_message->buffer = g_strdup (src_message->buffer);
dest_message->pos = dest_message->buffer + (src_message->pos - src_message->buffer);
dest_message->end = dest_message->buffer + (src_message->end - src_message->buffer);
}
static void
sshv2_copy_param_options (gftp_request * dest_request,
gftp_request * src_request)
{
sshv2_params * dparms, * sparms;
dparms = dest_request->protocol_data;
sparms = src_request->protocol_data;
memcpy (dparms->handle, sparms->handle, sizeof (*dparms->handle));
if (sparms->transfer_buffer != NULL)
{
dparms->transfer_buffer = g_malloc (sparms->transfer_buffer_len);
memcpy (dparms->transfer_buffer, sparms->transfer_buffer,
sparms->transfer_buffer_len);
}
else
dparms->transfer_buffer = NULL;
sshv2_copy_message (&sparms->message, &dparms->message);
dparms->id = sparms->id;
dparms->count = sparms->count;
dparms->handle_len = sparms->handle_len;
dparms->initialized = sparms->initialized;
dparms->dont_log_status = sparms->dont_log_status;
dparms->offset = sparms->offset;
}
int
sshv2_init (gftp_request * request)
{
sshv2_params * params;
g_return_val_if_fail (request != NULL, GFTP_EFATAL);
request->protonum = GFTP_SSHV2_NUM;
request->init = sshv2_init;
request->copy_param_options = sshv2_copy_param_options;
request->destroy = sshv2_destroy;
request->read_function = gftp_fd_read;
request->write_function = gftp_fd_write;
request->connect = sshv2_connect;
request->post_connect = NULL;
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->abort_transfer = sshv2_end_transfer; /* NOTE: uses sshv2_end_transfer */
request->stat_filename = sshv2_stat_filename;
request->list_files = sshv2_list_files;
request->get_next_file = sshv2_get_next_file;
request->get_next_dirlist_line = 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->set_config_options = sshv2_set_config_options;
request->swap_socks = sshv2_swap_socks;
request->url_prefix = "ssh2";
request->need_hostport = 1;
request->need_username = 1;
request->need_password = 1;
request->use_cache = 1;
request->always_connected = 0;
request->protocol_data = g_malloc0 (sizeof (sshv2_params));
request->server_type = GFTP_DIRTYPE_UNIX;
params = request->protocol_data;
params->id = 1;
return (gftp_set_config_options (request));
}
syntax highlighted by Code2HTML, v. 0.9.1