Blame src/Fish.cc

Packit 8f70b4
/*
Packit 8f70b4
 * lftp - file transfer program
Packit 8f70b4
 *
Packit 8f70b4
 * Copyright (c) 1996-2015 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 <stddef.h>
Packit 8f70b4
#include "Fish.h"
Packit 8f70b4
#include "trio.h"
Packit 8f70b4
#include <unistd.h>
Packit 8f70b4
#include <errno.h>
Packit 8f70b4
#include <stdarg.h>
Packit 8f70b4
#include <assert.h>
Packit 8f70b4
#include <time.h>
Packit 8f70b4
#include "ascii_ctype.h"
Packit 8f70b4
#include "LsCache.h"
Packit 8f70b4
#include "misc.h"
Packit 8f70b4
#include "log.h"
Packit 8f70b4
#include "ArgV.h"
Packit 8f70b4
Packit 8f70b4
#define super SSH_Access
Packit 8f70b4
Packit 8f70b4
#define max_buf 0x10000
Packit 8f70b4
Packit 8f70b4
void Fish::GetBetterConnection(int level)
Packit 8f70b4
{
Packit 8f70b4
   for(FA *fo=FirstSameSite(); fo!=0; fo=NextSameSite(fo))
Packit 8f70b4
   {
Packit 8f70b4
      Fish *o=(Fish*)fo; // we are sure it is Fish.
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;
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
      return;
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int Fish::Do()
Packit 8f70b4
{
Packit 8f70b4
   int m=STALL;
Packit 8f70b4
   int fd;
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
      Disconnect();
Packit 8f70b4
      return MOVED;
Packit 8f70b4
   }
Packit 8f70b4
   if(state!=CONNECTING_1)
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 || !RespQueueIsEmpty())
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
   const char *charset;
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 Fish 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
	 if(i>=2 && (connection_limit==0 || connection_limit>CountConnections()))
Packit 8f70b4
	    break;
Packit 8f70b4
	 GetBetterConnection(i);
Packit 8f70b4
	 if(state!=DISCONNECTED)
Packit 8f70b4
	    return MOVED;
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 *shell=Query("shell",hostname);
Packit 8f70b4
      const char *init=xstring::cat("echo FISH:;",shell,NULL);
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(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
      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)\n",_("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
      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
Packit 8f70b4
      charset=ResMgr::Query("fish:charset",hostname);
Packit 8f70b4
      if(charset && *charset)
Packit 8f70b4
      {
Packit 8f70b4
	 send_buf->SetTranslation(charset,false);
Packit 8f70b4
	 recv_buf->SetTranslation(charset,true);
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      Send("#FISH\n"
Packit 8f70b4
	   "TZ=GMT;export TZ;LC_ALL=C;export LC_ALL;"
Packit 8f70b4
	   "exec 2>&1;echo;start_fish_server;"
Packit 8f70b4
	   "echo '### 200'\n");
Packit 8f70b4
      PushExpect(EXPECT_FISH);
Packit 8f70b4
      Send("#VER 0.0.2\n"
Packit 8f70b4
	   "echo '### 000'\n");
Packit 8f70b4
      PushExpect(EXPECT_VER);
Packit 8f70b4
      if(home_auto==0)
Packit 8f70b4
      {
Packit 8f70b4
	 Send("#PWD\n"
Packit 8f70b4
	      "pwd; echo '### 200'\n");
Packit 8f70b4
	 PushExpect(EXPECT_PWD);
Packit 8f70b4
      }
Packit 8f70b4
      state=CONNECTED;
Packit 8f70b4
      m=MOVED;
Packit 8f70b4
Packit 8f70b4
   case CONNECTED:
Packit 8f70b4
      if(mode==CLOSED)
Packit 8f70b4
	 return m;
Packit 8f70b4
      if(home.path==0 && !RespQueueIsEmpty())
Packit 8f70b4
	 break;
Packit 8f70b4
      ExpandTildeInCWD();
Packit 8f70b4
      if(mode!=CHANGE_DIR && xstrcmp(cwd,real_cwd))
Packit 8f70b4
      {
Packit 8f70b4
	 if(xstrcmp(path_queue.LastString(),cwd))
Packit 8f70b4
	 {
Packit 8f70b4
	    Send("#CWD %s\n"
Packit 8f70b4
		 "cd %s; echo '### 000'\n",cwd.path.get(),shell_encode(cwd).get());
Packit 8f70b4
	    PushExpect(EXPECT_CWD);
Packit 8f70b4
	    PushDirectory(cwd);
Packit 8f70b4
	 }
Packit 8f70b4
	 if(!RespQueueIsEmpty())
Packit 8f70b4
	    break;
Packit 8f70b4
      }
Packit 8f70b4
      SendMethod();
Packit 8f70b4
      if(mode==LONG_LIST || mode==LIST || mode==QUOTE_CMD)
Packit 8f70b4
      {
Packit 8f70b4
	 state=FILE_RECV;
Packit 8f70b4
	 m=MOVED;
Packit 8f70b4
	 break;
Packit 8f70b4
      }
Packit 8f70b4
      state=WAITING;
Packit 8f70b4
      m=MOVED;
Packit 8f70b4
   case WAITING:
Packit 8f70b4
      if(RespQueueSize()==1 && mode==RETRIEVE)
Packit 8f70b4
      {
Packit 8f70b4
	 state=FILE_RECV;
Packit 8f70b4
	 m=MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      if(RespQueueSize()==1 && mode==STORE)
Packit 8f70b4
      {
Packit 8f70b4
	 state=FILE_SEND;
Packit 8f70b4
	 real_pos=0;
Packit 8f70b4
	 pos=0;
Packit 8f70b4
	 m=MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      if(RespQueueSize()==0)
Packit 8f70b4
      {
Packit 8f70b4
	 state=DONE;
Packit 8f70b4
	 m=MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      break;
Packit 8f70b4
   case FILE_RECV:
Packit 8f70b4
      if(recv_buf->Size()>=rate_limit->BytesAllowedToGet())
Packit 8f70b4
      {
Packit 8f70b4
	 recv_buf->Suspend();
Packit 8f70b4
	 Timeout(1000);
Packit 8f70b4
      }
Packit 8f70b4
      else if(recv_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
   case DONE:
Packit 8f70b4
      break;
Packit 8f70b4
   }
Packit 8f70b4
   return m;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Fish::MoveConnectionHere(Fish *o)
Packit 8f70b4
{
Packit 8f70b4
   super::MoveConnectionHere(o);
Packit 8f70b4
   rate_limit=o->rate_limit.borrow();
Packit 8f70b4
   path_queue.MoveHere(o->path_queue);
Packit 8f70b4
   RespQueue.move_here(o->RespQueue);
Packit 8f70b4
   timeout_timer.Reset(o->timeout_timer);
Packit 8f70b4
   set_real_cwd(o->real_cwd);
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 Fish::DisconnectLL()
Packit 8f70b4
{
Packit 8f70b4
   super::DisconnectLL();
Packit 8f70b4
   EmptyRespQueue();
Packit 8f70b4
   EmptyPathQueue();
Packit 8f70b4
   state=DISCONNECTED;
Packit 8f70b4
   if(mode==STORE)
Packit 8f70b4
      SetError(STORE_FAILED,0);
Packit 8f70b4
   home_auto.set(FindHomeAuto());
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Fish::Init()
Packit 8f70b4
{
Packit 8f70b4
   state=DISCONNECTED;
Packit 8f70b4
   max_send=0;
Packit 8f70b4
   eof=false;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
Fish::Fish() : SSH_Access("FISH:")
Packit 8f70b4
{
Packit 8f70b4
   Init();
Packit 8f70b4
   Reconfig(0);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
Fish::~Fish()
Packit 8f70b4
{
Packit 8f70b4
   Disconnect();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
Fish::Fish(const Fish *o) : super(o)
Packit 8f70b4
{
Packit 8f70b4
   Init();
Packit 8f70b4
   Reconfig(0);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Fish::Close()
Packit 8f70b4
{
Packit 8f70b4
   switch(state)
Packit 8f70b4
   {
Packit 8f70b4
   case(DISCONNECTED):
Packit 8f70b4
   case(CONNECTED):
Packit 8f70b4
   case(DONE):
Packit 8f70b4
      break;
Packit 8f70b4
   case(WAITING):
Packit 8f70b4
      if(mode==STORE || mode==RETRIEVE)
Packit 8f70b4
	 Disconnect();
Packit 8f70b4
      break;
Packit 8f70b4
   case(FILE_SEND):
Packit 8f70b4
      if(!RespQueueIsEmpty())
Packit 8f70b4
	 Disconnect();
Packit 8f70b4
      break;
Packit 8f70b4
   case(FILE_RECV):
Packit 8f70b4
   case(CONNECTING):
Packit 8f70b4
   case(CONNECTING_1):
Packit 8f70b4
      Disconnect();
Packit 8f70b4
   }
Packit 8f70b4
//    if(!RespQueueIsEmpty())
Packit 8f70b4
//       Disconnect(); // play safe.
Packit 8f70b4
   CloseExpectQueue();
Packit 8f70b4
   state=(recv_buf?CONNECTED:DISCONNECTED);
Packit 8f70b4
   eof=false;
Packit 8f70b4
   encode_file=true;
Packit 8f70b4
   super::Close();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Fish::Send(const char *format,...)
Packit 8f70b4
{
Packit 8f70b4
   va_list va;
Packit 8f70b4
   va_start(va,format);
Packit 8f70b4
   xstring& str=xstring::vformat(format,va);
Packit 8f70b4
   va_end(va);
Packit 8f70b4
   LogSend(5,str);
Packit 8f70b4
   send_buf->Put(str);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Fish::SendArrayInfoRequests()
Packit 8f70b4
{
Packit 8f70b4
   for(int i=fileset_for_info->curr_index(); i<fileset_for_info->count(); i++)
Packit 8f70b4
   {
Packit 8f70b4
      FileInfo *fi=(*fileset_for_info)[i];
Packit 8f70b4
      if(fi->need)
Packit 8f70b4
      {
Packit 8f70b4
	 const char *e=shell_encode(fi->name);
Packit 8f70b4
	 Send("#INFO %s\n"
Packit 8f70b4
	      "ls -lLd %s; echo '### 200'\n",fi->name.get(),e);
Packit 8f70b4
	 PushExpect(EXPECT_INFO);
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Fish::SendMethod()
Packit 8f70b4
{
Packit 8f70b4
   const char *e=file?alloca_strdup(shell_encode(file)):0;
Packit 8f70b4
   const char *e1=shell_encode(file1);
Packit 8f70b4
   switch((open_mode)mode)
Packit 8f70b4
   {
Packit 8f70b4
   case CHANGE_DIR:
Packit 8f70b4
      Send("#CWD %s\n"
Packit 8f70b4
	   "cd %s; echo '### 000'\n",e,e);
Packit 8f70b4
      PushExpect(EXPECT_CWD);
Packit 8f70b4
      PushDirectory(file);
Packit 8f70b4
      break;
Packit 8f70b4
   case LONG_LIST:
Packit 8f70b4
      if(!encode_file)
Packit 8f70b4
	 e=file;
Packit 8f70b4
      Send("#LIST %s\n"
Packit 8f70b4
	   "ls -la %s; echo '### 200'\n",e,e);
Packit 8f70b4
      PushExpect(EXPECT_DIR);
Packit 8f70b4
      real_pos=0;
Packit 8f70b4
      break;
Packit 8f70b4
   case LIST:
Packit 8f70b4
      if(!encode_file)
Packit 8f70b4
	 e=file;
Packit 8f70b4
      Send("#LIST %s\n"
Packit 8f70b4
	   "ls -a %s; echo '### 200'\n",e,e);
Packit 8f70b4
      PushExpect(EXPECT_DIR);
Packit 8f70b4
      real_pos=0;
Packit 8f70b4
      break;
Packit 8f70b4
   case RETRIEVE:
Packit 8f70b4
      if(pos>0)
Packit 8f70b4
      {
Packit 8f70b4
	 int bs=0x1000;
Packit 8f70b4
	 real_pos=pos-pos%bs;
Packit 8f70b4
	 // non-standard extension
Packit 8f70b4
	 Send("#RETRP %lld %s\n"
Packit 8f70b4
	      "ls -lLd %s; "
Packit 8f70b4
	      "echo '### 100'; "
Packit 8f70b4
	      "dd ibs=%d skip=%lld if=%s 2>/dev/null; "
Packit 8f70b4
	      "echo '### 200'\n",
Packit 8f70b4
	    (long long)real_pos,e,e,bs,(long long)real_pos/bs,e);
Packit 8f70b4
      }
Packit 8f70b4
      else
Packit 8f70b4
      {
Packit 8f70b4
	 Send("#RETR %s\n"
Packit 8f70b4
	   "ls -lLd %s; "
Packit 8f70b4
	   "echo '### 100'; cat %s; echo '### 200'\n",e,e,e);
Packit 8f70b4
	 real_pos=0;
Packit 8f70b4
      }
Packit 8f70b4
      PushExpect(EXPECT_RETR_INFO);
Packit 8f70b4
      PushExpect(EXPECT_RETR);
Packit 8f70b4
      break;
Packit 8f70b4
   case STORE:
Packit 8f70b4
      if(entity_size<0)
Packit 8f70b4
      {
Packit 8f70b4
	 SetError(NO_FILE,"Have to know file size before upload");
Packit 8f70b4
	 break;
Packit 8f70b4
      }
Packit 8f70b4
      if(entity_size>0)
Packit 8f70b4
      {
Packit 8f70b4
	 Send("#STOR %lld %s\n"
Packit 8f70b4
	      "rest=%lld;file=%s;:>$file;echo '### 001';"
Packit 8f70b4
	      "if echo 1|head -c 1 -q ->/dev/null 2>&1;then "
Packit 8f70b4
		  "head -c $rest -q -|(cat>$file;cat>/dev/null);"
Packit 8f70b4
	      "else while [ $rest -gt 0 ];do "
Packit 8f70b4
		  "bs=4096;cnt=`expr $rest / $bs`;"
Packit 8f70b4
		  "[ $cnt -eq 0 ] && { cnt=1;bs=$rest; }; "
Packit 8f70b4
		  "n=`dd ibs=$bs count=$cnt 2>/dev/null|tee -a $file|wc -c`;"
Packit 8f70b4
		  "[ \"$n\" -le 0 ] && exit;"
Packit 8f70b4
		  "rest=`expr $rest - $n`; "
Packit 8f70b4
	      "done;fi;echo '### 200'\n",
Packit 8f70b4
	    (long long)entity_size,e,(long long)entity_size,e);
Packit 8f70b4
#if 0
Packit 8f70b4
	 // dd pays attension to read boundaries and reads wrong number
Packit 8f70b4
	 // of bytes when ibs>1. Have to use the inefficient ibs=1.
Packit 8f70b4
	 Send("#STOR %lld %s\n"
Packit 8f70b4
	      ">%s;echo '### 001';"
Packit 8f70b4
	      "dd ibs=1 count=%lld 2>/dev/null"
Packit 8f70b4
	      "|(cat>%s;cat>/dev/null);echo '### 200'\n",
Packit 8f70b4
	      (long long)entity_size,e,
Packit 8f70b4
	      e,
Packit 8f70b4
	      (long long)entity_size,
Packit 8f70b4
	      e);
Packit 8f70b4
#endif
Packit 8f70b4
      }
Packit 8f70b4
      else
Packit 8f70b4
      {
Packit 8f70b4
	 Send("#STOR %lld %s\n"
Packit 8f70b4
	      ">%s;echo '### 001';echo '### 200'\n",
Packit 8f70b4
	      (long long)entity_size,e,e);
Packit 8f70b4
      }
Packit 8f70b4
      PushExpect(EXPECT_STOR_PRELIMINARY);
Packit 8f70b4
      PushExpect(EXPECT_STOR);
Packit 8f70b4
      real_pos=0;
Packit 8f70b4
      pos=0;
Packit 8f70b4
      break;
Packit 8f70b4
   case ARRAY_INFO:
Packit 8f70b4
      SendArrayInfoRequests();
Packit 8f70b4
      break;
Packit 8f70b4
   case REMOVE:
Packit 8f70b4
      Send("#DELE %s\n"
Packit 8f70b4
	   "rm -f %s; echo '### 000'\n",e,e);
Packit 8f70b4
      PushExpect(EXPECT_DEFAULT);
Packit 8f70b4
      break;
Packit 8f70b4
   case REMOVE_DIR:
Packit 8f70b4
      Send("#RMD %s\n"
Packit 8f70b4
	   "rmdir %s; echo '### 000'\n",e,e);
Packit 8f70b4
      PushExpect(EXPECT_DEFAULT);
Packit 8f70b4
      break;
Packit 8f70b4
   case MAKE_DIR:
Packit 8f70b4
      Send("#MKD %s\n"
Packit 8f70b4
	   "mkdir %s; echo '### 000'\n",e,e);
Packit 8f70b4
      PushExpect(EXPECT_DEFAULT);
Packit 8f70b4
      break;
Packit 8f70b4
   case RENAME:
Packit 8f70b4
      Send("#RENAME %s %s\n"
Packit 8f70b4
	   "mv %s %s; echo '### 000'\n",e,e1,e,e1);
Packit 8f70b4
      PushExpect(EXPECT_DEFAULT);
Packit 8f70b4
      break;
Packit 8f70b4
   case CHANGE_MODE:
Packit 8f70b4
      Send("#CHMOD %04o %s\n"
Packit 8f70b4
	   "chmod %04o %s; echo '### 000'\n",chmod_mode,e,chmod_mode,e);
Packit 8f70b4
      PushExpect(EXPECT_DEFAULT);
Packit 8f70b4
      break;
Packit 8f70b4
   case LINK:
Packit 8f70b4
      Send("#LINK %s %s\n"
Packit 8f70b4
	   "ln %s %s; echo '### 000'\n",e,e1,e,e1);
Packit 8f70b4
      PushExpect(EXPECT_DEFAULT);
Packit 8f70b4
      break;
Packit 8f70b4
   case SYMLINK:
Packit 8f70b4
      Send("#SYMLINK %s %s\n"
Packit 8f70b4
	   "ln -s %s %s; echo '### 000'\n",e,e1,e,e1);
Packit 8f70b4
      PushExpect(EXPECT_DEFAULT);
Packit 8f70b4
      break;
Packit 8f70b4
   case QUOTE_CMD:
Packit 8f70b4
      // non-standard extension
Packit 8f70b4
      Send("#EXEC %s\n"
Packit 8f70b4
	   "%s; echo '### 200'\n",e,file.get());
Packit 8f70b4
      PushExpect(EXPECT_QUOTE);
Packit 8f70b4
      real_pos=0;
Packit 8f70b4
      break;
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
int Fish::ReplyLogPriority(int code)
Packit 8f70b4
{
Packit 8f70b4
   if(code==-1)
Packit 8f70b4
      return 3;
Packit 8f70b4
   return 4;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int Fish::HandleReplies()
Packit 8f70b4
{
Packit 8f70b4
   int m=STALL;
Packit 8f70b4
   if(recv_buf==0)
Packit 8f70b4
      return m;
Packit 8f70b4
   if(state==FILE_RECV) {
Packit 8f70b4
      const char *err=pty_recv_buf->Get();
Packit 8f70b4
      if(err && err[0]) {
Packit 8f70b4
	 const char *eol=strchr(err,'\n');
Packit 8f70b4
	 if(eol) {
Packit 8f70b4
	    xstring &e=xstring::get_tmp(err,eol-err);
Packit 8f70b4
	    LogError(0,"%s",e.get());
Packit 8f70b4
	    SetError(NO_FILE,e);
Packit 8f70b4
	    if(pty_recv_buf)
Packit 8f70b4
	       pty_recv_buf->Skip(eol-err+1);
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
      if(pty_recv_buf->Eof()) {
Packit 8f70b4
	 Disconnect();
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      if(entity_size!=NO_SIZE && real_pos
Packit 8f70b4
	 return m;
Packit 8f70b4
      if(entity_size==NO_SIZE)
Packit 8f70b4
	 return m;
Packit 8f70b4
   }
Packit 8f70b4
   recv_buf->Put(pty_recv_buf->Get(),pty_recv_buf->Size()); // join the messages.
Packit 8f70b4
   pty_recv_buf->Skip(pty_recv_buf->Size());
Packit 8f70b4
   if(recv_buf->Size()<5)
Packit 8f70b4
   {
Packit 8f70b4
   hup:
Packit 8f70b4
      if(recv_buf->Error())
Packit 8f70b4
      {
Packit 8f70b4
	 Disconnect();
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      if(recv_buf->Eof())
Packit 8f70b4
      {
Packit 8f70b4
	 LogError(0,_("Peer closed connection"));
Packit 8f70b4
	 // Solaris' shell exists when is given with wrong directory
Packit 8f70b4
	 if(!RespQueueIsEmpty() && RespQueue[0]==EXPECT_CWD && message)
Packit 8f70b4
	    SetError(NO_FILE,message);
Packit 8f70b4
	 Disconnect();
Packit 8f70b4
	 m=MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      return m;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   const char *b;
Packit 8f70b4
   int s;
Packit 8f70b4
   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(recv_buf->Eof() || recv_buf->Error())
Packit 8f70b4
	 goto hup;
Packit 8f70b4
      return m;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   m=MOVED;
Packit 8f70b4
   s=eol-b+1;
Packit 8f70b4
   line.nset(b,s-1);
Packit 8f70b4
   recv_buf->Skip(s);
Packit 8f70b4
Packit 8f70b4
   int code=-1;
Packit 8f70b4
   if(s>7 && !memcmp(line,"### ",4)) {
Packit 8f70b4
      if(sscanf(line+4,"%3d",&code)!=1)
Packit 8f70b4
	 code=-1;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   LogRecv(ReplyLogPriority(code),line);
Packit 8f70b4
   if(code==-1)
Packit 8f70b4
   {
Packit 8f70b4
      if(message==0)
Packit 8f70b4
	 message.set(line);
Packit 8f70b4
      else {
Packit 8f70b4
	 message.append('\n');
Packit 8f70b4
	 message.append(line);
Packit 8f70b4
      }
Packit 8f70b4
      return m;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(RespQueueIsEmpty())
Packit 8f70b4
   {
Packit 8f70b4
      LogError(3,_("extra server response"));
Packit 8f70b4
      message.set(0);
Packit 8f70b4
      return m;
Packit 8f70b4
   }
Packit 8f70b4
   expect_t e=RespQueue.next();
Packit 8f70b4
Packit 8f70b4
   bool keep_message=false;
Packit 8f70b4
   switch(e)
Packit 8f70b4
   {
Packit 8f70b4
   case EXPECT_FISH:
Packit 8f70b4
   case EXPECT_VER:
Packit 8f70b4
      /* nothing yet */
