summaryrefslogtreecommitdiffstats
path: root/src/scp.c
diff options
context:
space:
mode:
authorAndreas Schneider <asn@cynapses.org>2010-09-06 14:28:38 +0200
committerAndreas Schneider <asn@cynapses.org>2010-09-06 14:28:38 +0200
commitf7842e3a4b9acea2126ff725f993c299aef0e6db (patch)
tree18239f819a5edbcfc7f2961c48f3f9297314ef22 /src/scp.c
parent38421403d2dc45636e597f2a909daa6ae31976de (diff)
downloadlibssh-f7842e3a4b9acea2126ff725f993c299aef0e6db.tar.gz
libssh-f7842e3a4b9acea2126ff725f993c299aef0e6db.tar.xz
libssh-f7842e3a4b9acea2126ff725f993c299aef0e6db.zip
misc: Rename libssh/ to src/
Diffstat (limited to 'src/scp.c')
-rw-r--r--src/scp.c752
1 files changed, 752 insertions, 0 deletions
diff --git a/src/scp.c b/src/scp.c
new file mode 100644
index 00000000..4a6f6f14
--- /dev/null
+++ b/src/scp.c
@@ -0,0 +1,752 @@
+/*
+ * scp - SSH scp wrapper functions
+ *
+ * This file is part of the SSH Library
+ *
+ * Copyright (c) 2009 by Aris Adamantiadis <aris@0xbadc0de.be>
+ *
+ * The SSH Library is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or (at your
+ * option) any later version.
+ *
+ * The SSH Library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the SSH Library; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+ * MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "libssh/priv.h"
+#include "libssh/scp.h"
+
+/**
+ * @defgroup libssh_scp The SSH scp functions
+ * @ingroup libssh
+ *
+ * SCP protocol over SSH functions
+ *
+ * @{
+ */
+
+/**
+ * @brief Create a new scp session.
+ *
+ * @param[in] session The SSH session to use.
+ *
+ * @param[in] mode One of SSH_SCP_WRITE or SSH_SCP_READ, depending if you
+ * need to drop files remotely or read them.
+ * It is not possible to combine read and write.
+ *
+ * @param[in] location The directory in which write or read will be done. Any
+ * push or pull will be relative to this place.
+ *
+ * @returns A ssh_scp handle, NULL if the creation was impossible.
+ */
+ssh_scp ssh_scp_new(ssh_session session, int mode, const char *location){
+ ssh_scp scp=malloc(sizeof(struct ssh_scp_struct));
+ if(scp == NULL){
+ ssh_set_error(session,SSH_FATAL,"Error allocating memory for ssh_scp");
+ return NULL;
+ }
+ ZERO_STRUCTP(scp);
+ if((mode&~SSH_SCP_RECURSIVE) != SSH_SCP_WRITE && (mode &~SSH_SCP_RECURSIVE) != SSH_SCP_READ){
+ ssh_set_error(session,SSH_FATAL,"Invalid mode %d for ssh_scp_new()",mode);
+ ssh_scp_free(scp);
+ return NULL;
+ }
+ scp->location=strdup(location);
+ if (scp->location == NULL) {
+ ssh_set_error(session,SSH_FATAL,"Error allocating memory for ssh_scp");
+ ssh_scp_free(scp);
+ return NULL;
+ }
+ scp->session=session;
+ scp->mode=mode & ~SSH_SCP_RECURSIVE;
+ scp->recursive = (mode & SSH_SCP_RECURSIVE) != 0;
+ scp->channel=NULL;
+ scp->state=SSH_SCP_NEW;
+ return scp;
+}
+
+int ssh_scp_init(ssh_scp scp){
+ int r;
+ char execbuffer[1024];
+ uint8_t code;
+ if(scp==NULL)
+ return SSH_ERROR;
+ if(scp->state != SSH_SCP_NEW){
+ ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_init called under invalid state");
+ return SSH_ERROR;
+ }
+ ssh_log(scp->session,SSH_LOG_PROTOCOL,"Initializing scp session %s %son location '%s'",
+ scp->mode==SSH_SCP_WRITE?"write":"read",
+ scp->recursive?"recursive ":"",
+ scp->location);
+ scp->channel=ssh_channel_new(scp->session);
+ if(scp->channel == NULL){
+ scp->state=SSH_SCP_ERROR;
+ return SSH_ERROR;
+ }
+ r= ssh_channel_open_session(scp->channel);
+ if(r==SSH_ERROR){
+ scp->state=SSH_SCP_ERROR;
+ return SSH_ERROR;
+ }
+ if(scp->mode == SSH_SCP_WRITE)
+ snprintf(execbuffer,sizeof(execbuffer),"scp -t %s %s",
+ scp->recursive ? "-r":"", scp->location);
+ else
+ snprintf(execbuffer,sizeof(execbuffer),"scp -f %s %s",
+ scp->recursive ? "-r":"", scp->location);
+ if(ssh_channel_request_exec(scp->channel,execbuffer) == SSH_ERROR){
+ scp->state=SSH_SCP_ERROR;
+ return SSH_ERROR;
+ }
+ if(scp->mode == SSH_SCP_WRITE){
+ r=ssh_channel_read(scp->channel,&code,1,0);
+ if(r<=0){
+ ssh_set_error(scp->session,SSH_FATAL, "Error reading status code: %s",ssh_get_error(scp->session));
+ scp->state=SSH_SCP_ERROR;
+ return SSH_ERROR;
+ }
+ if(code != 0){
+ ssh_set_error(scp->session,SSH_FATAL, "scp status code %ud not valid", code);
+ scp->state=SSH_SCP_ERROR;
+ return SSH_ERROR;
+ }
+ } else {
+ ssh_channel_write(scp->channel,"",1);
+ }
+ if(scp->mode == SSH_SCP_WRITE)
+ scp->state=SSH_SCP_WRITE_INITED;
+ else
+ scp->state=SSH_SCP_READ_INITED;
+ return SSH_OK;
+}
+
+int ssh_scp_close(ssh_scp scp){
+ char buffer[128];
+ int err;
+ if(scp==NULL)
+ return SSH_ERROR;
+ if(scp->channel != NULL){
+ if(ssh_channel_send_eof(scp->channel) == SSH_ERROR){
+ scp->state=SSH_SCP_ERROR;
+ return SSH_ERROR;
+ }
+ /* avoid situations where data are buffered and
+ * not yet stored on disk. This can happen if the close is sent
+ * before we got the EOF back
+ */
+ while(!ssh_channel_is_eof(scp->channel)){
+ err=ssh_channel_read(scp->channel,buffer,sizeof(buffer),0);
+ if(err==SSH_ERROR)
+ break;
+ }
+ if(ssh_channel_close(scp->channel) == SSH_ERROR){
+ scp->state=SSH_SCP_ERROR;
+ return SSH_ERROR;
+ }
+ ssh_channel_free(scp->channel);
+ scp->channel=NULL;
+ }
+ scp->state=SSH_SCP_NEW;
+ return SSH_OK;
+}
+
+void ssh_scp_free(ssh_scp scp){
+ if(scp==NULL)
+ return;
+ if(scp->state != SSH_SCP_NEW)
+ ssh_scp_close(scp);
+ if(scp->channel)
+ ssh_channel_free(scp->channel);
+ SAFE_FREE(scp->location);
+ SAFE_FREE(scp->request_name);
+ SAFE_FREE(scp->warning);
+ SAFE_FREE(scp);
+}
+
+/**
+ * @brief Create a directory in a scp in sink mode.
+ *
+ * @param[in] scp The scp handle.
+ *
+ * @param[in] dirname The name of the directory being created.
+ *
+ * @param[in] mode The UNIX permissions for the new directory, e.g. 0755.
+ *
+ * @returns SSH_OK if the directory has been created, SSH_ERROR if
+ * an error occured.
+ *
+ * @see ssh_scp_leave_directory()
+ */
+int ssh_scp_push_directory(ssh_scp scp, const char *dirname, int mode){
+ char buffer[1024];
+ int r;
+ uint8_t code;
+ char *dir;
+ char *perms;
+ if(scp==NULL)
+ return SSH_ERROR;
+ if(scp->state != SSH_SCP_WRITE_INITED){
+ ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_push_directory called under invalid state");
+ return SSH_ERROR;
+ }
+ dir=ssh_basename(dirname);
+ perms=ssh_scp_string_mode(mode);
+ snprintf(buffer, sizeof(buffer), "D%s 0 %s\n", perms, dir);
+ SAFE_FREE(dir);
+ SAFE_FREE(perms);
+ r=ssh_channel_write(scp->channel,buffer,strlen(buffer));
+ if(r==SSH_ERROR){
+ scp->state=SSH_SCP_ERROR;
+ return SSH_ERROR;
+ }
+ r=ssh_channel_read(scp->channel,&code,1,0);
+ if(r<=0){
+ ssh_set_error(scp->session,SSH_FATAL, "Error reading status code: %s",ssh_get_error(scp->session));
+ scp->state=SSH_SCP_ERROR;
+ return SSH_ERROR;
+ }
+ if(code != 0){
+ ssh_set_error(scp->session,SSH_FATAL, "scp status code %ud not valid", code);
+ scp->state=SSH_SCP_ERROR;
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+/**
+ * @brief Leave a directory.
+ *
+ * @returns SSH_OK if the directory has been left,SSH_ERROR if an
+ * error occured.
+ *
+ * @see ssh_scp_push_directory()
+ */
+ int ssh_scp_leave_directory(ssh_scp scp){
+ char buffer[]="E\n";
+ int r;
+ uint8_t code;
+ if(scp==NULL)
+ return SSH_ERROR;
+ if(scp->state != SSH_SCP_WRITE_INITED){
+ ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_leave_directory called under invalid state");
+ return SSH_ERROR;
+ }
+ r=ssh_channel_write(scp->channel,buffer,strlen(buffer));
+ if(r==SSH_ERROR){
+ scp->state=SSH_SCP_ERROR;
+ return SSH_ERROR;
+ }
+ r=ssh_channel_read(scp->channel,&code,1,0);
+ if(r<=0){
+ ssh_set_error(scp->session,SSH_FATAL, "Error reading status code: %s",ssh_get_error(scp->session));
+ scp->state=SSH_SCP_ERROR;
+ return SSH_ERROR;
+ }
+ if(code != 0){
+ ssh_set_error(scp->session,SSH_FATAL, "scp status code %ud not valid", code);
+ scp->state=SSH_SCP_ERROR;
+ return SSH_ERROR;
+ }
+ return SSH_OK;
+}
+
+/**
+ * @brief Initialize the sending of a file to a scp in sink mode.
+ *
+ * @param[in] scp The scp handle.
+ *
+ * @param[in] filename The name of the file being sent. It should not contain
+ * any path indicator
+ *
+ * @param[in] size Exact size in bytes of the file being sent.
+ *
+ * @param[in] mode The UNIX permissions for the new file, e.g. 0644.
+ *
+ * @returns SSH_OK if the file is ready to be sent, SSH_ERROR if an
+ * error occured.
+ */
+int ssh_scp_push_file(ssh_scp scp, const char *filename, size_t size, int mode){
+ char buffer[1024];
+ int r;
+ uint8_t code;
+ char *file;
+ char *perms;
+ if(scp==NULL)
+ return SSH_ERROR;
+ if(scp->state != SSH_SCP_WRITE_INITED){
+ ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_push_file called under invalid state");
+ return SSH_ERROR;
+ }
+ file=ssh_basename(filename);
+ perms=ssh_scp_string_mode(mode);
+ ssh_log(scp->session,SSH_LOG_PROTOCOL,"SCP pushing file %s, size %" PRIdS " with permissions '%s'",file,size,perms);
+ snprintf(buffer, sizeof(buffer), "C%s %" PRIdS " %s\n", perms, size, file);
+ SAFE_FREE(file);
+ SAFE_FREE(perms);
+ r=ssh_channel_write(scp->channel,buffer,strlen(buffer));
+ if(r==SSH_ERROR){
+ scp->state=SSH_SCP_ERROR;
+ return SSH_ERROR;
+ }
+ r=ssh_channel_read(scp->channel,&code,1,0);
+ if(r<=0){
+ ssh_set_error(scp->session,SSH_FATAL, "Error reading status code: %s",ssh_get_error(scp->session));
+ scp->state=SSH_SCP_ERROR;
+ return SSH_ERROR;
+ }
+ if(code != 0){
+ ssh_set_error(scp->session,SSH_FATAL, "scp status code %ud not valid", code);
+ scp->state=SSH_SCP_ERROR;
+ return SSH_ERROR;
+ }
+ scp->filelen = size;
+ scp->processed = 0;
+ scp->state=SSH_SCP_WRITE_WRITING;
+ return SSH_OK;
+}
+
+/**
+ * @internal
+ *
+ * @brief Wait for a response of the scp server.
+ *
+ * @param[in] scp The scp handle.
+ *
+ * @param[out] response A pointer where the response message must be copied if
+ * any. This pointer must then be free'd.
+ *
+ * @returns The return code, SSH_ERROR a error occured.
+ */
+int ssh_scp_response(ssh_scp scp, char **response){
+ unsigned char code;
+ int r;
+ char msg[128];
+ if(scp==NULL)
+ return SSH_ERROR;
+ r=ssh_channel_read(scp->channel,&code,1,0);
+ if(r == SSH_ERROR)
+ return SSH_ERROR;
+ if(code == 0)
+ return 0;
+ if(code > 2){
+ ssh_set_error(scp->session,SSH_FATAL, "SCP: invalid status code %ud received", code);
+ scp->state=SSH_SCP_ERROR;
+ return SSH_ERROR;
+ }
+ r=ssh_scp_read_string(scp,msg,sizeof(msg));
+ if(r==SSH_ERROR)
+ return r;
+ /* Warning */
+ if(code == 1){
+ ssh_set_error(scp->session,SSH_REQUEST_DENIED, "SCP: Warning: status code 1 received: %s", msg);
+ ssh_log(scp->session,SSH_LOG_RARE,"SCP: Warning: status code 1 received: %s", msg);
+ if(response)
+ *response=strdup(msg);
+ return 1;
+ }
+ if(code == 2){
+ ssh_set_error(scp->session,SSH_FATAL, "SCP: Error: status code 2 received: %s", msg);
+ if(response)
+ *response=strdup(msg);
+ return 2;
+ }
+ /* Not reached */
+ return SSH_ERROR;
+}
+
+/**
+ * @brief Write into a remote scp file.
+ *
+ * @param[in] scp The scp handle.
+ *
+ * @param[in] buffer The buffer to write.
+ *
+ * @param[in] len The number of bytes to write.
+ *
+ * @returns SSH_OK if the write was successful, SSH_ERROR an error
+ * occured while writing.
+ */
+int ssh_scp_write(ssh_scp scp, const void *buffer, size_t len){
+ int w;
+ //int r;
+ //uint8_t code;
+ if(scp==NULL)
+ return SSH_ERROR;
+ if(scp->state != SSH_SCP_WRITE_WRITING){
+ ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_write called under invalid state");
+ return SSH_ERROR;
+ }
+ if(scp->processed + len > scp->filelen)
+ len = scp->filelen - scp->processed;
+ /* hack to avoid waiting for window change */
+ ssh_channel_poll(scp->channel,0);
+ w=ssh_channel_write(scp->channel,buffer,len);
+ if(w != SSH_ERROR)
+ scp->processed += w;
+ else {
+ scp->state=SSH_SCP_ERROR;
+ //return=channel_get_exit_status(scp->channel);
+ return SSH_ERROR;
+ }
+ /* Check if we arrived at end of file */
+ if(scp->processed == scp->filelen) {
+/* r=channel_read(scp->channel,&code,1,0);
+ if(r==SSH_ERROR){
+ scp->state=SSH_SCP_ERROR;
+ return SSH_ERROR;
+ }
+ if(code != 0){
+ ssh_set_error(scp->session,SSH_FATAL, "scp status code %ud not valid", code);
+ scp->state=SSH_SCP_ERROR;
+ return SSH_ERROR;
+ }
+*/
+ scp->processed=scp->filelen=0;
+ scp->state=SSH_SCP_WRITE_INITED;
+ }
+ return SSH_OK;
+}
+
+/**
+ * @brief Read a string on a channel, terminated by '\n'
+ *
+ * @param[in] scp The scp handle.
+ *
+ * @param[out] buffer A pointer to a buffer to place the string.
+ *
+ * @param[in] len The size of the buffer in bytes. If the string is bigger
+ * than len-1, only len-1 bytes are read and the string is
+ * null-terminated.
+ *
+ * @returns SSH_OK if the string was read, SSH_ERROR if an error
+ * occured while reading.
+ */
+int ssh_scp_read_string(ssh_scp scp, char *buffer, size_t len){
+ size_t r=0;
+ int err=SSH_OK;
+ if(scp==NULL)
+ return SSH_ERROR;
+ while(r<len-1){
+ err=ssh_channel_read(scp->channel,&buffer[r],1,0);
+ if(err==SSH_ERROR){
+ break;
+ }
+ if(err==0){
+ ssh_set_error(scp->session,SSH_FATAL,"End of file while reading string");
+ err=SSH_ERROR;
+ break;
+ }
+ r++;
+ if(buffer[r-1] == '\n')
+ break;
+ }
+ buffer[r]=0;
+ return err;
+}
+
+/**
+ * @brief Wait for a scp request (file, directory).
+ *
+ * @returns SSH_SCP_REQUEST_NEWFILE: The other side is sending
+ * a file
+ * SSH_SCP_REQUEST_NEWDIRECTORY: The other side is sending
+ * a directory
+ * SSH_SCP_REQUEST_END_DIRECTORY: The other side has
+ * finished with the current
+ * directory
+ * SSH_ERROR: Some error happened
+ *
+ * @see ssh_scp_read()
+ * @see ssh_scp_deny_request()
+ * @see ssh_scp_accept_request()
+ */
+int ssh_scp_pull_request(ssh_scp scp){
+ char buffer[4096];
+ char *mode=NULL;
+ char *p,*tmp;
+ size_t size;
+ char *name=NULL;
+ int err;
+ if(scp==NULL)
+ return SSH_ERROR;
+ if(scp->state != SSH_SCP_READ_INITED){
+ ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_pull_request called under invalid state");
+ return SSH_ERROR;
+ }
+ err=ssh_scp_read_string(scp,buffer,sizeof(buffer));
+ if(err==SSH_ERROR){
+ if(ssh_channel_is_eof(scp->channel)){
+ scp->state=SSH_SCP_TERMINATED;
+ return SSH_SCP_REQUEST_EOF;
+ }
+ return err;
+ }
+ p=strchr(buffer,'\n');
+ if(p!=NULL)
+ *p='\0';
+ ssh_log(scp->session,SSH_LOG_PROTOCOL,"Received SCP request: '%s'",buffer);
+ switch(buffer[0]){
+ case 'C':
+ /* File */
+ case 'D':
+ /* Directory */
+ p=strchr(buffer,' ');
+ if(p==NULL)
+ goto error;
+ *p='\0';
+ p++;
+ //mode=strdup(&buffer[1]);
+ scp->request_mode=ssh_scp_integer_mode(&buffer[1]);
+ tmp=p;
+ p=strchr(p,' ');
+ if(p==NULL)
+ goto error;
+ *p=0;
+ size=strtoull(tmp,NULL,10);
+ p++;
+ name=strdup(p);
+ SAFE_FREE(scp->request_name);
+ scp->request_name=name;
+ if(buffer[0]=='C'){
+ scp->filelen=size;
+ scp->request_type=SSH_SCP_REQUEST_NEWFILE;
+ } else {
+ scp->filelen='0';
+ scp->request_type=SSH_SCP_REQUEST_NEWDIR;
+ }
+ scp->state=SSH_SCP_READ_REQUESTED;
+ scp->processed = 0;
+ return scp->request_type;
+ break;
+ case 'E':
+ scp->request_type=SSH_SCP_REQUEST_ENDDIR;
+ ssh_channel_write(scp->channel,"",1);
+ return scp->request_type;
+ case 0x1:
+ ssh_set_error(scp->session,SSH_REQUEST_DENIED,"SCP: Warning: %s",&buffer[1]);
+ scp->request_type=SSH_SCP_REQUEST_WARNING;
+ SAFE_FREE(scp->warning);
+ scp->warning=strdup(&buffer[1]);
+ return scp->request_type;
+ case 0x2:
+ ssh_set_error(scp->session,SSH_FATAL,"SCP: Error: %s",&buffer[1]);
+ return SSH_ERROR;
+ case 'T':
+ /* Timestamp */
+ default:
+ ssh_set_error(scp->session,SSH_FATAL,"Unhandled message: (%d)%s",buffer[0],buffer);
+ return SSH_ERROR;
+ }
+
+ /* a parsing error occured */
+ error:
+ SAFE_FREE(name);
+ SAFE_FREE(mode);
+ ssh_set_error(scp->session,SSH_FATAL,"Parsing error while parsing message: %s",buffer);
+ return SSH_ERROR;
+}
+
+/**
+ * @brief Deny the transfer of a file or creation of a directory coming from the
+ * remote party.
+ *
+ * @param[in] scp The scp handle.
+ * @param[in] reason A nul-terminated string with a human-readable
+ * explanation of the deny.
+ *
+ * @returns SSH_OK if the message was sent, SSH_ERROR if the sending
+ * the message failed, or sending it in a bad state.
+ */
+int ssh_scp_deny_request(ssh_scp scp, const char *reason){
+ char buffer[4096];
+ int err;
+ if(scp==NULL)
+ return SSH_ERROR;
+ if(scp->state != SSH_SCP_READ_REQUESTED){
+ ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_deny_request called under invalid state");
+ return SSH_ERROR;
+ }
+ snprintf(buffer,sizeof(buffer),"%c%s\n",2,reason);
+ err=ssh_channel_write(scp->channel,buffer,strlen(buffer));
+ if(err==SSH_ERROR) {
+ return SSH_ERROR;
+ }
+ else {
+ scp->state=SSH_SCP_READ_INITED;
+ return SSH_OK;
+ }
+}
+
+/**
+ * @brief Accepts transfer of a file or creation of a directory coming from the
+ * remote party.
+ *
+ * @param[in] scp The scp handle.
+ *
+ * @returns SSH_OK if the message was sent, SSH_ERROR if sending the
+ * message failed, or sending it in a bad state.
+ */
+int ssh_scp_accept_request(ssh_scp scp){
+ char buffer[]={0x00};
+ int err;
+ if(scp==NULL)
+ return SSH_ERROR;
+ if(scp->state != SSH_SCP_READ_REQUESTED){
+ ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_deny_request called under invalid state");
+ return SSH_ERROR;
+ }
+ err=ssh_channel_write(scp->channel,buffer,1);
+ if(err==SSH_ERROR) {
+ return SSH_ERROR;
+ }
+ if(scp->request_type==SSH_SCP_REQUEST_NEWFILE)
+ scp->state=SSH_SCP_READ_READING;
+ else
+ scp->state=SSH_SCP_READ_INITED;
+ return SSH_OK;
+}
+
+/** @brief Read from a remote scp file
+ * @param[in] scp The scp handle.
+ *
+ * @param[in] buffer The destination buffer.
+ *
+ * @param[in] size The size of the buffer.
+ *
+ * @returns The nNumber of bytes read, SSH_ERROR if an error occured
+ * while reading.
+ */
+int ssh_scp_read(ssh_scp scp, void *buffer, size_t size){
+ int r;
+ int code;
+ if(scp==NULL)
+ return SSH_ERROR;
+ if(scp->state == SSH_SCP_READ_REQUESTED && scp->request_type == SSH_SCP_REQUEST_NEWFILE){
+ r=ssh_scp_accept_request(scp);
+ if(r==SSH_ERROR)
+ return r;
+ }
+ if(scp->state != SSH_SCP_READ_READING){
+ ssh_set_error(scp->session,SSH_FATAL,"ssh_scp_read called under invalid state");
+ return SSH_ERROR;
+ }
+ if(scp->processed + size > scp->filelen)
+ size = scp->filelen - scp->processed;
+ if(size > 65536)
+ size=65536; /* avoid too large reads */
+ r=ssh_channel_read(scp->channel,buffer,size,0);
+ if(r != SSH_ERROR)
+ scp->processed += r;
+ else {
+ scp->state=SSH_SCP_ERROR;
+ return SSH_ERROR;
+ }
+ /* Check if we arrived at end of file */
+ if(scp->processed == scp->filelen) {
+ scp->processed=scp->filelen=0;
+ ssh_channel_write(scp->channel,"",1);
+ code=ssh_scp_response(scp,NULL);
+ if(code == 0){
+ scp->state=SSH_SCP_READ_INITED;
+ return r;
+ }
+ if(code==1){
+ scp->state=SSH_SCP_READ_INITED;
+ return SSH_ERROR;
+ }
+ scp->state=SSH_SCP_ERROR;
+ return SSH_ERROR;
+ }
+ return r;
+}
+
+/**
+ * @brief Get the name of the directory or file being pushed from the other
+ * party.
+ *
+ * @returns The file name, NULL on error. The string should not be
+ * freed.
+ */
+const char *ssh_scp_request_get_filename(ssh_scp scp){
+ if(scp==NULL)
+ return NULL;
+ return scp->request_name;
+}
+
+/**
+ * @brief Get the permissions of the directory or file being pushed from the
+ * other party.
+ *
+ * @returns The UNIX permission, e.g 0644, -1 on error.
+ */
+int ssh_scp_request_get_permissions(ssh_scp scp){
+ if(scp==NULL)
+ return -1;
+ return scp->request_mode;
+}
+
+/** @brief Get the size of the file being pushed from the other party.
+ *
+ * @returns The numeric size of the file being read.
+ */
+size_t ssh_scp_request_get_size(ssh_scp scp){
+ if(scp==NULL)
+ return 0;
+ return scp->filelen;
+}
+
+/**
+ * @brief Convert a scp text mode to an integer.
+ *
+ * @param[in] mode The mode to convert, e.g. "0644".
+ *
+ * @returns An integer value, e.g. 420 for "0644".
+ */
+int ssh_scp_integer_mode(const char *mode){
+ int value=strtoul(mode,NULL,8) & 0xffff;
+ return value;
+}
+
+/**
+ * @brief Convert a unix mode into a scp string.
+ *
+ * @param[in] mode The mode to convert, e.g. 420 or 0644.
+ *
+ * @returns A pointer to a malloc'ed string containing the scp mode,
+ * e.g. "0644".
+ */
+char *ssh_scp_string_mode(int mode){
+ char buffer[16];
+ snprintf(buffer,sizeof(buffer),"%.4o",mode);
+ return strdup(buffer);
+}
+
+/**
+ * @brief Get the warning string from a scp handle.
+ *
+ * @param[in] scp The scp handle.
+ *
+ * @returns A warning string, or NULL on error. The string should
+ * not be freed.
+ */
+const char *ssh_scp_request_get_warning(ssh_scp scp){
+ if(scp==NULL)
+ return NULL;
+ return scp->warning;
+}
+
+/** @} */
+
+/* vim: set ts=4 sw=4 et cindent: */