Blame src/scp.c

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