Mercurial > gftp.yaz
view lib/sshv2.c @ 219:b9584a877daf
2003-7-9 Brian Masney <masneyb@gftp.org>
* lib/sshv2.c - if we are logging in to the server, and the message
size is too big from the server, assume that an error message follows
and log the ASCII text to the log window
* src/gtk/options_dialog.c - make sure the value is set properly for
the textcomboedt option types (the FTP proxy server type uses this)
* lib/rfc2068.c - HTTP proxy bug fixes. Also, moved rfc2068_connect()
call to rfc2068_send_command() and removed it from the now unneeded
places. Also, fix in call to HEAD command
* src/text/gftp-text.c - fixed NULL pointer deference in changing
directory
* docs/sample.gftp/gftprc - enable require SSH user/pass by default
author | masneyb |
---|---|
date | Wed, 09 Jul 2003 23:25:57 +0000 |
parents | de6a2e8e51cb |
children | a85a097bbb02 |
line wrap: on
line source
/*****************************************************************************/ /* 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$"; #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, 0, 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}, {"ssh2_sftp_path", N_("SSH2 sftp-server path:"), gftp_option_type_text, NULL, NULL, 0, N_("Default remote SSH2 sftp-server path"), GFTP_PORT_ALL, NULL}, {"ssh_need_userpass", N_("Need SSH User/Pass"), gftp_option_type_checkbox, GINT_TO_POINTER(1), NULL, 0, N_("Require a username/password for SSH connections"), GFTP_PORT_ALL, NULL}, {"ssh_use_askpass", N_("Use ssh-askpass utility"), gftp_option_type_checkbox, GINT_TO_POINTER(0), NULL, 0, N_("Use the ssh-askpass utility to supply the remote password"), GFTP_PORT_GTK, NULL}, {"sshv2_use_sftp_subsys", N_("Use SSH2 SFTP subsys"), gftp_option_type_checkbox, GINT_TO_POINTER(0), NULL, 0, N_("Call ssh with the -s sftp flag. This is helpful because you won't have to know the remote path to the remote sftp-server"), GFTP_PORT_GTK, NULL}, {NULL, NULL, 0, NULL, NULL, 0, NULL, 0, NULL} }; 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; unsigned int initialized : 1; #ifdef G_HAVE_GINT64 gint64 offset; #else gint32 offset; #endif char *read_buffer; } 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_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 #define SSH_LOGIN_BUFSIZE 200 #define SSH_ERROR_BADPASS -1 #define SSH_ERROR_QUESTION -2 #define SSH_WARNING -3 static void sshv2_add_exec_args (char **logstr, size_t *logstr_len, char ***args, size_t *args_len, size_t *args_cur, 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_cur == *args_len + 1) { *args_cur += 10; *args = g_realloc (*args, sizeof (char *) * *args_cur); } (*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, char *execname, int use_sftp_subsys) { 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); if (use_sftp_subsys) sshv2_add_exec_args (&logstr, &logstr_len, &args, &args_len, &args_cur, " %s -s sftp", request->hostname); else sshv2_add_exec_args (&logstr, &logstr_len, &args, &args_len, &args_cur, " %s \"%s\"", request->hostname, execname); request->logging_function (gftp_logging_misc, request, _("Running program %s\n"), logstr); g_free (logstr); return (args); } static char * sshv2_start_login_sequence (gftp_request * request, int fd) { char *tempstr, *pwstr, *tmppos; size_t rem, len, diff, lastdiff, key_pos; int wrotepw, ok; ssize_t rd; rem = len = SSH_LOGIN_BUFSIZE; tempstr = g_malloc0 (len + 1); key_pos = diff = lastdiff = 0; wrotepw = 0; ok = 1; if (gftp_fd_set_sockblocking (request, fd, 1) == -1) return (NULL); pwstr = g_strconcat (request->password, "\n", NULL); errno = 0; while (1) { if ((rd = gftp_fd_read (request, tempstr + diff, rem - 1, fd)) <= 0) { ok = 0; break; } rem -= rd; diff += rd; tempstr[diff] = '\0'; if (diff >= 10 && strcmp (tempstr + diff - 9, "assword: ") == 0) { if (wrotepw) { ok = SSH_ERROR_BADPASS; break; } if (strstr (tempstr, "WARNING") != NULL || strstr (tempstr, _("WARNING")) != NULL) { ok = SSH_WARNING; break; } wrotepw = 1; if (gftp_fd_write (request, pwstr, strlen (pwstr), fd) < 0) { ok = 0; break; } } else if (diff > 2 && strcmp (tempstr + diff - 2, ": ") == 0 && ((tmppos = strstr (tempstr + key_pos, "Enter passphrase for RSA key")) != NULL || ((tmppos = strstr (tempstr + key_pos, "Enter passphrase for key '")) != NULL))) { key_pos = diff; if (wrotepw) { ok = SSH_ERROR_BADPASS; break; } if (strstr (tempstr, "WARNING") != NULL || strstr (tempstr, _("WARNING")) != NULL) { ok = SSH_WARNING; break; } wrotepw = 1; if (gftp_fd_write (request, pwstr, strlen (pwstr), fd) < 0) { ok = 0; break; } } else if (diff > 10 && strcmp (tempstr + diff - 10, "(yes/no)? ") == 0) { ok = SSH_ERROR_QUESTION; break; } else if (diff >= 5 && strcmp (tempstr + diff - 5, "xsftp") == 0) break; else if (rem <= 1) { request->logging_function (gftp_logging_recv, request, "%s", tempstr + lastdiff); len += SSH_LOGIN_BUFSIZE; rem += SSH_LOGIN_BUFSIZE; lastdiff = diff; tempstr = g_realloc (tempstr, len); continue; } } g_free (pwstr); if (*(tempstr + lastdiff) != '\0') request->logging_function (gftp_logging_recv, request, "%s\n", tempstr + lastdiff); if (ok <= 0) { if (ok == SSH_ERROR_BADPASS) request->logging_function (gftp_logging_error, request, _("Error: An incorrect password was entered\n")); else if (ok == SSH_ERROR_QUESTION) request->logging_function (gftp_logging_error, request, _("Please connect to this host with the command line SSH utility and answer this question appropriately.\n")); else if (ok == SSH_WARNING) request->logging_function (gftp_logging_error, request, _("Please correct the above warning to connect to this host.\n")); g_free (tempstr); return (NULL); } return (tempstr); } #ifdef G_HAVE_GINT64 static gint64 hton64 (gint64 val) { if (G_BYTE_ORDER != G_BIG_ENDIAN) return (GINT64_TO_BE (val)); else return (val); } #endif 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, _("%d: Protocol Initialization\n"), id); break; case SSH_FXP_VERSION: memcpy (&num, message, 4); num = ntohl (num); request->logging_function (level, request, _("%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, _("%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_READ: case SSH_FXP_WRITE: break; 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); 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, _("%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_DATA: 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, gint32 len) { char buf[34000]; gint32 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: "); 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, 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 ((int) 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); while ((numread = gftp_fd_read (request, 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 ((int) numread); rem -= numread; pos += numread; } 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 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, _("Received wrong response from server, disconnecting\n")); sshv2_message_free (message); gftp_disconnect (request); return (GFTP_EFATAL); } 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, _("Received wrong response from server, disconnecting\n")); sshv2_message_free (message); gftp_disconnect (request); return (GFTP_EFATAL); } 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, _("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_getcwd (gftp_request * request) { sshv2_message message; sshv2_params * params; char *tempstr, *dir; gint32 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); 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 ((ret = sshv2_send_command (request, SSH_FXP_REALPATH, tempstr, len + 8)) < 0) { g_free (tempstr); return (ret); } g_free (tempstr); if (request->directory) { g_free (request->directory); request->directory = NULL; } memset (&message, 0, sizeof (message)); ret = sshv2_read_response (request, &message, -1); if (ret < 0) return (ret); else if (ret == SSH_FXP_STATUS) { sshv2_message_free (&message); return (GFTP_ERETRYABLE); } else if (ret != SSH_FXP_NAME) { request->logging_function (gftp_logging_error, request, _("Received wrong response from server, disconnecting\n")); sshv2_message_free (&message); gftp_disconnect (request); return (GFTP_EFATAL); } message.pos += 4; if ((ret = sshv2_buffer_get_int32 (request, &message, 1)) < 0) return (ret); if ((request->directory = sshv2_buffer_get_string (request, &message)) == 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) { int version, ret, ssh_use_askpass, sshv2_use_sftp_subsys, fdm; char **args, *tempstr, *p1, p2, *exepath, *ssh2_sftp_path; struct servent serv_struct; sshv2_params * params; sshv2_message message; 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); /* 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 */ gftp_lookup_request_option (request, "ssh2_sftp_path", &ssh2_sftp_path); gftp_lookup_request_option (request, "ssh_use_askpass", &ssh_use_askpass); gftp_lookup_request_option (request, "sshv2_use_sftp_subsys", &sshv2_use_sftp_subsys); if (ssh2_sftp_path == NULL || *ssh2_sftp_path == '\0') { p1 = ""; p2 = ' '; } else { p1 = ssh2_sftp_path; p2 = '/'; } 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); } exepath = g_strdup_printf ("echo -n xsftp ; %s%csftp-server", p1, p2); args = sshv2_gen_exec_args (request, exepath, sshv2_use_sftp_subsys); if (ssh_use_askpass || sshv2_use_sftp_subsys) child = gftp_exec_without_new_pty (request, &fdm, args); else child = gftp_exec_with_new_pty (request, &fdm, args); if (child == 0) exit (0); else if (child < 0) return (GFTP_ERETRYABLE); if (!sshv2_use_sftp_subsys) { tempstr = sshv2_start_login_sequence (request, fdm); if (!tempstr || !(strlen (tempstr) > 4 && strcmp (tempstr + strlen (tempstr) - 5, "xsftp") == 0)) { sshv2_free_args (args); g_free (exepath); return (GFTP_EFATAL); } g_free (tempstr); } sshv2_free_args (args); g_free (exepath); request->datafd = fdm; version = htonl (SSH_MY_VERSION); if ((ret = sshv2_send_command (request, SSH_FXP_INIT, (char *) &version, 4)) < 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) { request->logging_function (gftp_logging_error, request, _("Received wrong response from server, disconnecting\n")); sshv2_message_free (&message); gftp_disconnect (request); return (GFTP_EFATAL); } 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); } static int sshv2_end_transfer (gftp_request * request) { sshv2_params * params; sshv2_message message; gint32 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->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); memset (&message, 0, sizeof (message)); if ((ret = sshv2_read_response (request, &message, -1)) != SSH_FXP_STATUS) { request->logging_function (gftp_logging_error, request, _("Received wrong response from server, disconnecting\n")); sshv2_message_free (&message); gftp_disconnect (request); if (ret < 0) return (ret); else return (GFTP_ERETRYABLE); } 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; 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")); 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 ((ret = sshv2_send_command (request, SSH_FXP_OPENDIR, tempstr, strlen (request->directory) + 8)) < 0) { g_free (tempstr); return (ret); } g_free (tempstr); memset (&message, 0, sizeof (message)); ret = sshv2_read_response (request, &message, -1); if (ret < 0) return (ret); else if (ret == SSH_FXP_STATUS) { sshv2_message_free (&message); return (GFTP_ERETRYABLE); } else if (ret != SSH_FXP_HANDLE) { request->logging_function (gftp_logging_error, request, _("Received wrong response from server, disconnecting\n")); sshv2_message_free (&message); gftp_disconnect (request); return (GFTP_EFATAL); } 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_get_next_file (gftp_request * request, gftp_file * fle, int fd) { gint32 len, attrs, longnamelen; int ret, i, count, retsize; sshv2_params *params; char *longname; 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 ((params->count = sshv2_buffer_get_int32 (request, ¶ms->message, -1)) < 0) return (params->count); } } if (ret == SSH_FXP_NAME) { if ((len = sshv2_buffer_get_int32 (request, ¶ms->message, -1)) < 0 || params->message.pos + len > params->message.end) return (GFTP_EFATAL); params->message.pos += len; if ((longnamelen = sshv2_buffer_get_int32 (request, ¶ms->message, -1)) < 0 || params->message.pos + longnamelen > params->message.end) return (GFTP_EFATAL); longname = params->message.pos; params->message.pos += longnamelen; if ((attrs = sshv2_buffer_get_int32 (request, ¶ms->message, -1)) < 0) return (attrs); if (attrs & SSH_FILEXFER_ATTR_SIZE) { params->message.pos += 8; if (params->message.pos > params->message.end) { request->logging_function (gftp_logging_error, request, _("Received wrong response from server, disconnecting\n")); sshv2_message_free (¶ms->message); gftp_disconnect (request); return (GFTP_EFATAL); } } if (attrs & SSH_FILEXFER_ATTR_UIDGID) { params->message.pos += 8; if (params->message.pos > params->message.end) { request->logging_function (gftp_logging_error, request, _("Received wrong response from server, disconnecting\n")); sshv2_message_free (¶ms->message); gftp_disconnect (request); return (GFTP_EFATAL); } } if (attrs & SSH_FILEXFER_ATTR_PERMISSIONS) { params->message.pos += 4; if (params->message.pos > params->message.end) { request->logging_function (gftp_logging_error, request, _("Received wrong response from server, disconnecting\n")); sshv2_message_free (¶ms->message); gftp_disconnect (request); return (GFTP_EFATAL); } } if (attrs & SSH_FILEXFER_ATTR_ACMODTIME) { params->message.pos += 8; if (params->message.pos > params->message.end) { request->logging_function (gftp_logging_error, request, _("Received wrong response from server, disconnecting\n")); sshv2_message_free (¶ms->message); gftp_disconnect (request); return (GFTP_EFATAL); } } if (attrs & SSH_FILEXFER_ATTR_EXTENDED) { if ((count = sshv2_buffer_get_int32 (request, ¶ms->message, -1)) < 0) return (GFTP_EFATAL); 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 (GFTP_EFATAL); 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 (GFTP_EFATAL); 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 ((ret = gftp_parse_ls (request, longname, fle)) < 0) { gftp_file_destroy (fle); return (ret); } 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, _("Received wrong response from server, disconnecting\n")); sshv2_message_free (¶ms->message); gftp_disconnect (request); return (GFTP_EFATAL); } 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; 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 (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 ((ret = sshv2_send_command (request, SSH_FXP_REALPATH, tempstr, len)) < 0) { g_free (tempstr); return (ret); } g_free (tempstr); memset (&message, 0, sizeof (message)); ret = sshv2_read_response (request, &message, -1); if (ret < 0) return (ret); else if (ret == SSH_FXP_STATUS) { sshv2_message_free (&message); return (GFTP_ERETRYABLE); } else if (ret != SSH_FXP_NAME) { request->logging_function (gftp_logging_error, request, _("Received wrong response from server, disconnecting\n")); sshv2_message_free (&message); gftp_disconnect (request); return (GFTP_EFATAL); } message.pos += 4; if (sshv2_buffer_get_int32 (request, &message, 1) != 1) return (GFTP_EFATAL); if ((dir = sshv2_buffer_get_string (request, &message)) == NULL) return (GFTP_EFATAL); if (request->directory) g_free (request->directory); request->directory = dir; sshv2_message_free (&message); return (0); } else return (sshv2_getcwd (request)); } static int sshv2_rmdir (gftp_request * request, const char *directory) { sshv2_params * params; sshv2_message message; char *tempstr; gint32 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 (directory != NULL, GFTP_EFATAL); 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 (GFTP_ERETRYABLE); } g_free (tempstr); 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)) < 0) return (ret); 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; 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); 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 (GFTP_ERETRYABLE); } g_free (tempstr); 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)) < 0) return (ret); 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; 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); 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 (GFTP_ERETRYABLE); } g_free (tempstr); 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)) < 0) return (ret); 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; 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); 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 (GFTP_ERETRYABLE); } g_free (tempstr); 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)) < 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; sshv2_params * params; sshv2_message message; size_t oldlen, newlen; gint32 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 (oldname != NULL, GFTP_EFATAL); g_return_val_if_fail (newname != NULL, GFTP_EFATAL); params = request->protocol_data; if (*oldname == '/') { oldlen = strlen (oldname); oldstr = g_strdup (oldname); } else { oldlen = strlen (request->directory) + strlen (oldname) + 1; oldstr = g_build_path ("/", request->directory, oldname, NULL); } if (*newname == '/') { newlen = strlen (newname); newstr = g_strdup (newname); } else { newlen = strlen (request->directory) + strlen (newname) + 1; newstr = g_build_path ("/", 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 (GFTP_ERETRYABLE); } g_free (tempstr); 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)) < 0) return (ret); 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; 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); 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 (GFTP_ERETRYABLE); } g_free (tempstr); 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)) < 0) return (ret); 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; int serv_ret; #ifdef G_HAVE_GINT64 gint64 ret; #endif 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); 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 (GFTP_ERETRYABLE); } g_free (tempstr); memset (¶ms->message, 0, sizeof (params->message)); serv_ret = sshv2_read_response (request, ¶ms->message, -1); if (serv_ret < 0) return (serv_ret); else if (serv_ret == SSH_FXP_STATUS) { sshv2_message_free (¶ms->message); return (GFTP_ERETRYABLE); } else if (serv_ret != SSH_FXP_ATTRS) { request->logging_function (gftp_logging_error, request, _("Received wrong response from server, disconnecting\n")); sshv2_message_free (¶ms->message); gftp_disconnect (request); return (GFTP_ERETRYABLE); } if (params->message.length < 5) return (GFTP_ERETRYABLE); params->message.pos += 4; if ((attrs = sshv2_buffer_get_int32 (request, ¶ms->message, -1)) < 0) return (GFTP_ERETRYABLE); if (attrs & SSH_FILEXFER_ATTR_SIZE) { if ((highnum = sshv2_buffer_get_int32 (request, ¶ms->message, -1)) < 0) return (GFTP_ERETRYABLE); if ((lownum = sshv2_buffer_get_int32 (request, ¶ms->message, -1)) < 0) return (GFTP_ERETRYABLE); 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 off_t sshv2_get_file (gftp_request * request, const char *file, int fd, off_t startsize) { sshv2_params * params; sshv2_message message; char *tempstr; size_t stlen; gint32 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); /* 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 (GFTP_ERETRYABLE); } g_free (tempstr); memset (&message, 0, sizeof (message)); ret = sshv2_read_response (request, &message, -1); if (ret < 0) return (ret); else if (ret == SSH_FXP_STATUS) { sshv2_message_free (&message); return (GFTP_ERETRYABLE); } else if (ret != SSH_FXP_HANDLE) { request->logging_function (gftp_logging_error, request, _("Received wrong response from server, disconnecting\n")); sshv2_message_free (&message); gftp_disconnect (request); return (GFTP_ERETRYABLE); } 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 (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) { sshv2_params * params; sshv2_message message; char *tempstr; size_t stlen; gint32 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); /* 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 (GFTP_ERETRYABLE); } g_free (tempstr); memset (&message, 0, sizeof (message)); ret = sshv2_read_response (request, &message, -1); if (ret < 0) return (ret); else if (ret == SSH_FXP_STATUS) { sshv2_message_free (&message); return (GFTP_ERETRYABLE); } else if (ret != SSH_FXP_HANDLE) { request->logging_function (gftp_logging_error, request, _("Received wrong response from server, disconnecting\n")); sshv2_message_free (&message); gftp_disconnect (request); return (GFTP_ERETRYABLE); } 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 ssize_t sshv2_get_next_file_chunk (gftp_request * request, char *buf, size_t size) { sshv2_params * params; sshv2_message message; gint32 num; int ret; #ifdef G_HAVE_GINT64 gint64 offset; #else gint32 offset; #endif 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->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 (GFTP_ERETRYABLE); 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 ((num = sshv2_buffer_get_int32 (request, &message, SSH_FX_OK)) < 0) return (num); sshv2_message_free (&message); if (num != SSH_FX_EOF) { request->logging_function (gftp_logging_error, request, _("Received wrong response from server, disconnecting\n")); gftp_disconnect (request); return (GFTP_ERETRYABLE); } 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; char tempstr[32768]; gint32 num; int ret; #ifdef G_HAVE_GINT64 gint64 offset; #else gint32 offset; #endif 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); g_return_val_if_fail (size <= 32500, GFTP_EFATAL); 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 (GFTP_ERETRYABLE); } 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) { request->logging_function (gftp_logging_error, request, _("Received wrong response from server, disconnecting\n")); sshv2_message_free (&message); gftp_disconnect (request); return (GFTP_ERETRYABLE); } message.pos += 4; if ((num = sshv2_buffer_get_int32 (request, &message, SSH_FX_OK)) < 0) return (GFTP_ERETRYABLE); sshv2_message_free (&message); if (num == SSH_FX_EOF) return (0); else if (num != SSH_FX_OK) return (GFTP_ERETRYABLE); params->offset += size; return (size); } static int sshv2_set_config_options (gftp_request * request) { int ssh_need_userpass; gftp_lookup_request_option (request, "ssh_need_userpass", &ssh_need_userpass); request->need_userpass = 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); } 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->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->list_files = sshv2_list_files; request->get_next_file = sshv2_get_next_file; 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_userpass = 1; request->use_cache = 1; request->use_threads = 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)); }