|
Packit |
8f70b4 |
/*
|
|
Packit |
8f70b4 |
* lftp - file transfer program
|
|
Packit |
8f70b4 |
*
|
|
Packit |
8f70b4 |
* Copyright (c) 1996-2017 by Alexander V. Lukyanov (lav@yars.free.net)
|
|
Packit |
8f70b4 |
*
|
|
Packit |
8f70b4 |
* This program is free software; you can redistribute it and/or modify
|
|
Packit |
8f70b4 |
* it under the terms of the GNU General Public License as published by
|
|
Packit |
8f70b4 |
* the Free Software Foundation; either version 3 of the License, or
|
|
Packit |
8f70b4 |
* (at your option) any later version.
|
|
Packit |
8f70b4 |
*
|
|
Packit |
8f70b4 |
* This program is distributed in the hope that it will be useful,
|
|
Packit |
8f70b4 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
8f70b4 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit |
8f70b4 |
* GNU General Public License for more details.
|
|
Packit |
8f70b4 |
*
|
|
Packit |
8f70b4 |
* You should have received a copy of the GNU General Public License
|
|
Packit |
8f70b4 |
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
Packit |
8f70b4 |
*/
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
#include <config.h>
|
|
Packit |
8f70b4 |
#include "SFtp.h"
|
|
Packit |
8f70b4 |
#include "ArgV.h"
|
|
Packit |
8f70b4 |
#include "log.h"
|
|
Packit |
8f70b4 |
#include "ascii_ctype.h"
|
|
Packit |
8f70b4 |
#include "FileGlob.h"
|
|
Packit |
8f70b4 |
#include "misc.h"
|
|
Packit |
8f70b4 |
#include "LsCache.h"
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
#include <assert.h>
|
|
Packit |
8f70b4 |
#include <errno.h>
|
|
Packit |
8f70b4 |
#include <stddef.h>
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
#define max_buf 0x10000
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
#define super SSH_Access
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
bool SFtp::GetBetterConnection(int level,bool limit_reached)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
bool need_sleep=false;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
for(FA *fo=FirstSameSite(); fo!=0; fo=NextSameSite(fo))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
SFtp *o=(SFtp*)fo; // we are sure it is SFtp.
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(!o->recv_buf)
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(o->state!=CONNECTED || o->mode!=CLOSED)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(level<2)
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
if(!connection_takeover || (o->priority>=priority && !o->IsSuspended()))
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
o->Disconnect();
|
|
Packit |
8f70b4 |
return need_sleep;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(level==0 && xstrcmp(real_cwd,o->real_cwd))
|
|
Packit |
8f70b4 |
continue;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
// borrow the connection
|
|
Packit |
8f70b4 |
MoveConnectionHere(o);
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
return need_sleep;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
int SFtp::Do()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
int m=STALL;
|
|
Packit |
8f70b4 |
const char *b;
|
|
Packit |
8f70b4 |
int s;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
// check if idle time exceeded
|
|
Packit |
8f70b4 |
if(mode==CLOSED && send_buf && idle_timer.Stopped())
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
LogNote(1,_("Closing idle connection"));
|
|
Packit |
8f70b4 |
Disconnect();
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(Error())
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(!hostname)
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(send_buf && send_buf->Error())
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
LogError(0,"send: %s",send_buf->ErrorText());
|
|
Packit |
8f70b4 |
Disconnect(send_buf->ErrorText());
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(state!=CONNECTING_1 && state!=CONNECTING_2)
|
|
Packit |
8f70b4 |
m|=HandleReplies();
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(Error())
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(send_buf)
|
|
Packit |
8f70b4 |
timeout_timer.Reset(send_buf->EventTime());
|
|
Packit |
8f70b4 |
if(recv_buf)
|
|
Packit |
8f70b4 |
timeout_timer.Reset(recv_buf->EventTime());
|
|
Packit |
8f70b4 |
if(pty_send_buf)
|
|
Packit |
8f70b4 |
timeout_timer.Reset(pty_send_buf->EventTime());
|
|
Packit |
8f70b4 |
if(pty_recv_buf)
|
|
Packit |
8f70b4 |
timeout_timer.Reset(pty_recv_buf->EventTime());
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
// check for timeout only if there should be connection activity.
|
|
Packit |
8f70b4 |
if(state!=DISCONNECTED && state!=CONNECTED
|
|
Packit |
8f70b4 |
&& mode!=CLOSED && CheckTimeout())
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if((state==FILE_RECV || state==FILE_SEND)
|
|
Packit |
8f70b4 |
&& rate_limit==0)
|
|
Packit |
8f70b4 |
rate_limit=new RateLimit(hostname);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
switch(state)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
case DISCONNECTED:
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(mode==CLOSED)
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
if(mode==CONNECT_VERIFY)
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
// walk through SFtp classes and try to find identical idle session
|
|
Packit |
8f70b4 |
// first try "easy" cases of session take-over.
|
|
Packit |
8f70b4 |
for(int i=0; i<3; i++)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
bool limit_reached=(connection_limit>0
|
|
Packit |
8f70b4 |
&& connection_limit<=CountConnections());
|
|
Packit |
8f70b4 |
if(i>=2 && !limit_reached)
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
bool need_sleep=GetBetterConnection(i,limit_reached);
|
|
Packit |
8f70b4 |
if(state!=DISCONNECTED)
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
if(need_sleep)
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(!ReconnectAllowed())
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(!NextTry())
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
const char *init=Query("server-program",hostname);
|
|
Packit |
8f70b4 |
const char *prog=Query("connect-program",hostname);
|
|
Packit |
8f70b4 |
if(!prog || !prog[0])
|
|
Packit |
8f70b4 |
prog="ssh -a -x";
|
|
Packit |
8f70b4 |
ArgV args;
|
|
Packit |
8f70b4 |
if(!strchr(init,'/'))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(init[0])
|
|
Packit |
8f70b4 |
args.Add("-s"); // run ssh2 subsystem
|
|
Packit |
8f70b4 |
// sftpd does not have a greeting
|
|
Packit |
8f70b4 |
received_greeting=true;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
else
|
|
Packit |
8f70b4 |
init=xstring::cat("echo SFTP: >&2;",init,NULL);
|
|
Packit |
8f70b4 |
if(user)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
args.Add("-l");
|
|
Packit |
8f70b4 |
args.Add(user);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(portname)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
args.Add("-p");
|
|
Packit |
8f70b4 |
args.Add(portname);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
args.Add(hostname);
|
|
Packit |
8f70b4 |
if(init[0])
|
|
Packit |
8f70b4 |
args.Add(init);
|
|
Packit |
8f70b4 |
xstring_ca cmd_q(args.CombineShellQuoted(0));
|
|
Packit |
8f70b4 |
xstring& cmd_str=xstring::cat(prog," ",cmd_q.get(),NULL);
|
|
Packit |
8f70b4 |
LogNote(9,"%s (%s)",_("Running connect program"),cmd_str.get());
|
|
Packit |
8f70b4 |
ssh=new PtyShell(cmd_str);
|
|
Packit |
8f70b4 |
ssh->UsePipes();
|
|
Packit |
8f70b4 |
state=CONNECTING;
|
|
Packit |
8f70b4 |
timeout_timer.Reset();
|
|
Packit |
8f70b4 |
m=MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
case CONNECTING:
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
int fd=ssh->getfd();
|
|
Packit |
8f70b4 |
if(fd==-1)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(ssh->error())
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
SetError(FATAL,ssh->error_text);
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
TimeoutS(1);
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
MakePtyBuffers();
|
|
Packit |
8f70b4 |
set_real_cwd("~");
|
|
Packit |
8f70b4 |
state=CONNECTING_1;
|
|
Packit |
8f70b4 |
m=MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
case CONNECTING_1:
|
|
Packit |
8f70b4 |
m|=HandleSSHMessage();
|
|
Packit |
8f70b4 |
if(state!=CONNECTING_1)
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
if(!received_greeting)
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
SendRequest(new Request_INIT(Query("protocol-version",hostname)),Expect::FXP_VERSION);
|
|
Packit |
8f70b4 |
state=CONNECTING_2;
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
case CONNECTING_2:
|
|
Packit |
8f70b4 |
m|=HandleSSHMessage();
|
|
Packit |
8f70b4 |
if(state!=CONNECTING_2)
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
m|=HandleReplies();
|
|
Packit |
8f70b4 |
if(protocol_version==0)
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
if(home_auto==0)
|
|
Packit |
8f70b4 |
SendRequest(new Request_REALPATH("."),Expect::HOME_PATH);
|
|
Packit |
8f70b4 |
state=CONNECTED;
|
|
Packit |
8f70b4 |
m=MOVED;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
case CONNECTED:
|
|
Packit |
8f70b4 |
if(home.path==0 && !RespQueueIsEmpty())
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(mode==CLOSED)
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
SendRequest();
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
case FILE_RECV:
|
|
Packit |
8f70b4 |
if(file_buf->Size()>=rate_limit->BytesAllowedToGet())
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
recv_buf->Suspend();
|
|
Packit |
8f70b4 |
Timeout(1000);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
else if(file_buf->Size()>=max_buf)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
recv_buf->Suspend();
|
|
Packit |
8f70b4 |
m=MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
else if(recv_buf->IsSuspended())
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
recv_buf->Resume();
|
|
Packit |
8f70b4 |
if(recv_buf->Size()>0 || (recv_buf->Size()==0 && recv_buf->Eof()))
|
|
Packit |
8f70b4 |
m=MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case FILE_SEND:
|
|
Packit |
8f70b4 |
// pack data from file_buf.
|
|
Packit |
8f70b4 |
file_buf->Get(&b,&s);
|
|
Packit |
8f70b4 |
if(s==0 && !eof)
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
if(s
|
|
Packit |
8f70b4 |
return m; // wait for more data before sending.
|
|
Packit |
8f70b4 |
if(RespQueueSize()>max_packets_in_flight)
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
if(s==0)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
// no more data, set attributes and close the file.
|
|
Packit |
8f70b4 |
Request_FSETSTAT *req=new Request_FSETSTAT(handle,protocol_version);
|
|
Packit |
8f70b4 |
if(entity_date!=NO_DATE) {
|
|
Packit |
8f70b4 |
req->attrs.mtime=entity_date;
|
|
Packit |
8f70b4 |
req->attrs.flags|=SSH_FILEXFER_ATTR_MODIFYTIME;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
req->attrs.size=pos;
|
|
Packit |
8f70b4 |
req->attrs.flags|=SSH_FILEXFER_ATTR_SIZE;
|
|
Packit |
8f70b4 |
SendRequest(req,Expect::IGNORE);
|
|
Packit |
8f70b4 |
CloseHandle(Expect::DEFAULT);
|
|
Packit |
8f70b4 |
state=WAITING;
|
|
Packit |
8f70b4 |
m=MOVED;
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(s>size_write)
|
|
Packit |
8f70b4 |
s=size_write;
|
|
Packit |
8f70b4 |
SendRequest(new Request_WRITE(handle,request_pos,b,s),Expect::WRITE_STATUS);
|
|
Packit |
8f70b4 |
file_buf->Skip(s);
|
|
Packit |
8f70b4 |
request_pos+=s;
|
|
Packit |
8f70b4 |
flush_timer.Reset();
|
|
Packit |
8f70b4 |
m=MOVED;
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case WAITING:
|
|
Packit |
8f70b4 |
if(mode==ARRAY_INFO)
|
|
Packit |
8f70b4 |
SendArrayInfoRequests();
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case DONE:
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void SFtp::MoveConnectionHere(SFtp *o)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
super::MoveConnectionHere(o);
|
|
Packit |
8f70b4 |
protocol_version=o->protocol_version;
|
|
Packit |
8f70b4 |
recv_translate=o->recv_translate.borrow();
|
|
Packit |
8f70b4 |
send_translate=o->send_translate.borrow();
|
|
Packit |
8f70b4 |
rate_limit=o->rate_limit.borrow();
|
|
Packit |
8f70b4 |
expect_queue.move_here(o->expect_queue);
|
|
Packit |
8f70b4 |
timeout_timer.Reset(o->timeout_timer);
|
|
Packit |
8f70b4 |
ssh_id=o->ssh_id;
|
|
Packit |
8f70b4 |
state=CONNECTED;
|
|
Packit |
8f70b4 |
o->Disconnect();
|
|
Packit |
8f70b4 |
if(!home)
|
|
Packit |
8f70b4 |
set_home(home_auto);
|
|
Packit |
8f70b4 |
ResumeInternal();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void SFtp::DisconnectLL()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
super::DisconnectLL();
|
|
Packit |
8f70b4 |
handle.set(0);
|
|
Packit |
8f70b4 |
file_buf=0;
|
|
Packit |
8f70b4 |
EmptyRespQueue();
|
|
Packit |
8f70b4 |
state=DISCONNECTED;
|
|
Packit |
8f70b4 |
if(mode==STORE)
|
|
Packit |
8f70b4 |
SetError(STORE_FAILED);
|
|
Packit |
8f70b4 |
protocol_version=0;
|
|
Packit |
8f70b4 |
send_translate=0;
|
|
Packit |
8f70b4 |
recv_translate=0;
|
|
Packit |
8f70b4 |
ssh_id=0;
|
|
Packit |
8f70b4 |
home_auto.set(FindHomeAuto());
|
|
Packit |
8f70b4 |
// may have to resend file info queries.
|
|
Packit |
8f70b4 |
if(fileset_for_info)
|
|
Packit |
8f70b4 |
fileset_for_info->rewind();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void SFtp::Init()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
state=DISCONNECTED;
|
|
Packit |
8f70b4 |
ssh_id=0;
|
|
Packit |
8f70b4 |
eof=false;
|
|
Packit |
8f70b4 |
received_greeting=false;
|
|
Packit |
8f70b4 |
password_sent=0;
|
|
Packit |
8f70b4 |
protocol_version=0;
|
|
Packit |
8f70b4 |
send_translate=0;
|
|
Packit |
8f70b4 |
recv_translate=0;
|
|
Packit |
8f70b4 |
max_packets_in_flight=16;
|
|
Packit |
8f70b4 |
max_packets_in_flight_slow_start=1;
|
|
Packit |
8f70b4 |
size_read=0x8000;
|
|
Packit |
8f70b4 |
size_write=0x8000;
|
|
Packit |
8f70b4 |
use_full_path=false;
|
|
Packit |
8f70b4 |
flush_timer.Set(0,500);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
SFtp::SFtp() : SSH_Access("SFTP:")
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
Init();
|
|
Packit |
8f70b4 |
Reconfig(0);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
SFtp::~SFtp()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
Disconnect();
|
|
Packit |
8f70b4 |
Close();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
SFtp::SFtp(const SFtp *o) : super(o)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
Init();
|
|
Packit |
8f70b4 |
Reconfig(0);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
bool SFtp::Packet::HasID()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
return(type!=SSH_FXP_INIT && type!=SSH_FXP_VERSION);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void SFtp::Packet::PackString(Buffer *b,const char *str,int len)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(len==-1)
|
|
Packit |
8f70b4 |
len=strlen(str);
|
|
Packit |
8f70b4 |
b->PackUINT32BE(len);
|
|
Packit |
8f70b4 |
b->Put(str,len);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
SFtp::unpack_status_t SFtp::Packet::UnpackString(const Buffer *b,int *offset,int limit,xstring *str_out)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(limit-*offset<4)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
// We unpack strings when we have already received complete packet,
|
|
Packit |
8f70b4 |
// so it is not possible to receive any more data.
|
|
Packit |
8f70b4 |
LogError(2,"bad string in reply (truncated length field)");
|
|
Packit |
8f70b4 |
return UNPACK_WRONG_FORMAT;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
int len=b->UnpackUINT32BE(*offset);
|
|
Packit |
8f70b4 |
if(len>limit-*offset-4)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
LogError(2,"bad string in reply (invalid length field)");
|
|
Packit |
8f70b4 |
return UNPACK_WRONG_FORMAT;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
*offset+=4;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
const char *data;
|
|
Packit |
8f70b4 |
int data_len;
|
|
Packit |
8f70b4 |
b->Get(&data,&data_len);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
str_out->nset(data+*offset,len);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
*offset+=len;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
return UNPACK_SUCCESS;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
SFtp::unpack_status_t SFtp::Packet::Unpack(const Buffer *b)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
unpacked=0;
|
|
Packit |
8f70b4 |
if(b->Size()<4)
|
|
Packit |
8f70b4 |
return b->Eof()?UNPACK_PREMATURE_EOF:UNPACK_NO_DATA_YET;
|
|
Packit |
8f70b4 |
length=b->UnpackUINT32BE(0);
|
|
Packit |
8f70b4 |
unpacked+=4;
|
|
Packit |
8f70b4 |
if(length<1)
|
|
Packit |
8f70b4 |
return UNPACK_WRONG_FORMAT;
|
|
Packit |
8f70b4 |
if(b->Size()
|
|
Packit |
8f70b4 |
return b->Eof()?UNPACK_PREMATURE_EOF:UNPACK_NO_DATA_YET;
|
|
Packit |
8f70b4 |
int t=b->UnpackUINT8(4);
|
|
Packit |
8f70b4 |
unpacked++;
|
|
Packit |
8f70b4 |
if(!is_valid_reply(t))
|
|
Packit |
8f70b4 |
return UNPACK_WRONG_FORMAT;
|
|
Packit |
8f70b4 |
type=(packet_type)t;
|
|
Packit |
8f70b4 |
if(HasID())
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(length<5)
|
|
Packit |
8f70b4 |
return UNPACK_WRONG_FORMAT;
|
|
Packit |
8f70b4 |
id=b->UnpackUINT32BE(5);
|
|
Packit |
8f70b4 |
unpacked+=4;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
else
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
id=0;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
return UNPACK_SUCCESS;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
SFtp::unpack_status_t SFtp::UnpackPacket(Buffer *b,SFtp::Packet **p)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
Packet *&pp=*p;
|
|
Packit |
8f70b4 |
pp=0;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
Packet probe;
|
|
Packit |
8f70b4 |
unpack_status_t res=probe.Unpack(b);
|
|
Packit |
8f70b4 |
if(res!=UNPACK_SUCCESS)
|
|
Packit |
8f70b4 |
return res;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
LogRecvF(9,"got a packet, length=%d, type=%d(%s), id=%u\n",
|
|
Packit |
8f70b4 |
probe.GetLength(),probe.GetPacketType(),probe.GetPacketTypeText(),probe.GetID());
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
switch(probe.GetPacketType())
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
case SSH_FXP_VERSION:
|
|
Packit |
8f70b4 |
pp=new Reply_VERSION();
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case SSH_FXP_NAME:
|
|
Packit |
8f70b4 |
pp=new Reply_NAME(protocol_version);
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case SSH_FXP_ATTRS:
|
|
Packit |
8f70b4 |
pp=new Reply_ATTRS(protocol_version);
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case SSH_FXP_STATUS:
|
|
Packit |
8f70b4 |
pp=new Reply_STATUS(protocol_version);
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case SSH_FXP_HANDLE:
|
|
Packit |
8f70b4 |
pp=new Reply_HANDLE();
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case SSH_FXP_DATA:
|
|
Packit |
8f70b4 |
pp=new Reply_DATA();
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case SSH_FXP_INIT:
|
|
Packit |
8f70b4 |
case SSH_FXP_OPEN:
|
|
Packit |
8f70b4 |
case SSH_FXP_CLOSE:
|
|
Packit |
8f70b4 |
case SSH_FXP_READ:
|
|
Packit |
8f70b4 |
case SSH_FXP_WRITE:
|
|
Packit |
8f70b4 |
case SSH_FXP_LSTAT:
|
|
Packit |
8f70b4 |
case SSH_FXP_FSTAT:
|
|
Packit |
8f70b4 |
case SSH_FXP_SETSTAT:
|
|
Packit |
8f70b4 |
case SSH_FXP_FSETSTAT:
|
|
Packit |
8f70b4 |
case SSH_FXP_OPENDIR:
|
|
Packit |
8f70b4 |
case SSH_FXP_READDIR:
|
|
Packit |
8f70b4 |
case SSH_FXP_REMOVE:
|
|
Packit |
8f70b4 |
case SSH_FXP_MKDIR:
|
|
Packit |
8f70b4 |
case SSH_FXP_RMDIR:
|
|
Packit |
8f70b4 |
case SSH_FXP_REALPATH:
|
|
Packit |
8f70b4 |
case SSH_FXP_STAT:
|
|
Packit |
8f70b4 |
case SSH_FXP_RENAME:
|
|
Packit |
8f70b4 |
case SSH_FXP_READLINK:
|
|
Packit |
8f70b4 |
case SSH_FXP_SYMLINK:
|
|
Packit |
8f70b4 |
case SSH_FXP_LINK:
|
|
Packit |
8f70b4 |
case SSH_FXP_BLOCK:
|
|
Packit |
8f70b4 |
case SSH_FXP_UNBLOCK:
|
|
Packit |
8f70b4 |
case SSH_FXP_EXTENDED:
|
|
Packit |
8f70b4 |
LogError(0,"request in reply??");
|
|
Packit |
8f70b4 |
return UNPACK_WRONG_FORMAT;
|
|
Packit |
8f70b4 |
case SSH_FXP_EXTENDED_REPLY:
|
|
Packit |
8f70b4 |
LogError(0,"unexpected SSH_FXP_EXTENDED_REPLY");
|
|
Packit |
8f70b4 |
return UNPACK_WRONG_FORMAT;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
res=pp->Unpack(b);
|
|
Packit |
8f70b4 |
if(res!=UNPACK_SUCCESS)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
switch(res)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
case UNPACK_PREMATURE_EOF:
|
|
Packit |
8f70b4 |
LogError(0,"premature eof");
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case UNPACK_WRONG_FORMAT:
|
|
Packit |
8f70b4 |
LogError(0,"wrong packet format");
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case UNPACK_NO_DATA_YET:
|
|
Packit |
8f70b4 |
case UNPACK_SUCCESS:
|
|
Packit |
8f70b4 |
;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
probe.DropData(b);
|
|
Packit |
8f70b4 |
delete pp;
|
|
Packit |
8f70b4 |
pp=0;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
return res;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void SFtp::SendRequest(Packet *request,Expect::expect_t tag,int i)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
request->SetID(ssh_id++);
|
|
Packit |
8f70b4 |
request->ComputeLength();
|
|
Packit |
8f70b4 |
LogSendF(9,"sending a packet, length=%d, type=%d(%s), id=%u\n",
|
|
Packit |
8f70b4 |
request->GetLength(),request->GetPacketType(),request->GetPacketTypeText(),request->GetID());
|
|
Packit |
8f70b4 |
request->Pack(send_buf.get_non_const());
|
|
Packit |
8f70b4 |
PushExpect(new Expect(request,tag,i));
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
const char *SFtp::SkipHome(const char *path)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(path[0]=='~' && path[1]=='/' && path[2])
|
|
Packit |
8f70b4 |
return path+2;
|
|
Packit |
8f70b4 |
if(path[0]=='~' && !path[1])
|
|
Packit |
8f70b4 |
return ".";
|
|
Packit |
8f70b4 |
if(!home)
|
|
Packit |
8f70b4 |
return path;
|
|
Packit |
8f70b4 |
int home_len=home.path.length();
|
|
Packit |
8f70b4 |
if(strncmp(home,path,home_len))
|
|
Packit |
8f70b4 |
return path;
|
|
Packit |
8f70b4 |
if(path[home_len]=='/' && path[home_len+1] && path[home_len+1]!='/')
|
|
Packit |
8f70b4 |
return path+home_len+1;
|
|
Packit |
8f70b4 |
if(!path[home_len])
|
|
Packit |
8f70b4 |
return ".";
|
|
Packit |
8f70b4 |
return path;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
const char *SFtp::WirePath(const char *path)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
path=dir_file(cwd,path);
|
|
Packit |
8f70b4 |
if(!use_full_path || path[0]=='~')
|
|
Packit |
8f70b4 |
path=SkipHome(path);
|
|
Packit |
8f70b4 |
LogNote(9,"path on wire is `%s'",path);
|
|
Packit |
8f70b4 |
return lc_to_utf8(path);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void SFtp::SendRequest()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
max_packets_in_flight_slow_start=1;
|
|
Packit |
8f70b4 |
ExpandTildeInCWD();
|
|
Packit |
8f70b4 |
switch((open_mode)mode)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
case CHANGE_DIR:
|
|
Packit |
8f70b4 |
LogNote(9,"checking directory `%s'",file.get());
|
|
Packit |
8f70b4 |
SendRequest(new Request_STAT(lc_to_utf8(file),0,protocol_version),Expect::CWD);
|
|
Packit |
8f70b4 |
SendRequest(new Request_STAT(lc_to_utf8(dir_file(file,".")),0,protocol_version),Expect::CWD);
|
|
Packit |
8f70b4 |
state=WAITING;
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case RETRIEVE:
|
|
Packit |
8f70b4 |
SendRequest(new Request_OPEN(WirePath(file),SSH_FXF_READ,
|
|
Packit |
8f70b4 |
ACE4_READ_DATA|ACE4_READ_ATTRIBUTES,SSH_FXF_OPEN_EXISTING,protocol_version),Expect::HANDLE);
|
|
Packit |
8f70b4 |
state=WAITING;
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case LIST:
|
|
Packit |
8f70b4 |
case LONG_LIST:
|
|
Packit |
8f70b4 |
SendRequest(new Request_OPENDIR(WirePath(file)),Expect::HANDLE);
|
|
Packit |
8f70b4 |
state=WAITING;
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case STORE:
|
|
Packit |
8f70b4 |
SendRequest(
|
|
Packit |
8f70b4 |
new Request_OPEN(WirePath(file),
|
|
Packit |
8f70b4 |
SSH_FXF_WRITE|SSH_FXF_CREAT|(pos==0?SSH_FXF_TRUNC:0),
|
|
Packit |
8f70b4 |
ACE4_WRITE_DATA|ACE4_WRITE_ATTRIBUTES,
|
|
Packit |
8f70b4 |
pos==0?SSH_FXF_CREATE_TRUNCATE:SSH_FXF_OPEN_OR_CREATE,
|
|
Packit |
8f70b4 |
protocol_version),
|
|
Packit |
8f70b4 |
Expect::HANDLE);
|
|
Packit |
8f70b4 |
state=WAITING;
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case ARRAY_INFO:
|
|
Packit |
8f70b4 |
state=WAITING;
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case RENAME:
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(protocol_version<3)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
SetError(NOT_SUPP);
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
unsigned options=0;
|
|
Packit |
8f70b4 |
if(rename_f) {
|
|
Packit |
8f70b4 |
options=SSH_FXF_RENAME_OVERWRITE;
|
|
Packit |
8f70b4 |
if(protocol_version<5) {
|
|
Packit |
8f70b4 |
// overwrite is not supported, remove the target explicitly
|
|
Packit |
8f70b4 |
SendRequest(new Request_REMOVE(WirePath(file1)),Expect::IGNORE);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
SendRequest(new Request_RENAME(WirePath(file),WirePath(file1),
|
|
Packit |
8f70b4 |
options,protocol_version),Expect::DEFAULT);
|
|
Packit |
8f70b4 |
state=WAITING;
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
case CHANGE_MODE:
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
Request_SETSTAT *req=new Request_SETSTAT(WirePath(file),protocol_version);
|
|
Packit |
8f70b4 |
req->attrs.permissions=chmod_mode;
|
|
Packit |
8f70b4 |
req->attrs.flags|=SSH_FILEXFER_ATTR_PERMISSIONS;
|
|
Packit |
8f70b4 |
SendRequest(req,Expect::DEFAULT);
|
|
Packit |
8f70b4 |
state=WAITING;
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
case MAKE_DIR:
|
|
Packit |
8f70b4 |
if(mkdir_p)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
Ref<StringSet> dirs(MkdirMakeSet());
|
|
Packit |
8f70b4 |
for(int i=0; i<dirs->Count(); i++)
|
|
Packit |
8f70b4 |
SendRequest(new Request_MKDIR(WirePath(dirs->String(i)),protocol_version),Expect::IGNORE);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
SendRequest(new Request_MKDIR(WirePath(file),protocol_version),Expect::DEFAULT);
|
|
Packit |
8f70b4 |
state=WAITING;
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case REMOVE_DIR:
|
|
Packit |
8f70b4 |
SendRequest(new Request_RMDIR(WirePath(file)),Expect::DEFAULT);
|
|
Packit |
8f70b4 |
state=WAITING;
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case REMOVE:
|
|
Packit |
8f70b4 |
SendRequest(new Request_REMOVE(WirePath(file)),Expect::DEFAULT);
|
|
Packit |
8f70b4 |
state=WAITING;
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case LINK:
|
|
Packit |
8f70b4 |
if(protocol_version<6) {
|
|
Packit |
8f70b4 |
SetError(NOT_SUPP);
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
case SYMLINK:
|
|
Packit |
8f70b4 |
if(protocol_version<3) {
|
|
Packit |
8f70b4 |
SetError(NOT_SUPP);
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(protocol_version>=6)
|
|
Packit |
8f70b4 |
SendRequest(new Request_LINK(mode==SYMLINK?lc_to_utf8(file):WirePath(file),WirePath(file1),mode==SYMLINK),Expect::DEFAULT);
|
|
Packit |
8f70b4 |
else
|
|
Packit |
8f70b4 |
SendRequest(new Request_SYMLINK(lc_to_utf8(file),WirePath(file1)),Expect::DEFAULT);
|
|
Packit |
8f70b4 |
state=WAITING;
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case QUOTE_CMD:
|
|
Packit |
8f70b4 |
case MP_LIST:
|
|
Packit |
8f70b4 |
SetError(NOT_SUPP);
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case CONNECT_VERIFY:
|
|
Packit |
8f70b4 |
case CLOSED:
|
|
Packit |
8f70b4 |
abort();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void SFtp::SendArrayInfoRequests()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
for(FileInfo *fi=fileset_for_info->curr();
|
|
Packit |
8f70b4 |
fi && RespQueueSize()
|
|
Packit |
8f70b4 |
fi=fileset_for_info->next())
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(fi->need&(fi->SIZE|fi->DATE|fi->MODE|fi->TYPE|fi->USER|fi->GROUP)) {
|
|
Packit |
8f70b4 |
unsigned flags=0;
|
|
Packit |
8f70b4 |
if(fi->need&fi->SIZE) flags|=SSH_FILEXFER_ATTR_SIZE;
|
|
Packit |
8f70b4 |
if(fi->need&fi->DATE) flags|=SSH_FILEXFER_ATTR_MODIFYTIME;
|
|
Packit |
8f70b4 |
if(fi->need&fi->MODE) flags|=SSH_FILEXFER_ATTR_PERMISSIONS;
|
|
Packit |
8f70b4 |
if(fi->need&(fi->USER|fi->GROUP)) flags|=SSH_FILEXFER_ATTR_OWNERGROUP;
|
|
Packit |
8f70b4 |
SendRequest(new Request_STAT(WirePath(fi->name),flags,
|
|
Packit |
8f70b4 |
protocol_version),Expect::INFO,fileset_for_info->curr_index());
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(fi->need&fi->SYMLINK_DEF && protocol_version>=3)
|
|
Packit |
8f70b4 |
SendRequest(new Request_READLINK(WirePath(fi->name)),
|
|
Packit |
8f70b4 |
Expect::INFO_READLINK,fileset_for_info->curr_index());
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(RespQueueIsEmpty())
|
|
Packit |
8f70b4 |
state=DONE;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void SFtp::CloseHandle(Expect::expect_t c)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(handle)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
SendRequest(new Request_CLOSE(handle),c);
|
|
Packit |
8f70b4 |
handle.set(0);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void SFtp::Close()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
switch(state)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
case(DISCONNECTED):
|
|
Packit |
8f70b4 |
case(WAITING):
|
|
Packit |
8f70b4 |
case(CONNECTED):
|
|
Packit |
8f70b4 |
case(DONE):
|
|
Packit |
8f70b4 |
case(FILE_RECV):
|
|
Packit |
8f70b4 |
case(FILE_SEND):
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case(CONNECTING):
|
|
Packit |
8f70b4 |
case(CONNECTING_1):
|
|
Packit |
8f70b4 |
case(CONNECTING_2):
|
|
Packit |
8f70b4 |
Disconnect();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
CloseExpectQueue();
|
|
Packit |
8f70b4 |
state=(recv_buf?CONNECTED:DISCONNECTED);
|
|
Packit |
8f70b4 |
eof=false;
|
|
Packit |
8f70b4 |
file_buf=0;
|
|
Packit |
8f70b4 |
file_set=0;
|
|
Packit |
8f70b4 |
CloseHandle(Expect::IGNORE);
|
|
Packit |
8f70b4 |
super::Close();
|
|
Packit |
8f70b4 |
// don't need these out-of-order packets anymore
|
|
Packit |
8f70b4 |
ooo_chain.truncate();
|
|
Packit |
8f70b4 |
if(recv_buf)
|
|
Packit |
8f70b4 |
recv_buf->Resume();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
int SFtp::HandlePty()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
int m=STALL;
|
|
Packit |
8f70b4 |
if(pty_recv_buf==0)
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
const char *b;
|
|
Packit |
8f70b4 |
int s;
|
|
Packit |
8f70b4 |
pty_recv_buf->Get(&b,&s);
|
|
Packit |
8f70b4 |
const char *eol=(const char*)memchr(b,'\n',s);
|
|
Packit |
8f70b4 |
if(!eol)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(pty_recv_buf->Eof())
|
|
Packit |
8f70b4 |
LogError(0,_("Peer closed connection"));
|
|
Packit |
8f70b4 |
if(pty_recv_buf->Error())
|
|
Packit |
8f70b4 |
LogError(0,"pty read: %s",pty_recv_buf->ErrorText());
|
|
Packit |
8f70b4 |
if(pty_recv_buf->Eof() || pty_recv_buf->Error())
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
Disconnect(pty_recv_buf->ErrorText());
|
|
Packit |
8f70b4 |
m=MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
m=MOVED;
|
|
Packit |
8f70b4 |
s=eol-b+1;
|
|
Packit |
8f70b4 |
char *line=string_alloca(s);
|
|
Packit |
8f70b4 |
memcpy(line,b,s-1);
|
|
Packit |
8f70b4 |
line[s-1]=0;
|
|
Packit |
8f70b4 |
pty_recv_buf->Skip(s);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
LogRecv(4,line);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void SFtp::HandleExpect(Expect *e)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
const Packet *reply=e->reply;
|
|
Packit |
8f70b4 |
if(reply->TypeIs(SSH_FXP_STATUS))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
Reply_STATUS *r=(Reply_STATUS*)reply;
|
|
Packit |
8f70b4 |
const char *message=r->GetMessage();
|
|
Packit |
8f70b4 |
LogNote(9,"status code=%d(%s), message=%s",r->GetCode(),r->GetCodeText(),
|
|
Packit |
8f70b4 |
message?message:"NULL");
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
switch(e->tag)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
case Expect::FXP_VERSION:
|
|
Packit |
8f70b4 |
if(reply->TypeIs(SSH_FXP_VERSION))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
protocol_version=((Reply_VERSION*)reply)->GetVersion();
|
|
Packit |
8f70b4 |
LogNote(9,"protocol version set to %d",protocol_version);
|
|
Packit |
8f70b4 |
const char *charset=0;
|
|
Packit |
8f70b4 |
if(protocol_version>=4)
|
|
Packit |
8f70b4 |
charset="UTF-8";
|
|
Packit |
8f70b4 |
else
|
|
Packit |
8f70b4 |
charset=ResMgr::Query("sftp:charset",hostname);
|
|
Packit |
8f70b4 |
if(charset && *charset)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
send_translate=new DirectedBuffer(DirectedBuffer::PUT);
|
|
Packit |
8f70b4 |
recv_translate=new DirectedBuffer(DirectedBuffer::GET);
|
|
Packit |
8f70b4 |
send_translate->SetTranslation(charset,false);
|
|
Packit |
8f70b4 |
recv_translate->SetTranslation(charset,true);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
else
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
Disconnect();
|
|
Packit |
8f70b4 |
SetError(FATAL,"cannot negotiate protocol version");
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case Expect::HOME_PATH:
|
|
Packit |
8f70b4 |
if(reply->TypeIs(SSH_FXP_NAME))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
Reply_NAME *r=(Reply_NAME*)reply;
|
|
Packit |
8f70b4 |
const NameAttrs *a=r->GetNameAttrs(0);
|
|
Packit |
8f70b4 |
if(a && !home_auto)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
home_auto.set(utf8_to_lc(a->name));
|
|
Packit |
8f70b4 |
LogNote(9,"home set to %s",home_auto.get());
|
|
Packit |
8f70b4 |
PropagateHomeAuto();
|
|
Packit |
8f70b4 |
if(!home)
|
|
Packit |
8f70b4 |
set_home(home_auto);
|
|
Packit |
8f70b4 |
cache->SetDirectory(this, home, true);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case Expect::CWD:
|
|
Packit |
8f70b4 |
if(reply->TypeIs(SSH_FXP_ATTRS))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
const FileAttrs *a=((Reply_ATTRS*)reply)->GetAttrs();
|
|
Packit |
8f70b4 |
if(a->type!=SSH_FILEXFER_TYPE_DIRECTORY
|
|
Packit |
8f70b4 |
&& a->type!=SSH_FILEXFER_TYPE_SYMLINK) // workaround for RouterOS v6
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
LogError(1,"got file type %d",a->type);
|
|
Packit |
8f70b4 |
cache->SetDirectory(this,cwd,false);
|
|
Packit |
8f70b4 |
SetError(NO_FILE,strerror(ENOTDIR));
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(mode==CHANGE_DIR && GetExpectCount(Expect::CWD)==0)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
cwd.Set(file);
|
|
Packit |
8f70b4 |
eof=true;
|
|
Packit |
8f70b4 |
cache->SetDirectory(this,cwd,true);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
else
|
|
Packit |
8f70b4 |
SetError(NO_FILE,reply);
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case Expect::HANDLE:
|
|
Packit |
8f70b4 |
if(reply->TypeIs(SSH_FXP_HANDLE))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
handle.set(((Reply_HANDLE*)reply)->GetHandle());
|
|
Packit |
8f70b4 |
state=(mode==STORE?FILE_SEND:FILE_RECV);
|
|
Packit |
8f70b4 |
file_buf=new Buffer;
|
|
Packit |
8f70b4 |
xstring handle_x("");
|
|
Packit |
8f70b4 |
int handle_len=handle.length();
|
|
Packit |
8f70b4 |
for(int i=0; i
|
|
Packit |
8f70b4 |
handle_x.appendf("%02X",(unsigned char)handle[i]);
|
|
Packit |
8f70b4 |
LogNote(9,"got file handle %s (%d)",handle_x.get(),handle_len);
|
|
Packit |
8f70b4 |
request_pos=real_pos=pos;
|
|
Packit |
8f70b4 |
if(mode==RETRIEVE) {
|
|
Packit |
8f70b4 |
SendRequest(new Request_FSTAT(handle,
|
|
Packit |
8f70b4 |
SSH_FILEXFER_ATTR_SIZE|SSH_FILEXFER_ATTR_MODIFYTIME|SSH_FILEXFER_ATTR_PERMISSIONS,
|
|
Packit |
8f70b4 |
protocol_version),Expect::INFO);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
else
|
|
Packit |
8f70b4 |
SetError(NO_FILE,reply);
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case Expect::HANDLE_STALE:
|
|
Packit |
8f70b4 |
if(reply->TypeIs(SSH_FXP_HANDLE))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
// close the handle immediately.
|
|
Packit |
8f70b4 |
const xstring &handle=((Reply_HANDLE*)reply)->GetHandle();
|
|
Packit |
8f70b4 |
SendRequest(new Request_CLOSE(handle),Expect::IGNORE);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case Expect::DATA:
|
|
Packit |
8f70b4 |
if(max_packets_in_flight_slow_start
|
|
Packit |
8f70b4 |
max_packets_in_flight_slow_start++;
|
|
Packit |
8f70b4 |
if(reply->TypeIs(SSH_FXP_DATA))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
const Request_READ *r=e->request.Cast<Request_READ>();
|
|
Packit |
8f70b4 |
Reply_DATA *d=(Reply_DATA*)reply;
|
|
Packit |
8f70b4 |
if(r->pos==pos+file_buf->Size())
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
const char *b; int s;
|
|
Packit |
8f70b4 |
d->GetData(&b,&s);
|
|
Packit |
8f70b4 |
LogNote(9,"data packet: pos=%lld, size=%d",(long long)r->pos,s);
|
|
Packit |
8f70b4 |
file_buf->Put(b,s);
|
|
Packit |
8f70b4 |
if(d->Eof())
|
|
Packit |
8f70b4 |
goto eof;
|
|
Packit |
8f70b4 |
if(r->len > unsigned(s)) // received less than requested?
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
// if we have not yet requested next chunk of data,
|
|
Packit |
8f70b4 |
// then adjust request position, else re-request missed data.
|
|
Packit |
8f70b4 |
if(r->pos+r->len==request_pos)
|
|
Packit |
8f70b4 |
request_pos=r->pos+s;
|
|
Packit |
8f70b4 |
else
|
|
Packit |
8f70b4 |
SendRequest(new Request_READ(handle,r->pos+s,r->len-s),Expect::DATA);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
else
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
LogNote(9,"put a packet with id=%d on out-of-order chain (need_pos=%lld packet_pos=%lld)",
|
|
Packit |
8f70b4 |
reply->GetID(),(long long)(pos+file_buf->Size()),(long long)r->pos);
|
|
Packit |
8f70b4 |
if(ooo_chain.count()>=64)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
LogError(0,"Too many out-of-order packets");
|
|
Packit |
8f70b4 |
Disconnect();
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
ooo_chain.append(e);
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
else if(reply->TypeIs(SSH_FXP_NAME))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
Reply_NAME *r=(Reply_NAME*)reply;
|
|
Packit |
8f70b4 |
LogNote(9,"file name count=%d",r->GetCount());
|
|
Packit |
8f70b4 |
for(int i=0; i<r->GetCount(); i++)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
const NameAttrs *a=r->GetNameAttrs(i);
|
|
Packit |
8f70b4 |
FileInfo *info=MakeFileInfo(a);
|
|
Packit |
8f70b4 |
if(mode==LIST)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
file_buf->Put(a->name);
|
|
Packit |
8f70b4 |
if(a->attrs.type==SSH_FILEXFER_TYPE_DIRECTORY)
|
|
Packit |
8f70b4 |
file_buf->Put("/");
|
|
Packit |
8f70b4 |
file_buf->Put("\n");
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
else if(mode==LONG_LIST)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(a->longname)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
file_buf->Put(a->longname);
|
|
Packit |
8f70b4 |
file_buf->Put("\n");
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
else if(info)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
info->MakeLongName();
|
|
Packit |
8f70b4 |
file_buf->Put(info->longname);
|
|
Packit |
8f70b4 |
file_buf->Put("\n");
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(info)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(!file_set)
|
|
Packit |
8f70b4 |
file_set=new FileSet;
|
|
Packit |
8f70b4 |
file_set->Add(info);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(r->Eof())
|
|
Packit |
8f70b4 |
goto eof;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
else
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(reply->TypeIs(SSH_FXP_STATUS))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(((Reply_STATUS*)reply)->GetCode()==SSH_FX_EOF)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
eof:
|
|
Packit |
8f70b4 |
if(!eof)
|
|
Packit |
8f70b4 |
LogNote(9,"eof");
|
|
Packit |
8f70b4 |
eof=true;
|
|
Packit |
8f70b4 |
state=DONE;
|
|
Packit |
8f70b4 |
if(file_buf && ooo_chain.count()==0)
|
|
Packit |
8f70b4 |
file_buf->PutEOF();
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
SetError(NO_FILE,reply);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case Expect::INFO:
|
|
Packit |
8f70b4 |
if(mode==ARRAY_INFO)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(reply->TypeIs(SSH_FXP_ATTRS)) {
|
|
Packit |
8f70b4 |
FileInfo *fi=(*fileset_for_info)[e->i];
|
|
Packit |
8f70b4 |
MergeAttrs(fi,((Reply_ATTRS*)reply)->GetAttrs());
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
entity_size=NO_SIZE;
|
|
Packit |
8f70b4 |
entity_date=NO_DATE;
|
|
Packit |
8f70b4 |
if(reply->TypeIs(SSH_FXP_ATTRS))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
const FileAttrs *a=((Reply_ATTRS*)reply)->GetAttrs();
|
|
Packit |
8f70b4 |
if(a->flags&SSH_FILEXFER_ATTR_SIZE)
|
|
Packit |
8f70b4 |
entity_size=a->size;
|
|
Packit |
8f70b4 |
if(a->flags&SSH_FILEXFER_ATTR_MODIFYTIME)
|
|
Packit |
8f70b4 |
entity_date=a->mtime;
|
|
Packit |
8f70b4 |
LogNote(9,"file info: size=%lld, date=%s",(long long)entity_size,ctime(&entity_date));
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(opt_size)
|
|
Packit |
8f70b4 |
*opt_size=entity_size;
|
|
Packit |
8f70b4 |
if(opt_date)
|
|
Packit |
8f70b4 |
*opt_date=entity_date;
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case Expect::INFO_READLINK:
|
|
Packit |
8f70b4 |
if(reply->TypeIs(SSH_FXP_NAME)) {
|
|
Packit |
8f70b4 |
Reply_NAME *r=(Reply_NAME*)reply;
|
|
Packit |
8f70b4 |
const NameAttrs *a=r->GetNameAttrs(0);
|
|
Packit |
8f70b4 |
LogNote(9,"file info: symlink=%s",a->name.get());
|
|
Packit |
8f70b4 |
if(mode==ARRAY_INFO)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
FileInfo *fi=(*fileset_for_info)[e->i];
|
|
Packit |
8f70b4 |
fi->SetSymlink(a->name);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case Expect::WRITE_STATUS:
|
|
Packit |
8f70b4 |
if(reply->TypeIs(SSH_FXP_STATUS))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(((Reply_STATUS*)reply)->GetCode()==SSH_FX_OK) {
|
|
Packit |
8f70b4 |
TrySuccess();
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
SetError(NO_FILE,reply);
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case Expect::DEFAULT:
|
|
Packit |
8f70b4 |
if(reply->TypeIs(SSH_FXP_STATUS))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(((Reply_STATUS*)reply)->GetCode()==SSH_FX_OK)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
state=DONE;
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
SetError(NO_FILE,reply);
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case Expect::IGNORE:
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
delete e;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void SFtp::RequestMoreData()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
Enter(this);
|
|
Packit |
8f70b4 |
if(mode==RETRIEVE) {
|
|
Packit |
8f70b4 |
int req_len=size_read;
|
|
Packit |
8f70b4 |
SendRequest(new Request_READ(handle,request_pos,req_len),Expect::DATA);
|
|
Packit |
8f70b4 |
request_pos+=req_len;
|
|
Packit |
8f70b4 |
} else if(mode==LIST || mode==LONG_LIST) {
|
|
Packit |
8f70b4 |
SendRequest(new Request_READDIR(handle),Expect::DATA);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
Leave(this);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
int SFtp::HandleReplies()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
int m=STALL;
|
|
Packit |
8f70b4 |
if(recv_buf==0)
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(state!=CONNECTING_2)
|
|
Packit |
8f70b4 |
m|=HandlePty();
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(!recv_buf)
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(file_buf) {
|
|
Packit |
8f70b4 |
off_t need_pos=pos+file_buf->Size();
|
|
Packit |
8f70b4 |
// there are usually a few of out-of-order packets, no need for fast search
|
|
Packit |
8f70b4 |
for(int i=0; i
|
|
Packit |
8f70b4 |
if(ooo_chain[i]->has_data_at_pos(need_pos)) {
|
|
Packit |
8f70b4 |
Expect *e=ooo_chain[i];
|
|
Packit |
8f70b4 |
ooo_chain[i]=0; // to keep the Expect
|
|
Packit |
8f70b4 |
ooo_chain.remove(i);
|
|
Packit |
8f70b4 |
HandleExpect(e);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(ooo_chain.count()==0 && eof && file_buf && !file_buf->Eof())
|
|
Packit |
8f70b4 |
file_buf->PutEOF();
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(recv_buf->Size()<4)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(recv_buf->Error())
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
LogError(0,"receive: %s",recv_buf->ErrorText());
|
|
Packit |
8f70b4 |
Disconnect(recv_buf->ErrorText());
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(recv_buf->Eof() && pty_recv_buf->Size()==0)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
LogError(0,_("Peer closed connection"));
|
|
Packit |
8f70b4 |
Disconnect(last_ssh_message?last_ssh_message.get():_("Peer closed connection"));
|
|
Packit |
8f70b4 |
m=MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(recv_buf->IsSuspended())
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
Packet *reply=0;
|
|
Packit |
8f70b4 |
unpack_status_t st=UnpackPacket(recv_buf.get_non_const(),&reply);
|
|
Packit |
8f70b4 |
if(st==UNPACK_NO_DATA_YET)
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
if(st!=UNPACK_SUCCESS)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
LogError(2,_("invalid server response format"));
|
|
Packit |
8f70b4 |
Disconnect(_("invalid server response format"));
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
reply->DropData(recv_buf.get_non_const());
|
|
Packit |
8f70b4 |
Expect *e=FindExpectExclusive(reply);
|
|
Packit |
8f70b4 |
if(e==0)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
LogError(3,_("extra server response"));
|
|
Packit |
8f70b4 |
delete reply;
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
HandleExpect(e);
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void SFtp::PushExpect(Expect *e)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
expect_queue.add(e->request->GetKey(),e);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
SFtp::Expect *SFtp::FindExpectExclusive(Packet *p)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
Expect *e=expect_queue.borrow(p->GetKey());
|
|
Packit |
8f70b4 |
if(e)
|
|
Packit |
8f70b4 |
e->reply=p;
|
|
Packit |
8f70b4 |
return e;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void SFtp::CloseExpectQueue()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
for(Expect *e=expect_queue.each_begin(); e; e=expect_queue.each_next())
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
switch(e->tag)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
case Expect::IGNORE:
|
|
Packit |
8f70b4 |
case Expect::HANDLE_STALE:
|
|
Packit |
8f70b4 |
case Expect::HOME_PATH:
|
|
Packit |
8f70b4 |
case Expect::FXP_VERSION:
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case Expect::CWD:
|
|
Packit |
8f70b4 |
case Expect::INFO:
|
|
Packit |
8f70b4 |
case Expect::INFO_READLINK:
|
|
Packit |
8f70b4 |
case Expect::DEFAULT:
|
|
Packit |
8f70b4 |
case Expect::DATA:
|
|
Packit |
8f70b4 |
case Expect::WRITE_STATUS:
|
|
Packit |
8f70b4 |
e->tag=Expect::IGNORE;
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case Expect::HANDLE:
|
|
Packit |
8f70b4 |
e->tag=Expect::HANDLE_STALE;
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
int SFtp::GetExpectCount(Expect::expect_t tag)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
int count=0;
|
|
Packit |
8f70b4 |
for(Expect *e=expect_queue.each_begin(); e; e=expect_queue.each_next())
|
|
Packit |
8f70b4 |
count+=(e->tag==tag);
|
|
Packit |
8f70b4 |
return count;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
Glob *SFtp::MakeGlob(const char *pat)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
return new GenericGlob(this,pat);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
ListInfo *SFtp::MakeListInfo(const char *dir)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
return new SFtpListInfo(this,dir);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
int SFtp::Read(Buffer *buf,int size)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(Error())
|
|
Packit |
8f70b4 |
return error_code;
|
|
Packit |
8f70b4 |
if(mode==CLOSED)
|
|
Packit |
8f70b4 |
return 0;
|
|
Packit |
8f70b4 |
if(state==DONE && !(file_buf && file_buf->Size()>0))
|
|
Packit |
8f70b4 |
return 0; // eof
|
|
Packit |
8f70b4 |
if(state==FILE_RECV)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
// keep some packets in flight.
|
|
Packit |
8f70b4 |
int limit=(entity_size>=0?max_packets_in_flight:max_packets_in_flight_slow_start);
|
|
Packit |
8f70b4 |
if(RespQueueSize()<limit && !file_buf->Eof())
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
// but don't request much after possible EOF.
|
|
Packit |
8f70b4 |
if(entity_size<0 || request_pos
|
|
Packit |
8f70b4 |
RequestMoreData();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(file_buf && file_buf->Size()>0)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
const char *buf1;
|
|
Packit |
8f70b4 |
int size1;
|
|
Packit |
8f70b4 |
file_buf->Get(&buf1,&size1);
|
|
Packit |
8f70b4 |
if(buf1==0)
|
|
Packit |
8f70b4 |
return 0;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
int bytes_allowed=rate_limit->BytesAllowedToGet();
|
|
Packit |
8f70b4 |
if(size1>bytes_allowed)
|
|
Packit |
8f70b4 |
size1=bytes_allowed;
|
|
Packit |
8f70b4 |
if(size1==0)
|
|
Packit |
8f70b4 |
return DO_AGAIN;
|
|
Packit |
8f70b4 |
if(size>size1)
|
|
Packit |
8f70b4 |
size=size1;
|
|
Packit |
8f70b4 |
size=buf->MoveDataHere(file_buf,size);
|
|
Packit |
8f70b4 |
if(size<=0)
|
|
Packit |
8f70b4 |
return DO_AGAIN;
|
|
Packit |
8f70b4 |
pos+=size;
|
|
Packit |
8f70b4 |
real_pos+=size;
|
|
Packit |
8f70b4 |
rate_limit->BytesGot(size);
|
|
Packit |
8f70b4 |
TrySuccess();
|
|
Packit |
8f70b4 |
return size;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
return DO_AGAIN;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
int SFtp::Write(const void *buf,int size)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(mode!=STORE)
|
|
Packit |
8f70b4 |
return(0);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
Resume();
|
|
Packit |
8f70b4 |
Enter(this);
|
|
Packit |
8f70b4 |
Do();
|
|
Packit |
8f70b4 |
Leave(this);
|
|
Packit |
8f70b4 |
if(Error())
|
|
Packit |
8f70b4 |
return(error_code);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(state!=FILE_SEND || rate_limit==0
|
|
Packit |
8f70b4 |
|| send_buf->Size()>2*max_buf)
|
|
Packit |
8f70b4 |
return DO_AGAIN;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
int allowed=rate_limit->BytesAllowedToPut();
|
|
Packit |
8f70b4 |
if(allowed==0)
|
|
Packit |
8f70b4 |
return DO_AGAIN;
|
|
Packit |
8f70b4 |
if(size+file_buf->Size()>allowed)
|
|
Packit |
8f70b4 |
size=allowed-send_buf->Size();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(size+file_buf->Size()>max_buf)
|
|
Packit |
8f70b4 |
size=max_buf-file_buf->Size();
|
|
Packit |
8f70b4 |
if(entity_size>=0 && pos+size>entity_size)
|
|
Packit |
8f70b4 |
size=entity_size-pos;
|
|
Packit |
8f70b4 |
if(size<=0)
|
|
Packit |
8f70b4 |
return 0;
|
|
Packit |
8f70b4 |
file_buf->Put(static_cast<const char*>(buf),size);
|
|
Packit |
8f70b4 |
rate_limit->BytesPut(size);
|
|
Packit |
8f70b4 |
pos+=size;
|
|
Packit |
8f70b4 |
real_pos+=size;
|
|
Packit |
8f70b4 |
return(size);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
int SFtp::Buffered()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(file_buf==0)
|
|
Packit |
8f70b4 |
return 0;
|
|
Packit |
8f70b4 |
off_t b=file_buf->Size()+send_buf->Size()*size_write/(size_write+20);
|
|
Packit |
8f70b4 |
if(b<0)
|
|
Packit |
8f70b4 |
b=0;
|
|
Packit |
8f70b4 |
else if(b>real_pos)
|
|
Packit |
8f70b4 |
b=real_pos;
|
|
Packit |
8f70b4 |
return b;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
int SFtp::StoreStatus()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(Error())
|
|
Packit |
8f70b4 |
return error_code;
|
|
Packit |
8f70b4 |
if(state==FILE_SEND && !eof)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
eof=true;
|
|
Packit |
8f70b4 |
return IN_PROGRESS;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(state==DONE)
|
|
Packit |
8f70b4 |
return OK;
|
|
Packit |
8f70b4 |
return IN_PROGRESS;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
int SFtp::Done()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(mode==CLOSED)
|
|
Packit |
8f70b4 |
return OK;
|
|
Packit |
8f70b4 |
if(Error())
|
|
Packit |
8f70b4 |
return error_code;
|
|
Packit |
8f70b4 |
if(eof || state==DONE)
|
|
Packit |
8f70b4 |
return OK;
|
|
Packit |
8f70b4 |
if(mode==CONNECT_VERIFY)
|
|
Packit |
8f70b4 |
return OK;
|
|
Packit |
8f70b4 |
return IN_PROGRESS;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void SFtp::SuspendInternal()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
super::SuspendInternal();
|
|
Packit |
8f70b4 |
if(recv_buf)
|
|
Packit |
8f70b4 |
recv_buf->SuspendSlave();
|
|
Packit |
8f70b4 |
if(send_buf)
|
|
Packit |
8f70b4 |
send_buf->SuspendSlave();
|
|
Packit |
8f70b4 |
if(pty_send_buf)
|
|
Packit |
8f70b4 |
pty_send_buf->SuspendSlave();
|
|
Packit |
8f70b4 |
if(pty_recv_buf)
|
|
Packit |
8f70b4 |
pty_recv_buf->SuspendSlave();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void SFtp::ResumeInternal()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(recv_buf)
|
|
Packit |
8f70b4 |
recv_buf->ResumeSlave();
|
|
Packit |
8f70b4 |
if(send_buf)
|
|
Packit |
8f70b4 |
send_buf->ResumeSlave();
|
|
Packit |
8f70b4 |
if(pty_send_buf)
|
|
Packit |
8f70b4 |
pty_send_buf->ResumeSlave();
|
|
Packit |
8f70b4 |
if(pty_recv_buf)
|
|
Packit |
8f70b4 |
pty_recv_buf->ResumeSlave();
|
|
Packit |
8f70b4 |
super::ResumeInternal();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
const char *SFtp::CurrentStatus()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
switch(state)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
case DISCONNECTED:
|
|
Packit |
8f70b4 |
if(!ReconnectAllowed())
|
|
Packit |
8f70b4 |
return DelayingMessage();
|
|
Packit |
8f70b4 |
return _("Not connected");
|
|
Packit |
8f70b4 |
case CONNECTING:
|
|
Packit |
8f70b4 |
if(ssh && ssh->status)
|
|
Packit |
8f70b4 |
return ssh->status;
|
|
Packit |
8f70b4 |
case CONNECTING_1:
|
|
Packit |
8f70b4 |
case CONNECTING_2:
|
|
Packit |
8f70b4 |
return _("Connecting...");
|
|
Packit |
8f70b4 |
case CONNECTED:
|
|
Packit |
8f70b4 |
return _("Connected");
|
|
Packit |
8f70b4 |
case WAITING:
|
|
Packit |
8f70b4 |
return _("Waiting for response...");
|
|
Packit |
8f70b4 |
case FILE_RECV:
|
|
Packit |
8f70b4 |
return _("Receiving data");
|
|
Packit |
8f70b4 |
case FILE_SEND:
|
|
Packit |
8f70b4 |
return _("Sending data");
|
|
Packit |
8f70b4 |
case DONE:
|
|
Packit |
8f70b4 |
return _("Done");
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
return "";
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
bool SFtp::SameSiteAs(const FileAccess *fa) const
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(!SameProtoAs(fa))
|
|
Packit |
8f70b4 |
return false;
|
|
Packit |
8f70b4 |
SFtp *o=(SFtp*)fa;
|
|
Packit |
8f70b4 |
return(!xstrcasecmp(hostname,o->hostname) && !xstrcmp(portname,o->portname)
|
|
Packit |
8f70b4 |
&& !xstrcmp(user,o->user) && !xstrcmp(pass,o->pass));
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
bool SFtp::SameLocationAs(const FileAccess *fa) const
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(!SameSiteAs(fa))
|
|
Packit |
8f70b4 |
return false;
|
|
Packit |
8f70b4 |
SFtp *o=(SFtp*)fa;
|
|
Packit |
8f70b4 |
if(xstrcmp(cwd,o->cwd))
|
|
Packit |
8f70b4 |
return false;
|
|
Packit |
8f70b4 |
if(xstrcmp(home,o->home))
|
|
Packit |
8f70b4 |
return false;
|
|
Packit |
8f70b4 |
return true;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void SFtp::Reconfig(const char *name)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
super::Reconfig(name);
|
|
Packit |
8f70b4 |
const char *c=hostname;
|
|
Packit |
8f70b4 |
max_packets_in_flight=Query("max-packets-in-flight",c);
|
|
Packit |
8f70b4 |
if(max_packets_in_flight<1)
|
|
Packit |
8f70b4 |
max_packets_in_flight=1;
|
|
Packit |
8f70b4 |
if(max_packets_in_flight_slow_start>max_packets_in_flight)
|
|
Packit |
8f70b4 |
max_packets_in_flight_slow_start=max_packets_in_flight;
|
|
Packit |
8f70b4 |
size_read=Query("size-read",c);
|
|
Packit |
8f70b4 |
size_write=Query("size-write",c);
|
|
Packit |
8f70b4 |
if(size_read<16)
|
|
Packit |
8f70b4 |
size_read=16;
|
|
Packit |
8f70b4 |
if(size_write<16)
|
|
Packit |
8f70b4 |
size_write=16;
|
|
Packit |
8f70b4 |
use_full_path=QueryBool("use-full-path",c);
|
|
Packit |
8f70b4 |
if(!xstrcmp(name,"sftp:charset") && protocol_version && protocol_version<4)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(!IsSuspended())
|
|
Packit |
8f70b4 |
cache->TreeChanged(this,"/");
|
|
Packit |
8f70b4 |
const char *charset=ResMgr::Query("sftp:charset",hostname);
|
|
Packit |
8f70b4 |
if(charset && *charset)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(!send_translate)
|
|
Packit |
8f70b4 |
send_translate=new DirectedBuffer(DirectedBuffer::PUT);
|
|
Packit |
8f70b4 |
if(!recv_translate)
|
|
Packit |
8f70b4 |
recv_translate=new DirectedBuffer(DirectedBuffer::GET);
|
|
Packit |
8f70b4 |
send_translate->SetTranslation(charset,false);
|
|
Packit |
8f70b4 |
recv_translate->SetTranslation(charset,true);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
else
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
send_translate=0;
|
|
Packit |
8f70b4 |
recv_translate=0;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void SFtp::ClassInit()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
// register the class
|
|
Packit |
8f70b4 |
Register("sftp",SFtp::New);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
FileAccess *SFtp::New() { return new SFtp(); }
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
DirList *SFtp::MakeDirList(ArgV *args)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
return new SFtpDirList(this,args);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
struct code_text { int code; const char *text; };
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
const char *SFtp::Packet::GetPacketTypeText()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
struct code_text text_table[]={
|
|
Packit |
8f70b4 |
{ SSH_FXP_INIT, "INIT" },
|
|
Packit |
8f70b4 |
{ SSH_FXP_VERSION, "VERSION" },
|
|
Packit |
8f70b4 |
{ SSH_FXP_OPEN, "OPEN" },
|
|
Packit |
8f70b4 |
{ SSH_FXP_CLOSE, "CLOSE" },
|
|
Packit |
8f70b4 |
{ SSH_FXP_READ, "READ" },
|
|
Packit |
8f70b4 |
{ SSH_FXP_WRITE, "WRITE" },
|
|
Packit |
8f70b4 |
{ SSH_FXP_LSTAT, "LSTAT" },
|
|
Packit |
8f70b4 |
{ SSH_FXP_FSTAT, "FSTAT" },
|
|
Packit |
8f70b4 |
{ SSH_FXP_SETSTAT, "SETSTAT" },
|
|
Packit |
8f70b4 |
{ SSH_FXP_FSETSTAT, "FSETSTAT" },
|
|
Packit |
8f70b4 |
{ SSH_FXP_OPENDIR, "OPENDIR" },
|
|
Packit |
8f70b4 |
{ SSH_FXP_READDIR, "READDIR" },
|
|
Packit |
8f70b4 |
{ SSH_FXP_REMOVE, "REMOVE" },
|
|
Packit |
8f70b4 |
{ SSH_FXP_MKDIR, "MKDIR" },
|
|
Packit |
8f70b4 |
{ SSH_FXP_RMDIR, "RMDIR" },
|
|
Packit |
8f70b4 |
{ SSH_FXP_REALPATH, "REALPATH" },
|
|
Packit |
8f70b4 |
{ SSH_FXP_STAT, "STAT" },
|
|
Packit |
8f70b4 |
{ SSH_FXP_RENAME, "RENAME" },
|
|
Packit |
8f70b4 |
{ SSH_FXP_READLINK, "READLINK" },
|
|
Packit |
8f70b4 |
{ SSH_FXP_SYMLINK, "SYMLINK" },
|
|
Packit |
8f70b4 |
{ SSH_FXP_LINK, "LINK" },
|
|
Packit |
8f70b4 |
{ SSH_FXP_BLOCK, "BLOCK" },
|
|
Packit |
8f70b4 |
{ SSH_FXP_UNBLOCK, "UNBLOCK" },
|
|
Packit |
8f70b4 |
{ SSH_FXP_STATUS, "STATUS" },
|
|
Packit |
8f70b4 |
{ SSH_FXP_HANDLE, "HANDLE" },
|
|
Packit |
8f70b4 |
{ SSH_FXP_DATA, "DATA" },
|
|
Packit |
8f70b4 |
{ SSH_FXP_NAME, "NAME" },
|
|
Packit |
8f70b4 |
{ SSH_FXP_ATTRS, "ATTRS" },
|
|
Packit |
8f70b4 |
{ SSH_FXP_EXTENDED, "EXTENDED" },
|
|
Packit |
8f70b4 |
{ SSH_FXP_EXTENDED_REPLY,"EXTENDED_REPLY" },
|
|
Packit |
8f70b4 |
{0,0}
|
|
Packit |
8f70b4 |
};
|
|
Packit |
8f70b4 |
for(int i=0; text_table[i].text; i++)
|
|
Packit |
8f70b4 |
if(text_table[i].code==type)
|
|
Packit |
8f70b4 |
return text_table[i].text;
|
|
Packit |
8f70b4 |
return "UNKNOWN";
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
const char *SFtp::Reply_STATUS::GetCodeText()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
static const char *text_table[]={
|
|
Packit |
8f70b4 |
"OK",
|
|
Packit |
8f70b4 |
"EOF",
|
|
Packit |
8f70b4 |
"No such file",
|
|
Packit |
8f70b4 |
"Permission denied",
|
|
Packit |
8f70b4 |
"Failure",
|
|
Packit |
8f70b4 |
"Bad message",
|
|
Packit |
8f70b4 |
"No connection",
|
|
Packit |
8f70b4 |
"Connection lost",
|
|
Packit |
8f70b4 |
"Operation not supported",
|
|
Packit |
8f70b4 |
"Invalid handle",
|
|
Packit |
8f70b4 |
"No such path",
|
|
Packit |
8f70b4 |
"File already exists",
|
|
Packit |
8f70b4 |
"Write protect",
|
|
Packit |
8f70b4 |
"No media",
|
|
Packit |
8f70b4 |
"No space on filesystem",
|
|
Packit |
8f70b4 |
"Quota exceeded",
|
|
Packit |
8f70b4 |
"Unknown principal",
|
|
Packit |
8f70b4 |
"Lock conflict",
|
|
Packit |
8f70b4 |
"Directory not empty",
|
|
Packit |
8f70b4 |
"Not a directory",
|
|
Packit |
8f70b4 |
"Invalid file name",
|
|
Packit |
8f70b4 |
"Link loop",
|
|
Packit |
8f70b4 |
"Cannot delete",
|
|
Packit |
8f70b4 |
"Invalid parameter",
|
|
Packit |
8f70b4 |
"File is a directory",
|
|
Packit |
8f70b4 |
"Byte range lock conflict",
|
|
Packit |
8f70b4 |
"Byte range lock refused",
|
|
Packit |
8f70b4 |
"Delete pending",
|
|
Packit |
8f70b4 |
"File corrupt",
|
|
Packit |
8f70b4 |
"Owner invalid",
|
|
Packit |
8f70b4 |
"Group invalid"
|
|
Packit |
8f70b4 |
};
|
|
Packit |
8f70b4 |
if(code>=0 && code
|
|
Packit |
8f70b4 |
return text_table[code];
|
|
Packit |
8f70b4 |
return 0;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void SFtp::SetError(int code,const Packet *reply)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(!reply->TypeIs(SSH_FXP_STATUS))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
SetError(code);
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
Reply_STATUS *status=(Reply_STATUS*)reply;
|
|
Packit |
8f70b4 |
const char *message=status->GetMessage();
|
|
Packit |
8f70b4 |
if(message && *message)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
SetError(code,utf8_to_lc(message));
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
message=status->GetCodeText();
|
|
Packit |
8f70b4 |
if(message)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
SetError(code,_(message));
|
|
Packit |
8f70b4 |
return;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
SetError(code);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
#define UNPACK_GENERIC(out,size,fun) \
|
|
Packit |
8f70b4 |
do { \
|
|
Packit |
8f70b4 |
if(limit-*offset<(size)) \
|
|
Packit |
8f70b4 |
return UNPACK_WRONG_FORMAT; \
|
|
Packit |
8f70b4 |
out=b->fun(*offset); \
|
|
Packit |
8f70b4 |
*offset+=(size); \
|
|
Packit |
8f70b4 |
} while(0)
|
|
Packit |
8f70b4 |
#define UNPACK8(out) UNPACK_GENERIC(out,1,UnpackUINT8)
|
|
Packit |
8f70b4 |
#define UNPACK32(out) UNPACK_GENERIC(out,4,UnpackUINT32BE)
|
|
Packit |
8f70b4 |
#define UNPACK64(out) UNPACK_GENERIC(out,8,UnpackUINT64BE)
|
|
Packit |
8f70b4 |
#define UNPACK32_SIGNED(out) UNPACK_GENERIC(out,4,UnpackINT32BE)
|
|
Packit |
8f70b4 |
#define UNPACK64_SIGNED(out) UNPACK_GENERIC(out,8,UnpackINT64BE)
|
|
Packit |
8f70b4 |
#define PACK8(data) b->PackUINT8(data)
|
|
Packit |
8f70b4 |
#define PACK32(data) b->PackUINT32BE(data)
|
|
Packit |
8f70b4 |
#define PACK64(data) b->PackUINT64BE(data)
|
|
Packit |
8f70b4 |
#define PACK32_SIGNED(data) b->PackINT32BE(data)
|
|
Packit |
8f70b4 |
#define PACK64_SIGNED(data) b->PackINT64BE(data)
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
SFtp::unpack_status_t SFtp::PacketSTRING::Unpack(const Buffer *b)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
unpack_status_t res;
|
|
Packit |
8f70b4 |
res=Packet::Unpack(b);
|
|
Packit |
8f70b4 |
if(res!=UNPACK_SUCCESS)
|
|
Packit |
8f70b4 |
return res;
|
|
Packit |
8f70b4 |
res=UnpackString(b,&unpacked,length+4,&string);
|
|
Packit |
8f70b4 |
return res;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
SFtp::unpack_status_t SFtp::Reply_NAME::Unpack(const Buffer *b)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
unpack_status_t res=Packet::Unpack(b);
|
|
Packit |
8f70b4 |
if(res!=UNPACK_SUCCESS)
|
|
Packit |
8f70b4 |
return res;
|
|
Packit |
8f70b4 |
int *offset=&unpacked;
|
|
Packit |
8f70b4 |
int limit=length+4;
|
|
Packit |
8f70b4 |
UNPACK32(count);
|
|
Packit |
8f70b4 |
names=new NameAttrs[count];
|
|
Packit |
8f70b4 |
for(int i=0; i
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
res=names[i].Unpack(b,offset,limit,protocol_version);
|
|
Packit |
8f70b4 |
if(res!=UNPACK_SUCCESS)
|
|
Packit |
8f70b4 |
return res;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(*offset
|
|
Packit |
8f70b4 |
UNPACK8(eof);
|
|
Packit |
8f70b4 |
return UNPACK_SUCCESS;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
SFtp::unpack_status_t SFtp::Reply_DATA::Unpack(const Buffer *b)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
unpack_status_t res=PacketSTRING::Unpack(b);
|
|
Packit |
8f70b4 |
if(res!=UNPACK_SUCCESS)
|
|
Packit |
8f70b4 |
return res;
|
|
Packit |
8f70b4 |
int *offset=&unpacked;
|
|
Packit |
8f70b4 |
int limit=length+4;
|
|
Packit |
8f70b4 |
if(*offset
|
|
Packit |
8f70b4 |
UNPACK8(eof);
|
|
Packit |
8f70b4 |
return UNPACK_SUCCESS;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
SFtp::unpack_status_t SFtp::NameAttrs::Unpack(const Buffer *b,int *offset,int limit,int protocol_version)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
unpack_status_t res;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
res=Packet::UnpackString(b,offset,limit,&name);
|
|
Packit |
8f70b4 |
if(res!=UNPACK_SUCCESS)
|
|
Packit |
8f70b4 |
return res;
|
|
Packit |
8f70b4 |
if(protocol_version<=3)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
res=Packet::UnpackString(b,offset,limit,&longname);
|
|
Packit |
8f70b4 |
if(res!=UNPACK_SUCCESS)
|
|
Packit |
8f70b4 |
return res;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
res=attrs.Unpack(b,offset,limit,protocol_version);
|
|
Packit |
8f70b4 |
if(res!=UNPACK_SUCCESS)
|
|
Packit |
8f70b4 |
return res;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
return UNPACK_SUCCESS;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
SFtp::unpack_status_t SFtp::FileAttrs::Unpack(const Buffer *b,int *offset,int limit,int protocol_version)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
unpack_status_t res;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
UNPACK32(flags);
|
|
Packit |
8f70b4 |
if(protocol_version>=4)
|
|
Packit |
8f70b4 |
UNPACK8(type);
|
|
Packit |
8f70b4 |
if(flags & SSH_FILEXFER_ATTR_SIZE)
|
|
Packit |
8f70b4 |
UNPACK64(size);
|
|
Packit |
8f70b4 |
if(protocol_version<=3 && (flags & SSH_FILEXFER_ATTR_UIDGID))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
UNPACK32(uid);
|
|
Packit |
8f70b4 |
UNPACK32(gid);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(protocol_version>=4 && (flags & SSH_FILEXFER_ATTR_OWNERGROUP))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
res=Packet::UnpackString(b,offset,limit,&owner);
|
|
Packit |
8f70b4 |
if(res!=UNPACK_SUCCESS)
|
|
Packit |
8f70b4 |
return res;
|
|
Packit |
8f70b4 |
res=Packet::UnpackString(b,offset,limit,&group);
|
|
Packit |
8f70b4 |
if(res!=UNPACK_SUCCESS)
|
|
Packit |
8f70b4 |
return res;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(flags & SSH_FILEXFER_ATTR_PERMISSIONS)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
UNPACK32(permissions);
|
|
Packit |
8f70b4 |
if(protocol_version<=3)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
switch(permissions&S_IFMT)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
case S_IFREG: type=SSH_FILEXFER_TYPE_REGULAR; break;
|
|
Packit |
8f70b4 |
case S_IFDIR: type=SSH_FILEXFER_TYPE_DIRECTORY; break;
|
|
Packit |
8f70b4 |
case S_IFLNK: type=SSH_FILEXFER_TYPE_SYMLINK; break;
|
|
Packit |
8f70b4 |
case S_IFIFO:
|
|
Packit |
8f70b4 |
case S_IFCHR:
|
|
Packit |
8f70b4 |
case S_IFBLK: type=SSH_FILEXFER_TYPE_SPECIAL; break;
|
|
Packit |
8f70b4 |
default: type=SSH_FILEXFER_TYPE_UNKNOWN; break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(protocol_version<=3 && (flags & SSH_FILEXFER_ATTR_ACMODTIME))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
UNPACK32_SIGNED(atime);
|
|
Packit |
8f70b4 |
UNPACK32_SIGNED(mtime);
|
|
Packit |
8f70b4 |
flags|=SSH_FILEXFER_ATTR_MODIFYTIME;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(protocol_version>=4 && (flags & SSH_FILEXFER_ATTR_ACCESSTIME))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
UNPACK64_SIGNED(atime);
|
|
Packit |
8f70b4 |
if(flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES)
|
|
Packit |
8f70b4 |
UNPACK32(atime_nseconds);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(protocol_version>=4 && (flags & SSH_FILEXFER_ATTR_CREATETIME))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
UNPACK64_SIGNED(createtime);
|
|
Packit |
8f70b4 |
if(flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES)
|
|
Packit |
8f70b4 |
UNPACK32(createtime_nseconds);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(protocol_version>=4 && (flags & SSH_FILEXFER_ATTR_MODIFYTIME))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
UNPACK64_SIGNED(mtime);
|
|
Packit |
8f70b4 |
if(flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES)
|
|
Packit |
8f70b4 |
UNPACK32(mtime_nseconds);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(protocol_version>=5 && (flags & SSH_FILEXFER_ATTR_CTIME))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
UNPACK64_SIGNED(ctime);
|
|
Packit |
8f70b4 |
if(flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES)
|
|
Packit |
8f70b4 |
UNPACK32(ctime_nseconds);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(atime_nseconds>999999999 || createtime_nseconds>999999999 || mtime_nseconds>999999999 || ctime_nseconds>999999999)
|
|
Packit |
8f70b4 |
return UNPACK_WRONG_FORMAT;
|
|
Packit |
8f70b4 |
if(protocol_version>=4 && (flags & SSH_FILEXFER_ATTR_ACL))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
UNPACK32(ace_count);
|
|
Packit |
8f70b4 |
ace=new FileACE[ace_count];
|
|
Packit |
8f70b4 |
for(unsigned i=0; i
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
res=ace[i].Unpack(b,offset,limit);
|
|
Packit |
8f70b4 |
if(res!=UNPACK_SUCCESS)
|
|
Packit |
8f70b4 |
return res;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(protocol_version>=5 && (flags & SSH_FILEXFER_ATTR_BITS))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
UNPACK32(attrib_bits);
|
|
Packit |
8f70b4 |
if(protocol_version>=6)
|
|
Packit |
8f70b4 |
UNPACK32(attrib_bits_valid);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(protocol_version>=6 && (flags & SSH_FILEXFER_ATTR_TEXT_HINT))
|
|
Packit |
8f70b4 |
UNPACK8(text_hint);
|
|
Packit |
8f70b4 |
if(protocol_version>=6 && (flags & SSH_FILEXFER_ATTR_MIME_TYPE))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
res=Packet::UnpackString(b,offset,limit,&mime_type);
|
|
Packit |
8f70b4 |
if(res!=UNPACK_SUCCESS)
|
|
Packit |
8f70b4 |
return res;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(protocol_version>=6 && (flags & SSH_FILEXFER_ATTR_LINK_COUNT))
|
|
Packit |
8f70b4 |
UNPACK32(link_count);
|
|
Packit |
8f70b4 |
if(protocol_version>=6 && (flags & SSH_FILEXFER_ATTR_UNTRANSLATED_NAME))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
res=Packet::UnpackString(b,offset,limit,&untranslated_name);
|
|
Packit |
8f70b4 |
if(res!=UNPACK_SUCCESS)
|
|
Packit |
8f70b4 |
return res;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(flags & SSH_FILEXFER_ATTR_EXTENDED)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
UNPACK32(extended_count);
|
|
Packit |
8f70b4 |
extended_attrs=new ExtFileAttr[extended_count];
|
|
Packit |
8f70b4 |
for(unsigned i=0; i
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
res=extended_attrs[i].Unpack(b,offset,limit);
|
|
Packit |
8f70b4 |
if(res!=UNPACK_SUCCESS)
|
|
Packit |
8f70b4 |
return res;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
return UNPACK_SUCCESS;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void SFtp::FileAttrs::Pack(Buffer *b,int protocol_version)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(protocol_version<=3 && (flags & SSH_FILEXFER_ATTR_MODIFYTIME)
|
|
Packit |
8f70b4 |
&& !(flags & SSH_FILEXFER_ATTR_ACCESSTIME))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
flags|=SSH_FILEXFER_ATTR_ACMODTIME;
|
|
Packit |
8f70b4 |
atime=mtime;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
unsigned flags_mask=SSH_FILEXFER_ATTR_MASK_V3;
|
|
Packit |
8f70b4 |
if(protocol_version==4) flags_mask=SSH_FILEXFER_ATTR_MASK_V4;
|
|
Packit |
8f70b4 |
if(protocol_version==5) flags_mask=SSH_FILEXFER_ATTR_MASK_V5;
|
|
Packit |
8f70b4 |
if(protocol_version>=6) flags_mask=SSH_FILEXFER_ATTR_MASK_V6;
|
|
Packit |
8f70b4 |
PACK32(flags&flags_mask);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(protocol_version>=4)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(type==0)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
switch(permissions&S_IFMT)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
case S_IFREG: type=SSH_FILEXFER_TYPE_REGULAR; break;
|
|
Packit |
8f70b4 |
case S_IFDIR: type=SSH_FILEXFER_TYPE_DIRECTORY; break;
|
|
Packit |
8f70b4 |
case S_IFLNK: type=SSH_FILEXFER_TYPE_SYMLINK; break;
|
|
Packit |
8f70b4 |
case S_IFIFO:
|
|
Packit |
8f70b4 |
case S_IFCHR:
|
|
Packit |
8f70b4 |
case S_IFBLK: type=SSH_FILEXFER_TYPE_SPECIAL; break;
|
|
Packit |
8f70b4 |
default: type=SSH_FILEXFER_TYPE_UNKNOWN; break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
PACK8(type);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(flags & SSH_FILEXFER_ATTR_SIZE)
|
|
Packit |
8f70b4 |
PACK64(size);
|
|
Packit |
8f70b4 |
if(protocol_version<=3 && (flags & SSH_FILEXFER_ATTR_UIDGID))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
PACK32(uid);
|
|
Packit |
8f70b4 |
PACK32(gid);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(protocol_version>=4 && (flags & SSH_FILEXFER_ATTR_OWNERGROUP))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
Packet::PackString(b,owner);
|
|
Packit |
8f70b4 |
Packet::PackString(b,group);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(flags & SSH_FILEXFER_ATTR_PERMISSIONS)
|
|
Packit |
8f70b4 |
PACK32(permissions);
|
|
Packit |
8f70b4 |
if(protocol_version<=3 && (flags & SSH_FILEXFER_ATTR_ACMODTIME))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
PACK32_SIGNED(atime);
|
|
Packit |
8f70b4 |
PACK32_SIGNED(mtime);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(protocol_version>=4 && (flags & SSH_FILEXFER_ATTR_ACCESSTIME))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
PACK64_SIGNED(atime);
|
|
Packit |
8f70b4 |
if(flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES)
|
|
Packit |
8f70b4 |
PACK32(atime_nseconds);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(protocol_version>=4 && (flags & SSH_FILEXFER_ATTR_CREATETIME))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
PACK64_SIGNED(createtime);
|
|
Packit |
8f70b4 |
if(flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES)
|
|
Packit |
8f70b4 |
PACK32(createtime_nseconds);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(protocol_version>=4 && (flags & SSH_FILEXFER_ATTR_MODIFYTIME))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
PACK64_SIGNED(mtime);
|
|
Packit |
8f70b4 |
if(flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES)
|
|
Packit |
8f70b4 |
PACK32(mtime_nseconds);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(protocol_version>=5 && (flags & SSH_FILEXFER_ATTR_CTIME))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
PACK64_SIGNED(ctime);
|
|
Packit |
8f70b4 |
if(flags & SSH_FILEXFER_ATTR_SUBSECOND_TIMES)
|
|
Packit |
8f70b4 |
PACK32(ctime_nseconds);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(protocol_version>=4 && (flags & SSH_FILEXFER_ATTR_ACL))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
PACK32(ace_count);
|
|
Packit |
8f70b4 |
for(unsigned i=0; i
|
|
Packit |
8f70b4 |
ace[i].Pack(b);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(protocol_version>=5 && (flags & SSH_FILEXFER_ATTR_BITS))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
PACK32(attrib_bits);
|
|
Packit |
8f70b4 |
if(protocol_version>=6)
|
|
Packit |
8f70b4 |
PACK32(attrib_bits_valid);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(protocol_version>=6 && (flags & SSH_FILEXFER_ATTR_TEXT_HINT))
|
|
Packit |
8f70b4 |
PACK8(text_hint);
|
|
Packit |
8f70b4 |
if(protocol_version>=6 && (flags & SSH_FILEXFER_ATTR_MIME_TYPE))
|
|
Packit |
8f70b4 |
Packet::PackString(b,mime_type);
|
|
Packit |
8f70b4 |
if(protocol_version>=6 && (flags & SSH_FILEXFER_ATTR_LINK_COUNT))
|
|
Packit |
8f70b4 |
PACK32(link_count);
|
|
Packit |
8f70b4 |
if(protocol_version>=6 && (flags & SSH_FILEXFER_ATTR_UNTRANSLATED_NAME))
|
|
Packit |
8f70b4 |
Packet::PackString(b,untranslated_name);
|
|
Packit |
8f70b4 |
if(flags & SSH_FILEXFER_ATTR_EXTENDED)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
PACK32(extended_count);
|
|
Packit |
8f70b4 |
for(unsigned i=0; i
|
|
Packit |
8f70b4 |
extended_attrs[i].Pack(b);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
int SFtp::FileAttrs::ComputeLength(int protocol_version)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
Buffer b;
|
|
Packit |
8f70b4 |
Pack(&b,protocol_version);
|
|
Packit |
8f70b4 |
return b.Size();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
SFtp::FileAttrs::~FileAttrs()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
delete[] extended_attrs;
|
|
Packit |
8f70b4 |
delete[] ace;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
SFtp::unpack_status_t SFtp::FileAttrs::FileACE::Unpack(const Buffer *b,int *offset,int limit)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
UNPACK32(ace_type);
|
|
Packit |
8f70b4 |
UNPACK32(ace_flag);
|
|
Packit |
8f70b4 |
UNPACK32(ace_mask);
|
|
Packit |
8f70b4 |
return Packet::UnpackString(b,offset,limit,&who);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void SFtp::FileAttrs::FileACE::Pack(Buffer *b)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
PACK32(ace_type);
|
|
Packit |
8f70b4 |
PACK32(ace_flag);
|
|
Packit |
8f70b4 |
PACK32(ace_mask);
|
|
Packit |
8f70b4 |
Packet::PackString(b,who);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
SFtp::unpack_status_t SFtp::FileAttrs::ExtFileAttr::Unpack(const Buffer *b,int *offset,int limit)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
unpack_status_t res;
|
|
Packit |
8f70b4 |
res=Packet::UnpackString(b,offset,limit,&extended_type);
|
|
Packit |
8f70b4 |
if(res!=UNPACK_SUCCESS)
|
|
Packit |
8f70b4 |
return res;
|
|
Packit |
8f70b4 |
res=Packet::UnpackString(b,offset,limit,&extended_data);
|
|
Packit |
8f70b4 |
if(res!=UNPACK_SUCCESS)
|
|
Packit |
8f70b4 |
return res;
|
|
Packit |
8f70b4 |
return UNPACK_SUCCESS;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void SFtp::FileAttrs::ExtFileAttr::Pack(Buffer *b)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
Packet::PackString(b,extended_type);
|
|
Packit |
8f70b4 |
Packet::PackString(b,extended_data);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
SFtp::unpack_status_t SFtp::Reply_ATTRS::Unpack(const Buffer *b)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
unpack_status_t res=Packet::Unpack(b);
|
|
Packit |
8f70b4 |
if(res!=UNPACK_SUCCESS)
|
|
Packit |
8f70b4 |
return res;
|
|
Packit |
8f70b4 |
return attrs.Unpack(b,&unpacked,length+4,protocol_version);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
SFtp::unpack_status_t SFtp::Reply_STATUS::Unpack(const Buffer *b)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
unpack_status_t res=Packet::Unpack(b);
|
|
Packit |
8f70b4 |
if(res!=UNPACK_SUCCESS)
|
|
Packit |
8f70b4 |
return res;
|
|
Packit |
8f70b4 |
int *offset=&unpacked;
|
|
Packit |
8f70b4 |
int limit=length+4;
|
|
Packit |
8f70b4 |
UNPACK32(code);
|
|
Packit |
8f70b4 |
if(protocol_version>=3)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(unpacked>=limit)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
LogError(2,"Status reply lacks `error message' field");
|
|
Packit |
8f70b4 |
return UNPACK_SUCCESS;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
res=Packet::UnpackString(b,offset,limit,&message);
|
|
Packit |
8f70b4 |
if(res!=UNPACK_SUCCESS)
|
|
Packit |
8f70b4 |
return res;
|
|
Packit |
8f70b4 |
if(unpacked>=limit)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
LogError(2,"Status reply lacks `language tag' field");
|
|
Packit |
8f70b4 |
return UNPACK_SUCCESS;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
res=Packet::UnpackString(b,offset,limit,&language);
|
|
Packit |
8f70b4 |
if(res!=UNPACK_SUCCESS)
|
|
Packit |
8f70b4 |
return res;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
return UNPACK_SUCCESS;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void SFtp::Request_READ::Pack(Buffer *b)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
PacketSTRING::Pack(b);
|
|
Packit |
8f70b4 |
PACK64(pos);
|
|
Packit |
8f70b4 |
PACK32(len);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void SFtp::Request_WRITE::Pack(Buffer *b)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
PacketSTRING::Pack(b);
|
|
Packit |
8f70b4 |
PACK64(pos);
|
|
Packit |
8f70b4 |
int len=data.length();
|
|
Packit |
8f70b4 |
PACK32(len);
|
|
Packit |
8f70b4 |
b->Put(data,len);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void SFtp::Request_OPEN::Pack(Buffer *b)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
PacketSTRING::Pack(b);
|
|
Packit |
8f70b4 |
if(protocol_version<=4)
|
|
Packit |
8f70b4 |
PACK32(pflags);
|
|
Packit |
8f70b4 |
if(protocol_version>=5)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
PACK32(desired_access);
|
|
Packit |
8f70b4 |
PACK32(flags);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
attrs.Pack(b,protocol_version);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void SFtp::Request_RENAME::ComputeLength()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
Packet::ComputeLength();
|
|
Packit |
8f70b4 |
length+=4+strlen(oldpath)+4+strlen(newpath);
|
|
Packit |
8f70b4 |
if(protocol_version>=5)
|
|
Packit |
8f70b4 |
length+=4; // flags
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void SFtp::Request_RENAME::Pack(Buffer *b)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
Packet::Pack(b);
|
|
Packit |
8f70b4 |
Packet::PackString(b,oldpath);
|
|
Packit |
8f70b4 |
Packet::PackString(b,newpath);
|
|
Packit |
8f70b4 |
if(protocol_version>=5)
|
|
Packit |
8f70b4 |
PACK32(flags);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void SFtp::Request_SYMLINK::Pack(Buffer *b)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
Packet::Pack(b);
|
|
Packit |
8f70b4 |
Packet::PackString(b,oldpath);
|
|
Packit |
8f70b4 |
Packet::PackString(b,newpath);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void SFtp::Request_LINK::Pack(Buffer *b)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
Packet::Pack(b);
|
|
Packit |
8f70b4 |
Packet::PackString(b,newpath);
|
|
Packit |
8f70b4 |
Packet::PackString(b,oldpath);
|
|
Packit |
8f70b4 |
PACK8(symbolic);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
const char *SFtp::utf8_to_lc(const char *s)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(!recv_translate || !s)
|
|
Packit |
8f70b4 |
return s;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
recv_translate->ResetTranslation();
|
|
Packit |
8f70b4 |
recv_translate->PutTranslated(s);
|
|
Packit |
8f70b4 |
recv_translate->Buffer::Put("",1);
|
|
Packit |
8f70b4 |
int len;
|
|
Packit |
8f70b4 |
recv_translate->Get(&s,&len;;
|
|
Packit |
8f70b4 |
recv_translate->Skip(len);
|
|
Packit |
8f70b4 |
return xstring::get_tmp(s,len);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
const char *SFtp::lc_to_utf8(const char *s)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(!send_translate || !s)
|
|
Packit |
8f70b4 |
return s;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
send_translate->ResetTranslation();
|
|
Packit |
8f70b4 |
send_translate->PutTranslated(s);
|
|
Packit |
8f70b4 |
send_translate->Buffer::Put("",1);
|
|
Packit |
8f70b4 |
int len;
|
|
Packit |
8f70b4 |
send_translate->Get(&s,&len;;
|
|
Packit |
8f70b4 |
send_translate->Skip(len);
|
|
Packit |
8f70b4 |
return xstring::get_tmp(s,len);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
FileSet *SFtp::GetFileSet()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
FileSet *fset=file_set.borrow();
|
|
Packit |
8f70b4 |
return fset?fset:new FileSet;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void SFtp::MergeAttrs(FileInfo *fi,const FileAttrs *a)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
switch(a->type)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
case SSH_FILEXFER_TYPE_REGULAR: fi->SetType(fi->NORMAL); break;
|
|
Packit |
8f70b4 |
case SSH_FILEXFER_TYPE_DIRECTORY:fi->SetType(fi->DIRECTORY); break;
|
|
Packit |
8f70b4 |
case SSH_FILEXFER_TYPE_SYMLINK: fi->SetType(fi->SYMLINK); break;
|
|
Packit |
8f70b4 |
default: break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(a->flags&SSH_FILEXFER_ATTR_SIZE)
|
|
Packit |
8f70b4 |
fi->SetSize(a->size);
|
|
Packit |
8f70b4 |
if(a->flags&SSH_FILEXFER_ATTR_UIDGID)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
char id[24];
|
|
Packit |
8f70b4 |
snprintf(id,sizeof(id),"%u",a->uid);
|
|
Packit |
8f70b4 |
fi->SetUser(id);
|
|
Packit |
8f70b4 |
snprintf(id,sizeof(id),"%u",a->gid);
|
|
Packit |
8f70b4 |
fi->SetGroup(id);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(a->flags&SSH_FILEXFER_ATTR_OWNERGROUP)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
fi->SetUser (utf8_to_lc(a->owner));
|
|
Packit |
8f70b4 |
fi->SetGroup(utf8_to_lc(a->group));
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(a->flags&SSH_FILEXFER_ATTR_PERMISSIONS)
|
|
Packit |
8f70b4 |
fi->SetMode(a->permissions&07777);
|
|
Packit |
8f70b4 |
if(a->flags&SSH_FILEXFER_ATTR_MODIFYTIME)
|
|
Packit |
8f70b4 |
fi->SetDate(a->mtime,0);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
FileInfo *SFtp::MakeFileInfo(const NameAttrs *na)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
const FileAttrs *a=&na->attrs;
|
|
Packit |
8f70b4 |
const char *name=utf8_to_lc(na->name);
|
|
Packit |
8f70b4 |
const char *longname=utf8_to_lc(na->longname);
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
LogNote(10,"NameAttrs(name=\"%s\",type=%d,longname=\"%s\")\n",name?name:"",a->type,longname?longname:"");
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(!name || !name[0])
|
|
Packit |
8f70b4 |
return 0;
|
|
Packit |
8f70b4 |
if(name[0]=='~')
|
|
Packit |
8f70b4 |
name=dir_file(".",name);
|
|
Packit |
8f70b4 |
Ref<FileInfo> fi(new FileInfo(name));
|
|
Packit |
8f70b4 |
switch(a->type)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
case SSH_FILEXFER_TYPE_REGULAR:
|
|
Packit |
8f70b4 |
case SSH_FILEXFER_TYPE_DIRECTORY:
|
|
Packit |
8f70b4 |
case SSH_FILEXFER_TYPE_SYMLINK:
|
|
Packit |
8f70b4 |
case SSH_FILEXFER_TYPE_UNKNOWN: break;
|
|
Packit |
8f70b4 |
default: return 0;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(longname)
|
|
Packit |
8f70b4 |
fi->SetLongName(longname);
|
|
Packit |
8f70b4 |
MergeAttrs(fi.get_non_const(),a);
|
|
Packit |
8f70b4 |
if(fi->longname && !a->owner)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
// try to extract owner/group from long name.
|
|
Packit |
8f70b4 |
Ref<FileInfo> ls(FileInfo::parse_ls_line(fi->longname,0));
|
|
Packit |
8f70b4 |
if(ls)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(ls->user)
|
|
Packit |
8f70b4 |
fi->SetUser(ls->user);
|
|
Packit |
8f70b4 |
if(ls->group)
|
|
Packit |
8f70b4 |
fi->SetGroup(ls->group);
|
|
Packit |
8f70b4 |
if(ls->nlinks>0)
|
|
Packit |
8f70b4 |
fi->SetNlink(ls->nlinks);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
return fi.borrow();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
#undef super
|
|
Packit |
8f70b4 |
#define super DirList
|
|
Packit |
8f70b4 |
#include "ArgV.h"
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
int SFtpDirList::Do()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
int m=STALL;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(done)
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(buf->Eof())
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
done=true;
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(!ubuf)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
const char *cache_buffer=0;
|
|
Packit |
8f70b4 |
int cache_buffer_size=0;
|
|
Packit |
8f70b4 |
int err;
|
|
Packit |
8f70b4 |
const FileSet *fset_c;
|
|
Packit |
8f70b4 |
if(use_cache && FileAccess::cache->Find(session,dir,FA::LONG_LIST,&err,
|
|
Packit |
8f70b4 |
&cache_buffer,&cache_buffer_size,&fset_c))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(err)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
SetErrorCached(cache_buffer);
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
ubuf=new IOBuffer(IOBuffer::GET);
|
|
Packit |
8f70b4 |
ubuf->Put(cache_buffer,cache_buffer_size);
|
|
Packit |
8f70b4 |
ubuf->PutEOF();
|
|
Packit |
8f70b4 |
fset=new FileSet(fset_c);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
else
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
session->Open(dir,FA::LONG_LIST);
|
|
Packit |
8f70b4 |
ubuf=new IOBufferFileAccess(session);
|
|
Packit |
8f70b4 |
if(FileAccess::cache->IsEnabled(session->GetHostName()))
|
|
Packit |
8f70b4 |
ubuf->Save(FileAccess::cache->SizeLimit());
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
const char *b;
|
|
Packit |
8f70b4 |
int len;
|
|
Packit |
8f70b4 |
ubuf->Get(&b,&len;;
|
|
Packit |
8f70b4 |
if(b==0) // eof
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(!fset && session->IsOpen())
|
|
Packit |
8f70b4 |
fset=session.Cast<SFtp>()->GetFileSet();
|
|
Packit |
8f70b4 |
FileAccess::cache->Add(session,dir,FA::LONG_LIST,FA::OK,ubuf,fset);
|
|
Packit |
8f70b4 |
if(use_file_set)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
fset->Sort(fset->BYNAME,false);
|
|
Packit |
8f70b4 |
for(fset->rewind(); fset->curr(); fset->next())
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
FileInfo *fi=fset->curr();
|
|
Packit |
8f70b4 |
buf->Put(fi->GetLongName());
|
|
Packit |
8f70b4 |
buf->Put("\n");
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
fset=0;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
ubuf=0;
|
|
Packit |
8f70b4 |
dir=args->getnext();
|
|
Packit |
8f70b4 |
if(!dir)
|
|
Packit |
8f70b4 |
buf->PutEOF();
|
|
Packit |
8f70b4 |
else
|
|
Packit |
8f70b4 |
buf->Format("\n%s:\n",dir);
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(len>0)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(!use_file_set)
|
|
Packit |
8f70b4 |
buf->Put(b,len);
|
|
Packit |
8f70b4 |
ubuf->Skip(len);
|
|
Packit |
8f70b4 |
m=MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
if(ubuf->Error())
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
SetError(ubuf->ErrorText());
|
|
Packit |
8f70b4 |
m=MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
SFtpDirList::SFtpDirList(SFtp *s,ArgV *a)
|
|
Packit |
8f70b4 |
: DirList(s,a)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
use_file_set=true;
|
|
Packit |
8f70b4 |
args->rewind();
|
|
Packit |
8f70b4 |
int opt;
|
|
Packit |
8f70b4 |
while((opt=args->getopt("fCFl"))!=EOF)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
switch(opt)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
case('a'):
|
|
Packit |
8f70b4 |
ls_options.show_all=true;
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case('C'):
|
|
Packit |
8f70b4 |
ls_options.multi_column=true;
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
case('F'):
|
|
Packit |
8f70b4 |
ls_options.append_type=true;
|
|
Packit |
8f70b4 |
break;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
while(args->getindex()>1)
|
|
Packit |
8f70b4 |
args->delarg(1); // remove options.
|
|
Packit |
8f70b4 |
if(args->count()<2)
|
|
Packit |
8f70b4 |
args->Append("");
|
|
Packit |
8f70b4 |
args->rewind();
|
|
Packit |
8f70b4 |
dir=args->getnext();
|
|
Packit |
8f70b4 |
if(args->getindex()+1<args->count())
|
|
Packit |
8f70b4 |
buf->Format("%s:\n",dir);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
const char *SFtpDirList::Status()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(ubuf && !ubuf->Eof() && session->IsOpen())
|
|
Packit |
8f70b4 |
return xstring::format(_("Getting file list (%lld) [%s]"),
|
|
Packit |
8f70b4 |
(long long)session->GetPos(),session->CurrentStatus());
|
|
Packit |
8f70b4 |
return "";
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
void SFtpDirList::SuspendInternal()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
super::SuspendInternal();
|
|
Packit |
8f70b4 |
if(ubuf)
|
|
Packit |
8f70b4 |
ubuf->SuspendSlave();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
void SFtpDirList::ResumeInternal()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(ubuf)
|
|
Packit |
8f70b4 |
ubuf->ResumeSlave();
|
|
Packit |
8f70b4 |
super::ResumeInternal();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
#undef super
|
|
Packit |
8f70b4 |
#define super ListInfo
|
|
Packit |
8f70b4 |
int SFtpListInfo::Do()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
int m=STALL;
|
|
Packit |
8f70b4 |
if(done)
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
if(!ubuf && !result)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
const char *cache_buffer=0;
|
|
Packit |
8f70b4 |
int cache_buffer_size=0;
|
|
Packit |
8f70b4 |
int err;
|
|
Packit |
8f70b4 |
const FileSet *fset_c;
|
|
Packit |
8f70b4 |
if(use_cache && FileAccess::cache->Find(session,"",FA::LONG_LIST,&err,
|
|
Packit |
8f70b4 |
&cache_buffer,&cache_buffer_size,&fset_c))
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(err)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
SetErrorCached(cache_buffer);
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
ubuf=new IOBuffer(IOBuffer::GET);
|
|
Packit |
8f70b4 |
ubuf->Put(cache_buffer,cache_buffer_size);
|
|
Packit |
8f70b4 |
ubuf->PutEOF();
|
|
Packit |
8f70b4 |
result=new FileSet(fset_c);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
else
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
session->Open("",FA::LONG_LIST);
|
|
Packit |
8f70b4 |
ubuf=new IOBufferFileAccess(session);
|
|
Packit |
8f70b4 |
if(FileAccess::cache->IsEnabled(session->GetHostName()))
|
|
Packit |
8f70b4 |
ubuf->Save(FileAccess::cache->SizeLimit());
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(!result) {
|
|
Packit |
8f70b4 |
const char *b;
|
|
Packit |
8f70b4 |
int len;
|
|
Packit |
8f70b4 |
ubuf->Get(&b,&len;;
|
|
Packit |
8f70b4 |
if(len>0)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
ubuf->Skip(len);
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(ubuf->Error())
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
SetError(ubuf->ErrorText());
|
|
Packit |
8f70b4 |
return MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(b)
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
// eof
|
|
Packit |
8f70b4 |
if(!result && session->IsOpen())
|
|
Packit |
8f70b4 |
result=session.Cast<SFtp>()->GetFileSet();
|
|
Packit |
8f70b4 |
FileAccess::cache->Add(session,"",FA::LONG_LIST,FA::OK,ubuf,result);
|
|
Packit |
8f70b4 |
result->Exclude(exclude_prefix,exclude);
|
|
Packit |
8f70b4 |
m=MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(result && session->OpenMode()!=FA::ARRAY_INFO)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
ubuf=0;
|
|
Packit |
8f70b4 |
result->ExcludeCompound();
|
|
Packit |
8f70b4 |
result->rewind();
|
|
Packit |
8f70b4 |
for(FileInfo *file=result->curr(); file!=0; file=result->next())
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
file->need=0;
|
|
Packit |
8f70b4 |
if(file->defined & file->TYPE)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(file->filetype==file->SYMLINK && follow_symlinks)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
file->filetype=file->UNKNOWN;
|
|
Packit |
8f70b4 |
file->defined &= ~(file->SIZE|file->DATE|file->SYMLINK_DEF|file->MODE|file->TYPE|file->USER|file->GROUP);
|
|
Packit |
8f70b4 |
file->Need(file->SIZE|file->DATE|file->MODE|file->TYPE|file->USER|file->GROUP);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
else if(file->filetype==file->SYMLINK)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
// need the link target
|
|
Packit |
8f70b4 |
if(!file->Has(file->SYMLINK_DEF))
|
|
Packit |
8f70b4 |
file->Need(file->SYMLINK_DEF);
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
session->GetInfoArray(result.get_non_const());
|
|
Packit |
8f70b4 |
session->Roll();
|
|
Packit |
8f70b4 |
m=MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
if(session->OpenMode()==FA::ARRAY_INFO)
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
int res=session->Done();
|
|
Packit |
8f70b4 |
if(res==FA::DO_AGAIN)
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
if(res==FA::IN_PROGRESS)
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
session->Close();
|
|
Packit |
8f70b4 |
done=true;
|
|
Packit |
8f70b4 |
m=MOVED;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
return m;
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
const char *SFtpListInfo::Status()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
if(ubuf && !ubuf->Eof() && session->IsOpen())
|
|
Packit |
8f70b4 |
return xstring::format(_("Getting file list (%lld) [%s]"),
|
|
Packit |
8f70b4 |
(long long)session->GetPos(),session->CurrentStatus());
|
|
Packit |
8f70b4 |
return "";
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
|
|
Packit |
8f70b4 |
#include "modconfig.h"
|
|
Packit |
8f70b4 |
#ifdef MODULE_PROTO_SFTP
|
|
Packit |
8f70b4 |
void module_init()
|
|
Packit |
8f70b4 |
{
|
|
Packit |
8f70b4 |
SFtp::ClassInit();
|
|
Packit |
8f70b4 |
}
|
|
Packit |
8f70b4 |
#endif
|