Packit 8f70b4
      break;;
Packit 8f70b4
   case EXPECT_PWD:
Packit 8f70b4
      if(!message)
Packit 8f70b4
	 break;
Packit 8f70b4
      home_auto.set(message);
Packit 8f70b4
      LogNote(9,"home set to %s\n",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
      break;
Packit 8f70b4
   case EXPECT_CWD: {
Packit 8f70b4
      xstring p;
Packit 8f70b4
      PopDirectory(&p);
Packit 8f70b4
      if(message==0)
Packit 8f70b4
      {
Packit 8f70b4
	 set_real_cwd(p);
Packit 8f70b4
	 if(mode==CHANGE_DIR && RespQueueIsEmpty())
Packit 8f70b4
	 {
Packit 8f70b4
	    cwd.Set(p);
Packit 8f70b4
	    eof=true;
Packit 8f70b4
	 }
Packit 8f70b4
	 cache->SetDirectory(this,p,true);
Packit 8f70b4
      }
Packit 8f70b4
      else
Packit 8f70b4
	 SetError(NO_FILE,message);
Packit 8f70b4
      break;
Packit 8f70b4
   }
Packit 8f70b4
   case EXPECT_RETR_INFO:
Packit 8f70b4
      if(message && is_ascii_digit(message[0]) && !strchr(message,':'))
Packit 8f70b4
      {
Packit 8f70b4
	 long long size_ll;
Packit 8f70b4
	 if(1==sscanf(message,"%lld",&size_ll))
Packit 8f70b4
	 {
Packit 8f70b4
	    entity_size=size_ll;
Packit 8f70b4
	    if(opt_size)
Packit 8f70b4
	       *opt_size=entity_size;
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
      else if(message && message[0]!='#')
Packit 8f70b4
      {
Packit 8f70b4
	 FileInfo *fi=FileInfo::parse_ls_line(message,"GMT");
Packit 8f70b4
	 if(!fi || !strncmp(message,"ls: ",4))
Packit 8f70b4
	 {
Packit 8f70b4
	    SetError(NO_FILE,message);
Packit 8f70b4
	    break;
Packit 8f70b4
	 }
Packit 8f70b4
	 if(fi->defined&fi->SIZE)
Packit 8f70b4
	 {
Packit 8f70b4
	    entity_size=fi->size;
Packit 8f70b4
	    if(opt_size)
Packit 8f70b4
	       *opt_size=entity_size;
Packit 8f70b4
	 }
Packit 8f70b4
	 if(fi->defined&fi->DATE)
Packit 8f70b4
	 {
Packit 8f70b4
	    entity_date=fi->date;
Packit 8f70b4
	    if(opt_date)
Packit 8f70b4
	       *opt_date=entity_date;
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
      state=FILE_RECV;
Packit 8f70b4
      break;
Packit 8f70b4
   case EXPECT_INFO:
Packit 8f70b4
   {
Packit 8f70b4
      Ref<FileInfo> new_info(FileInfo::parse_ls_line(message,"GMT"));
Packit 8f70b4
      FileInfo *fi=fileset_for_info->curr();
Packit 8f70b4
      while(!fi->need)
Packit 8f70b4
	 fi=fileset_for_info->next();
Packit 8f70b4
      fi->Merge(*new_info);
Packit 8f70b4
      fi->need=0;
Packit 8f70b4
      break;
Packit 8f70b4
   }
Packit 8f70b4
   case EXPECT_RETR:
Packit 8f70b4
   case EXPECT_DIR:
Packit 8f70b4
   case EXPECT_QUOTE:
Packit 8f70b4
      eof=true;
Packit 8f70b4
      state=DONE;
Packit 8f70b4
      break;
Packit 8f70b4
   case EXPECT_DEFAULT:
Packit 8f70b4
      if(message)
Packit 8f70b4
	 SetError(NO_FILE,message);
Packit 8f70b4
      break;
Packit 8f70b4
   case EXPECT_STOR_PRELIMINARY:
Packit 8f70b4
      if(message)
Packit 8f70b4
      {
Packit 8f70b4
	 Disconnect();
Packit 8f70b4
	 SetError(NO_FILE,message);
Packit 8f70b4
      }
Packit 8f70b4
      break;
Packit 8f70b4
   case EXPECT_STOR:
Packit 8f70b4
      if(message)
Packit 8f70b4
      {
Packit 8f70b4
	 Disconnect();
Packit 8f70b4
	 SetError(NO_FILE,message);
Packit 8f70b4
      }
Packit 8f70b4
      break;
Packit 8f70b4
   case EXPECT_IGNORE:
Packit 8f70b4
      break;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(!keep_message)
Packit 8f70b4
      message.set(0);
Packit 8f70b4
Packit 8f70b4
   return m;
Packit 8f70b4
}
Packit 8f70b4
void Fish::PushExpect(expect_t e)
Packit 8f70b4
{
Packit 8f70b4
   RespQueue.push(e);
Packit 8f70b4
}
Packit 8f70b4
void Fish::CloseExpectQueue()
Packit 8f70b4
{
Packit 8f70b4
   int count=RespQueue.count();
Packit 8f70b4
   for(int i=0; i
Packit 8f70b4
   {
Packit 8f70b4
      switch(RespQueue[i])
Packit 8f70b4
      {
Packit 8f70b4
      case EXPECT_IGNORE:
Packit 8f70b4
      case EXPECT_FISH:
Packit 8f70b4
      case EXPECT_VER:
Packit 8f70b4
      case EXPECT_PWD:
Packit 8f70b4
      case EXPECT_CWD:
Packit 8f70b4
	 break;
Packit 8f70b4
      case EXPECT_INFO:
Packit 8f70b4
      case EXPECT_DIR:
Packit 8f70b4
      case EXPECT_DEFAULT:
Packit 8f70b4
	 RespQueue[i]=EXPECT_IGNORE;
Packit 8f70b4
	 break;
Packit 8f70b4
      case EXPECT_QUOTE:
Packit 8f70b4
      case EXPECT_RETR_INFO:
Packit 8f70b4
      case EXPECT_RETR:
Packit 8f70b4
      case EXPECT_STOR_PRELIMINARY:
Packit 8f70b4
      case EXPECT_STOR:
Packit 8f70b4
	 Disconnect();
Packit 8f70b4
	 break;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const char *memstr(const char *mem,size_t len,const char *str)
Packit 8f70b4
{
Packit 8f70b4
   size_t str_len=strlen(str);
Packit 8f70b4
   while(len>=str_len)
Packit 8f70b4
   {
Packit 8f70b4
      if(!memcmp(mem,str,str_len))
Packit 8f70b4
	 return mem;
Packit 8f70b4
      mem++;
Packit 8f70b4
      len--;
Packit 8f70b4
   }
Packit 8f70b4
   return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int Fish::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)
Packit 8f70b4
      return 0;	  // eof
Packit 8f70b4
   if(state==FILE_RECV && real_pos>=0)
Packit 8f70b4
   {
Packit 8f70b4
      const char *buf1;
Packit 8f70b4
      int size1;
Packit 8f70b4
   get_again:
Packit 8f70b4
      if(recv_buf->Size()==0 && recv_buf->Error())
Packit 8f70b4
      {
Packit 8f70b4
	 Disconnect();
Packit 8f70b4
	 return DO_AGAIN;
Packit 8f70b4
      }
Packit 8f70b4
      recv_buf->Get(&buf1,&size1);
Packit 8f70b4
      if(buf1==0) // eof
Packit 8f70b4
      {
Packit 8f70b4
	 Disconnect();
Packit 8f70b4
	 return DO_AGAIN;
Packit 8f70b4
      }
Packit 8f70b4
      if(size1==0)
Packit 8f70b4
	 return DO_AGAIN;
Packit 8f70b4
      if(entity_size!=NO_SIZE && real_pos
Packit 8f70b4
      {
Packit 8f70b4
	 if(real_pos+size1>entity_size)
Packit 8f70b4
	    size1=entity_size-real_pos;
Packit 8f70b4
      }
Packit 8f70b4
      else
Packit 8f70b4
      {
Packit 8f70b4
	 const char *end=memstr(buf1,size1,"### ");
Packit 8f70b4
	 if(end)
Packit 8f70b4
	 {
Packit 8f70b4
	    size1=end-buf1;
Packit 8f70b4
	    if(size1==0)
Packit 8f70b4
	    {
Packit 8f70b4
	       state=WAITING;
Packit 8f70b4
	       if(HandleReplies()==MOVED)
Packit 8f70b4
		  current->Timeout(0);
Packit 8f70b4
	       return DO_AGAIN;
Packit 8f70b4
	    }
Packit 8f70b4
	 }
Packit 8f70b4
	 else
Packit 8f70b4
	 {
Packit 8f70b4
	    for(int j=0; j<3; j++)
Packit 8f70b4
	       if(size1>0 && buf1[size1-1]=='#')
Packit 8f70b4
		  size1--;
Packit 8f70b4
	    if(size1==0 && recv_buf->Eof())
Packit 8f70b4
	    {
Packit 8f70b4
	       Disconnect();
Packit 8f70b4
	       return DO_AGAIN;
Packit 8f70b4
	    }
Packit 8f70b4
	 }
Packit 8f70b4
      }
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(norest_manual && real_pos==0 && pos>0)
Packit 8f70b4
	 return DO_AGAIN;
Packit 8f70b4
      if(real_pos
Packit 8f70b4
      {
Packit 8f70b4
	 off_t to_skip=pos-real_pos;
Packit 8f70b4
	 if(to_skip>size1)
Packit 8f70b4
	    to_skip=size1;
Packit 8f70b4
	 recv_buf->Skip(to_skip);
Packit 8f70b4
	 real_pos+=to_skip;
Packit 8f70b4
	 goto get_again;
Packit 8f70b4
      }
Packit 8f70b4
      if(size>size1)
Packit 8f70b4
	 size=size1;
Packit 8f70b4
      size=buf->MoveDataHere(recv_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 Fish::Write(const void *buf,int size)
Packit 8f70b4
{
Packit 8f70b4
   if(mode!=STORE)
Packit 8f70b4
      return(0);
Packit 8f70b4
Packit 8f70b4
   Resume();
Packit 8f70b4
   Do();
Packit 8f70b4
   if(Error())
Packit 8f70b4
      return(error_code);
Packit 8f70b4
Packit 8f70b4
   if(state!=FILE_SEND || rate_limit==0)
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+send_buf->Size()>allowed)
Packit 8f70b4
	 size=allowed-send_buf->Size();
Packit 8f70b4
   }
Packit 8f70b4
   if(size+send_buf->Size()>0x4000)
Packit 8f70b4
      size=0x4000-send_buf->Size();
Packit 8f70b4
   if(pos+size>entity_size)
Packit 8f70b4
   {
Packit 8f70b4
      size=entity_size-pos;
Packit 8f70b4
      // tried to write more than originally requested. Make it retry with Open:
Packit 8f70b4
      if(size==0)
Packit 8f70b4
	 return STORE_FAILED;
Packit 8f70b4
   }
Packit 8f70b4
   if(size<=0)
Packit 8f70b4
      return 0;
Packit 8f70b4
   send_buf->Put((char*)buf,size);
Packit 8f70b4
   TrySuccess();
Packit 8f70b4
   rate_limit->BytesPut(size);
Packit 8f70b4
   pos+=size;
Packit 8f70b4
   real_pos+=size;
Packit 8f70b4
   return(size);
Packit 8f70b4
}
Packit 8f70b4
int Fish::Buffered()
Packit 8f70b4
{
Packit 8f70b4
   if(send_buf==0)
Packit 8f70b4
      return 0;
Packit 8f70b4
   return send_buf->Size();
Packit 8f70b4
}
Packit 8f70b4
int Fish::StoreStatus()
Packit 8f70b4
{
Packit 8f70b4
   if(Error())
Packit 8f70b4
      return error_code;
Packit 8f70b4
   if(state!=FILE_SEND)
Packit 8f70b4
      return IN_PROGRESS;
Packit 8f70b4
   if(real_pos!=entity_size)
Packit 8f70b4
   {
Packit 8f70b4
      Disconnect();
Packit 8f70b4
      return IN_PROGRESS;
Packit 8f70b4
   }
Packit 8f70b4
   if(RespQueueSize()==0)
Packit 8f70b4
      return OK;
Packit 8f70b4
   return IN_PROGRESS;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int Fish::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 Fish::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
}
Packit 8f70b4
void Fish::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
   super::ResumeInternal();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const char *Fish::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
      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 Fish::SameSiteAs(const FileAccess *fa) const
Packit 8f70b4
{
Packit 8f70b4
   if(!SameProtoAs(fa))
Packit 8f70b4
      return false;
Packit 8f70b4
   Fish *o=(Fish*)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 Fish::SameLocationAs(const FileAccess *fa)	const
Packit 8f70b4
{
Packit 8f70b4
   if(!SameSiteAs(fa))
Packit 8f70b4
      return false;
Packit 8f70b4
   Fish *o=(Fish*)fa;
Packit 8f70b4
   if(xstrcmp(cwd,o->cwd))
Packit 8f70b4
      return false;
Packit 8f70b4
   return true;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Fish::Reconfig(const char *name)
Packit 8f70b4
{
Packit 8f70b4
   super::Reconfig(name);
Packit 8f70b4
   if(!xstrcmp(name,"fish:charset") && recv_buf && send_buf)
Packit 8f70b4
   {
Packit 8f70b4
      if(!IsSuspended())
Packit 8f70b4
	 cache->TreeChanged(this,"/");
Packit 8f70b4
      const char *charset=ResMgr::Query("fish:charset",hostname);
Packit 8f70b4
      if(charset && *charset)
Packit 8f70b4
      {
Packit 8f70b4
	 send_buf->SetTranslation(charset,false);
Packit 8f70b4
	 recv_buf->SetTranslation(charset,true);
Packit 8f70b4
      }
Packit 8f70b4
      else
Packit 8f70b4
      {
Packit 8f70b4
	 send_buf->SetTranslator(0);
Packit 8f70b4
	 recv_buf->SetTranslator(0);
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void Fish::ClassInit()
Packit 8f70b4
{
Packit 8f70b4
   // register the class
Packit 8f70b4
   Register("fish",Fish::New);
Packit 8f70b4
}
Packit 8f70b4
FileAccess *Fish::New() { return new Fish(); }
Packit 8f70b4
Packit 8f70b4
DirList *Fish::MakeDirList(ArgV *args)
Packit 8f70b4
{
Packit 8f70b4
   return new FishDirList(this,args);
Packit 8f70b4
}
Packit 8f70b4
#include "FileGlob.h"
Packit 8f70b4
Glob *Fish::MakeGlob(const char *pattern)
Packit 8f70b4
{
Packit 8f70b4
   return new GenericGlob(this,pattern);
Packit 8f70b4
}
Packit 8f70b4
ListInfo *Fish::MakeListInfo(const char *p)
Packit 8f70b4
{
Packit 8f70b4
   return new FishListInfo(this,p);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
#undef super
Packit 8f70b4
#define super DirList
Packit 8f70b4
#include "ArgV.h"
Packit 8f70b4
Packit 8f70b4
int FishDirList::Do()
Packit 8f70b4
{
Packit 8f70b4
   if(done)
Packit 8f70b4
      return STALL;
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
      if(use_cache && FileAccess::cache->Find(session,pattern,FA::LONG_LIST,&err,
Packit 8f70b4
				    &cache_buffer,&cache_buffer_size))
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
      }
Packit 8f70b4
      else
Packit 8f70b4
      {
Packit 8f70b4
	 session->Open(pattern,FA::LONG_LIST);
Packit 8f70b4
	 session.Cast<Fish>()->DontEncodeFile();
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
      buf->PutEOF();
Packit 8f70b4
      FileAccess::cache->Add(session,pattern,FA::LONG_LIST,FA::OK,ubuf);
Packit 8f70b4
      return MOVED;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   int m=STALL;
Packit 8f70b4
Packit 8f70b4
   if(len>0)
Packit 8f70b4
   {
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
const char *FishDirList::Status()
Packit 8f70b4
{
Packit 8f70b4
   if(ubuf && !ubuf->Eof() && session->IsOpen())
Packit 8f70b4
   {
Packit 8f70b4
      return xstring::format(_("Getting file list (%lld) [%s]"),
Packit 8f70b4
		     (long long)session->GetPos(),session->CurrentStatus());
Packit 8f70b4
   }
Packit 8f70b4
   return "";
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FishDirList::SuspendInternal()
Packit 8f70b4
{
Packit 8f70b4
   super::SuspendInternal();
Packit 8f70b4
   if(ubuf)
Packit 8f70b4
      ubuf->SuspendSlave();
Packit 8f70b4
}
Packit 8f70b4
void FishDirList::ResumeInternal()
Packit 8f70b4
{
Packit 8f70b4
   if(ubuf)
Packit 8f70b4
      ubuf->ResumeSlave();
Packit 8f70b4
   super::ResumeInternal();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
static FileSet *ls_to_FileSet(const char *b,int len)
Packit 8f70b4
{
Packit 8f70b4
   FileSet *set=new FileSet;
Packit 8f70b4
   while(len>0) {
Packit 8f70b4
      // find one line
Packit 8f70b4
      const char *line=b;
Packit 8f70b4
      int ll=len;
Packit 8f70b4
      const char *eol=find_char(b,len,'\n');
Packit 8f70b4
      if(eol) {
Packit 8f70b4
	 ll=eol-b;
Packit 8f70b4
	 len-=ll+1;
Packit 8f70b4
	 b+=ll+1;
Packit 8f70b4
      } else {
Packit 8f70b4
	 len=0;
Packit 8f70b4
      }
Packit 8f70b4
      if(ll && line[ll-1]=='\r')
Packit 8f70b4
	 --ll;
Packit 8f70b4
      if(ll==0)
Packit 8f70b4
	 continue;
Packit 8f70b4
Packit 8f70b4
      FileInfo *f=FileInfo::parse_ls_line(line,ll,"GMT");
Packit 8f70b4
Packit 8f70b4
      if(!f)
Packit 8f70b4
	 continue;
Packit 8f70b4
Packit 8f70b4
      set->Add(f);
Packit 8f70b4
   }
Packit 8f70b4
   return set;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
FileSet *Fish::ParseLongList(const char *b,int len,int *err) const
Packit 8f70b4
{
Packit 8f70b4
   if(err)
Packit 8f70b4
      *err=0;
Packit 8f70b4
   return ls_to_FileSet(b,len);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
// FishListInfo implementation
Packit 8f70b4
FileSet *FishListInfo::Parse(const char *b,int len)
Packit 8f70b4
{
Packit 8f70b4
   return ls_to_FileSet(b,len);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
Packit 8f70b4
#include "modconfig.h"
Packit 8f70b4
#ifdef MODULE_PROTO_FISH
Packit 8f70b4
void module_init()
Packit 8f70b4
{
Packit 8f70b4
   Fish::ClassInit();
Packit 8f70b4
}
Packit 8f70b4
#endif