Blame src/FileCopy.cc

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
/* FileCopyPeer behaviour:
Packit 8f70b4
    1) when suspended, does nothing
Packit 8f70b4
    2) tries to read some data at seek_pos, sets pos to position of Get (get).
Packit 8f70b4
    2.5) tries to position to seek_pos and gets ready to write (put).
Packit 8f70b4
    3) if it cannot seek to seek_pos, changes pos to what it can seek.
Packit 8f70b4
    4) if it knows that it cannot seek to pos>0, CanSeek()==false
Packit 8f70b4
    5) if it knows that it cannot seek to pos==0, CanSeek0()==false
Packit 8f70b4
    6) it tries to get date/size if told to. (get)
Packit 8f70b4
    7) it sets date on the file if eof is reached and date is known (put).
Packit 8f70b4
    8) if put needs size/date before it writes data, NeedSizeDateBeforehand()==true.
Packit 8f70b4
 */
Packit 8f70b4
Packit 8f70b4
#include <config.h>
Packit 8f70b4
#include <assert.h>
Packit 8f70b4
#include <unistd.h>
Packit 8f70b4
#include <errno.h>
Packit 8f70b4
#include <sys/types.h>
Packit 8f70b4
#include <sys/stat.h>
Packit 8f70b4
#include <fcntl.h>
Packit 8f70b4
#include <math.h>
Packit 8f70b4
#include <stddef.h>
Packit 8f70b4
#include "FileCopy.h"
Packit 8f70b4
#include "url.h"
Packit 8f70b4
#include "log.h"
Packit 8f70b4
#include "misc.h"
Packit 8f70b4
#include "LsCache.h"
Packit 8f70b4
#include "plural.h"
Packit 8f70b4
#include "ArgV.h"
Packit 8f70b4
Packit 8f70b4
#define skip_threshold 0x1000
Packit 8f70b4
Packit 8f70b4
ResDecl rate_period  ("xfer:rate-period","15", ResMgr::UNumberValidate,ResMgr::NoClosure);
Packit 8f70b4
ResDecl eta_period   ("xfer:eta-period", "120",ResMgr::UNumberValidate,ResMgr::NoClosure);
Packit 8f70b4
ResDecl max_redir    ("xfer:max-redirections", "5",ResMgr::UNumberValidate,ResMgr::NoClosure);
Packit 8f70b4
ResDecl buffer_size  ("xfer:buffer-size","0x10000",ResMgr::UNumberValidate,ResMgr::NoClosure);
Packit 8f70b4
Packit 8f70b4
// It's bad when lftp receives data in small chunks, try to accumulate
Packit 8f70b4
// data in a kernel buffer using a delay and slurp it at once:
Packit 8f70b4
enum {
Packit 8f70b4
   // Delays in microseconds
Packit 8f70b4
   MAX_DELAY=30000,
Packit 8f70b4
   DELAY_STEP=30,
Packit 8f70b4
   // This size is related to socket buffer size.
Packit 8f70b4
   // When it is too large, tcp slowdown happens.
Packit 8f70b4
   // SSL has packet size 0x4000, so we have to use a lower threshold.
Packit 8f70b4
   MAX_READ_TO_DELAY=0x3F00,
Packit 8f70b4
};
Packit 8f70b4
Packit 8f70b4
// FileCopy
Packit 8f70b4
#define super SMTask
Packit 8f70b4
Packit 8f70b4
#define set_state(s) do { state=(s); \
Packit 8f70b4
   Log::global->Format(11,"FileCopy(%p) enters state %s\n", this, #s); } while(0)
Packit 8f70b4
Packit 8f70b4
int FileCopy::Do()
Packit 8f70b4
{
Packit 8f70b4
   int m=STALL;
Packit 8f70b4
   const char *b;
Packit 8f70b4
   int s;
Packit 8f70b4
   int rate_add;
Packit 8f70b4
Packit 8f70b4
   if(Error() || Done())
Packit 8f70b4
      return m;
Packit 8f70b4
   switch(state)
Packit 8f70b4
   {
Packit 8f70b4
   pre_INITIAL:
Packit 8f70b4
      set_state(INITIAL);
Packit 8f70b4
      m=MOVED;
Packit 8f70b4
   case(INITIAL):
Packit 8f70b4
      if(remove_target_first && !put->FileRemoved())
Packit 8f70b4
	 return m;
Packit 8f70b4
      remove_target_first=false;
Packit 8f70b4
      if(cont && put->CanSeek())
Packit 8f70b4
	 put->WantSize();
Packit 8f70b4
      if(put->NeedSizeDateBeforehand() || (cont && put->CanSeek() && put->GetSize()==NO_SIZE_YET))
Packit 8f70b4
      {
Packit 8f70b4
	 if(get->GetSize()==NO_SIZE_YET || get->GetDate()==NO_DATE_YET)
Packit 8f70b4
	 {
Packit 8f70b4
	    put->Suspend();
Packit 8f70b4
	    get->DontStartTransferYet();
Packit 8f70b4
	    get->Resume();
Packit 8f70b4
	    get->WantSize();
Packit 8f70b4
	    if(put->NeedDate())
Packit 8f70b4
	       get->WantDate();
Packit 8f70b4
	    goto pre_GET_INFO_WAIT;
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
      if(get->GetSize()==NO_SIZE_YET)
Packit 8f70b4
	 get->WantSize();
Packit 8f70b4
      if(get->GetSize()!=NO_SIZE && get->GetSize()!=NO_SIZE_YET)
Packit 8f70b4
	 put->SetEntitySize(get->GetSize());
Packit 8f70b4
      if(get->GetDate()!=NO_DATE && get->GetDate()!=NO_DATE_YET)
Packit 8f70b4
	 put->SetDate(get->GetDate());
Packit 8f70b4
      else if(get->GetDate()==NO_DATE_YET)
Packit 8f70b4
      {
Packit 8f70b4
	 if(put->NeedDate())
Packit 8f70b4
	    get->WantDate();
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      if(cont && put->CanSeek())
Packit 8f70b4
	 put->Seek(FILE_END);
Packit 8f70b4
      else
Packit 8f70b4
      {
Packit 8f70b4
	 if(put->range_start>0 && put->CanSeek())
Packit 8f70b4
	    put->Seek(put->range_start);
Packit 8f70b4
	 if(get->range_start>0 && get->CanSeek())
Packit 8f70b4
	    get->Seek(get->range_start);
Packit 8f70b4
	 goto pre_DO_COPY;
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      get->Suspend();
Packit 8f70b4
      put->Resume();
Packit 8f70b4
      set_state(PUT_WAIT);
Packit 8f70b4
      m=MOVED;
Packit 8f70b4
      /* fallthrough */
Packit 8f70b4
   case(PUT_WAIT):
Packit 8f70b4
      if(put->Error())
Packit 8f70b4
	 goto put_error;
Packit 8f70b4
      if(put->GetSeekPos()!=FILE_END && get->GetSize()>=0
Packit 8f70b4
      && put->GetSeekPos()>=get->GetSize())
Packit 8f70b4
      {
Packit 8f70b4
	 debug((9,_("copy: destination file is already complete\n")));
Packit 8f70b4
	 if(get->GetDate()!=NO_DATE)
Packit 8f70b4
	    goto pre_CONFIRM_WAIT;  // have to set the date.
Packit 8f70b4
	 goto pre_GET_DONE_WAIT;
Packit 8f70b4
      }
Packit 8f70b4
      if(!put->IOReady())
Packit 8f70b4
	 return m;
Packit 8f70b4
      /* now we know if put's seek failed. Seek get accordingly. */
Packit 8f70b4
      if(get->CanSeek())
Packit 8f70b4
	 get->Seek(put->GetRealPos());
Packit 8f70b4
   pre_DO_COPY:
Packit 8f70b4
      get->Resume();
Packit 8f70b4
      get->StartTransfer();
Packit 8f70b4
      RateReset();
Packit 8f70b4
      set_state(DO_COPY);
Packit 8f70b4
      m=MOVED;
Packit 8f70b4
      /* fallthrough */
Packit 8f70b4
   case(DO_COPY): {
Packit 8f70b4
      if(put->Error())
Packit 8f70b4
      {
Packit 8f70b4
      put_error:
Packit 8f70b4
	 SetError(put->ErrorText());
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      if(get->Error() && get->Size()==0)
Packit 8f70b4
      {
Packit 8f70b4
	 put->DontVerify();
Packit 8f70b4
	 if(put->GetPos()>0)
Packit 8f70b4
	 {
Packit 8f70b4
	    put->PutEOF();
Packit 8f70b4
	    put->Roll();
Packit 8f70b4
	 }
Packit 8f70b4
      get_error:
Packit 8f70b4
	 SetError(get->ErrorText());
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      if(put->Broken())
Packit 8f70b4
      {
Packit 8f70b4
	 get->Suspend();
Packit 8f70b4
	 if(!put->Done())
Packit 8f70b4
	    return m;
Packit 8f70b4
	 debug((9,_("copy: put is broken\n")));
Packit 8f70b4
	 if(fail_if_broken)
Packit 8f70b4
	 {
Packit 8f70b4
	    SetError(strerror(EPIPE));
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
	 goto pre_GET_DONE_WAIT;
Packit 8f70b4
      }
Packit 8f70b4
      put->Resume();
Packit 8f70b4
      if(put->GetSeekPos()==FILE_END)   // put position is not known yet.
Packit 8f70b4
      {
Packit 8f70b4
	 get->Suspend();
Packit 8f70b4
	 return m;
Packit 8f70b4
      }
Packit 8f70b4
      get->Resume();
Packit 8f70b4
      if(fail_if_cannot_seek && (get->GetRealPos()<get->range_start
Packit 8f70b4
			      || put->GetRealPos()<put->range_start
Packit 8f70b4
			      || get->GetRealPos()!=put->GetRealPos()))
Packit 8f70b4
      {
Packit 8f70b4
	 SetError(_("seek failed"));
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      if(high_watermark_timeout.Stopped())
Packit 8f70b4
      {
Packit 8f70b4
	 SetError(_("no progress timeout"));
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      if(get->GetSize()>0 && get->GetRealPos()>get->GetSize())
Packit 8f70b4
      {
Packit 8f70b4
	 get->SetSize(NO_SIZE_YET);
Packit 8f70b4
	 get->SetDate(NO_DATE_YET);
Packit 8f70b4
      }
Packit 8f70b4
      long lbsize=0;
Packit 8f70b4
      if(line_buffer)
Packit 8f70b4
	 lbsize=line_buffer->Size();
Packit 8f70b4
      /* check if positions are correct */
Packit 8f70b4
      off_t get_pos=get->GetRealPos()-get->range_start;
Packit 8f70b4
      off_t put_pos=put->GetRealPos()-put->range_start;
Packit 8f70b4
      if(get_pos-lbsize!=put_pos)
Packit 8f70b4
      {
Packit 8f70b4
	 if(line_buffer)
Packit 8f70b4
	    line_buffer->Empty();
Packit 8f70b4
	 if(get_pos==put_pos)
Packit 8f70b4
	 {  // rare case.
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
	 if(put_pos
Packit 8f70b4
	 {
Packit 8f70b4
	    if(!get->CanSeek(put->GetRealPos()))
Packit 8f70b4
	    {
Packit 8f70b4
	       // we lose... How about a large buffer?
Packit 8f70b4
	       SetError(_("cannot seek on data source"));
Packit 8f70b4
	       return MOVED;
Packit 8f70b4
	    }
Packit 8f70b4
	    debug((9,_("copy: put rolled back to %lld, seeking get accordingly\n"),
Packit 8f70b4
		     (long long)put->GetRealPos()));
Packit 8f70b4
	    debug((10,"copy: get position was %lld\n",
Packit 8f70b4
		     (long long)get->GetRealPos()));
Packit 8f70b4
	    get->Seek(put->GetRealPos());
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
	 else // put_pos > get_pos
Packit 8f70b4
	 {
Packit 8f70b4
	    off_t size=get->GetSize();
Packit 8f70b4
	    if(size>=0 && put->GetRealPos()>=size)
Packit 8f70b4
	    {
Packit 8f70b4
	       // simulate eof, as we have already have the whole file.
Packit 8f70b4
	       debug((9,_("copy: all data received, but get rolled back\n")));
Packit 8f70b4
	       goto eof;
Packit 8f70b4
	    }
Packit 8f70b4
	    off_t skip=put->GetRealPos()-get->GetRealPos();
Packit 8f70b4
	    if(!put->CanSeek(get->GetRealPos()) || skip
Packit 8f70b4
	    {
Packit 8f70b4
	       // we have to skip some data
Packit 8f70b4
	       get->Get(&b,&s);
Packit 8f70b4
	       if(skip>s)
Packit 8f70b4
		  skip=s;
Packit 8f70b4
	       if(skip==0)
Packit 8f70b4
		  return m;
Packit 8f70b4
	       get->Skip(skip);
Packit 8f70b4
	       bytes_count+=skip;
Packit 8f70b4
	       return MOVED;
Packit 8f70b4
	    }
Packit 8f70b4
	    debug((9,_("copy: get rolled back to %lld, seeking put accordingly\n"),
Packit 8f70b4
		     (long long)get->GetRealPos()));
Packit 8f70b4
	    put->Seek(get->GetRealPos());
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
      if(put->IsFull())
Packit 8f70b4
	 get->Suspend(); // stall the get.
Packit 8f70b4
      get->Get(&b,&s);
Packit 8f70b4
      if(b==0) // eof
Packit 8f70b4
      {
Packit 8f70b4
	 debug((10,"copy: get hit eof\n"));
Packit 8f70b4
	 goto eof;
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      rate_add=put_buf;
Packit 8f70b4
Packit 8f70b4
      if(s==0)
Packit 8f70b4
      {
Packit 8f70b4
	 put_buf=put->Buffered();
Packit 8f70b4
	 rate_add-=put_buf;
Packit 8f70b4
	 RateAdd(rate_add);
Packit 8f70b4
Packit 8f70b4
	 if(put->Size()==0)
Packit 8f70b4
	    put->Suspend();
Packit 8f70b4
	 return m;
Packit 8f70b4
      }
Packit 8f70b4
      m=MOVED;
Packit 8f70b4
Packit 8f70b4
      if(get->range_limit!=FILE_END && get->range_limit<get->GetRealPos()+s)
Packit 8f70b4
      {
Packit 8f70b4
	 s=get->range_limit-get->GetRealPos();
Packit 8f70b4
	 if(s<0)
Packit 8f70b4
	    s=0;
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      if(line_buffer)
Packit 8f70b4
      {
Packit 8f70b4
	 const char *lb;
Packit 8f70b4
	 int ls;
Packit 8f70b4
	 if(line_buffer->Size()>line_buffer_max)
Packit 8f70b4
	 {
Packit 8f70b4
	    line_buffer->Get(&lb,&ls);
Packit 8f70b4
	    put->Put(lb,ls);
Packit 8f70b4
	    line_buffer->Skip(ls);
Packit 8f70b4
	 }
Packit 8f70b4
	 line_buffer->Put(b,s);
Packit 8f70b4
	 get->Skip(s);
Packit 8f70b4
	 bytes_count+=s;
Packit 8f70b4
Packit 8f70b4
	 // now find eol in line_buffer.
Packit 8f70b4
	 line_buffer->Get(&lb,&ls);
Packit 8f70b4
	 const char *eol=0;
Packit 8f70b4
	 if(get->Eof() || get->Error())
Packit 8f70b4
	    eol=lb+ls-1;
Packit 8f70b4
	 else
Packit 8f70b4
	    eol=memrchr(lb,'\n',ls);
Packit 8f70b4
	 if(eol)
Packit 8f70b4
	 {
Packit 8f70b4
	    put->Put(lb,eol-lb+1);
Packit 8f70b4
	    line_buffer->Skip(eol-lb+1);
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
      else
Packit 8f70b4
      {
Packit 8f70b4
	 put->Put(b,s);
Packit 8f70b4
	 get->Skip(s);
Packit 8f70b4
	 bytes_count+=s;
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      put_buf=put->Buffered();
Packit 8f70b4
      rate_add-=put_buf-s;
Packit 8f70b4
      RateAdd(rate_add);
Packit 8f70b4
Packit 8f70b4
      if(high_watermark
Packit 8f70b4
      {
Packit 8f70b4
	 high_watermark=put_pos+s;
Packit 8f70b4
	 high_watermark_timeout.Reset();
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      if(get->range_limit!=FILE_END && get->range_limit<=get->GetRealPos())
Packit 8f70b4
      {
Packit 8f70b4
	 debug((10,"copy: get reached range limit\n"));
Packit 8f70b4
	 goto eof;
Packit 8f70b4
      }
Packit 8f70b4
      return m;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   eof:
Packit 8f70b4
      if(line_buffer)
Packit 8f70b4
      {
Packit 8f70b4
	 line_buffer->Get(&b,&s);
Packit 8f70b4
	 put->Put(b,s);
Packit 8f70b4
	 line_buffer->Skip(s);
Packit 8f70b4
      }
Packit 8f70b4
      if(!CheckFileSizeAtEOF())
Packit 8f70b4
      {
Packit 8f70b4
	 SetError(_("file size decreased during transfer"));
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
   pre_CONFIRM_WAIT:
Packit 8f70b4
      put->SetSuggestedFileName(get->GetSuggestedFileName());
Packit 8f70b4
      put->SetDate(get->GetDate());
Packit 8f70b4
      if(get->GetSize()!=NO_SIZE && get->GetSize()!=NO_SIZE_YET)
Packit 8f70b4
	 put->SetEntitySize(get->GetSize());
Packit 8f70b4
      put->PutEOF();
Packit 8f70b4
      get->Suspend();
Packit 8f70b4
      put->Resume();
Packit 8f70b4
      put_eof_pos=put->GetRealPos();
Packit 8f70b4
      debug((10,"copy: waiting for put confirmation\n"));
Packit 8f70b4
      set_state(CONFIRM_WAIT);
Packit 8f70b4
      m=MOVED;
Packit 8f70b4
   case(CONFIRM_WAIT):
Packit 8f70b4
      if(put->Error())
Packit 8f70b4
	 goto put_error;
Packit 8f70b4
      /* check if put position is correct */
Packit 8f70b4
      if(put_eof_pos!=put->GetRealPos() || put->GetSeekPos()==FILE_END)
Packit 8f70b4
      {
Packit 8f70b4
	 set_state(DO_COPY);
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
Packit 8f70b4
      rate_add=put_buf;
Packit 8f70b4
      put_buf=put->Buffered();
Packit 8f70b4
      rate_add-=put_buf;
Packit 8f70b4
      RateAdd(rate_add);
Packit 8f70b4
Packit 8f70b4
      if(!put->Done())
Packit 8f70b4
	 return m;
Packit 8f70b4
      debug((10,"copy: put confirmed store\n"));
Packit 8f70b4
Packit 8f70b4
   pre_GET_DONE_WAIT:
Packit 8f70b4
      get->Empty();
Packit 8f70b4
      get->PutEOF();
Packit 8f70b4
      get->Resume();
Packit 8f70b4
      set_state(GET_DONE_WAIT);
Packit 8f70b4
      m=MOVED;
Packit 8f70b4
      end_time=now;
Packit 8f70b4
      put->Suspend();
Packit 8f70b4
      /* fallthrough */
Packit 8f70b4
   case(GET_DONE_WAIT):
Packit 8f70b4
      if(get->Error())
Packit 8f70b4
	 goto get_error;
Packit 8f70b4
      if(remove_source_later)
Packit 8f70b4
      {
Packit 8f70b4
	 get->RemoveFile();
Packit 8f70b4
	 remove_source_later=false;
Packit 8f70b4
      }
Packit 8f70b4
      if(!get->Done())
Packit 8f70b4
	 return m;
Packit 8f70b4
      debug((10,"copy: get is finished - all done\n"));
Packit 8f70b4
      set_state(ALL_DONE);
Packit 8f70b4
      get->Suspend();
Packit 8f70b4
      LogTransfer();
Packit 8f70b4
      return MOVED;
Packit 8f70b4
Packit 8f70b4
   pre_GET_INFO_WAIT:
Packit 8f70b4
      set_state(GET_INFO_WAIT);
Packit 8f70b4
      m=MOVED;
Packit 8f70b4
   case(GET_INFO_WAIT):
Packit 8f70b4
      if(get->Error())
Packit 8f70b4
	 goto get_error;
Packit 8f70b4
      if(get->GetSize()==NO_SIZE_YET || get->GetDate()==NO_DATE_YET)
Packit 8f70b4
	 return m;
Packit 8f70b4
      goto pre_INITIAL;
Packit 8f70b4
Packit 8f70b4
   case(ALL_DONE):
Packit 8f70b4
      return m;
Packit 8f70b4
   }
Packit 8f70b4
   return m;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
FileCopy::FileCopy(FileCopyPeer *s,FileCopyPeer *d,bool c)
Packit 8f70b4
   : get(s), put(d), cont(c),
Packit 8f70b4
   rate("xfer:rate-period"),
Packit 8f70b4
   rate_for_eta("xfer:eta-period"),
Packit 8f70b4
   high_watermark_timeout("xfer:timeout",0)
Packit 8f70b4
{
Packit 8f70b4
   set_state(INITIAL);
Packit 8f70b4
   int max_buf=buffer_size.Query(0);
Packit 8f70b4
   if(max_buf<1)
Packit 8f70b4
      max_buf=1;
Packit 8f70b4
   s->SetMaxBuffered(max_buf);
Packit 8f70b4
   d->SetMaxBuffered(max_buf);
Packit 8f70b4
   put_buf=0;
Packit 8f70b4
   put_eof_pos=0;
Packit 8f70b4
   high_watermark=0;
Packit 8f70b4
   bytes_count=0;
Packit 8f70b4
   fail_if_cannot_seek=false;
Packit 8f70b4
   fail_if_broken=true;
Packit 8f70b4
   remove_source_later=false;
Packit 8f70b4
   remove_target_first=false;
Packit 8f70b4
   line_buffer_max=0;
Packit 8f70b4
}
Packit 8f70b4
FileCopy::~FileCopy()
Packit 8f70b4
{
Packit 8f70b4
}
Packit 8f70b4
FileCopy *FileCopy::New(FileCopyPeer *s,FileCopyPeer *d,bool c)
Packit 8f70b4
{
Packit 8f70b4
   FileCopy *res=0;
Packit 8f70b4
   if(fxp_create)
Packit 8f70b4
      res=fxp_create(s,d,c);
Packit 8f70b4
   if(res)
Packit 8f70b4
      return res;
Packit 8f70b4
   return new FileCopy(s,d,c);
Packit 8f70b4
}
Packit 8f70b4
void FileCopy::SuspendInternal()
Packit 8f70b4
{
Packit 8f70b4
   super::SuspendInternal();
Packit 8f70b4
   if(get) get->SuspendSlave();
Packit 8f70b4
   if(put) put->SuspendSlave();
Packit 8f70b4
}
Packit 8f70b4
void FileCopy::ResumeInternal()
Packit 8f70b4
{
Packit 8f70b4
   if(get) get->ResumeSlave();
Packit 8f70b4
   if(put) put->ResumeSlave();
Packit 8f70b4
   super::ResumeInternal();
Packit 8f70b4
}
Packit 8f70b4
void FileCopy::Fg()
Packit 8f70b4
{
Packit 8f70b4
   if(get) get->Fg();
Packit 8f70b4
   if(put) put->Fg();
Packit 8f70b4
}
Packit 8f70b4
void FileCopy::Bg()
Packit 8f70b4
{
Packit 8f70b4
   if(get) get->Bg();
Packit 8f70b4
   if(put) put->Bg();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FileCopy::SetError(const char *str)
Packit 8f70b4
{
Packit 8f70b4
   error_text.set(str);
Packit 8f70b4
   get=0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FileCopy::LineBuffered(int s)
Packit 8f70b4
{
Packit 8f70b4
   if(!line_buffer)
Packit 8f70b4
      line_buffer=new Buffer();
Packit 8f70b4
   line_buffer_max=s;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
off_t FileCopy::GetPos() const
Packit 8f70b4
{
Packit 8f70b4
   if(put) {
Packit 8f70b4
      off_t pos = put->GetRealPos() - put->Buffered();
Packit 8f70b4
      // sometimes Buffered overestimates the amount of buffered data
Packit 8f70b4
      if(pos<0)
Packit 8f70b4
	 pos=0;
Packit 8f70b4
      return pos;
Packit 8f70b4
   }
Packit 8f70b4
   if(get)
Packit 8f70b4
      return get->GetRealPos();
Packit 8f70b4
   return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
off_t FileCopy::GetSize() const
Packit 8f70b4
{
Packit 8f70b4
   if(get)
Packit 8f70b4
      return get->GetSize();
Packit 8f70b4
   return NO_SIZE;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int FileCopy::GetPercentDone() const
Packit 8f70b4
{
Packit 8f70b4
   if(!get || !put)
Packit 8f70b4
      return 100;
Packit 8f70b4
   off_t size=get->GetSize();
Packit 8f70b4
   if(size==NO_SIZE || size==NO_SIZE_YET)
Packit 8f70b4
      return -1;
Packit 8f70b4
   if(size==0)
Packit 8f70b4
      return 0;
Packit 8f70b4
   off_t ppos=put->GetRealPos() - put->Buffered() - put->range_start;
Packit 8f70b4
   if(ppos<0)
Packit 8f70b4
      return 0;
Packit 8f70b4
   off_t psize=size-put->range_start;
Packit 8f70b4
   if(put->range_limit!=FILE_END)
Packit 8f70b4
      psize=put->range_limit-put->range_start;
Packit 8f70b4
   if(psize<0)
Packit 8f70b4
      return 100;
Packit 8f70b4
   if(ppos>psize)
Packit 8f70b4
      return -1;
Packit 8f70b4
   return percent(ppos,psize);
Packit 8f70b4
}
Packit 8f70b4
const char *FileCopy::GetPercentDoneStr() const
Packit 8f70b4
{
Packit 8f70b4
   int pct=GetPercentDone();
Packit 8f70b4
   if(pct==-1)
Packit 8f70b4
      return "";
Packit 8f70b4
   static char buf[8];
Packit 8f70b4
   snprintf(buf,8,"(%d%%) ",pct);
Packit 8f70b4
   return buf;
Packit 8f70b4
}
Packit 8f70b4
void FileCopy::RateAdd(int a)
Packit 8f70b4
{
Packit 8f70b4
   rate.Add(a);
Packit 8f70b4
   rate_for_eta.Add(a);
Packit 8f70b4
}
Packit 8f70b4
void FileCopy::RateReset()
Packit 8f70b4
{
Packit 8f70b4
   start_time=now;
Packit 8f70b4
   rate.Reset();
Packit 8f70b4
   rate_for_eta.Reset();
Packit 8f70b4
}
Packit 8f70b4
float FileCopy::GetRate()
Packit 8f70b4
{
Packit 8f70b4
   if(!rate.Valid() || !put)
Packit 8f70b4
      return 0;
Packit 8f70b4
   return rate.Get();
Packit 8f70b4
}
Packit 8f70b4
const char *FileCopy::GetRateStr()
Packit 8f70b4
{
Packit 8f70b4
   if(!rate.Valid() || !put)
Packit 8f70b4
      return "";
Packit 8f70b4
   return rate.GetStrS();
Packit 8f70b4
}
Packit 8f70b4
off_t FileCopy::GetBytesRemaining()
Packit 8f70b4
{
Packit 8f70b4
   if(!get)
Packit 8f70b4
      return 0;
Packit 8f70b4
   if(get->range_limit==FILE_END)
Packit 8f70b4
   {
Packit 8f70b4
      off_t size=get->GetSize();
Packit 8f70b4
      if(size<=0 || size<get->GetRealPos() || !rate_for_eta.Valid())
Packit 8f70b4
	 return -1;
Packit 8f70b4
      return(size-GetPos());
Packit 8f70b4
   }
Packit 8f70b4
   return get->range_limit-GetPos();
Packit 8f70b4
}
Packit 8f70b4
const char *FileCopy::GetETAStr()
Packit 8f70b4
{
Packit 8f70b4
   off_t b=GetBytesRemaining();
Packit 8f70b4
   if(b<0 || !put)
Packit 8f70b4
      return "";
Packit 8f70b4
   return rate_for_eta.GetETAStrSFromSize(b);
Packit 8f70b4
}
Packit 8f70b4
long FileCopy::GetETA(off_t b)
Packit 8f70b4
{
Packit 8f70b4
   if(b<0 || !rate_for_eta.Valid())
Packit 8f70b4
      return -1;
Packit 8f70b4
   return (long)(double(b) / rate_for_eta.Get() + 0.5);
Packit 8f70b4
}
Packit 8f70b4
const char *FileCopy::GetStatus()
Packit 8f70b4
{
Packit 8f70b4
   static xstring buf;
Packit 8f70b4
   const char *get_st=get?get->GetStatus():0;
Packit 8f70b4
   const char *put_st=put?put->GetStatus():0;
Packit 8f70b4
   if(get_st && get_st[0] && put_st && put_st[0])
Packit 8f70b4
      buf.vset("[",get_st,"->",put_st,"]",NULL);
Packit 8f70b4
   else if(get_st && get_st[0])
Packit 8f70b4
      buf.vset("[",get_st,"]",NULL);
Packit 8f70b4
   else if(put_st && put_st[0])
Packit 8f70b4
      buf.vset("[",put_st,"]",NULL);
Packit 8f70b4
   else
Packit 8f70b4
      return "";
Packit 8f70b4
   return buf;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
double FileCopy::GetTimeSpent()
Packit 8f70b4
{
Packit 8f70b4
   if(end_time
Packit 8f70b4
      return 0;
Packit 8f70b4
   return TimeDiff(end_time,start_time);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
FgData *FileCopy::GetFgData(bool fg)
Packit 8f70b4
{
Packit 8f70b4
   // NOTE: only one of get/put can have FgData in this implementation.
Packit 8f70b4
   FgData *f=0;
Packit 8f70b4
   if(get) f=get->GetFgData(fg);
Packit 8f70b4
   if(f) return f;
Packit 8f70b4
   if(put) f=put->GetFgData(fg);
Packit 8f70b4
   return f;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
pid_t FileCopy::GetProcGroup()
Packit 8f70b4
{
Packit 8f70b4
   pid_t p=0;
Packit 8f70b4
   if(get) p=get->GetProcGroup();
Packit 8f70b4
   if(p) return p;
Packit 8f70b4
   if(put) p=put->GetProcGroup();
Packit 8f70b4
   return p;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FileCopy::Kill(int sig)
Packit 8f70b4
{
Packit 8f70b4
   if(get) get->Kill(sig);
Packit 8f70b4
   if(put) put->Kill(sig);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
Ref<Log> FileCopy::transfer_log;
Packit 8f70b4
Packit 8f70b4
void FileCopy::LogTransfer()
Packit 8f70b4
{
Packit 8f70b4
   const char *log_ctx="xfer";
Packit 8f70b4
   if(!ResMgr::QueryBool("log:enabled",log_ctx))
Packit 8f70b4
      return;
Packit 8f70b4
   const char *src=get->GetURL();
Packit 8f70b4
   if(!src)
Packit 8f70b4
      return;
Packit 8f70b4
   src=alloca_strdup(src);
Packit 8f70b4
   const char *dst=put->GetURL();
Packit 8f70b4
   if(!dst)
Packit 8f70b4
      return;
Packit 8f70b4
   dst=alloca_strdup(dst);
Packit 8f70b4
   if(!transfer_log)
Packit 8f70b4
      transfer_log=new Log(log_ctx);
Packit 8f70b4
   long long range_limit=GetRangeLimit();
Packit 8f70b4
   if(range_limit==FILE_END)
Packit 8f70b4
      range_limit=get->GetPos();
Packit 8f70b4
   transfer_log->Format(0,"%s -> %s %lld-%lld %s\n",
Packit 8f70b4
      url::remove_password(src),url::remove_password(dst),
Packit 8f70b4
      (long long)GetRangeStart(),range_limit,
Packit 8f70b4
      Speedometer::GetStrProper(GetBytesCount()/GetTimeSpent()).get());
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FileCopy::SetRange(off_t s,off_t lim)
Packit 8f70b4
{
Packit 8f70b4
   get->SetRange(s,lim);
Packit 8f70b4
   put->SetRange(s,lim);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
bool FileCopy::CheckFileSizeAtEOF() const
Packit 8f70b4
{
Packit 8f70b4
   long long range_limit=GetRangeLimit();
Packit 8f70b4
   if(range_limit==FILE_END)
Packit 8f70b4
   {
Packit 8f70b4
      const long long size=GetSize();
Packit 8f70b4
      if(size==NO_SIZE || size==NO_SIZE_YET)
Packit 8f70b4
	 return true;   // nothing to compare with.
Packit 8f70b4
      range_limit=size;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   const long long get_pos=get->GetRealPos();
Packit 8f70b4
   const long long put_pos=put->GetRealPos();
Packit 8f70b4
   const long long pos=(get_pos>put_pos ? get_pos : put_pos);
Packit 8f70b4
   if(pos<=0 || pos>=range_limit)
Packit 8f70b4
      return true;
Packit 8f70b4
Packit 8f70b4
   debug((0,"expected pos=%lld, actual pos=%lld\n",range_limit,pos));
Packit 8f70b4
   return false;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
// FileCopyPeer implementation
Packit 8f70b4
#undef super
Packit 8f70b4
#define super Buffer
Packit 8f70b4
off_t FileCopyPeer::GetSize()
Packit 8f70b4
{
Packit 8f70b4
   if(size>=0 && pos>size)
Packit 8f70b4
      WantSize();
Packit 8f70b4
   return size;
Packit 8f70b4
}
Packit 8f70b4
void FileCopyPeer::SetSize(off_t s)
Packit 8f70b4
{
Packit 8f70b4
   size=s;
Packit 8f70b4
   if(seek_pos==FILE_END)
Packit 8f70b4
   {
Packit 8f70b4
      if(size!=NO_SIZE && size!=NO_SIZE_YET)
Packit 8f70b4
	 seek_pos=size;
Packit 8f70b4
      else
Packit 8f70b4
	 seek_pos=0;
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
void FileCopyPeer::SetDate(time_t d,int p)
Packit 8f70b4
{
Packit 8f70b4
   date.set(d,p);
Packit 8f70b4
   if(d==NO_DATE || d==NO_DATE_YET)
Packit 8f70b4
      date_set=true;
Packit 8f70b4
   else
Packit 8f70b4
      date_set=false;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FileCopyPeer::SetRange(const off_t s,const off_t lim)
Packit 8f70b4
{
Packit 8f70b4
   range_start=s;
Packit 8f70b4
   range_limit=lim;
Packit 8f70b4
   if(mode==PUT || range_start>GetPos()+0x4000)
Packit 8f70b4
      Seek(range_start);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
bool FileCopyPeer::Done()
Packit 8f70b4
{
Packit 8f70b4
   if(Error())
Packit 8f70b4
      return true;
Packit 8f70b4
   if(eof && Size()==0)
Packit 8f70b4
   {
Packit 8f70b4
      if(removing)
Packit 8f70b4
	 return false;
Packit 8f70b4
      if(mode==PUT)
Packit 8f70b4
	 return done;
Packit 8f70b4
      return true;
Packit 8f70b4
   }
Packit 8f70b4
   if(broken)
Packit 8f70b4
      return true;
Packit 8f70b4
   return false;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FileCopyPeer::Seek(off_t offs)
Packit 8f70b4
{
Packit 8f70b4
   seek_pos=offs;
Packit 8f70b4
   if(mode==PUT)
Packit 8f70b4
      pos-=Size();
Packit 8f70b4
   Empty();
Packit 8f70b4
   eof=false;
Packit 8f70b4
   broken=false;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const char *FileCopy::TempFileName(const char *file)
Packit 8f70b4
{
Packit 8f70b4
   if(!ResMgr::QueryBool("xfer:use-temp-file",0))
Packit 8f70b4
      return file;
Packit 8f70b4
Packit 8f70b4
   xstring &temp=xstring::get_tmp(ResMgr::Query("xfer:temp-file-name",0));
Packit 8f70b4
   if(temp.length()==0 || temp.eq("*"))
Packit 8f70b4
      return file;
Packit 8f70b4
Packit 8f70b4
   const char *name=basename_ptr(file);
Packit 8f70b4
   int subst_pos=temp.instr('*');
Packit 8f70b4
   if(subst_pos>=0)
Packit 8f70b4
      temp.set_substr(subst_pos,1,name);
Packit 8f70b4
   else {
Packit 8f70b4
      if(temp.last_char()=='.')
Packit 8f70b4
	 temp.append(name);
Packit 8f70b4
      else if(temp[0]=='.')
Packit 8f70b4
	 temp.set_substr(0,0,name);
Packit 8f70b4
      else
Packit 8f70b4
	 temp.append('.').append(name);
Packit 8f70b4
   }
Packit 8f70b4
   return dir_file(dirname(file),temp);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const char *FileCopyPeer::UseTempFile(const char *file)
Packit 8f70b4
{
Packit 8f70b4
   const char *temp=FileCopy::TempFileName(file);
Packit 8f70b4
   if(temp==file)
Packit 8f70b4
      return file;
Packit 8f70b4
Packit 8f70b4
   auto_rename=true;
Packit 8f70b4
   temp_file=true;
Packit 8f70b4
   SetSuggestedFileName(basename_ptr(file));
Packit 8f70b4
Packit 8f70b4
   return temp;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
FileCopyPeer::FileCopyPeer(dir_t m) : IOBuffer(m)
Packit 8f70b4
{
Packit 8f70b4
   want_size=false;
Packit 8f70b4
   want_date=false;
Packit 8f70b4
   start_transfer=true;
Packit 8f70b4
   size=NO_SIZE_YET;
Packit 8f70b4
   date=NO_DATE_YET;
Packit 8f70b4
   e_size=NO_SIZE;
Packit 8f70b4
   seek_pos=0;
Packit 8f70b4
   can_seek=false;
Packit 8f70b4
   can_seek0=false;
Packit 8f70b4
   date_set=false;
Packit 8f70b4
   do_set_date=true;
Packit 8f70b4
   do_verify=true;
Packit 8f70b4
   ascii=false;
Packit 8f70b4
   range_start=0;
Packit 8f70b4
   range_limit=FILE_END;
Packit 8f70b4
   removing=false;
Packit 8f70b4
   file_removed=false;
Packit 8f70b4
   temp_file=false;
Packit 8f70b4
   do_mkdir=false;
Packit 8f70b4
   use_cache=true;
Packit 8f70b4
   write_allowed=true;
Packit 8f70b4
   done=false;
Packit 8f70b4
   auto_rename=false;
Packit 8f70b4
   Suspend();  // don't do anything too early
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
// FileCopyPeerFA implementation
Packit 8f70b4
#undef super
Packit 8f70b4
#define super FileCopyPeer
Packit 8f70b4
int FileCopyPeerFA::Do()
Packit 8f70b4
{
Packit 8f70b4
   int m=STALL;
Packit 8f70b4
   int res;
Packit 8f70b4
Packit 8f70b4
   if(session->OpenMode()==FA::MAKE_DIR)
Packit 8f70b4
   {
Packit 8f70b4
      // doing mkdir
Packit 8f70b4
      int res=session->Done();
Packit 8f70b4
      if(res==FA::IN_PROGRESS)
Packit 8f70b4
	 return m;
Packit 8f70b4
      if(res<0)
Packit 8f70b4
	 debug((3,"mkdir failed: %s\n",session->StrError(res)));
Packit 8f70b4
      session->Close();
Packit 8f70b4
      m=MOVED;
Packit 8f70b4
   }
Packit 8f70b4
   else if(session->OpenMode()==FA::RENAME)
Packit 8f70b4
   {
Packit 8f70b4
      int res=session->Done();
Packit 8f70b4
      if(res==FA::IN_PROGRESS)
Packit 8f70b4
	 return m;
Packit 8f70b4
      if(res<0) {
Packit 8f70b4
	 if(temp_file)
Packit 8f70b4
	    SetError(session->StrError(res));
Packit 8f70b4
	 else
Packit 8f70b4
	    debug((3,"rename failed: %s\n",session->StrError(res)));
Packit 8f70b4
      }
Packit 8f70b4
      session->Close();
Packit 8f70b4
      done=true;
Packit 8f70b4
      return MOVED;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(do_mkdir) {
Packit 8f70b4
      // do mkdir just once
Packit 8f70b4
      do_mkdir=false;
Packit 8f70b4
      assert(!session->IsOpen());
Packit 8f70b4
      const xstring& dir=dirname(file);
Packit 8f70b4
      if(dir.length()>0 && dir.ne("/") && dir.ne(".") && dir.ne("..")) {
Packit 8f70b4
	 // FIXME: .././.. should be also excluded
Packit 8f70b4
	 session->Mkdir(dirname(file),true);
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(removing)
Packit 8f70b4
   {
Packit 8f70b4
      res=session->Done();
Packit 8f70b4
      if(res<=0)
Packit 8f70b4
      {
Packit 8f70b4
	 removing=false;
Packit 8f70b4
	 file_removed=true;
Packit 8f70b4
	 session->Close();
Packit 8f70b4
	 Suspend();
Packit 8f70b4
	 m=MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      return m;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(Done() || Error())
Packit 8f70b4
      return m;
Packit 8f70b4
Packit 8f70b4
   if(verify)
Packit 8f70b4
   {
Packit 8f70b4
      if(verify->Error())
Packit 8f70b4
      {
Packit 8f70b4
	 SetError(verify->ErrorText());
Packit 8f70b4
	 m=MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      else if(verify->Done())
Packit 8f70b4
      {
Packit 8f70b4
	 if(suggested_filename && auto_rename)
Packit 8f70b4
	 {
Packit 8f70b4
	    const char *new_name=dir_file(dirname(file),suggested_filename);
Packit 8f70b4
	    bool clobber=temp_file;
Packit 8f70b4
	    session->Rename(file,new_name,clobber);
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
	 done=true;
Packit 8f70b4
	 m=MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      return m;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   // if we need some info and cannot start the transfer (yet),
Packit 8f70b4
   // then use ARRAY_INFO to fetch the file information.
Packit 8f70b4
   if(((want_size && size==NO_SIZE_YET) || (want_date && date==NO_DATE_YET))
Packit 8f70b4
   && (mode==PUT || !start_transfer) && session->IsClosed())
Packit 8f70b4
   {
Packit 8f70b4
      FileInfo *fi=new FileInfo(file);
Packit 8f70b4
      if(want_size)
Packit 8f70b4
	 fi->Need(fi->SIZE);
Packit 8f70b4
      if(want_date)
Packit 8f70b4
	 fi->Need(fi->DATE);
Packit 8f70b4
      info.Empty();
Packit 8f70b4
      info.Add(fi);
Packit 8f70b4
      session->GetInfoArray(&info;;
Packit 8f70b4
      m=MOVED;
Packit 8f70b4
   }
Packit 8f70b4
   if(session->OpenMode()==FA::ARRAY_INFO)
Packit 8f70b4
   {
Packit 8f70b4
      res=session->Done();
Packit 8f70b4
      if(res==FA::IN_PROGRESS)
Packit 8f70b4
	 return m;
Packit 8f70b4
      if(res<0)
Packit 8f70b4
      {
Packit 8f70b4
	 session->Close();
Packit 8f70b4
	 SetSize(NO_SIZE);
Packit 8f70b4
	 SetDate(NO_DATE);
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      FileInfo *fi=info[0];
Packit 8f70b4
      if(want_size)
Packit 8f70b4
	 SetSize(fi->size);
Packit 8f70b4
      if(want_date)
Packit 8f70b4
	 SetDate(fi->date);
Packit 8f70b4
      session->Close();
Packit 8f70b4
      return MOVED;
Packit 8f70b4
   }
Packit 8f70b4
   switch(mode)
Packit 8f70b4
   {
Packit 8f70b4
   case PUT:
Packit 8f70b4
      if(fxp)
Packit 8f70b4
      {
Packit 8f70b4
	 if(eof)
Packit 8f70b4
	    goto fxp_eof;
Packit 8f70b4
	 return m;
Packit 8f70b4
      }
Packit 8f70b4
      res=Put_LL(buffer+buffer_ptr,Size());
Packit 8f70b4
      if(res>0)
Packit 8f70b4
      {
Packit 8f70b4
	 buffer_ptr+=res;
Packit 8f70b4
	 m=MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      else if(res<0)
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      if(Size()==0)
Packit 8f70b4
      {
Packit 8f70b4
	 if(eof)
Packit 8f70b4
	 {
Packit 8f70b4
	    if(date!=NO_DATE && date!=NO_DATE_YET)
Packit 8f70b4
	       session->SetDate(date);
Packit 8f70b4
	    if(e_size!=NO_SIZE && e_size!=NO_SIZE_YET)
Packit 8f70b4
	       session->SetSize(e_size);
Packit 8f70b4
	    res=session->StoreStatus();
Packit 8f70b4
	    if(res==FA::OK)
Packit 8f70b4
	    {
Packit 8f70b4
	       session->Close();
Packit 8f70b4
	    fxp_eof:
Packit 8f70b4
	       // FIXME: set date for real.
Packit 8f70b4
	       date_set=true;
Packit 8f70b4
	       if(!verify && do_verify)
Packit 8f70b4
		  verify=new FileVerificator(session,file);
Packit 8f70b4
	       else
Packit 8f70b4
		  done=true;
Packit 8f70b4
	       return MOVED;
Packit 8f70b4
	    }
Packit 8f70b4
	    else if(res==FA::IN_PROGRESS)
Packit 8f70b4
	       return m;
Packit 8f70b4
	    else
Packit 8f70b4
	    {
Packit 8f70b4
	       if(res==FA::DO_AGAIN)
Packit 8f70b4
		  return m;
Packit 8f70b4
	       if(res==FA::STORE_FAILED)
Packit 8f70b4
	       {
Packit 8f70b4
		  upload_state.Save(session);
Packit 8f70b4
		  session->Close();
Packit 8f70b4
		  if(can_seek && seek_pos>0)
Packit 8f70b4
		     Seek(FILE_END);
Packit 8f70b4
		  else
Packit 8f70b4
		     Seek(0);
Packit 8f70b4
		  return MOVED;
Packit 8f70b4
	       }
Packit 8f70b4
	       SetError(session->StrError(res));
Packit 8f70b4
	       return MOVED;
Packit 8f70b4
	    }
Packit 8f70b4
	    return m;
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
      break;
Packit 8f70b4
Packit 8f70b4
   case GET:
Packit 8f70b4
      if(eof)
Packit 8f70b4
	 return m;
Packit 8f70b4
      if(fxp)
Packit 8f70b4
	 return m;
Packit 8f70b4
      res=TuneGetSize(Get_LL(get_size));
Packit 8f70b4
      if(res>0)
Packit 8f70b4
      {
Packit 8f70b4
	 EmbraceNewData(res);
Packit 8f70b4
	 SaveMaxCheck(0);
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      if(res<0)
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      if(eof)
Packit 8f70b4
      {
Packit 8f70b4
	 session->Close();
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      break;
Packit 8f70b4
   }
Packit 8f70b4
   return m;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
bool FileCopyPeerFA::IOReady()
Packit 8f70b4
{
Packit 8f70b4
   if(seek_pos==0)
Packit 8f70b4
      return true;
Packit 8f70b4
   if(seek_pos==FILE_END && size==NO_SIZE_YET)
Packit 8f70b4
      return false;
Packit 8f70b4
   return session->IOReady();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FileCopyPeerFA::SuspendInternal()
Packit 8f70b4
{
Packit 8f70b4
   if(fxp && mode==PUT)
Packit 8f70b4
      return;
Packit 8f70b4
   if(session->IsOpen())
Packit 8f70b4
      session->SuspendSlave();
Packit 8f70b4
   super::SuspendInternal();
Packit 8f70b4
}
Packit 8f70b4
void FileCopyPeerFA::ResumeInternal()
Packit 8f70b4
{
Packit 8f70b4
   super::ResumeInternal();
Packit 8f70b4
   session->ResumeSlave();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const char *FileCopyPeerFA::GetStatus()
Packit 8f70b4
{
Packit 8f70b4
   if(verify)
Packit 8f70b4
      return verify->Status();
Packit 8f70b4
   if(!session->IsOpen())
Packit 8f70b4
      return 0;
Packit 8f70b4
   return session->CurrentStatus();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FileCopyPeerFA::Seek(off_t new_pos)
Packit 8f70b4
{
Packit 8f70b4
   if(pos==new_pos)
Packit 8f70b4
      return;
Packit 8f70b4
   super::Seek(new_pos);
Packit 8f70b4
   session->Close();
Packit 8f70b4
   if(seek_pos==FILE_END)
Packit 8f70b4
      WantSize();
Packit 8f70b4
   else
Packit 8f70b4
      pos=new_pos;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FileCopyPeerFA::OpenSession()
Packit 8f70b4
{
Packit 8f70b4
   current->Timeout(0);	// mark it MOVED.
Packit 8f70b4
   if(mode==GET)
Packit 8f70b4
   {
Packit 8f70b4
      if(size!=NO_SIZE && size!=NO_SIZE_YET && !ascii
Packit 8f70b4
      && (seek_pos>size || (seek_pos==size && size>0)))
Packit 8f70b4
      {
Packit 8f70b4
      past_eof:
Packit 8f70b4
	 debug((10,"copy src: seek past eof (seek_pos=%lld, size=%lld)\n",
Packit 8f70b4
		  (long long)seek_pos,(long long)size));
Packit 8f70b4
	 pos=seek_pos;
Packit 8f70b4
	 eof=true;
Packit 8f70b4
	 return;
Packit 8f70b4
      }
Packit 8f70b4
      const char *b;
Packit 8f70b4
      int s;
Packit 8f70b4
      int err;
Packit 8f70b4
      if(use_cache && FileAccess::cache->Find(session,file,FAmode,&err,&b,&s))
Packit 8f70b4
      {
Packit 8f70b4
	 if(err)
Packit 8f70b4
	 {
Packit 8f70b4
	    SetError(b);
Packit 8f70b4
	    return;
Packit 8f70b4
	 }
Packit 8f70b4
	 size=s;
Packit 8f70b4
	 if(seek_pos>=s)
Packit 8f70b4
	    goto past_eof;
Packit 8f70b4
	 b+=seek_pos;
Packit 8f70b4
	 s-=seek_pos;
Packit 8f70b4
	 Save(0);
Packit 8f70b4
	 Put(b,s);
Packit 8f70b4
	 pos=seek_pos;
Packit 8f70b4
	 eof=true;
Packit 8f70b4
	 return;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   else // mode==PUT
Packit 8f70b4
   {
Packit 8f70b4
      if(e_size>=0 && size>=0 && seek_pos>=e_size)
Packit 8f70b4
      {
Packit 8f70b4
	 debug((10,"copy dst: seek past eof (seek_pos=%lld, size=%lld)\n",
Packit 8f70b4
		  (long long)seek_pos,(long long)e_size));
Packit 8f70b4
	 eof=true;
Packit 8f70b4
	 if(date==NO_DATE || date==NO_DATE_YET)
Packit 8f70b4
	    return;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   session->Open(file,FAmode,seek_pos);
Packit 8f70b4
   session->SetFileURL(orig_url);
Packit 8f70b4
   session->SetLimit(range_limit);
Packit 8f70b4
   if(mode==PUT) {
Packit 8f70b4
      upload_state.Restore(session);
Packit 8f70b4
      if(e_size!=NO_SIZE && e_size!=NO_SIZE_YET)
Packit 8f70b4
	 session->SetSize(e_size);
Packit 8f70b4
      if(date!=NO_DATE && date!=NO_DATE_YET)
Packit 8f70b4
	 session->SetDate(date);
Packit 8f70b4
   } else {
Packit 8f70b4
      if(size!=NO_SIZE && size!=NO_SIZE_YET)
Packit 8f70b4
	 session->SetSize(size);
Packit 8f70b4
   }
Packit 8f70b4
   session->RereadManual();
Packit 8f70b4
   if(ascii)
Packit 8f70b4
      session->AsciiTransfer();
Packit 8f70b4
   if(want_size && size==NO_SIZE_YET)
Packit 8f70b4
      session->WantSize(&size);
Packit 8f70b4
   if(want_date && (date==NO_DATE_YET || date.ts_prec>0))
Packit 8f70b4
      session->WantDate(&date);
Packit 8f70b4
   if(mode==GET)
Packit 8f70b4
      SaveRollback(seek_pos);
Packit 8f70b4
   else
Packit 8f70b4
      pos=seek_pos+Size();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FileCopyPeerFA::WantSize()
Packit 8f70b4
{
Packit 8f70b4
   struct stat st;
Packit 8f70b4
   if(!strcmp(session->GetProto(),"file")
Packit 8f70b4
   && stat(dir_file(session->GetCwd(),file),&st)!=-1)
Packit 8f70b4
      SetSize(S_ISREG(st.st_mode)?st.st_size:NO_SIZE);
Packit 8f70b4
   else
Packit 8f70b4
      super::WantSize();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FileCopyPeerFA::RemoveFile()
Packit 8f70b4
{
Packit 8f70b4
   session->Open(file,FA::REMOVE);
Packit 8f70b4
   removing=true;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int FileCopyPeerFA::Get_LL(int len)
Packit 8f70b4
{
Packit 8f70b4
   if(get_delay>0)
Packit 8f70b4
   {
Packit 8f70b4
      if(!get_ll_timer.Stopped())
Packit 8f70b4
	 return 0;
Packit 8f70b4
      session->ResumeSlave();
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   int res=0;
Packit 8f70b4
Packit 8f70b4
   if(session->IsClosed())
Packit 8f70b4
      OpenSession();
Packit 8f70b4
Packit 8f70b4
   if(eof)  // OpenSession can set eof=true.
Packit 8f70b4
      return 0;
Packit 8f70b4
Packit 8f70b4
   off_t io_at=pos;
Packit 8f70b4
   if(GetRealPos()!=io_at) // GetRealPos can alter pos.
Packit 8f70b4
      return 0;
Packit 8f70b4
Packit 8f70b4
   res=session->Read(this,len);
Packit 8f70b4
   if(res<0)
Packit 8f70b4
   {
Packit 8f70b4
      if(res==FA::DO_AGAIN)
Packit 8f70b4
	 return 0;
Packit 8f70b4
      if(res==FA::FILE_MOVED)
Packit 8f70b4
      {
Packit 8f70b4
	 // handle redirection.
Packit 8f70b4
	 assert(!fxp);
Packit 8f70b4
	 const char *loc_c=session->GetNewLocation();
Packit 8f70b4
	 int max_redirections=max_redir.Query(0);
Packit 8f70b4
	 if(loc_c && loc_c[0] && max_redirections>0)
Packit 8f70b4
	 {
Packit 8f70b4
	    Log::global->Format(3,_("copy: received redirection to `%s'\n"),loc_c);
Packit 8f70b4
	    if(++redirections>max_redirections)
Packit 8f70b4
	    {
Packit 8f70b4
	       SetError(_("Too many redirections"));
Packit 8f70b4
	       return -1;
Packit 8f70b4
	    }
Packit 8f70b4
	    if(FAmode==FA::QUOTE_CMD)
Packit 8f70b4
	       FAmode=FA::RETRIEVE;
Packit 8f70b4
Packit 8f70b4
	    xstring loc(loc_c);
Packit 8f70b4
	    session->Close(); // loc_c is no longer valid.
Packit 8f70b4
	    loc_c=0;
Packit 8f70b4
Packit 8f70b4
	    ParsedURL u(loc,true);
Packit 8f70b4
Packit 8f70b4
	    if(u.proto)
Packit 8f70b4
	    {
Packit 8f70b4
	       my_session=FileAccess::New(&u);
Packit 8f70b4
	       session=my_session;
Packit 8f70b4
Packit 8f70b4
	       file.set(u.path?u.path.get():"");
Packit 8f70b4
	       orig_url.set(loc);
Packit 8f70b4
	    }
Packit 8f70b4
	    else // !proto
Packit 8f70b4
	    {
Packit 8f70b4
	       if(orig_url)
Packit 8f70b4
	       {
Packit 8f70b4
		  int p_ind=url::path_index(orig_url);
Packit 8f70b4
		  const char *s=strrchr(orig_url,'/');
Packit 8f70b4
		  int s_ind=s?s-orig_url:-1;
Packit 8f70b4
		  if(p_ind==-1 || s_ind==-1 || s_ind
Packit 8f70b4
		     s_ind=p_ind=strlen(orig_url);
Packit 8f70b4
		  if(loc[0]=='/')
Packit 8f70b4
		  {
Packit 8f70b4
		     orig_url.truncate(p_ind);
Packit 8f70b4
		     orig_url.append(loc);
Packit 8f70b4
		  }
Packit 8f70b4
		  else
Packit 8f70b4
		  {
Packit 8f70b4
		     orig_url.truncate(s_ind);
Packit 8f70b4
		     orig_url.append('/');
Packit 8f70b4
		     orig_url.append(loc);
Packit 8f70b4
		  }
Packit 8f70b4
	       }
Packit 8f70b4
Packit 8f70b4
	       loc.url_decode();
Packit 8f70b4
	       const char *slash=strrchr(file,'/');
Packit 8f70b4
	       if(loc[0]!='/' && slash)
Packit 8f70b4
	       {
Packit 8f70b4
		  file.truncate(slash-file);
Packit 8f70b4
		  file.set(dir_file(file,loc));
Packit 8f70b4
	       }
Packit 8f70b4
	       else
Packit 8f70b4
		  file.set(loc);
Packit 8f70b4
	    }
Packit 8f70b4
Packit 8f70b4
	    if(want_size || size!=NO_SIZE)
Packit 8f70b4
	       WantSize();
Packit 8f70b4
	    if(want_date || date!=NO_DATE)
Packit 8f70b4
	       WantDate();
Packit 8f70b4
Packit 8f70b4
	    upload_state.Clear();
Packit 8f70b4
	    current->Timeout(0); // retry with new location.
Packit 8f70b4
	    return 0;
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
      SetError(session->StrError(res));
Packit 8f70b4
      return -1;
Packit 8f70b4
   }
Packit 8f70b4
   else if(res==0)
Packit 8f70b4
   {
Packit 8f70b4
      debug((10,"copy-peer: EOF on %s\n",session->GetFileURL(session->GetFile()).get()));
Packit 8f70b4
      eof=true;
Packit 8f70b4
      FileAccess::cache->Add(session,file,FAmode,FA::OK,this);
Packit 8f70b4
      SetSuggestedFileName(session->GetSuggestedFileName());
Packit 8f70b4
      session->Close();
Packit 8f70b4
   }
Packit 8f70b4
   else if(res<=MAX_READ_TO_DELAY)
Packit 8f70b4
   {
Packit 8f70b4
      if(get_delay<=MAX_DELAY-DELAY_STEP)
Packit 8f70b4
	 get_delay+=DELAY_STEP;
Packit 8f70b4
      get_ll_timer.SetMicroSeconds(get_delay);
Packit 8f70b4
      session->SuspendSlave();
Packit 8f70b4
   }
Packit 8f70b4
   else if(res>MAX_READ_TO_DELAY && get_delay>=DELAY_STEP)
Packit 8f70b4
      get_delay-=DELAY_STEP;
Packit 8f70b4
   return res;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int FileCopyPeerFA::Put_LL(const char *buf,int len)
Packit 8f70b4
{
Packit 8f70b4
   if(do_mkdir)
Packit 8f70b4
      return 0;	  // can't write yet
Packit 8f70b4
Packit 8f70b4
   if(session->IsClosed())
Packit 8f70b4
      OpenSession();
Packit 8f70b4
Packit 8f70b4
   off_t io_at=pos; // GetRealPos can alter pos, save it.
Packit 8f70b4
   if(GetRealPos()!=io_at)
Packit 8f70b4
      return 0;
Packit 8f70b4
Packit 8f70b4
   if(len==0 && eof)
Packit 8f70b4
      return 0;
Packit 8f70b4
Packit 8f70b4
   int res=session->Write(buf,len);
Packit 8f70b4
   if(res<0)
Packit 8f70b4
   {
Packit 8f70b4
      if(res==FA::DO_AGAIN)
Packit 8f70b4
	 return 0;
Packit 8f70b4
      if(res==FA::STORE_FAILED)
Packit 8f70b4
      {
Packit 8f70b4
	 upload_state.Save(session);
Packit 8f70b4
	 session->Close();
Packit 8f70b4
	 if(can_seek && seek_pos>0)
Packit 8f70b4
	    Seek(FILE_END);
Packit 8f70b4
	 else
Packit 8f70b4
	    Seek(0);
Packit 8f70b4
	 return 0;
Packit 8f70b4
      }
Packit 8f70b4
      SetError(session->StrError(res));
Packit 8f70b4
      return -1;
Packit 8f70b4
   }
Packit 8f70b4
   seek_pos+=res; // mainly to indicate that there was some output.
Packit 8f70b4
   return res;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int FileCopyPeerFA::PutEOF_LL()
Packit 8f70b4
{
Packit 8f70b4
   if(mode==GET && session)
Packit 8f70b4
      session->Close();
Packit 8f70b4
   return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
off_t FileCopyPeerFA::GetRealPos()
Packit 8f70b4
{
Packit 8f70b4
   if(session->OpenMode()!=FAmode || fxp)
Packit 8f70b4
      return pos;
Packit 8f70b4
   if(mode==PUT)
Packit 8f70b4
   {
Packit 8f70b4
      if(pos-Size()!=session->GetPos())
Packit 8f70b4
      {
Packit 8f70b4
	 Empty();
Packit 8f70b4
	 can_seek=false;
Packit 8f70b4
	 pos=session->GetPos();
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   else
Packit 8f70b4
   {
Packit 8f70b4
      if(eof)
Packit 8f70b4
	 return pos;
Packit 8f70b4
      if(session->GetRealPos()==0 && session->GetPos()>0)
Packit 8f70b4
      {
Packit 8f70b4
	 can_seek=false;
Packit 8f70b4
	 session->SeekReal();
Packit 8f70b4
      }
Packit 8f70b4
      if(pos+Size()!=session->GetPos())
Packit 8f70b4
	 SaveRollback(session->GetPos());
Packit 8f70b4
   }
Packit 8f70b4
   return pos;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FileCopyPeerFA::Init()
Packit 8f70b4
{
Packit 8f70b4
   get_delay=0;
Packit 8f70b4
   fxp=false;
Packit 8f70b4
   redirections=0;
Packit 8f70b4
   can_seek=true;
Packit 8f70b4
   can_seek0=true;
Packit 8f70b4
   if(FAmode==FA::LIST || FAmode==FA::LONG_LIST)
Packit 8f70b4
      Save(FileAccess::cache->SizeLimit());
Packit 8f70b4
   if(mode==PUT)
Packit 8f70b4
      file.set(UseTempFile(file));
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
FileCopyPeerFA::FileCopyPeerFA(FileAccess *s,const char *f,int m)
Packit 8f70b4
   : FileCopyPeer(m==FA::STORE ? PUT : GET), file(f),
Packit 8f70b4
     my_session(s), session(my_session), FAmode(m)
Packit 8f70b4
{
Packit 8f70b4
   Init();
Packit 8f70b4
}
Packit 8f70b4
FileCopyPeerFA::FileCopyPeerFA(const FileAccessRef& s,const char *f,int m)
Packit 8f70b4
   : FileCopyPeer(m==FA::STORE ? PUT : GET), file(f),
Packit 8f70b4
     session(s), FAmode(m)
Packit 8f70b4
{
Packit 8f70b4
   Init();
Packit 8f70b4
}
Packit 8f70b4
FileCopyPeerFA::FileCopyPeerFA(const ParsedURL *u,int m)
Packit 8f70b4
   : FileCopyPeer(m==FA::STORE ? PUT : GET), file(u->path), orig_url(u->orig_url),
Packit 8f70b4
     my_session(FileAccess::New(u)), session(my_session), FAmode(m)
Packit 8f70b4
{
Packit 8f70b4
   Init();
Packit 8f70b4
   if(!file)
Packit 8f70b4
      SetError(_("file name missed in URL"));
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FileCopyPeerFA::PrepareToDie()
Packit 8f70b4
{
Packit 8f70b4
   if(session)
Packit 8f70b4
      session->Close();
Packit 8f70b4
}
Packit 8f70b4
FileCopyPeerFA::~FileCopyPeerFA() {}
Packit 8f70b4
Packit 8f70b4
FileCopyPeerFA *FileCopyPeerFA::New(FileAccess *s,const char *url,int m)
Packit 8f70b4
{
Packit 8f70b4
   ParsedURL u(url,true);
Packit 8f70b4
   if(u.proto)
Packit 8f70b4
   {
Packit 8f70b4
      SessionPool::Reuse(s);
Packit 8f70b4
      return new FileCopyPeerFA(&u,m);
Packit 8f70b4
   }
Packit 8f70b4
   return new FileCopyPeerFA(s,url,m);
Packit 8f70b4
}
Packit 8f70b4
FileCopyPeerFA *FileCopyPeerFA::New(const FileAccessRef& s,const char *url,int m)
Packit 8f70b4
{
Packit 8f70b4
   ParsedURL u(url,true);
Packit 8f70b4
   if(u.proto)
Packit 8f70b4
      return new FileCopyPeerFA(&u,m);
Packit 8f70b4
   return new FileCopyPeerFA(s,url,m);
Packit 8f70b4
}
Packit 8f70b4
FileCopyPeer *FileCopyPeerFA::Clone()
Packit 8f70b4
{
Packit 8f70b4
   FileCopyPeerFA *c=new FileCopyPeerFA(session->Clone(),file,FAmode);
Packit 8f70b4
   c->orig_url.set(orig_url);
Packit 8f70b4
   return c;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
// FileCopyPeerFDStream
Packit 8f70b4
#undef super
Packit 8f70b4
#define super FileCopyPeer
Packit 8f70b4
Packit 8f70b4
FileCopyPeerFDStream::FileCopyPeerFDStream(FDStream *o,dir_t m)
Packit 8f70b4
   : FileCopyPeer(m), my_stream(o?o:new FDStream(1,"<stdout>")), stream(my_stream), close_when_done(o!=0)
Packit 8f70b4
{
Packit 8f70b4
   Init();
Packit 8f70b4
}
Packit 8f70b4
FileCopyPeerFDStream::FileCopyPeerFDStream(const Ref<FDStream>& o,dir_t m)
Packit 8f70b4
   : FileCopyPeer(m), stream(o), close_when_done(false)
Packit 8f70b4
{
Packit 8f70b4
   Init();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FileCopyPeerFDStream::Init()
Packit 8f70b4
{
Packit 8f70b4
   seek_base=0;
Packit 8f70b4
   create_fg_data=true;
Packit 8f70b4
   need_seek=false;
Packit 8f70b4
   can_seek = can_seek0 = stream->can_seek();
Packit 8f70b4
   if(can_seek && stream->fd!=-1)
Packit 8f70b4
   {
Packit 8f70b4
      seek_base=lseek(stream->fd,0,SEEK_CUR);
Packit 8f70b4
      if(seek_base==-1)
Packit 8f70b4
      {
Packit 8f70b4
	 can_seek=false;
Packit 8f70b4
	 can_seek0=false;
Packit 8f70b4
	 seek_base=0;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   if(stream->usesfd(1))
Packit 8f70b4
      write_allowed=false;
Packit 8f70b4
   if(mode==PUT)
Packit 8f70b4
      put_ll_timer=new Timer(0,200);
Packit 8f70b4
   if(mode==PUT && stream->fd==-1 && stream->can_setmtime())
Packit 8f70b4
      stream->full_name.set(UseTempFile(stream->full_name));
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FileCopyPeerFDStream::Seek_LL()
Packit 8f70b4
{
Packit 8f70b4
   int fd=stream->fd;
Packit 8f70b4
   assert(fd!=-1);
Packit 8f70b4
   if(CanSeek(seek_pos))
Packit 8f70b4
   {
Packit 8f70b4
      if(seek_pos==FILE_END)
Packit 8f70b4
      {
Packit 8f70b4
	 seek_pos=lseek(fd,0,SEEK_END);
Packit 8f70b4
	 if(seek_pos==-1)
Packit 8f70b4
	 {
Packit 8f70b4
	    can_seek=false;
Packit 8f70b4
	    can_seek0=false;
Packit 8f70b4
	    seek_pos=0;
Packit 8f70b4
	 }
Packit 8f70b4
	 else
Packit 8f70b4
	 {
Packit 8f70b4
	    SetSize(seek_pos);
Packit 8f70b4
	    if(seek_pos>seek_base)
Packit 8f70b4
	       seek_pos-=seek_base;
Packit 8f70b4
	    else
Packit 8f70b4
	       seek_pos=0;
Packit 8f70b4
	 }
Packit 8f70b4
	 pos=seek_pos;
Packit 8f70b4
      }
Packit 8f70b4
      else
Packit 8f70b4
      {
Packit 8f70b4
	 if(lseek(fd,seek_pos+seek_base,SEEK_SET)==-1)
Packit 8f70b4
	 {
Packit 8f70b4
	    can_seek=false;
Packit 8f70b4
	    can_seek0=false;
Packit 8f70b4
	    seek_pos=0;
Packit 8f70b4
	 }
Packit 8f70b4
	 pos=seek_pos;
Packit 8f70b4
      }
Packit 8f70b4
      if(mode==PUT)
Packit 8f70b4
	 pos+=Size();
Packit 8f70b4
   }
Packit 8f70b4
   else
Packit 8f70b4
   {
Packit 8f70b4
      seek_pos=pos;
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int FileCopyPeerFDStream::getfd()
Packit 8f70b4
{
Packit 8f70b4
   if(!stream)
Packit 8f70b4
      return -1;
Packit 8f70b4
   if(stream->fd!=-1)
Packit 8f70b4
      return stream->fd;
Packit 8f70b4
   int fd=stream->getfd();
Packit 8f70b4
   if(fd==-1)
Packit 8f70b4
   {
Packit 8f70b4
      if(stream->error())
Packit 8f70b4
      {
Packit 8f70b4
	 SetError(stream->error_text);
Packit 8f70b4
	 current->Timeout(0);
Packit 8f70b4
      }
Packit 8f70b4
      else
Packit 8f70b4
      {
Packit 8f70b4
	 current->TimeoutS(1);
Packit 8f70b4
      }
Packit 8f70b4
      return -1;
Packit 8f70b4
   }
Packit 8f70b4
   stream->clear_status();
Packit 8f70b4
   pos=0;
Packit 8f70b4
   if(mode==PUT)
Packit 8f70b4
      pos+=Size();
Packit 8f70b4
   Seek_LL();
Packit 8f70b4
   return fd;
Packit 8f70b4
}
Packit 8f70b4
int FileCopyPeerFDStream::Do()
Packit 8f70b4
{
Packit 8f70b4
   int m=STALL;
Packit 8f70b4
   if(Done() || Error())
Packit 8f70b4
      return m;
Packit 8f70b4
Packit 8f70b4
   if(do_mkdir) {
Packit 8f70b4
      do_mkdir=false;
Packit 8f70b4
      create_directories(dirname(stream->full_name).get_non_const());
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(verify)
Packit 8f70b4
   {
Packit 8f70b4
      if(verify->Error())
Packit 8f70b4
      {
Packit 8f70b4
	 SetError(verify->ErrorText());
Packit 8f70b4
	 m=MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      else if(verify->Done())
Packit 8f70b4
      {
Packit 8f70b4
	 if(suggested_filename && stream && stream->full_name && auto_rename)
Packit 8f70b4
	 {
Packit 8f70b4
	    const char *new_name=dir_file(dirname(stream->full_name),suggested_filename);
Packit 8f70b4
	    struct stat st;
Packit 8f70b4
	    if(temp_file || (lstat(new_name,&st)==-1 && errno==ENOENT) || ResMgr::QueryBool("xfer:clobber",0)) {
Packit 8f70b4
	       debug((5,"copy: renaming `%s' to `%s'\n",stream->full_name.get(),suggested_filename.get()));
Packit 8f70b4
	       int res=rename(stream->full_name,new_name);
Packit 8f70b4
	       if(res==-1 && errno==EIO) {
Packit 8f70b4
		  // FUSE with HadoopFS workaround
Packit 8f70b4
		  unlink(new_name);
Packit 8f70b4
		  res=rename(stream->full_name,new_name);
Packit 8f70b4
	       }
Packit 8f70b4
	       if(res==-1) {
Packit 8f70b4
		  const char *err=xstring::format("rename(%s, %s): %s\n",stream->full_name.get(),new_name,strerror(errno));
Packit 8f70b4
		  if(temp_file)
Packit 8f70b4
		     SetError(err);
Packit 8f70b4
		  else
Packit 8f70b4
		     debug((3,"%s\n",err));
Packit 8f70b4
	       }
Packit 8f70b4
	    }
Packit 8f70b4
	 }
Packit 8f70b4
	 done=true;
Packit 8f70b4
	 m=MOVED;
Packit 8f70b4
      }
Packit 8f70b4
      return m;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   bool check_min_size=true;
Packit 8f70b4
#ifndef NATIVE_CRLF
Packit 8f70b4
   if(ascii)
Packit 8f70b4
      check_min_size=false;
Packit 8f70b4
#endif
Packit 8f70b4
   int res;
Packit 8f70b4
   switch(mode)
Packit 8f70b4
   {
Packit 8f70b4
   case PUT:
Packit 8f70b4
      if(Size()==0)
Packit 8f70b4
      {
Packit 8f70b4
	 if(eof)
Packit 8f70b4
	 {
Packit 8f70b4
	    // make sure the stream is open - it may create an empty file.
Packit 8f70b4
	    if(stream && !stream->is_closed() && getfd()==-1)
Packit 8f70b4
	       return m;
Packit 8f70b4
	    if(!date_set && date!=NO_DATE && do_set_date)
Packit 8f70b4
	    {
Packit 8f70b4
	       if(date==NO_DATE_YET)
Packit 8f70b4
		  return m;
Packit 8f70b4
	       stream->setmtime(date);
Packit 8f70b4
	       date_set=true;
Packit 8f70b4
	       m=MOVED;
Packit 8f70b4
	    }
Packit 8f70b4
	    if(stream && close_when_done && !stream->Done())
Packit 8f70b4
	       return m;
Packit 8f70b4
	    if(!verify && do_verify)
Packit 8f70b4
	       verify=new FileVerificator(stream);
Packit 8f70b4
	    else
Packit 8f70b4
	       done=true;
Packit 8f70b4
	    return MOVED;
Packit 8f70b4
	 }
Packit 8f70b4
	 if(seek_pos==0)
Packit 8f70b4
	    return m;
Packit 8f70b4
      }
Packit 8f70b4
      if(!write_allowed)
Packit 8f70b4
	 return m;
Packit 8f70b4
      if(getfd()==-1)
Packit 8f70b4
	 return m;
Packit 8f70b4
Packit 8f70b4
      if(check_min_size && !eof && Size()
Packit 8f70b4
      && put_ll_timer && !put_ll_timer->Stopped())
Packit 8f70b4
	 break;
Packit 8f70b4
      res=Put_LL(buffer+buffer_ptr,Size());
Packit 8f70b4
      if(res>0)
Packit 8f70b4
	 buffer_ptr+=res;
Packit 8f70b4
      if(res!=0)
Packit 8f70b4
	 m=MOVED;
Packit 8f70b4
      break;
Packit 8f70b4
Packit 8f70b4
   case GET:
Packit 8f70b4
      if(eof)
Packit 8f70b4
	 return m;
Packit 8f70b4
Packit 8f70b4
      res=TuneGetSize(Get_LL(get_size));
Packit 8f70b4
      if(res>0)
Packit 8f70b4
      {
Packit 8f70b4
	 EmbraceNewData(res);
Packit 8f70b4
	 SaveMaxCheck(0);
Packit 8f70b4
      }
Packit 8f70b4
      if(res!=0 || eof)
Packit 8f70b4
	 m=MOVED;
Packit 8f70b4
      break;
Packit 8f70b4
   }
Packit 8f70b4
   return m;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
bool FileCopyPeerFDStream::IOReady()
Packit 8f70b4
{
Packit 8f70b4
   return seek_pos==pos || stream->fd!=-1;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FileCopyPeerFDStream::Seek(off_t new_pos)
Packit 8f70b4
{
Packit 8f70b4
   if(pos==new_pos)
Packit 8f70b4
      return;
Packit 8f70b4
#ifndef NATIVE_CRLF
Packit 8f70b4
   if(ascii && new_pos!=0)
Packit 8f70b4
   {
Packit 8f70b4
      // it is possible to read file to determine right position,
Packit 8f70b4
      // but it is costly.
Packit 8f70b4
      can_seek=false;
Packit 8f70b4
      // can_seek0 is still true.
Packit 8f70b4
      return;
Packit 8f70b4
   }
Packit 8f70b4
#endif
Packit 8f70b4
   super::Seek(new_pos);
Packit 8f70b4
   int fd=stream->fd;
Packit 8f70b4
   if(fd==-1)
Packit 8f70b4
   {
Packit 8f70b4
      if(seek_pos!=FILE_END)
Packit 8f70b4
      {
Packit 8f70b4
	 pos=seek_pos;
Packit 8f70b4
	 if(mode==PUT)
Packit 8f70b4
	    pos+=Size();
Packit 8f70b4
	 return;
Packit 8f70b4
      }
Packit 8f70b4
      else
Packit 8f70b4
      {
Packit 8f70b4
	 off_t s=stream->get_size();
Packit 8f70b4
	 if(s!=-1)
Packit 8f70b4
	 {
Packit 8f70b4
	    SetSize(s);
Packit 8f70b4
	    pos=seek_pos+((mode==PUT)?Size():0);
Packit 8f70b4
	    return;
Packit 8f70b4
	 }
Packit 8f70b4
	 else
Packit 8f70b4
	 {
Packit 8f70b4
	    // ok, have to try getfd.
Packit 8f70b4
	    fd=getfd();
Packit 8f70b4
	 }
Packit 8f70b4
      }
Packit 8f70b4
      if(fd==-1)
Packit 8f70b4
	 return;
Packit 8f70b4
   }
Packit 8f70b4
   Seek_LL();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int FileCopyPeerFDStream::Get_LL(int len)
Packit 8f70b4
{
Packit 8f70b4
   int res=0;
Packit 8f70b4
Packit 8f70b4
   int fd=getfd();
Packit 8f70b4
   if(fd==-1)
Packit 8f70b4
      return 0;
Packit 8f70b4
Packit 8f70b4
   if((want_date && date==NO_DATE_YET)
Packit 8f70b4
   || (want_size && size==NO_SIZE_YET))
Packit 8f70b4
   {
Packit 8f70b4
      struct stat st;
Packit 8f70b4
      if(fstat(fd,&st)==-1)
Packit 8f70b4
      {
Packit 8f70b4
	 SetDate(NO_DATE);
Packit 8f70b4
	 SetSize(NO_SIZE);
Packit 8f70b4
      }
Packit 8f70b4
      else
Packit 8f70b4
      {
Packit 8f70b4
	 SetDate(st.st_mtime);
Packit 8f70b4
	 SetSize(S_ISREG(st.st_mode)?st.st_size:NO_SIZE);
Packit 8f70b4
#ifndef NATIVE_CRLF
Packit 8f70b4
	 if(ascii)
Packit 8f70b4
	    SetSize(NO_SIZE);
Packit 8f70b4
#endif
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   if(need_seek)  // this does not combine with ascii.
Packit 8f70b4
      lseek(fd,seek_base+pos,SEEK_SET);
Packit 8f70b4
Packit 8f70b4
   char *p=GetSpace(ascii?len*2:len);
Packit 8f70b4
   res=read(fd,p,len);
Packit 8f70b4
   if(res==-1)
Packit 8f70b4
   {
Packit 8f70b4
      if(E_RETRY(errno))
Packit 8f70b4
      {
Packit 8f70b4
	 Block(fd,POLLIN);
Packit 8f70b4
	 return 0;
Packit 8f70b4
      }
Packit 8f70b4
      if(stream->NonFatalError(errno))
Packit 8f70b4
	 return 0;
Packit 8f70b4
      stream->MakeErrorText();
Packit 8f70b4
      SetError(stream->error_text);
Packit 8f70b4
      return -1;
Packit 8f70b4
   }
Packit 8f70b4
   stream->clear_status();
Packit 8f70b4
Packit 8f70b4
#ifndef NATIVE_CRLF
Packit 8f70b4
   if(ascii)
Packit 8f70b4
   {
Packit 8f70b4
      for(int i=res; i>0; i--)
Packit 8f70b4
      {
Packit 8f70b4
	 if(*p=='\n')
Packit 8f70b4
	 {
Packit 8f70b4
	    memmove(p+1,p,i);
Packit 8f70b4
	    *p++='\r';
Packit 8f70b4
	    res++;
Packit 8f70b4
	 }
Packit 8f70b4
	 p++;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
#endif
Packit 8f70b4
Packit 8f70b4
   if(res==0) {
Packit 8f70b4
      debug((10,"copy-peer: EOF on FD %d\n",fd));
Packit 8f70b4
      eof=true;
Packit 8f70b4
   }
Packit 8f70b4
   return res;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int FileCopyPeerFDStream::Put_LL(const char *buf,int len)
Packit 8f70b4
{
Packit 8f70b4
   if(len==0)
Packit 8f70b4
      return 0;
Packit 8f70b4
Packit 8f70b4
   int fd=getfd();
Packit 8f70b4
   if(fd==-1)
Packit 8f70b4
      return 0;
Packit 8f70b4
Packit 8f70b4
   int skip_cr=0;
Packit 8f70b4
Packit 8f70b4
#ifndef NATIVE_CRLF
Packit 8f70b4
   if(ascii)
Packit 8f70b4
   {
Packit 8f70b4
      // find where line ends.
Packit 8f70b4
      const char *cr=buf;
Packit 8f70b4
      for(;;)
Packit 8f70b4
      {
Packit 8f70b4
	 cr=(const char *)memchr(cr,'\r',len-(cr-buf));
Packit 8f70b4
	 if(!cr)
Packit 8f70b4
	    break;
Packit 8f70b4
	 if(cr-buf
Packit 8f70b4
	 {
Packit 8f70b4
	    skip_cr=1;
Packit 8f70b4
	    len=cr-buf;
Packit 8f70b4
	    break;
Packit 8f70b4
	 }
Packit 8f70b4
	 if(cr-buf==len-1)
Packit 8f70b4
	 {
Packit 8f70b4
	    if(eof)
Packit 8f70b4
	       break;
Packit 8f70b4
	    len--;
Packit 8f70b4
	    break;
Packit 8f70b4
	 }
Packit 8f70b4
	 cr++;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
#endif	 // NATIVE_CRLF
Packit 8f70b4
Packit 8f70b4
   if(len==0)
Packit 8f70b4
      return skip_cr;
Packit 8f70b4
Packit 8f70b4
   if(need_seek)  // this does not combine with ascii.
Packit 8f70b4
      lseek(fd,seek_base+pos-Size(),SEEK_SET);
Packit 8f70b4
Packit 8f70b4
   int res=write(fd,buf,len);
Packit 8f70b4
   if(res<0)
Packit 8f70b4
   {
Packit 8f70b4
      if(E_RETRY(errno))
Packit 8f70b4
      {
Packit 8f70b4
	 Block(fd,POLLOUT);
Packit 8f70b4
	 return 0;
Packit 8f70b4
      }
Packit 8f70b4
      if(errno==EPIPE)
Packit 8f70b4
      {
Packit 8f70b4
	 broken=true;
Packit 8f70b4
	 buffer.truncate(buffer_ptr);
Packit 8f70b4
	 eof=true;
Packit 8f70b4
	 return -1;
Packit 8f70b4
      }
Packit 8f70b4
      if(stream->NonFatalError(errno))
Packit 8f70b4
      {
Packit 8f70b4
	 // in case of full disk, check file correctness.
Packit 8f70b4
	 if(errno==ENOSPC && can_seek)
Packit 8f70b4
	 {
Packit 8f70b4
	    struct stat st;
Packit 8f70b4
	    if(fstat(fd,&st)!=-1)
Packit 8f70b4
	    {
Packit 8f70b4
	       if(st.st_size
Packit 8f70b4
	       {
Packit 8f70b4
		  // workaround solaris nfs bug. It can lose data if disk is full.
Packit 8f70b4
		  if(buffer_ptr>=seek_base+pos-Size()-buffer_ptr-st.st_size)
Packit 8f70b4
		     UnSkip(seek_base+pos-Size()-st.st_size);
Packit 8f70b4
		  else
Packit 8f70b4
		  {
Packit 8f70b4
		     Empty();
Packit 8f70b4
		     pos=st.st_size;
Packit 8f70b4
		  }
Packit 8f70b4
	       }
Packit 8f70b4
	    }
Packit 8f70b4
	 }
Packit 8f70b4
	 return 0;
Packit 8f70b4
      }
Packit 8f70b4
      stream->MakeErrorText();
Packit 8f70b4
      SetError(stream->error_text);
Packit 8f70b4
      return -1;
Packit 8f70b4
   }
Packit 8f70b4
   stream->clear_status();
Packit 8f70b4
   if(res==len && skip_cr)
Packit 8f70b4
   {
Packit 8f70b4
      res+=skip_cr;
Packit 8f70b4
      // performance gets worse because of writing a single char,
Packit 8f70b4
      // but leaving uncomplete line on screen allows mixing it with debug text.
Packit 8f70b4
      if(write(fd,"\n",1)==1)
Packit 8f70b4
	 res+=1;
Packit 8f70b4
   }
Packit 8f70b4
   if(put_ll_timer)
Packit 8f70b4
      put_ll_timer->Reset();
Packit 8f70b4
   return res;
Packit 8f70b4
}
Packit 8f70b4
FgData *FileCopyPeerFDStream::GetFgData(bool fg)
Packit 8f70b4
{
Packit 8f70b4
   if(!my_stream || !create_fg_data)
Packit 8f70b4
      return 0;	  // if we don't own the stream, don't create FgData.
Packit 8f70b4
   if(stream->GetProcGroup())
Packit 8f70b4
      return new FgData(stream->GetProcGroup(),fg);
Packit 8f70b4
   return 0;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FileCopyPeerFDStream::WantSize()
Packit 8f70b4
{
Packit 8f70b4
   struct stat st;
Packit 8f70b4
   int res=-1;
Packit 8f70b4
Packit 8f70b4
   if(stream->fd!=-1)
Packit 8f70b4
      res=fstat(stream->fd,&st);
Packit 8f70b4
   else if(stream->full_name)
Packit 8f70b4
      res=stat(stream->full_name,&st);
Packit 8f70b4
Packit 8f70b4
   if(res!=-1)
Packit 8f70b4
      SetSize(S_ISREG(st.st_mode)?st.st_size:NO_SIZE);
Packit 8f70b4
   else
Packit 8f70b4
      super::WantSize();
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
void FileCopyPeerFDStream::RemoveFile()
Packit 8f70b4
{
Packit 8f70b4
   stream->remove();
Packit 8f70b4
   removing=false;   // it is instant.
Packit 8f70b4
   file_removed=true;
Packit 8f70b4
   Suspend();
Packit 8f70b4
   current->Timeout(0);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
const char *FileCopyPeerFDStream::GetStatus()
Packit 8f70b4
{
Packit 8f70b4
   if(verify)
Packit 8f70b4
      return verify->Status();
Packit 8f70b4
   return stream->status;
Packit 8f70b4
}
Packit 8f70b4
void FileCopyPeerFDStream::Kill(int sig)
Packit 8f70b4
{
Packit 8f70b4
   stream->Kill(sig);
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
Packit 8f70b4
FileCopyPeerFDStream *FileCopyPeerFDStream::NewPut(const char *file,bool cont)
Packit 8f70b4
{
Packit 8f70b4
   int flags=O_WRONLY|O_CREAT;
Packit 8f70b4
   if(!cont) {
Packit 8f70b4
      flags|=O_TRUNC;
Packit 8f70b4
      if(!ResMgr::QueryBool("xfer:clobber",0))
Packit 8f70b4
	 flags|=O_EXCL;
Packit 8f70b4
   }
Packit 8f70b4
   return new FileCopyPeerFDStream(new FileStream(file,flags),
Packit 8f70b4
				    FileCopyPeer::PUT);
Packit 8f70b4
}
Packit 8f70b4
FileCopyPeerFDStream *FileCopyPeerFDStream::NewGet(const char *file)
Packit 8f70b4
{
Packit 8f70b4
   return new FileCopyPeerFDStream(new FileStream(file,O_RDONLY),
Packit 8f70b4
				    FileCopyPeer::GET);
Packit 8f70b4
}
Packit 8f70b4
FileCopyPeer *FileCopyPeerFDStream::Clone()
Packit 8f70b4
{
Packit 8f70b4
   NeedSeek();
Packit 8f70b4
   FileCopyPeerFDStream *peer=new FileCopyPeerFDStream(stream,mode);
Packit 8f70b4
   peer->NeedSeek();
Packit 8f70b4
   peer->SetBase(0);
Packit 8f70b4
   return peer;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
// FileCopyPeerDirList
Packit 8f70b4
FileCopyPeerDirList::FileCopyPeerDirList(FA *s,ArgV *v)
Packit 8f70b4
   : FileCopyPeer(GET), session(s)
Packit 8f70b4
{
Packit 8f70b4
   dl=session->MakeDirList(v);
Packit 8f70b4
   if(dl==0)
Packit 8f70b4
      eof=true;
Packit 8f70b4
   can_seek=false;
Packit 8f70b4
   can_seek0=false;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
int FileCopyPeerDirList::Do()
Packit 8f70b4
{
Packit 8f70b4
   if(Done())
Packit 8f70b4
      return STALL;
Packit 8f70b4
   if(dl->Error())
Packit 8f70b4
   {
Packit 8f70b4
      SetError(dl->ErrorText());
Packit 8f70b4
      return MOVED;
Packit 8f70b4
   }
Packit 8f70b4
Packit 8f70b4
   const char *b;
Packit 8f70b4
   int s;
Packit 8f70b4
   dl->Get(&b,&s);
Packit 8f70b4
   if(b==0) // eof
Packit 8f70b4
   {
Packit 8f70b4
      eof=true;
Packit 8f70b4
      return MOVED;
Packit 8f70b4
   }
Packit 8f70b4
   if(s==0)
Packit 8f70b4
      return STALL;
Packit 8f70b4
   memcpy(GetSpace(s),b,s);
Packit 8f70b4
   SpaceAdd(s);
Packit 8f70b4
   dl->Skip(s);
Packit 8f70b4
   return MOVED;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
// FileCopyPeerMemory
Packit 8f70b4
int FileCopyPeerMemory::Do()
Packit 8f70b4
{
Packit 8f70b4
   int m=STALL;
Packit 8f70b4
   if(mode==PUT) {
Packit 8f70b4
      max_buf=max_size+1;
Packit 8f70b4
      if(Size()>max_size) {
Packit 8f70b4
	 SetError("buffer limit exceeded");
Packit 8f70b4
	 broken=true;
Packit 8f70b4
	 return MOVED;
Packit 8f70b4
      }
Packit 8f70b4
   }
Packit 8f70b4
   return m;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
// FileVerificator
Packit 8f70b4
void FileVerificator::Init0()
Packit 8f70b4
{
Packit 8f70b4
   done=false;
Packit 8f70b4
   if(!ResMgr::QueryBool("xfer:verify",0)
Packit 8f70b4
   || ResMgr::Query("xfer:verify-command",0).is_empty())
Packit 8f70b4
      done=true;
Packit 8f70b4
}
Packit 8f70b4
void FileVerificator::InitVerify(const char *f)
Packit 8f70b4
{
Packit 8f70b4
   if(done)
Packit 8f70b4
      return;
Packit 8f70b4
   ArgV *args=new ArgV(ResMgr::Query("xfer:verify-command",0));
Packit 8f70b4
   args->Append(f);
Packit 8f70b4
   Log::global->Format(9,"running %s %s\n",args->a0(),f);
Packit 8f70b4
   verify_process=new InputFilter(args);
Packit 8f70b4
   verify_process->StderrToStdout();
Packit 8f70b4
   verify_buffer=new IOBufferFDStream(verify_process.Cast<FDStream>(),IOBuffer::GET);
Packit 8f70b4
}
Packit 8f70b4
FileVerificator::FileVerificator(const char *f)
Packit 8f70b4
{
Packit 8f70b4
   Init0();
Packit 8f70b4
   InitVerify(f);
Packit 8f70b4
}
Packit 8f70b4
FileVerificator::FileVerificator(const FDStream *stream)
Packit 8f70b4
{
Packit 8f70b4
   Init0();
Packit 8f70b4
   if(done)
Packit 8f70b4
      return;
Packit 8f70b4
   const char *f=stream->full_name;
Packit 8f70b4
   if(!f)
Packit 8f70b4
   {
Packit 8f70b4
      done=true;
Packit 8f70b4
      return;
Packit 8f70b4
   }
Packit 8f70b4
   const char *cwd=stream->GetCwd();
Packit 8f70b4
   int cwd_len=xstrlen(cwd);
Packit 8f70b4
   if(cwd && cwd_len>0 && !strncmp(f,cwd,cwd_len))
Packit 8f70b4
   {
Packit 8f70b4
      f+=cwd_len;
Packit 8f70b4
      while(*f=='/')
Packit 8f70b4
	 f++;
Packit 8f70b4
      if(*f==0)
Packit 8f70b4
	 f=".";
Packit 8f70b4
   }
Packit 8f70b4
   InitVerify(f);
Packit 8f70b4
   if(verify_process)
Packit 8f70b4
   {
Packit 8f70b4
      verify_process->SetProcGroup(stream->GetProcGroup());
Packit 8f70b4
      verify_process->SetCwd(cwd);
Packit 8f70b4
   }
Packit 8f70b4
}
Packit 8f70b4
FileVerificator::FileVerificator(const FileAccess *session,const char *f)
Packit 8f70b4
{
Packit 8f70b4
   Init0();
Packit 8f70b4
   if(done)
Packit 8f70b4
      return;
Packit 8f70b4
   if(strcmp(session->GetProto(),"file"))
Packit 8f70b4
   {
Packit 8f70b4
      done=true;
Packit 8f70b4
      return;
Packit 8f70b4
   }
Packit 8f70b4
   InitVerify(f);
Packit 8f70b4
   verify_process->SetCwd(session->GetCwd());
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
FileVerificator::~FileVerificator() {}
Packit 8f70b4
Packit 8f70b4
int FileVerificator::Do()
Packit 8f70b4
{
Packit 8f70b4
   int m=STALL;
Packit 8f70b4
   if(done)
Packit 8f70b4
      return m;
Packit 8f70b4
   verify_process->Kill(SIGCONT);
Packit 8f70b4
   if(!verify_buffer->Eof())
Packit 8f70b4
      return m;
Packit 8f70b4
   if(verify_process->GetProcState()!=ProcWait::TERMINATED)
Packit 8f70b4
      return m;
Packit 8f70b4
   done=true;
Packit 8f70b4
   m=MOVED;
Packit 8f70b4
   if(verify_process->GetProcExitCode()!=0)
Packit 8f70b4
   {
Packit 8f70b4
      error_text.set(verify_buffer->Get());
Packit 8f70b4
      error_text.rtrim('\n');
Packit 8f70b4
      if(error_text.length()==0)
Packit 8f70b4
	 error_text.set(_("Verify command failed without a message"));
Packit 8f70b4
      const char *nl=strrchr(error_text,'\n');
Packit 8f70b4
      if(nl)
Packit 8f70b4
	 error_text.set(nl+1);
Packit 8f70b4
   }
Packit 8f70b4
   return m;
Packit 8f70b4
}
Packit 8f70b4
Packit 8f70b4
Packit 8f70b4
// special pointer to creator of ftp/ftp copier. It is init'ed in Ftp class.
Packit 8f70b4
FileCopy *(*FileCopy::fxp_create)(FileCopyPeer *src,FileCopyPeer *dst,bool cont);