2008-09-29 14:04:01 +00:00
|
|
|
/* Copyright (c) 2004-2008, Sara Golemon <sarag@libssh2.org>
|
2004-12-07 21:17:20 +00:00
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms,
|
|
|
|
* with or without modification, are permitted provided
|
|
|
|
* that the following conditions are met:
|
|
|
|
*
|
|
|
|
* Redistributions of source code must retain the above
|
|
|
|
* copyright notice, this list of conditions and the
|
|
|
|
* following disclaimer.
|
|
|
|
*
|
|
|
|
* Redistributions in binary form must reproduce the above
|
|
|
|
* copyright notice, this list of conditions and the following
|
|
|
|
* disclaimer in the documentation and/or other materials
|
|
|
|
* provided with the distribution.
|
|
|
|
*
|
|
|
|
* Neither the name of the copyright holder nor the names
|
|
|
|
* of any other contributors may be used to endorse or
|
|
|
|
* promote products derived from this software without
|
|
|
|
* specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
|
|
|
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
|
|
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
|
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
|
|
|
|
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
|
|
|
* OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "libssh2_priv.h"
|
|
|
|
#include <errno.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
2009-08-30 17:00:49 +02:00
|
|
|
#include "channel.h"
|
|
|
|
|
2009-03-15 23:28:25 +00:00
|
|
|
|
|
|
|
/* Max. length of a quoted string after libssh2_shell_quotearg() processing */
|
|
|
|
#define libssh2_shell_quotedsize(s) (3 * strlen(s) + 2)
|
|
|
|
|
|
|
|
/*
|
|
|
|
This function quotes a string in a way suitable to be used with a
|
|
|
|
shell, e.g. the file name
|
|
|
|
one two
|
|
|
|
becomes
|
|
|
|
'one two'
|
|
|
|
|
|
|
|
The resulting output string is crafted in a way that makes it usable
|
|
|
|
with the two most common shell types: Bourne Shell derived shells
|
|
|
|
(sh, ksh, ksh93, bash, zsh) and C-Shell derivates (csh, tcsh).
|
|
|
|
|
|
|
|
The following special cases are handled:
|
|
|
|
o If the string contains an apostrophy itself, the apostrophy
|
|
|
|
character is written in quotation marks, e.g. "'".
|
|
|
|
The shell cannot handle the syntax 'doesn\'t', so we close the
|
|
|
|
current argument word, add the apostrophe in quotation marks "",
|
|
|
|
and open a new argument word instead (_ indicate the input
|
|
|
|
string characters):
|
|
|
|
_____ _ _
|
|
|
|
'doesn' "'" 't'
|
|
|
|
|
|
|
|
Sequences of apostrophes are combined in one pair of quotation marks:
|
|
|
|
a'''b
|
|
|
|
becomes
|
|
|
|
_ ___ _
|
|
|
|
'a'"'''"'b'
|
|
|
|
|
|
|
|
o If the string contains an exclamation mark (!), the C-Shell
|
|
|
|
interprets it as an event number. Using \! (not within quotation
|
|
|
|
marks or single quotation marks) is a mechanism understood by
|
|
|
|
both Bourne Shell and C-Shell.
|
|
|
|
|
|
|
|
If a quotation was already started, the argument word is closed
|
|
|
|
first:
|
|
|
|
a!b
|
|
|
|
|
|
|
|
become
|
|
|
|
_ _ _
|
|
|
|
'a'\!'b'
|
|
|
|
|
|
|
|
The result buffer must be large enough for the expanded result. A
|
|
|
|
bad case regarding expansion is alternating characters and
|
|
|
|
apostrophes:
|
|
|
|
|
|
|
|
a'b'c'd' (length 8) gets converted to
|
|
|
|
'a'"'"'b'"'"'c'"'"'d'"'" (length 24)
|
|
|
|
|
|
|
|
This is the worst case.
|
|
|
|
|
|
|
|
Maximum length of the result:
|
|
|
|
1 + 6 * (length(input) + 1) / 2) + 1
|
|
|
|
|
|
|
|
=> 3 * length(input) + 2
|
|
|
|
|
|
|
|
Explanation:
|
|
|
|
o leading apostrophe
|
|
|
|
o one character / apostrophe pair (two characters) can get
|
|
|
|
represented as 6 characters: a' -> a'"'"'
|
|
|
|
o String terminator (+1)
|
|
|
|
|
|
|
|
A result buffer three times the size of the input buffer + 2
|
|
|
|
characters should be safe.
|
|
|
|
|
|
|
|
References:
|
|
|
|
o csh-compatible quotation (special handling for '!' etc.), see
|
|
|
|
http://www.grymoire.com/Unix/Csh.html#toc-uh-10
|
|
|
|
|
|
|
|
Return value:
|
|
|
|
Length of the resulting string (not counting the terminating '\0'),
|
|
|
|
or 0 in case of errors, e.g. result buffer too small
|
|
|
|
|
|
|
|
Note: this function could possible be used elsewhere within libssh2, but
|
|
|
|
until then it is kept static and in this source file.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static unsigned
|
|
|
|
libssh2_shell_quotearg(const char *path, unsigned char *buf,
|
|
|
|
unsigned bufsize)
|
|
|
|
{
|
|
|
|
const char *src;
|
|
|
|
unsigned char *dst, *endp;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Processing States:
|
|
|
|
* UQSTRING: unquoted string: ... -- used for quoting exclamation
|
|
|
|
* marks. This is the initial state
|
|
|
|
* SQSTRING: single-qouted-string: '... -- any character may follow
|
|
|
|
* QSTRING: quoted string: "... -- only apostrophes may follow
|
|
|
|
*/
|
|
|
|
enum { UQSTRING, SQSTRING, QSTRING } state = UQSTRING;
|
|
|
|
|
|
|
|
endp = &buf[bufsize];
|
|
|
|
src = path;
|
|
|
|
dst = buf;
|
|
|
|
while (*src && dst < endp - 1) {
|
|
|
|
|
|
|
|
switch (*src) {
|
|
|
|
/*
|
|
|
|
* Special handling for apostrophe.
|
|
|
|
* An apostrophe is always written in quotation marks, e.g.
|
|
|
|
* ' -> "'".
|
|
|
|
*/
|
|
|
|
|
|
|
|
case '\'':
|
|
|
|
switch (state) {
|
|
|
|
case UQSTRING: /* Unquoted string */
|
|
|
|
if (dst+1 >= endp)
|
|
|
|
return 0;
|
|
|
|
*dst++ = '"';
|
|
|
|
break;
|
|
|
|
case QSTRING: /* Continue quoted string */
|
|
|
|
break;
|
|
|
|
case SQSTRING: /* Close single quoted string */
|
|
|
|
if (dst+2 >= endp)
|
|
|
|
return 0;
|
|
|
|
*dst++ = '\'';
|
|
|
|
*dst++ = '"';
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
state = QSTRING;
|
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Special handling for exclamation marks. CSH interprets
|
|
|
|
* exclamation marks even when quoted with apostrophes. We convert
|
|
|
|
* it to the plain string \!, because both Bourne Shell and CSH
|
|
|
|
* interpret that as a verbatim exclamation mark.
|
|
|
|
*/
|
|
|
|
|
|
|
|
case '!':
|
|
|
|
switch (state) {
|
|
|
|
case UQSTRING:
|
|
|
|
if (dst+1 >= endp)
|
|
|
|
return 0;
|
|
|
|
*dst++ = '\\';
|
|
|
|
break;
|
|
|
|
case QSTRING:
|
|
|
|
if (dst+2 >= endp)
|
|
|
|
return 0;
|
|
|
|
*dst++ = '"'; /* Closing quotation mark */
|
|
|
|
*dst++ = '\\';
|
|
|
|
break;
|
|
|
|
case SQSTRING: /* Close single quoted string */
|
|
|
|
if (dst+2 >= endp)
|
|
|
|
return 0;
|
|
|
|
*dst++ = '\'';
|
|
|
|
*dst++ = '\\';
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
state = UQSTRING;
|
|
|
|
break;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Ordinary character: prefer single-quoted string
|
|
|
|
*/
|
|
|
|
|
|
|
|
default:
|
|
|
|
switch (state) {
|
|
|
|
case UQSTRING:
|
|
|
|
if (dst+1 >= endp)
|
|
|
|
return 0;
|
|
|
|
*dst++ = '\'';
|
|
|
|
break;
|
|
|
|
case QSTRING:
|
|
|
|
if (dst+2 >= endp)
|
|
|
|
return 0;
|
|
|
|
*dst++ = '"'; /* Closing quotation mark */
|
|
|
|
*dst++ = '\'';
|
|
|
|
break;
|
|
|
|
case SQSTRING: /* Continue single quoted string */
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
state = SQSTRING; /* Start single-quoted string */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dst+1 >= endp)
|
|
|
|
return 0;
|
|
|
|
*dst++ = *src++;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (state) {
|
|
|
|
case UQSTRING:
|
|
|
|
break;
|
|
|
|
case QSTRING: /* Close quoted string */
|
|
|
|
if (dst+1 >= endp)
|
|
|
|
return 0;
|
|
|
|
*dst++ = '"';
|
|
|
|
break;
|
|
|
|
case SQSTRING: /* Close single quoted string */
|
|
|
|
if (dst+1 >= endp)
|
|
|
|
return 0;
|
|
|
|
*dst++ = '\'';
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dst+1 >= endp)
|
|
|
|
return 0;
|
|
|
|
*dst = '\0';
|
|
|
|
|
|
|
|
/* The result cannot be larger than 3 * strlen(path) + 2 */
|
|
|
|
/* assert((dst - buf) <= (3 * (src - path) + 2)); */
|
|
|
|
|
|
|
|
return dst - buf;
|
|
|
|
}
|
|
|
|
|
2009-03-15 23:07:09 +00:00
|
|
|
/*
|
|
|
|
* libssh2_scp_recv
|
|
|
|
*
|
2004-12-07 21:17:20 +00:00
|
|
|
* Open a channel and request a remote file via SCP
|
2007-06-12 18:27:37 +00:00
|
|
|
*
|
2004-12-07 21:17:20 +00:00
|
|
|
*/
|
2007-08-06 20:48:04 +00:00
|
|
|
LIBSSH2_API LIBSSH2_CHANNEL *
|
|
|
|
libssh2_scp_recv(LIBSSH2_SESSION * session, const char *path, struct stat * sb)
|
2004-12-07 21:17:20 +00:00
|
|
|
{
|
2009-03-15 23:28:25 +00:00
|
|
|
int cmd_len;
|
2007-06-06 12:34:06 +00:00
|
|
|
int rc;
|
|
|
|
|
|
|
|
if (session->scpRecv_state == libssh2_NB_state_idle) {
|
|
|
|
session->scpRecv_mode = 0;
|
|
|
|
session->scpRecv_size = 0;
|
|
|
|
session->scpRecv_mtime = 0;
|
|
|
|
session->scpRecv_atime = 0;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-03-15 23:28:25 +00:00
|
|
|
session->scpRecv_command_len =
|
|
|
|
libssh2_shell_quotedsize(path) + sizeof("scp -f ") + (sb?1:0);
|
2007-05-28 17:56:08 +00:00
|
|
|
|
2007-08-06 20:48:04 +00:00
|
|
|
session->scpRecv_command =
|
|
|
|
LIBSSH2_ALLOC(session, session->scpRecv_command_len);
|
2009-03-15 23:28:25 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
if (!session->scpRecv_command) {
|
2007-08-06 20:48:04 +00:00
|
|
|
libssh2_error(session, LIBSSH2_ERROR_ALLOC,
|
|
|
|
"Unable to allocate a command buffer for SCP session",
|
|
|
|
0);
|
2007-05-28 17:56:08 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2009-03-15 22:38:47 +00:00
|
|
|
|
|
|
|
/* sprintf() is fine here since we allocated a large enough buffer */
|
2009-03-15 23:28:25 +00:00
|
|
|
sprintf((char *)session->scpRecv_command, "scp -%sf ", sb?"p":"");
|
|
|
|
|
|
|
|
cmd_len = strlen((char *)session->scpRecv_command);
|
|
|
|
|
|
|
|
(void) libssh2_shell_quotearg(path,
|
|
|
|
&session->scpRecv_command[cmd_len],
|
|
|
|
session->scpRecv_command_len - cmd_len);
|
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
|
2007-08-06 20:48:04 +00:00
|
|
|
_libssh2_debug(session, LIBSSH2_DBG_SCP,
|
|
|
|
"Opening channel for SCP receive");
|
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
session->scpRecv_state = libssh2_NB_state_created;
|
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
if (session->scpRecv_state == libssh2_NB_state_created) {
|
|
|
|
/* Allocate a channel */
|
2009-03-15 23:07:09 +00:00
|
|
|
session->scpRecv_channel =
|
|
|
|
libssh2_channel_open_ex(session, "session",
|
|
|
|
sizeof("session") - 1,
|
|
|
|
LIBSSH2_CHANNEL_WINDOW_DEFAULT,
|
|
|
|
LIBSSH2_CHANNEL_PACKET_DEFAULT, NULL,
|
|
|
|
0);
|
|
|
|
if (!session->scpRecv_channel) {
|
|
|
|
if (libssh2_session_last_errno(session) !=
|
|
|
|
LIBSSH2_ERROR_EAGAIN) {
|
|
|
|
LIBSSH2_FREE(session, session->scpRecv_command);
|
|
|
|
session->scpRecv_command = NULL;
|
|
|
|
session->scpRecv_state = libssh2_NB_state_idle;
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2009-03-15 23:07:09 +00:00
|
|
|
else {
|
|
|
|
libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
|
|
|
|
"Would block starting up channel", 0);
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
session->scpRecv_state = libssh2_NB_state_sent;
|
|
|
|
}
|
2007-05-28 17:56:08 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
if (session->scpRecv_state == libssh2_NB_state_sent) {
|
|
|
|
/* Request SCP for the desired file */
|
2007-08-06 20:48:04 +00:00
|
|
|
rc = libssh2_channel_process_startup(session->scpRecv_channel, "exec",
|
|
|
|
sizeof("exec") - 1,
|
|
|
|
(char *) session->scpRecv_command,
|
|
|
|
session->scpRecv_command_len);
|
2007-06-06 12:34:06 +00:00
|
|
|
if (rc == PACKET_EAGAIN) {
|
2007-08-06 20:48:04 +00:00
|
|
|
libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
|
|
|
|
"Would block requesting SCP startup", 0);
|
2007-05-28 17:56:08 +00:00
|
|
|
return NULL;
|
2007-08-06 20:48:04 +00:00
|
|
|
} else if (rc) {
|
2007-06-06 12:34:06 +00:00
|
|
|
LIBSSH2_FREE(session, session->scpRecv_command);
|
|
|
|
session->scpRecv_command = NULL;
|
2007-06-12 18:27:37 +00:00
|
|
|
goto scp_recv_error;
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-06-06 12:34:06 +00:00
|
|
|
LIBSSH2_FREE(session, session->scpRecv_command);
|
|
|
|
session->scpRecv_command = NULL;
|
2007-05-28 17:56:08 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
_libssh2_debug(session, LIBSSH2_DBG_SCP, "Sending initial wakeup");
|
|
|
|
/* SCP ACK */
|
|
|
|
session->scpRecv_response[0] = '\0';
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
session->scpRecv_state = libssh2_NB_state_sent1;
|
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
if (session->scpRecv_state == libssh2_NB_state_sent1) {
|
2007-08-06 20:48:04 +00:00
|
|
|
rc = libssh2_channel_write_ex(session->scpRecv_channel, 0,
|
|
|
|
(char *) session->scpRecv_response, 1);
|
2007-06-06 12:34:06 +00:00
|
|
|
if (rc == PACKET_EAGAIN) {
|
2007-08-06 20:48:04 +00:00
|
|
|
libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
|
|
|
|
"Would block sending initial wakeup", 0);
|
2007-05-28 17:56:08 +00:00
|
|
|
return NULL;
|
2007-08-06 20:48:04 +00:00
|
|
|
} else if (rc != 1) {
|
2007-06-12 18:27:37 +00:00
|
|
|
goto scp_recv_error;
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
/* Parse SCP response */
|
|
|
|
session->scpRecv_response_len = 0;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
session->scpRecv_state = libssh2_NB_state_sent2;
|
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
|
|
|
if ((session->scpRecv_state == libssh2_NB_state_sent2)
|
|
|
|
|| (session->scpRecv_state == libssh2_NB_state_sent3)) {
|
2009-03-15 23:07:09 +00:00
|
|
|
while (sb && (session->scpRecv_response_len <
|
|
|
|
LIBSSH2_SCP_RESPONSE_BUFLEN)) {
|
2007-06-06 12:34:06 +00:00
|
|
|
unsigned char *s, *p;
|
|
|
|
|
|
|
|
if (session->scpRecv_state == libssh2_NB_state_sent2) {
|
2009-08-30 17:00:49 +02:00
|
|
|
rc = _libssh2_channel_read(session->scpRecv_channel, 0,
|
|
|
|
(char *) session->
|
|
|
|
scpRecv_response +
|
|
|
|
session->scpRecv_response_len, 1);
|
2007-06-06 12:34:06 +00:00
|
|
|
if (rc == PACKET_EAGAIN) {
|
2007-08-06 20:48:04 +00:00
|
|
|
libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
|
|
|
|
"Would block waiting for SCP response", 0);
|
2007-06-06 12:34:06 +00:00
|
|
|
return NULL;
|
2007-08-06 20:48:04 +00:00
|
|
|
} else if (rc <= 0) {
|
2007-06-06 12:34:06 +00:00
|
|
|
/* Timeout, give up */
|
2007-08-06 20:48:04 +00:00
|
|
|
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
|
|
|
|
"Timed out waiting for SCP response", 0);
|
2007-06-12 18:27:37 +00:00
|
|
|
goto scp_recv_error;
|
2007-06-06 12:34:06 +00:00
|
|
|
}
|
|
|
|
session->scpRecv_response_len++;
|
|
|
|
|
|
|
|
if (session->scpRecv_response[0] != 'T') {
|
2007-07-05 15:31:19 +00:00
|
|
|
/*
|
|
|
|
* Set this as the default error for here, if
|
|
|
|
* we are successful it will be replaced
|
|
|
|
*/
|
2007-08-06 20:48:04 +00:00
|
|
|
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
|
|
|
|
"Invalid data in SCP response, missing Time data",
|
|
|
|
0);
|
|
|
|
|
|
|
|
session->scpRecv_err_len =
|
2009-03-17 13:48:35 +00:00
|
|
|
_libssh2_channel_packet_data_len(session->
|
|
|
|
scpRecv_channel, 0);
|
2007-08-06 20:48:04 +00:00
|
|
|
session->scpRecv_err_msg =
|
|
|
|
LIBSSH2_ALLOC(session, session->scpRecv_err_len + 1);
|
2007-07-05 15:31:19 +00:00
|
|
|
if (!session->scpRecv_err_msg) {
|
|
|
|
goto scp_recv_error;
|
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
memset(session->scpRecv_err_msg, 0,
|
|
|
|
session->scpRecv_err_len + 1);
|
|
|
|
|
2007-07-05 15:31:19 +00:00
|
|
|
/* Read the remote error message */
|
2009-08-30 17:00:49 +02:00
|
|
|
rc = _libssh2_channel_read(session->scpRecv_channel, 0,
|
|
|
|
session->scpRecv_err_msg,
|
|
|
|
session->scpRecv_err_len);
|
2007-07-05 15:31:19 +00:00
|
|
|
if (rc <= 0) {
|
|
|
|
/*
|
2009-03-15 23:07:09 +00:00
|
|
|
* Since we have alread started reading this packet,
|
|
|
|
* it is already in the systems so it can't return
|
|
|
|
* PACKET_EAGAIN
|
2007-07-05 15:31:19 +00:00
|
|
|
*/
|
|
|
|
LIBSSH2_FREE(session, session->scpRecv_err_msg);
|
|
|
|
session->scpRecv_err_msg = NULL;
|
2007-08-06 20:48:04 +00:00
|
|
|
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
|
|
|
|
"Unknown error while getting error string",
|
|
|
|
0);
|
2007-07-05 15:31:19 +00:00
|
|
|
goto scp_recv_error;
|
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
|
|
|
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
|
|
|
|
session->scpRecv_err_msg, 1);
|
2007-07-19 15:02:37 +00:00
|
|
|
session->scpRecv_err_msg = NULL;
|
2007-06-12 18:27:37 +00:00
|
|
|
goto scp_recv_error;
|
2007-06-06 12:34:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((session->scpRecv_response_len > 1) &&
|
2007-08-06 20:48:04 +00:00
|
|
|
((session->
|
|
|
|
scpRecv_response[session->scpRecv_response_len - 1] <
|
|
|
|
'0')
|
|
|
|
|| (session->
|
|
|
|
scpRecv_response[session->scpRecv_response_len - 1] >
|
|
|
|
'9'))
|
|
|
|
&& (session->
|
|
|
|
scpRecv_response[session->scpRecv_response_len - 1] !=
|
|
|
|
' ')
|
|
|
|
&& (session->
|
|
|
|
scpRecv_response[session->scpRecv_response_len - 1] !=
|
|
|
|
'\r')
|
|
|
|
&& (session->
|
|
|
|
scpRecv_response[session->scpRecv_response_len - 1] !=
|
|
|
|
'\n')) {
|
|
|
|
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
|
|
|
|
"Invalid data in SCP response", 0);
|
2007-06-12 18:27:37 +00:00
|
|
|
goto scp_recv_error;
|
2007-06-06 12:34:06 +00:00
|
|
|
}
|
|
|
|
|
2007-08-06 20:48:04 +00:00
|
|
|
if ((session->scpRecv_response_len < 9)
|
|
|
|
|| (session->
|
|
|
|
scpRecv_response[session->scpRecv_response_len - 1] !=
|
|
|
|
'\n')) {
|
|
|
|
if (session->scpRecv_response_len ==
|
|
|
|
LIBSSH2_SCP_RESPONSE_BUFLEN) {
|
2007-06-06 12:34:06 +00:00
|
|
|
/* You had your chance */
|
2007-08-06 20:48:04 +00:00
|
|
|
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
|
|
|
|
"Unterminated response from SCP server",
|
|
|
|
0);
|
2007-06-12 18:27:37 +00:00
|
|
|
goto scp_recv_error;
|
2007-06-06 12:34:06 +00:00
|
|
|
}
|
2009-03-15 23:07:09 +00:00
|
|
|
/* Way too short to be an SCP response, or not done yet,
|
|
|
|
short circuit */
|
2007-06-06 12:34:06 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2009-03-15 23:07:09 +00:00
|
|
|
/* We're guaranteed not to go under response_len == 0 by the
|
|
|
|
logic above */
|
2007-08-06 20:48:04 +00:00
|
|
|
while ((session->
|
|
|
|
scpRecv_response[session->scpRecv_response_len - 1] ==
|
|
|
|
'\r')
|
|
|
|
|| (session->
|
|
|
|
scpRecv_response[session->scpRecv_response_len -
|
|
|
|
1] == '\n'))
|
|
|
|
session->scpRecv_response_len--;
|
|
|
|
session->scpRecv_response[session->scpRecv_response_len] =
|
|
|
|
'\0';
|
2007-06-06 12:34:06 +00:00
|
|
|
|
|
|
|
if (session->scpRecv_response_len < 8) {
|
|
|
|
/* EOL came too soon */
|
2007-08-06 20:48:04 +00:00
|
|
|
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
|
|
|
|
"Invalid response from SCP server, too short",
|
|
|
|
0);
|
2007-06-12 18:27:37 +00:00
|
|
|
goto scp_recv_error;
|
2007-06-06 12:34:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
s = session->scpRecv_response + 1;
|
|
|
|
|
2007-08-06 20:48:04 +00:00
|
|
|
p = (unsigned char *) strchr((char *) s, ' ');
|
2007-06-06 12:34:06 +00:00
|
|
|
if (!p || ((p - s) <= 0)) {
|
|
|
|
/* No spaces or space in the wrong spot */
|
2007-08-06 20:48:04 +00:00
|
|
|
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
|
|
|
|
"Invalid response from SCP server, malformed mtime",
|
|
|
|
0);
|
2007-06-12 18:27:37 +00:00
|
|
|
goto scp_recv_error;
|
2007-06-06 12:34:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
*(p++) = '\0';
|
|
|
|
/* Make sure we don't get fooled by leftover values */
|
|
|
|
errno = 0;
|
2007-08-06 20:48:04 +00:00
|
|
|
session->scpRecv_mtime = strtol((char *) s, NULL, 10);
|
2007-06-06 12:34:06 +00:00
|
|
|
if (errno) {
|
2007-08-06 20:48:04 +00:00
|
|
|
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
|
|
|
|
"Invalid response from SCP server, invalid mtime",
|
|
|
|
0);
|
2007-06-12 18:27:37 +00:00
|
|
|
goto scp_recv_error;
|
2007-06-06 12:34:06 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
s = (unsigned char *) strchr((char *) p, ' ');
|
2007-06-06 12:34:06 +00:00
|
|
|
if (!s || ((s - p) <= 0)) {
|
|
|
|
/* No spaces or space in the wrong spot */
|
2007-08-06 20:48:04 +00:00
|
|
|
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
|
|
|
|
"Invalid response from SCP server, malformed mtime.usec",
|
|
|
|
0);
|
2007-06-12 18:27:37 +00:00
|
|
|
goto scp_recv_error;
|
2007-06-06 12:34:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Ignore mtime.usec */
|
|
|
|
s++;
|
2007-08-06 20:48:04 +00:00
|
|
|
p = (unsigned char *) strchr((char *) s, ' ');
|
2007-06-06 12:34:06 +00:00
|
|
|
if (!p || ((p - s) <= 0)) {
|
|
|
|
/* No spaces or space in the wrong spot */
|
2007-08-06 20:48:04 +00:00
|
|
|
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
|
|
|
|
"Invalid response from SCP server, too short or malformed",
|
|
|
|
0);
|
2007-06-12 18:27:37 +00:00
|
|
|
goto scp_recv_error;
|
2007-06-06 12:34:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
*(p++) = '\0';
|
|
|
|
/* Make sure we don't get fooled by leftover values */
|
|
|
|
errno = 0;
|
2007-08-06 20:48:04 +00:00
|
|
|
session->scpRecv_atime = strtol((char *) s, NULL, 10);
|
2007-06-06 12:34:06 +00:00
|
|
|
if (errno) {
|
2007-08-06 20:48:04 +00:00
|
|
|
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
|
|
|
|
"Invalid response from SCP server, invalid atime",
|
|
|
|
0);
|
2007-06-12 18:27:37 +00:00
|
|
|
goto scp_recv_error;
|
2007-06-06 12:34:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* SCP ACK */
|
|
|
|
session->scpRecv_response[0] = '\0';
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
session->scpRecv_state = libssh2_NB_state_sent3;
|
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
if (session->scpRecv_state == libssh2_NB_state_sent3) {
|
2007-08-06 20:48:04 +00:00
|
|
|
rc = libssh2_channel_write_ex(session->scpRecv_channel, 0,
|
|
|
|
(char *) session->
|
|
|
|
scpRecv_response, 1);
|
2007-06-06 12:34:06 +00:00
|
|
|
if (rc == PACKET_EAGAIN) {
|
2007-08-06 20:48:04 +00:00
|
|
|
libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
|
|
|
|
"Would block waiting to send SCP ACK", 0);
|
2007-06-06 12:34:06 +00:00
|
|
|
return NULL;
|
2007-08-06 20:48:04 +00:00
|
|
|
} else if (rc != 1) {
|
2007-06-12 18:27:37 +00:00
|
|
|
goto scp_recv_error;
|
2007-06-06 12:34:06 +00:00
|
|
|
}
|
|
|
|
|
2007-08-06 20:48:04 +00:00
|
|
|
_libssh2_debug(session, LIBSSH2_DBG_SCP,
|
|
|
|
"mtime = %ld, atime = %ld",
|
|
|
|
session->scpRecv_mtime, session->scpRecv_atime);
|
2007-06-06 12:34:06 +00:00
|
|
|
|
2009-03-15 23:07:09 +00:00
|
|
|
/* We *should* check that atime.usec is valid, but why let
|
|
|
|
that stop use? */
|
2007-06-06 12:34:06 +00:00
|
|
|
break;
|
|
|
|
}
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
session->scpRecv_state = libssh2_NB_state_sent4;
|
|
|
|
}
|
2007-05-28 17:56:08 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
if (session->scpRecv_state == libssh2_NB_state_sent4) {
|
|
|
|
session->scpRecv_response_len = 0;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
session->scpRecv_state = libssh2_NB_state_sent5;
|
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
|
|
|
if ((session->scpRecv_state == libssh2_NB_state_sent5)
|
|
|
|
|| (session->scpRecv_state == libssh2_NB_state_sent6)) {
|
2007-06-06 12:34:06 +00:00
|
|
|
while (session->scpRecv_response_len < LIBSSH2_SCP_RESPONSE_BUFLEN) {
|
|
|
|
char *s, *p, *e = NULL;
|
|
|
|
|
|
|
|
if (session->scpRecv_state == libssh2_NB_state_sent5) {
|
2009-08-30 17:00:49 +02:00
|
|
|
rc = _libssh2_channel_read(session->scpRecv_channel, 0,
|
|
|
|
(char *) session->
|
|
|
|
scpRecv_response +
|
|
|
|
session->scpRecv_response_len, 1);
|
2007-06-06 12:34:06 +00:00
|
|
|
if (rc == PACKET_EAGAIN) {
|
2007-08-06 20:48:04 +00:00
|
|
|
libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
|
|
|
|
"Would block waiting for SCP response", 0);
|
2007-06-06 12:34:06 +00:00
|
|
|
return NULL;
|
2007-08-06 20:48:04 +00:00
|
|
|
} else if (rc <= 0) {
|
2007-06-06 12:34:06 +00:00
|
|
|
/* Timeout, give up */
|
2007-08-06 20:48:04 +00:00
|
|
|
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
|
|
|
|
"Timed out waiting for SCP response", 0);
|
2007-06-12 18:27:37 +00:00
|
|
|
goto scp_recv_error;
|
2007-06-06 12:34:06 +00:00
|
|
|
}
|
|
|
|
session->scpRecv_response_len++;
|
|
|
|
|
|
|
|
if (session->scpRecv_response[0] != 'C') {
|
2007-08-06 20:48:04 +00:00
|
|
|
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
|
|
|
|
"Invalid response from SCP server", 0);
|
2007-06-12 18:27:37 +00:00
|
|
|
goto scp_recv_error;
|
2007-06-06 12:34:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((session->scpRecv_response_len > 1) &&
|
2007-08-06 20:48:04 +00:00
|
|
|
(session->
|
|
|
|
scpRecv_response[session->scpRecv_response_len - 1] !=
|
|
|
|
'\r')
|
|
|
|
&& (session->
|
|
|
|
scpRecv_response[session->scpRecv_response_len - 1] !=
|
|
|
|
'\n')
|
|
|
|
&&
|
|
|
|
((session->
|
|
|
|
scpRecv_response[session->scpRecv_response_len - 1] < 32)
|
|
|
|
|| (session->
|
|
|
|
scpRecv_response[session->scpRecv_response_len - 1] >
|
|
|
|
126))) {
|
|
|
|
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
|
|
|
|
"Invalid data in SCP response", 0);
|
2007-06-12 18:27:37 +00:00
|
|
|
goto scp_recv_error;
|
2007-06-06 12:34:06 +00:00
|
|
|
}
|
|
|
|
|
2007-08-06 20:48:04 +00:00
|
|
|
if ((session->scpRecv_response_len < 7)
|
|
|
|
|| (session->
|
|
|
|
scpRecv_response[session->scpRecv_response_len - 1] !=
|
|
|
|
'\n')) {
|
|
|
|
if (session->scpRecv_response_len ==
|
|
|
|
LIBSSH2_SCP_RESPONSE_BUFLEN) {
|
2007-06-06 12:34:06 +00:00
|
|
|
/* You had your chance */
|
2007-08-06 20:48:04 +00:00
|
|
|
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
|
|
|
|
"Unterminated response from SCP server",
|
|
|
|
0);
|
2007-06-12 18:27:37 +00:00
|
|
|
goto scp_recv_error;
|
2007-06-06 12:34:06 +00:00
|
|
|
}
|
2009-03-15 23:07:09 +00:00
|
|
|
/* Way too short to be an SCP response, or not done yet,
|
|
|
|
short circuit */
|
2007-06-06 12:34:06 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2009-03-15 23:07:09 +00:00
|
|
|
/* We're guaranteed not to go under response_len == 0 by the
|
|
|
|
logic above */
|
2007-08-06 20:48:04 +00:00
|
|
|
while ((session->
|
|
|
|
scpRecv_response[session->scpRecv_response_len - 1] ==
|
|
|
|
'\r')
|
|
|
|
|| (session->
|
|
|
|
scpRecv_response[session->scpRecv_response_len -
|
|
|
|
1] == '\n')) {
|
2007-06-06 12:34:06 +00:00
|
|
|
session->scpRecv_response_len--;
|
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
session->scpRecv_response[session->scpRecv_response_len] =
|
|
|
|
'\0';
|
2007-06-06 12:34:06 +00:00
|
|
|
|
|
|
|
if (session->scpRecv_response_len < 6) {
|
|
|
|
/* EOL came too soon */
|
2007-08-06 20:48:04 +00:00
|
|
|
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
|
|
|
|
"Invalid response from SCP server, too short",
|
|
|
|
0);
|
2007-06-12 18:27:37 +00:00
|
|
|
goto scp_recv_error;
|
2007-06-06 12:34:06 +00:00
|
|
|
}
|
|
|
|
|
2007-08-06 20:48:04 +00:00
|
|
|
s = (char *) session->scpRecv_response + 1;
|
2007-06-06 12:34:06 +00:00
|
|
|
|
|
|
|
p = strchr(s, ' ');
|
|
|
|
if (!p || ((p - s) <= 0)) {
|
|
|
|
/* No spaces or space in the wrong spot */
|
2007-08-06 20:48:04 +00:00
|
|
|
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
|
|
|
|
"Invalid response from SCP server, malformed mode",
|
|
|
|
0);
|
2007-06-12 18:27:37 +00:00
|
|
|
goto scp_recv_error;
|
2007-06-06 12:34:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
*(p++) = '\0';
|
|
|
|
/* Make sure we don't get fooled by leftover values */
|
|
|
|
errno = 0;
|
|
|
|
session->scpRecv_mode = strtol(s, &e, 8);
|
|
|
|
if ((e && *e) || errno) {
|
2007-08-06 20:48:04 +00:00
|
|
|
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
|
|
|
|
"Invalid response from SCP server, invalid mode",
|
|
|
|
0);
|
2007-06-12 18:27:37 +00:00
|
|
|
goto scp_recv_error;
|
2007-06-06 12:34:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
s = strchr(p, ' ');
|
|
|
|
if (!s || ((s - p) <= 0)) {
|
|
|
|
/* No spaces or space in the wrong spot */
|
2007-08-06 20:48:04 +00:00
|
|
|
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
|
|
|
|
"Invalid response from SCP server, too short or malformed",
|
2007-06-06 12:34:06 +00:00
|
|
|
0);
|
2007-06-12 18:27:37 +00:00
|
|
|
goto scp_recv_error;
|
2007-06-06 12:34:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
*(s++) = '\0';
|
|
|
|
/* Make sure we don't get fooled by leftover values */
|
|
|
|
errno = 0;
|
2008-09-29 14:04:01 +00:00
|
|
|
session->scpRecv_size = scpsize_strtol(p, &e, 10);
|
2007-06-06 12:34:06 +00:00
|
|
|
if ((e && *e) || errno) {
|
2007-08-06 20:48:04 +00:00
|
|
|
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
|
|
|
|
"Invalid response from SCP server, invalid size",
|
|
|
|
0);
|
2007-06-12 18:27:37 +00:00
|
|
|
goto scp_recv_error;
|
2007-06-06 12:34:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* SCP ACK */
|
|
|
|
session->scpRecv_response[0] = '\0';
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
session->scpRecv_state = libssh2_NB_state_sent6;
|
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
if (session->scpRecv_state == libssh2_NB_state_sent6) {
|
2007-08-06 20:48:04 +00:00
|
|
|
rc = libssh2_channel_write_ex(session->scpRecv_channel, 0,
|
|
|
|
(char *) session->
|
|
|
|
scpRecv_response, 1);
|
2007-06-06 12:34:06 +00:00
|
|
|
if (rc == PACKET_EAGAIN) {
|
2007-08-06 20:48:04 +00:00
|
|
|
libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
|
|
|
|
"Would block sending SCP ACK", 0);
|
2007-06-06 12:34:06 +00:00
|
|
|
return NULL;
|
2007-08-06 20:48:04 +00:00
|
|
|
} else if (rc != 1) {
|
2007-06-12 18:27:37 +00:00
|
|
|
goto scp_recv_error;
|
2007-06-06 12:34:06 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
_libssh2_debug(session, LIBSSH2_DBG_SCP,
|
|
|
|
"mode = 0%lo size = %ld", session->scpRecv_mode,
|
|
|
|
session->scpRecv_size);
|
2007-06-06 12:34:06 +00:00
|
|
|
|
2009-03-15 23:07:09 +00:00
|
|
|
/* We *should* check that basename is valid, but why let that
|
|
|
|
stop us? */
|
2007-06-06 12:34:06 +00:00
|
|
|
break;
|
|
|
|
}
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
session->scpRecv_state = libssh2_NB_state_sent7;
|
|
|
|
}
|
2007-05-28 17:56:08 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
if (sb) {
|
|
|
|
memset(sb, 0, sizeof(struct stat));
|
2007-05-28 17:56:08 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
sb->st_mtime = session->scpRecv_mtime;
|
|
|
|
sb->st_atime = session->scpRecv_atime;
|
|
|
|
sb->st_size = session->scpRecv_size;
|
|
|
|
sb->st_mode = session->scpRecv_mode;
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
session->scpRecv_state = libssh2_NB_state_idle;
|
|
|
|
return session->scpRecv_channel;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
|
|
|
scp_recv_error:
|
2007-06-12 18:27:37 +00:00
|
|
|
while (libssh2_channel_free(session->scpRecv_channel) == PACKET_EAGAIN);
|
|
|
|
session->scpRecv_channel = NULL;
|
|
|
|
session->scpRecv_state = libssh2_NB_state_idle;
|
|
|
|
return NULL;
|
2007-06-06 12:34:06 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-03-15 23:07:09 +00:00
|
|
|
/*
|
|
|
|
* libssh2_scp_send_ex
|
|
|
|
*
|
2007-06-06 12:34:06 +00:00
|
|
|
* Send a file using SCP
|
2007-06-12 18:27:37 +00:00
|
|
|
*
|
2007-06-06 12:34:06 +00:00
|
|
|
*/
|
|
|
|
LIBSSH2_API LIBSSH2_CHANNEL *
|
2007-08-06 20:48:04 +00:00
|
|
|
libssh2_scp_send_ex(LIBSSH2_SESSION * session, const char *path, int mode,
|
|
|
|
size_t size, long mtime, long atime)
|
2007-06-06 12:34:06 +00:00
|
|
|
{
|
2009-03-15 23:28:25 +00:00
|
|
|
int cmd_len;
|
2007-06-06 12:34:06 +00:00
|
|
|
unsigned const char *base;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
if (session->scpSend_state == libssh2_NB_state_idle) {
|
2009-03-15 23:28:25 +00:00
|
|
|
session->scpSend_command_len =
|
|
|
|
libssh2_shell_quotedsize(path) + sizeof("scp -t ") +
|
|
|
|
((mtime || atime)?1:0);
|
2007-05-28 17:56:08 +00:00
|
|
|
|
2007-08-06 20:48:04 +00:00
|
|
|
session->scpSend_command =
|
|
|
|
LIBSSH2_ALLOC(session, session->scpSend_command_len);
|
2007-06-06 12:34:06 +00:00
|
|
|
if (!session->scpSend_command) {
|
2007-08-06 20:48:04 +00:00
|
|
|
libssh2_error(session, LIBSSH2_ERROR_ALLOC,
|
|
|
|
"Unable to allocate a command buffer for scp session",
|
|
|
|
0);
|
2007-05-28 17:56:08 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2009-03-15 23:28:25 +00:00
|
|
|
sprintf((char *)session->scpSend_command, "scp -%st ",
|
|
|
|
(mtime || atime)?"p":"");
|
|
|
|
|
|
|
|
cmd_len = strlen((char *)session->scpSend_command);
|
|
|
|
|
|
|
|
(void)libssh2_shell_quotearg(path,
|
|
|
|
&session->scpSend_command[cmd_len],
|
|
|
|
session->scpSend_command_len - cmd_len);
|
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
session->scpSend_command[session->scpSend_command_len - 1] = '\0';
|
2007-05-28 17:56:08 +00:00
|
|
|
|
2007-08-06 20:48:04 +00:00
|
|
|
_libssh2_debug(session, LIBSSH2_DBG_SCP,
|
|
|
|
"Opening channel for SCP send");
|
2007-06-06 12:34:06 +00:00
|
|
|
/* Allocate a channel */
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
session->scpSend_state = libssh2_NB_state_created;
|
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
if (session->scpSend_state == libssh2_NB_state_created) {
|
2007-08-06 20:48:04 +00:00
|
|
|
session->scpSend_channel =
|
|
|
|
libssh2_channel_open_ex(session, "session", sizeof("session") - 1,
|
|
|
|
LIBSSH2_CHANNEL_WINDOW_DEFAULT,
|
|
|
|
LIBSSH2_CHANNEL_PACKET_DEFAULT, NULL, 0);
|
2007-06-06 12:34:06 +00:00
|
|
|
if (!session->scpSend_channel) {
|
2007-08-06 20:48:04 +00:00
|
|
|
if (libssh2_session_last_errno(session) != LIBSSH2_ERROR_EAGAIN) {
|
2009-03-15 23:07:09 +00:00
|
|
|
/* previous call set libssh2_session_last_error(), pass it
|
|
|
|
through */
|
2007-06-06 12:34:06 +00:00
|
|
|
LIBSSH2_FREE(session, session->scpSend_command);
|
|
|
|
session->scpSend_command = NULL;
|
|
|
|
session->scpSend_state = libssh2_NB_state_idle;
|
2009-03-15 23:07:09 +00:00
|
|
|
}
|
|
|
|
else {
|
2007-08-06 20:48:04 +00:00
|
|
|
libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
|
|
|
|
"Would block starting up channel", 0);
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2009-03-15 23:07:09 +00:00
|
|
|
return NULL;
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
session->scpSend_state = libssh2_NB_state_sent;
|
|
|
|
}
|
2007-05-28 17:56:08 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
if (session->scpSend_state == libssh2_NB_state_sent) {
|
|
|
|
/* Request SCP for the desired file */
|
2007-08-06 20:48:04 +00:00
|
|
|
rc = libssh2_channel_process_startup(session->scpSend_channel, "exec",
|
|
|
|
sizeof("exec") - 1,
|
|
|
|
(char *) session->scpSend_command,
|
|
|
|
session->scpSend_command_len);
|
2007-06-06 12:34:06 +00:00
|
|
|
if (rc == PACKET_EAGAIN) {
|
2007-08-06 20:48:04 +00:00
|
|
|
libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
|
|
|
|
"Would block requesting SCP startup", 0);
|
2007-05-28 17:56:08 +00:00
|
|
|
return NULL;
|
2009-03-15 23:07:09 +00:00
|
|
|
}
|
|
|
|
else if (rc) {
|
|
|
|
/* previous call set libssh2_session_last_error(), pass it
|
|
|
|
through */
|
2007-06-06 12:34:06 +00:00
|
|
|
LIBSSH2_FREE(session, session->scpSend_command);
|
|
|
|
session->scpSend_command = NULL;
|
2007-08-06 20:48:04 +00:00
|
|
|
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
|
|
|
|
"Unknown error while getting error string", 0);
|
2007-06-12 18:27:37 +00:00
|
|
|
goto scp_send_error;
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-06-06 12:34:06 +00:00
|
|
|
LIBSSH2_FREE(session, session->scpSend_command);
|
|
|
|
session->scpSend_command = NULL;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
session->scpSend_state = libssh2_NB_state_sent1;
|
|
|
|
}
|
2007-05-28 17:56:08 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
if (session->scpSend_state == libssh2_NB_state_sent1) {
|
|
|
|
/* Wait for ACK */
|
2009-08-30 17:00:49 +02:00
|
|
|
rc = _libssh2_channel_read(session->scpSend_channel, 0,
|
|
|
|
(char *) session->scpSend_response, 1);
|
2007-06-06 12:34:06 +00:00
|
|
|
if (rc == PACKET_EAGAIN) {
|
2007-08-06 20:48:04 +00:00
|
|
|
libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
|
|
|
|
"Would block waiting for response from remote", 0);
|
2007-05-28 17:56:08 +00:00
|
|
|
return NULL;
|
2007-08-06 20:48:04 +00:00
|
|
|
} else if ((rc <= 0) || (session->scpSend_response[0] != 0)) {
|
|
|
|
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
|
|
|
|
"Invalid ACK response from remote", 0);
|
2007-06-12 18:27:37 +00:00
|
|
|
goto scp_send_error;
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
if (mtime || atime) {
|
|
|
|
/* Send mtime and atime to be used for file */
|
2007-08-06 20:48:04 +00:00
|
|
|
session->scpSend_response_len =
|
|
|
|
snprintf((char *) session->scpSend_response,
|
|
|
|
LIBSSH2_SCP_RESPONSE_BUFLEN, "T%ld 0 %ld 0\n", mtime,
|
|
|
|
atime);
|
|
|
|
_libssh2_debug(session, LIBSSH2_DBG_SCP, "Sent %s",
|
|
|
|
session->scpSend_response);
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
session->scpSend_state = libssh2_NB_state_sent2;
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
/* Send mtime and atime to be used for file */
|
2007-05-28 17:56:08 +00:00
|
|
|
if (mtime || atime) {
|
2007-06-06 12:34:06 +00:00
|
|
|
if (session->scpSend_state == libssh2_NB_state_sent2) {
|
2007-08-06 20:48:04 +00:00
|
|
|
rc = libssh2_channel_write_ex(session->scpSend_channel, 0,
|
|
|
|
(char *) session->scpSend_response,
|
2007-06-06 12:34:06 +00:00
|
|
|
session->scpSend_response_len);
|
|
|
|
if (rc == PACKET_EAGAIN) {
|
2007-08-06 20:48:04 +00:00
|
|
|
libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
|
|
|
|
"Would block sending time data for SCP file", 0);
|
2007-06-06 12:34:06 +00:00
|
|
|
return NULL;
|
2009-01-01 22:47:06 +00:00
|
|
|
} else if (rc != (int)session->scpSend_response_len) {
|
2007-08-06 20:48:04 +00:00
|
|
|
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
|
|
|
|
"Unable to send time data for SCP file", 0);
|
2007-06-12 18:27:37 +00:00
|
|
|
goto scp_send_error;
|
2007-06-06 12:34:06 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
session->scpSend_state = libssh2_NB_state_sent3;
|
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
if (session->scpSend_state == libssh2_NB_state_sent3) {
|
|
|
|
/* Wait for ACK */
|
2009-08-30 17:00:49 +02:00
|
|
|
rc = _libssh2_channel_read(session->scpSend_channel, 0,
|
|
|
|
(char *) session->scpSend_response, 1);
|
2007-06-06 12:34:06 +00:00
|
|
|
if (rc == PACKET_EAGAIN) {
|
2007-08-06 20:48:04 +00:00
|
|
|
libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
|
|
|
|
"Would block waiting for response", 0);
|
2007-06-06 12:34:06 +00:00
|
|
|
return NULL;
|
2007-08-06 20:48:04 +00:00
|
|
|
} else if ((rc <= 0) || (session->scpSend_response[0] != 0)) {
|
|
|
|
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
|
|
|
|
"Invalid ACK response from remote", 0);
|
2007-06-12 18:27:37 +00:00
|
|
|
goto scp_send_error;
|
2007-06-06 12:34:06 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
session->scpSend_state = libssh2_NB_state_sent4;
|
|
|
|
}
|
2007-05-28 17:56:08 +00:00
|
|
|
} else {
|
2007-06-06 12:34:06 +00:00
|
|
|
if (session->scpSend_state == libssh2_NB_state_sent2) {
|
|
|
|
session->scpSend_state = libssh2_NB_state_sent4;
|
|
|
|
}
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
if (session->scpSend_state == libssh2_NB_state_sent4) {
|
|
|
|
/* Send mode, size, and basename */
|
2007-08-06 20:48:04 +00:00
|
|
|
base = (unsigned char *) strrchr(path, '/');
|
2007-06-06 12:34:06 +00:00
|
|
|
if (base) {
|
|
|
|
base++;
|
|
|
|
} else {
|
2007-08-06 20:48:04 +00:00
|
|
|
base = (unsigned char *) path;
|
2007-06-06 12:34:06 +00:00
|
|
|
}
|
2007-05-28 17:56:08 +00:00
|
|
|
|
2007-08-06 20:48:04 +00:00
|
|
|
session->scpSend_response_len =
|
|
|
|
snprintf((char *) session->scpSend_response,
|
|
|
|
LIBSSH2_SCP_RESPONSE_BUFLEN, "C0%o %lu %s\n", mode,
|
|
|
|
(unsigned long) size, base);
|
|
|
|
_libssh2_debug(session, LIBSSH2_DBG_SCP, "Sent %s",
|
|
|
|
session->scpSend_response);
|
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
session->scpSend_state = libssh2_NB_state_sent5;
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
if (session->scpSend_state == libssh2_NB_state_sent5) {
|
2007-08-06 20:48:04 +00:00
|
|
|
rc = libssh2_channel_write_ex(session->scpSend_channel, 0,
|
|
|
|
(char *) session->scpSend_response,
|
2007-06-06 12:34:06 +00:00
|
|
|
session->scpSend_response_len);
|
|
|
|
if (rc == PACKET_EAGAIN) {
|
2007-08-06 20:48:04 +00:00
|
|
|
libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
|
|
|
|
"Would block send core file data for SCP file", 0);
|
2007-05-28 17:56:08 +00:00
|
|
|
return NULL;
|
2009-01-01 22:47:06 +00:00
|
|
|
} else if (rc != (int)session->scpSend_response_len) {
|
2007-08-06 20:48:04 +00:00
|
|
|
libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
|
|
|
|
"Unable to send core file data for SCP file", 0);
|
2007-06-12 18:27:37 +00:00
|
|
|
goto scp_send_error;
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
session->scpSend_state = libssh2_NB_state_sent6;
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-07-05 11:08:17 +00:00
|
|
|
if (session->scpSend_state == libssh2_NB_state_sent6) {
|
|
|
|
/* Wait for ACK */
|
2009-08-30 17:00:49 +02:00
|
|
|
rc = _libssh2_channel_read(session->scpSend_channel, 0,
|
|
|
|
(char *) session->scpSend_response, 1);
|
2007-07-05 11:08:17 +00:00
|
|
|
if (rc == PACKET_EAGAIN) {
|
2007-08-06 20:48:04 +00:00
|
|
|
libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
|
|
|
|
"Would block waiting for response", 0);
|
2007-07-05 11:08:17 +00:00
|
|
|
return NULL;
|
2007-08-06 20:48:04 +00:00
|
|
|
} else if (rc <= 0) {
|
|
|
|
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
|
|
|
|
"Invalid ACK response from remote", 0);
|
2007-07-05 11:08:17 +00:00
|
|
|
goto scp_send_error;
|
2007-08-06 20:48:04 +00:00
|
|
|
} else if (session->scpSend_response[0] != 0) {
|
2007-07-05 11:08:17 +00:00
|
|
|
/*
|
|
|
|
* Set this as the default error for here, if
|
|
|
|
* we are successful it will be replaced
|
|
|
|
*/
|
2007-08-06 20:48:04 +00:00
|
|
|
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
|
|
|
|
"Invalid ACK response from remote", 0);
|
|
|
|
|
|
|
|
session->scpSend_err_len =
|
2009-03-17 13:48:35 +00:00
|
|
|
_libssh2_channel_packet_data_len(session->scpSend_channel, 0);
|
2007-08-06 20:48:04 +00:00
|
|
|
session->scpSend_err_msg =
|
|
|
|
LIBSSH2_ALLOC(session, session->scpSend_err_len + 1);
|
2007-07-05 11:08:17 +00:00
|
|
|
if (!session->scpSend_err_msg) {
|
|
|
|
goto scp_send_error;
|
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
memset(session->scpSend_err_msg, 0, session->scpSend_err_len + 1);
|
2007-07-05 11:08:17 +00:00
|
|
|
|
|
|
|
/* Read the remote error message */
|
2009-08-30 17:00:49 +02:00
|
|
|
rc = _libssh2_channel_read(session->scpSend_channel, 0,
|
|
|
|
session->scpSend_err_msg,
|
|
|
|
session->scpSend_err_len);
|
2007-07-05 15:31:19 +00:00
|
|
|
if (rc <= 0) {
|
|
|
|
/*
|
|
|
|
* Since we have alread started reading this packet, it is
|
|
|
|
* already in the systems so it can't return PACKET_EAGAIN
|
|
|
|
*/
|
2007-07-05 11:08:17 +00:00
|
|
|
LIBSSH2_FREE(session, session->scpSend_err_msg);
|
|
|
|
session->scpSend_err_msg = NULL;
|
|
|
|
goto scp_send_error;
|
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
|
|
|
libssh2_error(session, LIBSSH2_ERROR_SCP_PROTOCOL,
|
|
|
|
session->scpSend_err_msg, 1);
|
2007-07-19 15:02:37 +00:00
|
|
|
session->scpSend_err_msg = NULL;
|
2007-07-05 11:08:17 +00:00
|
|
|
goto scp_send_error;
|
|
|
|
}
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
session->scpSend_state = libssh2_NB_state_idle;
|
2007-05-28 17:56:08 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
return session->scpSend_channel;
|
2007-06-12 18:27:37 +00:00
|
|
|
|
2007-08-06 20:48:04 +00:00
|
|
|
scp_send_error:
|
2007-06-12 18:27:37 +00:00
|
|
|
while (libssh2_channel_free(session->scpSend_channel) == PACKET_EAGAIN);
|
|
|
|
session->scpSend_channel = NULL;
|
|
|
|
session->scpSend_state = libssh2_NB_state_idle;
|
|
|
|
return NULL;
|
2004-12-07 21:17:20 +00:00
|
|
|
}
|
|
|
|
|