function put($remote_file, $data, $mode = self::SOURCE_STRING, $start = -1, $local_start = -1, $progressCallback = null)
{
if (!($this->bitmap & SSH2::MASK_LOGIN)) {
return false;
}
$remote_file = $this->_realpath($remote_file);
if ($remote_file === false) {
return false;
}
$this->_remove_from_stat_cache($remote_file);
$flags = NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE;
// according to the SFTP specs, NET_SFTP_OPEN_APPEND should "force all writes to append data at the end of the file."
// in practice, it doesn't seem to do that.
//$flags|= ($mode & self::RESUME) ? NET_SFTP_OPEN_APPEND : NET_SFTP_OPEN_TRUNCATE;
if ($start >= 0) {
$offset = $start;
} elseif ($mode & self::RESUME) {
// if NET_SFTP_OPEN_APPEND worked as it should _size() wouldn't need to be called
$size = $this->size($remote_file);
$offset = $size !== false ? $size : 0;
} else {
$offset = 0;
$flags |= NET_SFTP_OPEN_TRUNCATE;
}
$packet = pack('Na*N2', strlen($remote_file), $remote_file, $flags, 0);
if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
return false;
}
$response = $this->_get_sftp_packet();
switch ($this->packet_type) {
case NET_SFTP_HANDLE:
$handle = substr($response, 4);
break;
case NET_SFTP_STATUS:
$this->_logError($response);
return false;
default:
throw new \UnexpectedValueException('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS');
}
// http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.2.3
$dataCallback = false;
switch (true) {
case $mode & self::SOURCE_CALLBACK:
if (!is_callable($data)) {
throw new \BadFunctionCallException("\$data should be is_callable() if you specify SOURCE_CALLBACK flag");
}
$dataCallback = $data;
// do nothing
break;
case is_resource($data):
$mode = $mode & ~self::SOURCE_LOCAL_FILE;
$fp = $data;
break;
case $mode & self::SOURCE_LOCAL_FILE:
if (!is_file($data)) {
throw new FileNotFoundException("{$data} is not a valid file");
}
$fp = @fopen($data, 'rb');
if (!$fp) {
return false;
}
}
if (isset($fp)) {
$stat = fstat($fp);
$size = $stat['size'];
if ($local_start >= 0) {
fseek($fp, $local_start);
$size -= $local_start;
}
} elseif ($dataCallback) {
$size = 0;
} else {
$size = strlen($data);
}
$sent = 0;
$size = $size < 0 ? ($size & 0x7fffffff) + 0x80000000 : $size;
$sftp_packet_size = 4096;
// PuTTY uses 4096
// make the SFTP packet be exactly 4096 bytes by including the bytes in the NET_SFTP_WRITE packets "header"
$sftp_packet_size -= strlen($handle) + 25;
$i = 0;
while ($dataCallback || ($size === 0 || $sent < $size)) {
if ($dataCallback) {
$temp = call_user_func($dataCallback, $sftp_packet_size);
if (is_null($temp)) {
break;
}
} else {
$temp = isset($fp) ? fread($fp, $sftp_packet_size) : substr($data, $sent, $sftp_packet_size);
if ($temp === false || $temp === '') {
break;
}
}
$subtemp = $offset + $sent;
$packet = pack('Na*N3a*', strlen($handle), $handle, $subtemp / 4294967296, $subtemp, strlen($temp), $temp);
if (!$this->_send_sftp_packet(NET_SFTP_WRITE, $packet)) {
if ($mode & self::SOURCE_LOCAL_FILE) {
fclose($fp);
}
return false;
}
$sent += strlen($temp);
if (is_callable($progressCallback)) {
call_user_func($progressCallback, $sent);
}
$i++;
if ($i == NET_SFTP_QUEUE_SIZE) {
if (!$this->_read_put_responses($i)) {
$i = 0;
break;
}
$i = 0;
}
}
if (!$this->_read_put_responses($i)) {
if ($mode & self::SOURCE_LOCAL_FILE) {
fclose($fp);
}
$this->_close_handle($handle);
return false;
}
if ($mode & self::SOURCE_LOCAL_FILE) {
fclose($fp);
}
return $this->_close_handle($handle);
}