Blame src/MirrorJob.cc

Packit Service a2489d
/*
Packit Service a2489d
 * lftp - file transfer program
Packit Service a2489d
 *
Packit Service a2489d
 * Copyright (c) 1996-2017 by Alexander V. Lukyanov (lav@yars.free.net)
Packit Service a2489d
 *
Packit Service a2489d
 * This program is free software; you can redistribute it and/or modify
Packit Service a2489d
 * it under the terms of the GNU General Public License as published by
Packit Service a2489d
 * the Free Software Foundation; either version 3 of the License, or
Packit Service a2489d
 * (at your option) any later version.
Packit Service a2489d
 *
Packit Service a2489d
 * This program is distributed in the hope that it will be useful,
Packit Service a2489d
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service a2489d
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service a2489d
 * GNU General Public License for more details.
Packit Service a2489d
 *
Packit Service a2489d
 * You should have received a copy of the GNU General Public License
Packit Service a2489d
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
Packit Service a2489d
 */
Packit Service a2489d
Packit Service a2489d
#include <config.h>
Packit Service a2489d
Packit Service a2489d
#include <sys/types.h>
Packit Service a2489d
#include <sys/stat.h>
Packit Service a2489d
#include <errno.h>
Packit Service a2489d
#include <fcntl.h>
Packit Service a2489d
#include <unistd.h>
Packit Service a2489d
#include <assert.h>
Packit Service a2489d
#include <mbswidth.h>
Packit Service a2489d
#include "MirrorJob.h"
Packit Service a2489d
#include "CmdExec.h"
Packit Service a2489d
#include "rmJob.h"
Packit Service a2489d
#include "mvJob.h"
Packit Service a2489d
#include "ChmodJob.h"
Packit Service a2489d
#include "mkdirJob.h"
Packit Service a2489d
#include "misc.h"
Packit Service a2489d
#include "plural.h"
Packit Service a2489d
#include "FindJob.h"
Packit Service a2489d
#include "url.h"
Packit Service a2489d
#include "CopyJob.h"
Packit Service a2489d
#include "pgetJob.h"
Packit Service a2489d
#include "log.h"
Packit Service a2489d
Packit Service a2489d
#define set_state(s) do { state=(s); \
Packit Service a2489d
   Log::global->Format(11,"mirror(%p) enters state %s\n", this, #s); } while(0)
Packit Service a2489d
#define waiting_num waiting.count()
Packit Service a2489d
#define transfer_count root_mirror->root_transfer_count
Packit Service a2489d
Packit Service a2489d
xstring& MirrorJob::FormatStatus(xstring& s,int v,const char *tab)
Packit Service a2489d
{
Packit Service a2489d
   if(Done())
Packit Service a2489d
      goto final;
Packit Service a2489d
Packit Service a2489d
   switch(state)
Packit Service a2489d
   {
Packit Service a2489d
   case(INITIAL_STATE):
Packit Service a2489d
   case(FINISHING):
Packit Service a2489d
   case(DONE):
Packit Service a2489d
   case(WAITING_FOR_TRANSFER):
Packit Service a2489d
   case(TARGET_REMOVE_OLD):
Packit Service a2489d
   case(TARGET_REMOVE_OLD_FIRST):
Packit Service a2489d
   case(TARGET_CHMOD):
Packit Service a2489d
   case(TARGET_MKDIR):
Packit Service a2489d
   case(SOURCE_REMOVING_SAME):
Packit Service a2489d
   case(LAST_EXEC):
Packit Service a2489d
      break;
Packit Service a2489d
Packit Service a2489d
   case(MAKE_TARGET_DIR):
Packit Service a2489d
      s.appendf("\tmkdir `%s' [%s]\n",target_dir.get(),target_session->CurrentStatus());
Packit Service a2489d
      break;
Packit Service a2489d
Packit Service a2489d
   case(CHANGING_DIR_SOURCE):
Packit Service a2489d
   case(CHANGING_DIR_TARGET):
Packit Service a2489d
      if(target_session->IsOpen())
Packit Service a2489d
	 s.appendf("\tcd `%s' [%s]\n",target_dir.get(),target_session->CurrentStatus());
Packit Service a2489d
      if(source_session->IsOpen())
Packit Service a2489d
	 s.appendf("\tcd `%s' [%s]\n",source_dir.get(),source_session->CurrentStatus());
Packit Service a2489d
      break;
Packit Service a2489d
Packit Service a2489d
   case(GETTING_LIST_INFO):
Packit Service a2489d
      if(target_list_info)
Packit Service a2489d
      {
Packit Service a2489d
	 if(target_relative_dir)
Packit Service a2489d
	    s.appendf("\t%s: %s\n",target_relative_dir.get(),target_list_info->Status());
Packit Service a2489d
	 else
Packit Service a2489d
	    s.appendf("\t%s\n",target_list_info->Status());
Packit Service a2489d
      }
Packit Service a2489d
      if(source_list_info)
Packit Service a2489d
      {
Packit Service a2489d
	 if(source_relative_dir)
Packit Service a2489d
	    s.appendf("\t%s: %s\n",source_relative_dir.get(),source_list_info->Status());
Packit Service a2489d
	 else
Packit Service a2489d
	    s.appendf("\t%s\n",source_list_info->Status());
Packit Service a2489d
      }
Packit Service a2489d
      break;
Packit Service a2489d
   }
Packit Service a2489d
   return s;
Packit Service a2489d
Packit Service a2489d
final:
Packit Service a2489d
   if(stats.dirs>0)
Packit Service a2489d
      s.appendf(plural("%sTotal: %d director$y|ies$, %d file$|s$, %d symlink$|s$\n",
Packit Service a2489d
		     stats.dirs,stats.tot_files,stats.tot_symlinks),
Packit Service a2489d
	 tab,stats.dirs,stats.tot_files,stats.tot_symlinks);
Packit Service a2489d
   if(stats.new_files || stats.new_symlinks)
Packit Service a2489d
      s.appendf(plural("%sNew: %d file$|s$, %d symlink$|s$\n",
Packit Service a2489d
		     stats.new_files,stats.new_symlinks),
Packit Service a2489d
	 tab,stats.new_files,stats.new_symlinks);
Packit Service a2489d
   if(stats.mod_files || stats.mod_symlinks)
Packit Service a2489d
      s.appendf(plural("%sModified: %d file$|s$, %d symlink$|s$\n",
Packit Service a2489d
		     stats.mod_files,stats.mod_symlinks),
Packit Service a2489d
	 tab,stats.mod_files,stats.mod_symlinks);
Packit Service a2489d
   if(stats.bytes)
Packit Service a2489d
      s.appendf("%s%s\n",tab,CopyJob::FormatBytesTimeRate(stats.bytes,transfer_time_elapsed));
Packit Service a2489d
   if(stats.del_dirs || stats.del_files || stats.del_symlinks)
Packit Service a2489d
      s.appendf(plural(FlagSet(DELETE) ?
Packit Service a2489d
	       "%sRemoved: %d director$y|ies$, %d file$|s$, %d symlink$|s$\n"
Packit Service a2489d
	      :"%sTo be removed: %d director$y|ies$, %d file$|s$, %d symlink$|s$\n",
Packit Service a2489d
	      stats.del_dirs,stats.del_files,stats.del_symlinks),
Packit Service a2489d
	 tab,stats.del_dirs,stats.del_files,stats.del_symlinks);
Packit Service a2489d
   if(stats.error_count)
Packit Service a2489d
      s.appendf(plural("%s%d error$|s$ detected\n",stats.error_count),
Packit Service a2489d
	       tab,stats.error_count);
Packit Service a2489d
   return s;
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
void  MirrorJob::ShowRunStatus(const SMTaskRef<StatusLine>& s)
Packit Service a2489d
{
Packit Service a2489d
   int w=s->GetWidthDelayed();
Packit Service a2489d
   switch(state)
Packit Service a2489d
   {
Packit Service a2489d
   case(INITIAL_STATE):
Packit Service a2489d
      break;
Packit Service a2489d
Packit Service a2489d
   // these have a sub-job
Packit Service a2489d
   case(WAITING_FOR_TRANSFER):
Packit Service a2489d
   case(TARGET_REMOVE_OLD):
Packit Service a2489d
   case(TARGET_REMOVE_OLD_FIRST):
Packit Service a2489d
   case(TARGET_CHMOD):
Packit Service a2489d
   case(TARGET_MKDIR):
Packit Service a2489d
   case(SOURCE_REMOVING_SAME):
Packit Service a2489d
   case(FINISHING):
Packit Service a2489d
   case(DONE):
Packit Service a2489d
   case(LAST_EXEC):
Packit Service a2489d
      Job::ShowRunStatus(s);
Packit Service a2489d
      break;
Packit Service a2489d
Packit Service a2489d
   case(MAKE_TARGET_DIR):
Packit Service a2489d
      s->Show("mkdir `%s' [%s]",target_dir.get(),target_session->CurrentStatus());
Packit Service a2489d
      break;
Packit Service a2489d
Packit Service a2489d
   case(CHANGING_DIR_SOURCE):
Packit Service a2489d
   case(CHANGING_DIR_TARGET):
Packit Service a2489d
      if(target_session->IsOpen() && (!source_session->IsOpen() || now%4>=2))
Packit Service a2489d
	 s->Show("cd `%s' [%s]",target_dir.get(),target_session->CurrentStatus());
Packit Service a2489d
      else if(source_session->IsOpen())
Packit Service a2489d
	 s->Show("cd `%s' [%s]",source_dir.get(),source_session->CurrentStatus());
Packit Service a2489d
      break;
Packit Service a2489d
Packit Service a2489d
   case(GETTING_LIST_INFO):
Packit Service a2489d
      if(target_list_info && (!source_list_info || now%4>=2))
Packit Service a2489d
      {
Packit Service a2489d
	 const char *status=target_list_info->Status();
Packit Service a2489d
	 int status_w=mbswidth(status, 0);
Packit Service a2489d
	 int dw=w-status_w;
Packit Service a2489d
	 if(dw<20)
Packit Service a2489d
	    dw=20;
Packit Service a2489d
	 if(target_relative_dir)
Packit Service a2489d
	    s->Show("%s: %s",squeeze_file_name(target_relative_dir,dw),status);
Packit Service a2489d
	 else
Packit Service a2489d
	    s->Show("%s",status);
Packit Service a2489d
      }
Packit Service a2489d
      else if(source_list_info)
Packit Service a2489d
      {
Packit Service a2489d
	 const char *status=source_list_info->Status();
Packit Service a2489d
	 int status_w=mbswidth(status, 0);
Packit Service a2489d
	 int dw=w-status_w;
Packit Service a2489d
	 if(dw<20)
Packit Service a2489d
	    dw=20;
Packit Service a2489d
	 if(source_relative_dir)
Packit Service a2489d
	    s->Show("%s: %s",squeeze_file_name(source_relative_dir,dw),status);
Packit Service a2489d
	 else
Packit Service a2489d
	    s->Show("%s",status);
Packit Service a2489d
      }
Packit Service a2489d
      break;
Packit Service a2489d
   }
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
xstring& MirrorJob::FormatShortStatus(xstring& s)
Packit Service a2489d
{
Packit Service a2489d
   if(bytes_to_transfer>0 && (!parent_mirror || parent_mirror->bytes_to_transfer!=bytes_to_transfer)) {
Packit Service a2489d
      long long curr_bytes_transferred=GetBytesCount();
Packit Service a2489d
      if(parent_mirror)
Packit Service a2489d
         curr_bytes_transferred+=bytes_transferred;
Packit Service a2489d
      s.appendf("%s/%s (%d%%)",
Packit Service a2489d
	 xhuman(curr_bytes_transferred),xhuman(bytes_to_transfer),
Packit Service a2489d
	 percent(curr_bytes_transferred,bytes_to_transfer));
Packit Service a2489d
      double rate=GetTransferRate();
Packit Service a2489d
      if(rate>=1)
Packit Service a2489d
	 s.append(' ').append(Speedometer::GetStrProper(rate));
Packit Service a2489d
   }
Packit Service a2489d
   return s;
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
void MirrorJob::TransferStarted(CopyJob *cp)
Packit Service a2489d
{
Packit Service a2489d
   if(transfer_count==0)
Packit Service a2489d
      root_mirror->transfer_start_ts=now;
Packit Service a2489d
   JobStarted(cp);
Packit Service a2489d
}
Packit Service a2489d
void MirrorJob::JobStarted(Job *j)
Packit Service a2489d
{
Packit Service a2489d
   AddWaiting(j);
Packit Service a2489d
   transfer_count++;
Packit Service a2489d
}
Packit Service a2489d
void MirrorJob::TransferFinished(Job *j)
Packit Service a2489d
{
Packit Service a2489d
   long long bytes_count=j->GetBytesCount();
Packit Service a2489d
   AddBytesTransferred(bytes_count);
Packit Service a2489d
   stats.bytes+=bytes_count;
Packit Service a2489d
   stats.time +=j->GetTimeSpent();
Packit Service a2489d
   if(j->ExitCode()==0 && verbose_report>=2) {
Packit Service a2489d
      xstring finished;
Packit Service a2489d
      const xstring& cmd=j->GetCmdLine();
Packit Service a2489d
      if(cmd[0]=='\\')
Packit Service a2489d
	 finished.append(cmd+1,cmd.length()-1);
Packit Service a2489d
      else
Packit Service a2489d
	 finished.append(cmd);
Packit Service a2489d
      const xstring& rate=Speedometer::GetStrProper(j->GetTransferRate());
Packit Service a2489d
      if(rate.length()>0)
Packit Service a2489d
	 finished.append(" (").append(rate).append(')');
Packit Service a2489d
      if(!(FlagSet(SCAN_ALL_FIRST) && finished.begins_with("mirror")))
Packit Service a2489d
	 Report(_("Finished %s"),finished.get());
Packit Service a2489d
   }
Packit Service a2489d
   JobFinished(j);
Packit Service a2489d
   if(transfer_count==0)
Packit Service a2489d
      root_mirror->transfer_time_elapsed += now-root_mirror->transfer_start_ts;
Packit Service a2489d
}
Packit Service a2489d
void MirrorJob::JobFinished(Job *j)
Packit Service a2489d
{
Packit Service a2489d
   if(j->ExitCode()!=0)
Packit Service a2489d
      stats.error_count++;
Packit Service a2489d
   RemoveWaiting(j);
Packit Service a2489d
   Delete(j);
Packit Service a2489d
   assert(transfer_count>0);
Packit Service a2489d
   transfer_count--;
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
off_t MirrorJob::GetBytesCount()
Packit Service a2489d
{
Packit Service a2489d
   long long bytes_count=Job::GetBytesCount();
Packit Service a2489d
   if(!parent_mirror) {
Packit Service a2489d
      // bytes_transferred is cumulative over the mirror tree,
Packit Service a2489d
      // add it on the top only
Packit Service a2489d
      bytes_count+=bytes_transferred;
Packit Service a2489d
   }
Packit Service a2489d
   return bytes_count;
Packit Service a2489d
}
Packit Service a2489d
double MirrorJob::GetTimeSpent()
Packit Service a2489d
{
Packit Service a2489d
   double t=transfer_time_elapsed;
Packit Service a2489d
   if(transfer_count>0)
Packit Service a2489d
      t+=now-root_mirror->transfer_start_ts;
Packit Service a2489d
   return t;
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
void  MirrorJob::HandleFile(FileInfo *file)
Packit Service a2489d
{
Packit Service a2489d
   int	 res;
Packit Service a2489d
   struct stat st;
Packit Service a2489d
Packit Service a2489d
   // TODO: get rid of local hacks.
Packit Service a2489d
Packit Service a2489d
   const char *dst_name=file->name;
Packit Service a2489d
   if(FlagSet(TARGET_FLAT))
Packit Service a2489d
      dst_name=basename_ptr(dst_name);
Packit Service a2489d
Packit Service a2489d
   // dir_name returns pointer to static data - need to dup it.
Packit Service a2489d
   const char *source_name=dir_file(source_dir,file->name);
Packit Service a2489d
   source_name=alloca_strdup(source_name);
Packit Service a2489d
   const char *target_name=dir_file(target_dir,dst_name);
Packit Service a2489d
   target_name=alloca_strdup(target_name);
Packit Service a2489d
Packit Service a2489d
   const char *source_name_rel=dir_file(source_relative_dir,file->name);
Packit Service a2489d
   source_name_rel=alloca_strdup(source_name_rel);
Packit Service a2489d
   const char *target_name_rel=dir_file(target_relative_dir,dst_name);
Packit Service a2489d
   target_name_rel=alloca_strdup(target_name_rel);
Packit Service a2489d
Packit Service a2489d
   FileInfo::type filetype=FileInfo::NORMAL;
Packit Service a2489d
   if(file->Has(file->TYPE))
Packit Service a2489d
      filetype=file->filetype;
Packit Service a2489d
   else
Packit Service a2489d
   {
Packit Service a2489d
      FileInfo *target=target_set->FindByName(file->name);
Packit Service a2489d
      if(target && target->Has(target->TYPE))
Packit Service a2489d
	 filetype=target->filetype;
Packit Service a2489d
   }
Packit Service a2489d
Packit Service a2489d
   switch(filetype)
Packit Service a2489d
   {
Packit Service a2489d
      case(FileInfo::NORMAL):
Packit Service a2489d
      case(FileInfo::REDIRECT):
Packit Service a2489d
      {
Packit Service a2489d
	 bool remove_target=false;
Packit Service a2489d
	 bool cont_this=false;
Packit Service a2489d
	 bool use_pget=(pget_n>1) && target_is_local;
Packit Service a2489d
	 if(file->Has(file->SIZE) && file->size
Packit Service a2489d
	    use_pget=false;
Packit Service a2489d
	 if(target_is_local)
Packit Service a2489d
	 {
Packit Service a2489d
	    if(lstat(target_name,&st)!=-1)
Packit Service a2489d
	    {
Packit Service a2489d
	       // few safety checks.
Packit Service a2489d
	       FileInfo *old=new_files_set->FindByName(file->name);
Packit Service a2489d
	       if(old)
Packit Service a2489d
		  goto skip;  // file has appeared after mirror start
Packit Service a2489d
	       old=old_files_set->FindByName(file->name);
Packit Service a2489d
	       if(old && ((old->Has(old->SIZE) && old->size!=st.st_size)
Packit Service a2489d
			||(old->Has(old->DATE) && old->date!=st.st_mtime)))
Packit Service a2489d
		  goto skip;  // the file has changed after mirror start
Packit Service a2489d
	       if(!script_only && access(target_name,W_OK)==-1)
Packit Service a2489d
	       {
Packit Service a2489d
		  // try to enable write access.
Packit Service a2489d
		  chmod(target_name,st.st_mode|0200);
Packit Service a2489d
	       }
Packit Service a2489d
	    }
Packit Service a2489d
	 }
Packit Service a2489d
	 FileInfo *old=target_set->FindByName(FileCopy::TempFileName(file->name));
Packit Service a2489d
	 if(old)
Packit Service a2489d
	 {
Packit Service a2489d
	    if(FlagSet(CONTINUE)
Packit Service a2489d
	    && old->Has(file->TYPE) && old->filetype==old->NORMAL
Packit Service a2489d
	    && (FlagSet(IGNORE_TIME) ||
Packit Service a2489d
		(file->Has(file->DATE) && old->Has(old->DATE)
Packit Service a2489d
		&& file->date + file->date.ts_prec < old->date - old->date.ts_prec))
Packit Service a2489d
	    && file->Has(file->SIZE) && old->Has(old->SIZE)
Packit Service a2489d
	    && file->size >= old->size)
Packit Service a2489d
	    {
Packit Service a2489d
	       cont_this=true;
Packit Service a2489d
	       stats.mod_files++;
Packit Service a2489d
	    }
Packit Service a2489d
	    else if(!to_rm_mismatched->FindByName(file->name))
Packit Service a2489d
	    {
Packit Service a2489d
	       if(!FlagSet(OVERWRITE)) {
Packit Service a2489d
		  remove_target=true;
Packit Service a2489d
		  Report(_("Removing old file `%s'"),target_name_rel);
Packit Service a2489d
	       } else {
Packit Service a2489d
		  Report(_("Overwriting old file `%s'"),target_name_rel);
Packit Service a2489d
	       }
Packit Service a2489d
	       stats.mod_files++;
Packit Service a2489d
	    }
Packit Service a2489d
	    else
Packit Service a2489d
	       stats.new_files++;
Packit Service a2489d
	 }
Packit Service a2489d
	 else if(FlagSet(ONLY_EXISTING))
Packit Service a2489d
	 {
Packit Service a2489d
	    Report(_("Skipping file `%s' (only-existing)"),source_name_rel);
Packit Service a2489d
	    goto skip;
Packit Service a2489d
	 }
Packit Service a2489d
	 else
Packit Service a2489d
	    stats.new_files++;
Packit Service a2489d
Packit Service a2489d
	 Report(_("Transferring file `%s'"),source_name_rel);
Packit Service a2489d
Packit Service a2489d
	 if(script)
Packit Service a2489d
	 {
Packit Service a2489d
	    ArgV args(use_pget?"pget":"get");
Packit Service a2489d
	    if(use_pget)
Packit Service a2489d
	    {
Packit Service a2489d
	       args.Append("-n");
Packit Service a2489d
	       args.Append(pget_n);
Packit Service a2489d
	    }
Packit Service a2489d
	    if(cont_this)
Packit Service a2489d
	       args.Append("-c");
Packit Service a2489d
	    if(remove_target)
Packit Service a2489d
	       args.Append("-e");
Packit Service a2489d
	    if(FlagSet(ASCII))
Packit Service a2489d
	       args.Append("-a");
Packit Service a2489d
	    if(remove_source_files)
Packit Service a2489d
	       args.Append("-E");
Packit Service a2489d
	    args.Append("-O");
Packit Service a2489d
	    args.Append(target_is_local?target_dir.get()
Packit Service a2489d
			:target_session->GetConnectURL().get());
Packit Service a2489d
	    args.Append(source_session->GetFileURL(file->name));
Packit Service a2489d
	    xstring_ca cmd(args.CombineQuoted());
Packit Service a2489d
	    fprintf(script,"%s\n",cmd.get());
Packit Service a2489d
	    if(script_only)
Packit Service a2489d
	       goto skip;
Packit Service a2489d
	 }
Packit Service a2489d
Packit Service a2489d
	 FileCopyPeer *src_peer=0;
Packit Service a2489d
	 if(source_is_local)
Packit Service a2489d
	    src_peer=new FileCopyPeerFDStream(new FileStream(source_name,O_RDONLY),FileCopyPeer::GET);
Packit Service a2489d
	 else
Packit Service a2489d
	    src_peer=new FileCopyPeerFA(source_session->Clone(),file->name,FA::RETRIEVE);
Packit Service a2489d
Packit Service a2489d
	 FileCopyPeer *dst_peer=0;
Packit Service a2489d
	 if(target_is_local)
Packit Service a2489d
	    dst_peer=new FileCopyPeerFDStream(new FileStream(target_name,O_WRONLY|O_CREAT|(cont_this?0:O_TRUNC)),FileCopyPeer::PUT);
Packit Service a2489d
	 else
Packit Service a2489d
	    dst_peer=new FileCopyPeerFA(target_session->Clone(),dst_name,FA::STORE);
Packit Service a2489d
Packit Service a2489d
	 FileCopy *c=FileCopy::New(src_peer,dst_peer,cont_this);
Packit Service a2489d
	 if(remove_source_files)
Packit Service a2489d
	    c->RemoveSourceLater();
Packit Service a2489d
	 if(remove_target)
Packit Service a2489d
	    c->RemoveTargetFirst();
Packit Service a2489d
	 if(FlagSet(ASCII))
Packit Service a2489d
	    c->Ascii();
Packit Service a2489d
	 CopyJob *cp=(use_pget ? new pgetJob(c,file->name,pget_n) : new CopyJob(c,file->name,"mirror"));
Packit Service a2489d
	 if(file->Has(file->DATE))
Packit Service a2489d
	    cp->SetDate(file->date);
Packit Service a2489d
	 if(file->Has(file->SIZE) && !FlagSet(IGNORE_SIZE))
Packit Service a2489d
	    cp->SetSize(file->size);
Packit Service a2489d
	 TransferStarted(cp);
Packit Service a2489d
	 cp->cmdline.vset("\\transfer `",source_name_rel,"'",NULL);
Packit Service a2489d
Packit Service a2489d
	 set_state(WAITING_FOR_TRANSFER);
Packit Service a2489d
	 break;
Packit Service a2489d
      }
Packit Service a2489d
      case(FileInfo::DIRECTORY):
Packit Service a2489d
      {
Packit Service a2489d
	 if(recursion_mode==RECURSION_NEVER || FlagSet(NO_RECURSION))
Packit Service a2489d
	    goto skip;
Packit Service a2489d
Packit Service a2489d
	 bool create_target_subdir=true;
Packit Service a2489d
	 const FileInfo *old=0;
Packit Service a2489d
Packit Service a2489d
	 if(FlagSet(TARGET_FLAT)) {
Packit Service a2489d
	    create_target_subdir=false;
Packit Service a2489d
	    target_name=target_dir;
Packit Service a2489d
	    goto do_submirror;
Packit Service a2489d
	 }
Packit Service a2489d
Packit Service a2489d
	 if(target_set)
Packit Service a2489d
	    old=target_set->FindByName(file->name);
Packit Service a2489d
	 if(!old)
Packit Service a2489d
	 {
Packit Service a2489d
	    if(FlagSet(ONLY_EXISTING))
Packit Service a2489d
	    {
Packit Service a2489d
	       Report(_("Skipping directory `%s' (only-existing)"),target_name_rel);
Packit Service a2489d
	       goto skip;
Packit Service a2489d
	    }
Packit Service a2489d
	 }
Packit Service a2489d
	 else if(old->TypeIs(old->DIRECTORY))
Packit Service a2489d
	 {
Packit Service a2489d
	    create_target_subdir=false;
Packit Service a2489d
	 }
Packit Service a2489d
	 if(target_is_local && !script_only)
Packit Service a2489d
	 {
Packit Service a2489d
	    if((FlagSet(RETR_SYMLINKS)?stat:lstat)(target_name,&st)!=-1)
Packit Service a2489d
	    {
Packit Service a2489d
	       if(S_ISDIR(st.st_mode))
Packit Service a2489d
	       {
Packit Service a2489d
		  // try to enable write access
Packit Service a2489d
		  // only if not enabled as chmod can clear sgid flags on directories
Packit Service a2489d
		  if(st.st_mode!=(st.st_mode|0700))
Packit Service a2489d
		     chmod(target_name,st.st_mode|0700);
Packit Service a2489d
		  create_target_subdir=false;
Packit Service a2489d
	       }
Packit Service a2489d
	       else
Packit Service a2489d
	       {
Packit Service a2489d
		  Report(_("Removing old local file `%s'"),target_name_rel);
Packit Service a2489d
		  if(remove(target_name)==-1)
Packit Service a2489d
		  {
Packit Service a2489d
		     eprintf("mirror: remove(%s): %s\n",target_name,strerror(errno));
Packit Service a2489d
		     goto skip;
Packit Service a2489d
		  }
Packit Service a2489d
		  create_target_subdir=true;
Packit Service a2489d
	       }
Packit Service a2489d
	    }
Packit Service a2489d
	 }
Packit Service a2489d
Packit Service a2489d
      do_submirror:
Packit Service a2489d
	 // launch sub-mirror
Packit Service a2489d
	 MirrorJob *mj=new MirrorJob(this,
Packit Service a2489d
	    source_session->Clone(),target_session->Clone(),
Packit Service a2489d
	    source_name,target_name);
Packit Service a2489d
	 AddWaiting(mj);
Packit Service a2489d
	 mj->cmdline.vset("\\mirror `",source_name_rel,"'",NULL);
Packit Service a2489d
Packit Service a2489d
	 mj->source_relative_dir.set(source_name_rel);
Packit Service a2489d
	 mj->target_relative_dir.set(target_name_rel);
Packit Service a2489d
Packit Service a2489d
	 mj->create_target_dir=create_target_subdir;
Packit Service a2489d
Packit Service a2489d
	 if(verbose_report>=3) {
Packit Service a2489d
	    if(FlagSet(SCAN_ALL_FIRST))
Packit Service a2489d
	       Report(_("Scanning directory `%s'"),mj->target_relative_dir.get());
Packit Service a2489d
	    else
Packit Service a2489d
	       Report(_("Mirroring directory `%s'"),mj->target_relative_dir.get());
Packit Service a2489d
	 }
Packit Service a2489d
Packit Service a2489d
	 break;
Packit Service a2489d
      }
Packit Service a2489d
      case(FileInfo::SYMLINK):
Packit Service a2489d
      {
Packit Service a2489d
	 if(FlagSet(NO_SYMLINKS))
Packit Service a2489d
	    goto skip;
Packit Service a2489d
Packit Service a2489d
	 if(!file->symlink)
Packit Service a2489d
	    goto skip;
Packit Service a2489d
Packit Service a2489d
	 if(!target_is_local)
Packit Service a2489d
	 {
Packit Service a2489d
	    if(script)
Packit Service a2489d
	    {
Packit Service a2489d
	       ArgV args("ln");
Packit Service a2489d
	       args.Append("-s");
Packit Service a2489d
	       args.Append(file->symlink);
Packit Service a2489d
	       args.Append(target_name);
Packit Service a2489d
	       xstring_ca cmd(args.CombineQuoted());
Packit Service a2489d
	       fprintf(script,"%s\n",cmd.get());
Packit Service a2489d
	       if(script_only)
Packit Service a2489d
		  goto skip;
Packit Service a2489d
	    }
Packit Service a2489d
	    bool remove_target=false;
Packit Service a2489d
	    FileInfo *old=target_set->FindByName(file->name);
Packit Service a2489d
	    if(old && !to_rm_mismatched->FindByName(file->name))
Packit Service a2489d
	    {
Packit Service a2489d
	       Report(_("Removing old file `%s'"),target_name_rel);
Packit Service a2489d
	       remove_target=true;
Packit Service a2489d
	       stats.mod_symlinks++;
Packit Service a2489d
	    }
Packit Service a2489d
	    else
Packit Service a2489d
	       stats.new_symlinks++;
Packit Service a2489d
	    Report(_("Making symbolic link `%s' to `%s'"),target_name_rel,file->symlink.get());
Packit Service a2489d
	    mvJob *j=new mvJob(target_session->Clone(),file->symlink,target_name,FA::SYMLINK);
Packit Service a2489d
	    if(remove_target)
Packit Service a2489d
	       j->RemoveTargetFirst();
Packit Service a2489d
	    JobStarted(j);
Packit Service a2489d
	    RemoveSourceLater(file);
Packit Service a2489d
	    break;
Packit Service a2489d
	 }
Packit Service a2489d
Packit Service a2489d
	 if(script)
Packit Service a2489d
	 {
Packit Service a2489d
	    ArgV args("shell");
Packit Service a2489d
	    args.Append("ln");
Packit Service a2489d
	    args.Append("-sf");
Packit Service a2489d
	    args.Append(shell_encode(file->symlink));
Packit Service a2489d
	    args.Append(shell_encode(target_name));
Packit Service a2489d
	    xstring_ca cmd(args.CombineQuoted());
Packit Service a2489d
	    fprintf(script,"%s\n",cmd.get());
Packit Service a2489d
	    if(script_only)
Packit Service a2489d
	       goto skip;
Packit Service a2489d
	 }
Packit Service a2489d
Packit Service a2489d
	 struct stat st;
Packit Service a2489d
	 if(lstat(target_name,&st)!=-1)
Packit Service a2489d
	 {
Packit Service a2489d
	    Report(_("Removing old local file `%s'"),target_name_rel);
Packit Service a2489d
	    stats.mod_symlinks++;
Packit Service a2489d
	    if(remove(target_name)==-1)
Packit Service a2489d
	    {
Packit Service a2489d
	       eprintf("mirror: remove(%s): %s\n",target_name,strerror(errno));
Packit Service a2489d
	       goto skip;
Packit Service a2489d
	    }
Packit Service a2489d
	 }
Packit Service a2489d
	 else
Packit Service a2489d
	 {
Packit Service a2489d
	    if(FlagSet(ONLY_EXISTING))
Packit Service a2489d
	    {
Packit Service a2489d
	       Report(_("Skipping symlink `%s' (only-existing)"),target_name_rel);
Packit Service a2489d
	       goto skip;
Packit Service a2489d
	    }
Packit Service a2489d
	    stats.new_symlinks++;
Packit Service a2489d
	 }
Packit Service a2489d
	 Report(_("Making symbolic link `%s' to `%s'"),target_name_rel,file->symlink.get());
Packit Service a2489d
	 res=symlink(file->symlink,target_name);
Packit Service a2489d
	 if(res==-1)
Packit Service a2489d
	    eprintf("mirror: symlink(%s): %s\n",target_name,strerror(errno));
Packit Service a2489d
	 RemoveSourceLater(file);
Packit Service a2489d
	 break;
Packit Service a2489d
      }
Packit Service a2489d
   case FileInfo::UNKNOWN:
Packit Service a2489d
      break;
Packit Service a2489d
   }
Packit Service a2489d
skip:
Packit Service a2489d
   return;
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
void  MirrorJob::InitSets()
Packit Service a2489d
{
Packit Service a2489d
   if(FlagSet(TARGET_FLAT) && !parent_mirror && target_set)
Packit Service a2489d
      source_set->Sort(FileSet::BYNAME_FLAT);
Packit Service a2489d
Packit Service a2489d
   source_set->Count(NULL,&stats.tot_files,&stats.tot_symlinks,&stats.tot_files);
Packit Service a2489d
Packit Service a2489d
   to_rm=new FileSet(target_set);
Packit Service a2489d
   to_rm->SubtractAny(source_set);
Packit Service a2489d
Packit Service a2489d
   if(FlagSet(DELETE_EXCLUDED) && target_set_excluded)
Packit Service a2489d
      to_rm->Merge(target_set_excluded);
Packit Service a2489d
Packit Service a2489d
   to_transfer=new FileSet(source_set);
Packit Service a2489d
Packit Service a2489d
   if(!FlagSet(TRANSFER_ALL)) {
Packit Service a2489d
      same=new FileSet(source_set);
Packit Service a2489d
Packit Service a2489d
      int ignore=0;
Packit Service a2489d
      if(FlagSet(ONLY_NEWER))
Packit Service a2489d
	 ignore|=FileInfo::IGNORE_SIZE_IF_OLDER|FileInfo::IGNORE_DATE_IF_OLDER;
Packit Service a2489d
      if(!FlagSet(UPLOAD_OLDER) && strcmp(target_session->GetProto(),"file"))
Packit Service a2489d
	 ignore|=FileInfo::IGNORE_DATE_IF_OLDER;
Packit Service a2489d
      if(FlagSet(IGNORE_TIME))
Packit Service a2489d
	 ignore|=FileInfo::DATE;
Packit Service a2489d
      if(FlagSet(IGNORE_SIZE))
Packit Service a2489d
	 ignore|=FileInfo::SIZE;
Packit Service a2489d
      to_transfer->SubtractSame(target_set,ignore);
Packit Service a2489d
Packit Service a2489d
      same->SubtractAny(to_transfer);
Packit Service a2489d
   }
Packit Service a2489d
Packit Service a2489d
   if(newer_than!=NO_DATE)
Packit Service a2489d
      to_transfer->SubtractNotNewerThan(newer_than);
Packit Service a2489d
   if(older_than!=NO_DATE)
Packit Service a2489d
      to_transfer->SubtractNotOlderThan(older_than);
Packit Service a2489d
   if(size_range)
Packit Service a2489d
      to_transfer->SubtractSizeOutside(size_range);
Packit Service a2489d
Packit Service a2489d
   if(FlagSet(SCAN_ALL_FIRST)) {
Packit Service a2489d
      to_mkdir=new FileSet(to_transfer);
Packit Service a2489d
      to_mkdir->SubtractNotDirs();
Packit Service a2489d
      to_mkdir->SubtractAny(target_set);
Packit Service a2489d
   }
Packit Service a2489d
Packit Service a2489d
   switch(recursion_mode) {
Packit Service a2489d
   case RECURSION_NEVER:
Packit Service a2489d
      to_transfer->SubtractDirs();
Packit Service a2489d
      break;
Packit Service a2489d
   case RECURSION_MISSING:
Packit Service a2489d
      to_transfer->SubtractDirs(target_set);
Packit Service a2489d
      break;
Packit Service a2489d
   case RECURSION_NEWER:
Packit Service a2489d
      to_transfer->SubtractNotOlderDirs(target_set);
Packit Service a2489d
      break;
Packit Service a2489d
   case RECURSION_ALWAYS:
Packit Service a2489d
      break;
Packit Service a2489d
   }
Packit Service a2489d
Packit Service a2489d
   if(skip_noaccess)
Packit Service a2489d
      to_transfer->ExcludeUnaccessible(source_session->GetUser());
Packit Service a2489d
Packit Service a2489d
   new_files_set=new FileSet(to_transfer);
Packit Service a2489d
   new_files_set->SubtractAny(target_set);
Packit Service a2489d
   old_files_set=new FileSet(target_set);
Packit Service a2489d
   old_files_set->SubtractNotIn(to_transfer);
Packit Service a2489d
Packit Service a2489d
   to_rm_mismatched=new FileSet(old_files_set);
Packit Service a2489d
   to_rm_mismatched->SubtractSameType(to_transfer);
Packit Service a2489d
   to_rm_mismatched->SubtractNotDirs();
Packit Service a2489d
Packit Service a2489d
   if(!FlagSet(DELETE))
Packit Service a2489d
      to_transfer->SubtractAny(to_rm_mismatched);
Packit Service a2489d
Packit Service a2489d
   if(FlagSet(TARGET_FLAT) && !parent_mirror && target_set) {
Packit Service a2489d
      source_set->Unsort();
Packit Service a2489d
      to_transfer->UnsortFlat();
Packit Service a2489d
      to_transfer->SubtractDirs();
Packit Service a2489d
      same->UnsortFlat();
Packit Service a2489d
      to_mkdir->Empty();
Packit Service a2489d
      new_files_set->UnsortFlat();
Packit Service a2489d
   }
Packit Service a2489d
Packit Service a2489d
   const char *sort_by=ResMgr::Query("mirror:sort-by",0);
Packit Service a2489d
   bool desc=strstr(sort_by,"-desc");
Packit Service a2489d
   if(!strncmp(sort_by,"name",4))
Packit Service a2489d
      to_transfer->SortByPatternList(ResMgr::Query("mirror:order",0));
Packit Service a2489d
   else if(!strncmp(sort_by,"date",4))
Packit Service a2489d
      to_transfer->Sort(FileSet::BYDATE);
Packit Service a2489d
   else if(!strncmp(sort_by,"size",4))
Packit Service a2489d
      to_transfer->Sort(FileSet::BYSIZE,false,true);
Packit Service a2489d
   if(desc)
Packit Service a2489d
      to_transfer->ReverseSort();
Packit Service a2489d
Packit Service a2489d
   int dir_count=0;
Packit Service a2489d
   if(to_mkdir) {
Packit Service a2489d
      to_mkdir->Count(&dir_count,NULL,NULL,NULL);
Packit Service a2489d
      only_dirs = (dir_count==to_mkdir->count());
Packit Service a2489d
   } else {
Packit Service a2489d
      to_transfer->Count(&dir_count,NULL,NULL,NULL);
Packit Service a2489d
      only_dirs = (dir_count==to_transfer->count());
Packit Service a2489d
   }
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
/* root_transfer_count of child mirrors contains the value to add or
Packit Service a2489d
   subtract from transfer_count when doing "cd", "mkdir", "ls" on the source
Packit Service a2489d
   or target directories. This would prevent other mirrors (siblings, etc)
Packit Service a2489d
   from starting more jobs.
Packit Service a2489d
Packit Service a2489d
   root_transfer_count is initialized once in ctor, so that change of
Packit Service a2489d
   mirror:parallel-directories setting won't disbalance the count.
Packit Service a2489d
*/
Packit Service a2489d
void MirrorJob::MirrorStarted()
Packit Service a2489d
{
Packit Service a2489d
   if(!parent_mirror)
Packit Service a2489d
      return;
Packit Service a2489d
   transfer_count+=root_transfer_count;
Packit Service a2489d
}
Packit Service a2489d
void MirrorJob::MirrorFinished()
Packit Service a2489d
{
Packit Service a2489d
   if(!parent_mirror)
Packit Service a2489d
      return;
Packit Service a2489d
   assert(transfer_count>=root_transfer_count);
Packit Service a2489d
   transfer_count-=root_transfer_count;
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
void MirrorJob::HandleChdir(FileAccessRef& session, int &redirections)
Packit Service a2489d
{
Packit Service a2489d
   if(!session->IsOpen())
Packit Service a2489d
      return;
Packit Service a2489d
   int res=session->Done();
Packit Service a2489d
   if(res<0)
Packit Service a2489d
   {
Packit Service a2489d
      if(res==FA::FILE_MOVED)
Packit Service a2489d
      {
Packit Service a2489d
	 // cd to another url.
Packit Service a2489d
	 const char *loc_c=session->GetNewLocation();
Packit Service a2489d
	 int max_redirections=ResMgr::Query("xfer:max-redirections",0);
Packit Service a2489d
	 if(loc_c && max_redirections>0)
Packit Service a2489d
	 {
Packit Service a2489d
	    if(++redirections>max_redirections)
Packit Service a2489d
	       goto cd_err_normal;
Packit Service a2489d
	    eprintf(_("%s: received redirection to `%s'\n"),"mirror",loc_c);
Packit Service a2489d
Packit Service a2489d
	    char *loc=alloca_strdup(loc_c);
Packit Service a2489d
	    ParsedURL u(loc,true);
Packit Service a2489d
Packit Service a2489d
	    bool is_file=(last_char(loc)!='/');
Packit Service a2489d
	    if(!u.proto)
Packit Service a2489d
	    {
Packit Service a2489d
	       FileAccess::Path new_cwd(session->GetNewCwd());
Packit Service a2489d
	       new_cwd.Change(0,is_file,loc);
Packit Service a2489d
	       session->PathVerify(new_cwd);
Packit Service a2489d
	       session->Roll();
Packit Service a2489d
	       return;
Packit Service a2489d
	    }
Packit Service a2489d
	    session->Close(); // loc_c is no longer valid.
Packit Service a2489d
	    session=FA::New(&u);
Packit Service a2489d
	    FileAccess::Path new_cwd(u.path,is_file,url::path_ptr(loc));
Packit Service a2489d
	    session->PathVerify(new_cwd);
Packit Service a2489d
	    return;
Packit Service a2489d
	 }
Packit Service a2489d
      }
Packit Service a2489d
   cd_err_normal:
Packit Service a2489d
      if(session==target_session && (script_only || FlagSet(SCAN_ALL_FIRST)))
Packit Service a2489d
      {
Packit Service a2489d
	 char *dir=alloca_strdup(session->GetFile());
Packit Service a2489d
	 session->Close();
Packit Service a2489d
	 session->Chdir(dir,false);
Packit Service a2489d
	 no_target_dir=true;
Packit Service a2489d
	 return;
Packit Service a2489d
      }
Packit Service a2489d
      if(session==source_session && create_target_dir
Packit Service a2489d
      && !FlagSet(NO_EMPTY_DIRS) && !skip_noaccess && parent_mirror)
Packit Service a2489d
      {
Packit Service a2489d
	 // create target dir even if failed to cd to source dir.
Packit Service a2489d
	 if(script)
Packit Service a2489d
	    fprintf(script,"mkdir %s\n",target_session->GetFileURL(target_dir).get());
Packit Service a2489d
	 if(!script_only)
Packit Service a2489d
	 {
Packit Service a2489d
	    ArgV *a=new ArgV("mkdir");
Packit Service a2489d
	    a->Append(target_dir);
Packit Service a2489d
	    mkdirJob *mkj=new mkdirJob(target_session->Clone(),a);
Packit Service a2489d
	    a->CombineTo(mkj->cmdline);
Packit Service a2489d
	    JobStarted(mkj);
Packit Service a2489d
	 }
Packit Service a2489d
      }
Packit Service a2489d
      remove_this_source_dir=false;
Packit Service a2489d
      eprintf("mirror: %s\n",session->StrError(res));
Packit Service a2489d
      stats.error_count++;
Packit Service a2489d
      MirrorFinished();
Packit Service a2489d
      set_state(FINISHING);
Packit Service a2489d
      source_session->Close();
Packit Service a2489d
      target_session->Close();
Packit Service a2489d
      return;
Packit Service a2489d
   }
Packit Service a2489d
   if(res==FA::OK)
Packit Service a2489d
      session->Close();
Packit Service a2489d
}
Packit Service a2489d
void MirrorJob::HandleListInfoCreation(const FileAccessRef& session,SMTaskRef<ListInfo>& list_info,const char *relative_dir)
Packit Service a2489d
{
Packit Service a2489d
   if(state!=GETTING_LIST_INFO)
Packit Service a2489d
      return;
Packit Service a2489d
Packit Service a2489d
   if(session==target_session && no_target_dir)
Packit Service a2489d
   {
Packit Service a2489d
      target_set=new FileSet();
Packit Service a2489d
      return;
Packit Service a2489d
   }
Packit Service a2489d
Packit Service a2489d
   list_info=session->MakeListInfo();
Packit Service a2489d
   if(list_info==0)
Packit Service a2489d
   {
Packit Service a2489d
      eprintf(_("mirror: protocol `%s' is not suitable for mirror\n"),
Packit Service a2489d
	       session->GetProto());
Packit Service a2489d
      MirrorFinished();
Packit Service a2489d
      set_state(FINISHING);
Packit Service a2489d
      return;
Packit Service a2489d
   }
Packit Service a2489d
   list_info->UseCache(use_cache);
Packit Service a2489d
   int need=FileInfo::ALL_INFO;
Packit Service a2489d
   if(FlagSet(IGNORE_TIME))
Packit Service a2489d
      need&=~FileInfo::DATE;
Packit Service a2489d
   if(FlagSet(IGNORE_SIZE))
Packit Service a2489d
      need&=~FileInfo::SIZE;
Packit Service a2489d
   list_info->Need(need);
Packit Service a2489d
   if(FlagSet(RETR_SYMLINKS))
Packit Service a2489d
      list_info->FollowSymlinks();
Packit Service a2489d
Packit Service a2489d
   list_info->SetExclude(relative_dir,top_exclude?top_exclude:exclude);
Packit Service a2489d
   list_info->Roll();
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
void MirrorJob::HandleListInfo(SMTaskRef<ListInfo>& list_info, Ref<FileSet>& set, Ref<FileSet> *fsx)
Packit Service a2489d
{
Packit Service a2489d
   if(!list_info)
Packit Service a2489d
      return;
Packit Service a2489d
   if(!list_info->Done())
Packit Service a2489d
      return;
Packit Service a2489d
   if(list_info->Error())
Packit Service a2489d
   {
Packit Service a2489d
      eprintf("mirror: %s\n",list_info->ErrorText());
Packit Service a2489d
      stats.error_count++;
Packit Service a2489d
      MirrorFinished();
Packit Service a2489d
      set_state(FINISHING);
Packit Service a2489d
      source_list_info=0;
Packit Service a2489d
      target_list_info=0;
Packit Service a2489d
      return;
Packit Service a2489d
   }
Packit Service a2489d
   set=list_info->GetResult();
Packit Service a2489d
   if(fsx)
Packit Service a2489d
      *fsx=list_info->GetExcluded();
Packit Service a2489d
   list_info=0;
Packit Service a2489d
   set->ExcludeDots(); // don't need .. and .
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
int   MirrorJob::Do()
Packit Service a2489d
{
Packit Service a2489d
   int	 res;
Packit Service a2489d
   int	 m=STALL;
Packit Service a2489d
   FileInfo *file;
Packit Service a2489d
   Job	 *j;
Packit Service a2489d
Packit Service a2489d
   switch(state)
Packit Service a2489d
   {
Packit Service a2489d
   case(INITIAL_STATE):
Packit Service a2489d
      remove_this_source_dir=(remove_source_dirs && source_dir.last_char()!='/');
Packit Service a2489d
      if(!strcmp(target_dir,".") || !strcmp(target_dir,"..") || (FlagSet(SCAN_ALL_FIRST) && parent_mirror))
Packit Service a2489d
	 create_target_dir=false;
Packit Service a2489d
Packit Service a2489d
      source_session->Chdir(source_dir);
Packit Service a2489d
      source_redirections=0;
Packit Service a2489d
      source_session->Roll();
Packit Service a2489d
      set_state(CHANGING_DIR_SOURCE);
Packit Service a2489d
      m=MOVED;
Packit Service a2489d
      /*fallthrough*/
Packit Service a2489d
   case(CHANGING_DIR_SOURCE):
Packit Service a2489d
      HandleChdir(source_session,source_redirections);
Packit Service a2489d
      if(state!=CHANGING_DIR_SOURCE)
Packit Service a2489d
	 return MOVED;
Packit Service a2489d
      if(source_session->IsOpen())
Packit Service a2489d
	 return m;
Packit Service a2489d
Packit Service a2489d
      source_dir.set(source_session->GetCwd().GetDirectory());
Packit Service a2489d
Packit Service a2489d
   pre_MAKE_TARGET_DIR:
Packit Service a2489d
   {
Packit Service a2489d
      if(!create_target_dir)
Packit Service a2489d
	 goto pre_CHANGING_DIR_TARGET;
Packit Service a2489d
      if(target_is_local)
Packit Service a2489d
      {
Packit Service a2489d
	 struct stat st;
Packit Service a2489d
	 if((FlagSet(RETR_SYMLINKS)?stat:lstat)(target_dir,&st)!=-1)
Packit Service a2489d
	 {
Packit Service a2489d
	    if(S_ISDIR(st.st_mode))
Packit Service a2489d
	    {
Packit Service a2489d
	       // try to enable write access
Packit Service a2489d
	       // only if not enabled as chmod can clear sgid flags on directories
Packit Service a2489d
	       if(!script_only && (st.st_mode!=(st.st_mode|0700)))
Packit Service a2489d
		  chmod(target_dir,st.st_mode|0700);
Packit Service a2489d
	       create_target_dir=false;
Packit Service a2489d
	       goto pre_CHANGING_DIR_TARGET;
Packit Service a2489d
	    }
Packit Service a2489d
	    else
Packit Service a2489d
	    {
Packit Service a2489d
	       Report(_("Removing old local file `%s'"),target_dir.get());
Packit Service a2489d
	       if(script)
Packit Service a2489d
	       {
Packit Service a2489d
		  ArgV args("rm");
Packit Service a2489d
		  args.Append(target_session->GetFileURL(target_dir));
Packit Service a2489d
		  xstring_ca cmd(args.CombineQuoted());
Packit Service a2489d
		  fprintf(script,"%s\n",cmd.get());
Packit Service a2489d
	       }
Packit Service a2489d
	       if(!script_only)
Packit Service a2489d
	       {
Packit Service a2489d
		  if(remove(target_dir)==-1)
Packit Service a2489d
		     eprintf("mirror: remove(%s): %s\n",target_dir.get(),strerror(errno));
Packit Service a2489d
	       }
Packit Service a2489d
	    }
Packit Service a2489d
	 }
Packit Service a2489d
      }
Packit Service a2489d
Packit Service a2489d
      if(FlagSet(DEPTH_FIRST))
Packit Service a2489d
	 goto pre_GETTING_LIST_INFO;
Packit Service a2489d
Packit Service a2489d
      if(target_relative_dir)
Packit Service a2489d
	 Report(_("Making directory `%s'"),target_relative_dir.get());
Packit Service a2489d
      bool mkdir_p=(parent_mirror==0 || parent_mirror->create_target_dir);
Packit Service a2489d
      if(script)
Packit Service a2489d
      {
Packit Service a2489d
	 ArgV args("mkdir");
Packit Service a2489d
	 if(mkdir_p)
Packit Service a2489d
	    args.Append("-p");
Packit Service a2489d
	 args.Append(target_session->GetFileURL(target_dir));
Packit Service a2489d
	 xstring_ca cmd(args.CombineQuoted());
Packit Service a2489d
	 fprintf(script,"%s\n",cmd.get());
Packit Service a2489d
	 if(script_only)
Packit Service a2489d
	    goto pre_CHANGING_DIR_TARGET;
Packit Service a2489d
      }
Packit Service a2489d
      target_session->Mkdir(target_dir,mkdir_p);
Packit Service a2489d
      set_state(MAKE_TARGET_DIR);
Packit Service a2489d
      m=MOVED;
Packit Service a2489d
   }
Packit Service a2489d
      /*fallthrough*/
Packit Service a2489d
   case(MAKE_TARGET_DIR):
Packit Service a2489d
      res=target_session->Done();
Packit Service a2489d
      if(res==FA::IN_PROGRESS)
Packit Service a2489d
	 return m;
Packit Service a2489d
      target_session->Close();
Packit Service a2489d
      create_target_dir=false;
Packit Service a2489d
Packit Service a2489d
   pre_CHANGING_DIR_TARGET:
Packit Service a2489d
      target_session->Chdir(target_dir);
Packit Service a2489d
      target_redirections=0;
Packit Service a2489d
      target_session->Roll();
Packit Service a2489d
      set_state(CHANGING_DIR_TARGET);
Packit Service a2489d
      m=MOVED;
Packit Service a2489d
      /*fallthrough*/
Packit Service a2489d
   case(CHANGING_DIR_TARGET):
Packit Service a2489d
      HandleChdir(target_session,target_redirections);
Packit Service a2489d
      if(state!=CHANGING_DIR_TARGET)
Packit Service a2489d
	 return MOVED;
Packit Service a2489d
      if(target_session->IsOpen())
Packit Service a2489d
	 return m;
Packit Service a2489d
      create_target_dir=false;
Packit Service a2489d
Packit Service a2489d
      target_dir.set(target_session->GetCwd().GetDirectory());
Packit Service a2489d
Packit Service a2489d
   pre_GETTING_LIST_INFO:
Packit Service a2489d
      set_state(GETTING_LIST_INFO);
Packit Service a2489d
      m=MOVED;
Packit Service a2489d
      if(!source_set)
Packit Service a2489d
	 HandleListInfoCreation(source_session,source_list_info,source_relative_dir);
Packit Service a2489d
      if(!target_set && !create_target_dir
Packit Service a2489d
      && (!FlagSet(DEPTH_FIRST) || FlagSet(ONLY_EXISTING))
Packit Service a2489d
      && !(FlagSet(TARGET_FLAT) && parent_mirror))
Packit Service a2489d
	 HandleListInfoCreation(target_session,target_list_info,target_relative_dir);
Packit Service a2489d
      if(state!=GETTING_LIST_INFO)
Packit Service a2489d
      {
Packit Service a2489d
	 source_list_info=0;
Packit Service a2489d
	 target_list_info=0;
Packit Service a2489d
      }
Packit Service a2489d
      return m;	  // give time to other tasks
Packit Service a2489d
   case(GETTING_LIST_INFO):
Packit Service a2489d
      HandleListInfo(source_list_info,source_set);
Packit Service a2489d
      HandleListInfo(target_list_info,target_set,&target_set_excluded);
Packit Service a2489d
      if(state!=GETTING_LIST_INFO)
Packit Service a2489d
	 return MOVED;
Packit Service a2489d
      if(source_list_info || target_list_info)
Packit Service a2489d
	 return m;
Packit Service a2489d
Packit Service a2489d
      MirrorFinished(); // leave room for transfers.
Packit Service a2489d
Packit Service a2489d
      if(FlagSet(DEPTH_FIRST) && source_set && !target_set)
Packit Service a2489d
      {
Packit Service a2489d
	 // transfer directories first
Packit Service a2489d
	 InitSets();
Packit Service a2489d
	 to_transfer->Unsort();
Packit Service a2489d
	 to_transfer->SubtractNotDirs();
Packit Service a2489d
	 goto pre_WAITING_FOR_TRANSFER;
Packit Service a2489d
      }
Packit Service a2489d
Packit Service a2489d
      // now we have both target and source file sets.
Packit Service a2489d
      if(parent_mirror)
Packit Service a2489d
	 stats.dirs++;
Packit Service a2489d
Packit Service a2489d
      if(FlagSet(SCAN_ALL_FIRST) && parent_mirror)
Packit Service a2489d
      {
Packit Service a2489d
	 source_set->PrependPath(source_relative_dir);
Packit Service a2489d
	 if(root_mirror->source_set_recursive)
Packit Service a2489d
	    root_mirror->source_set_recursive->Merge(source_set);
Packit Service a2489d
	 else
Packit Service a2489d
	    root_mirror->source_set_recursive=source_set.borrow();
Packit Service a2489d
	 if(target_set) {
Packit Service a2489d
	    target_set->PrependPath(target_relative_dir);
Packit Service a2489d
	    if(root_mirror->target_set_recursive)
Packit Service a2489d
	       root_mirror->target_set_recursive->Merge(target_set);
Packit Service a2489d
	    else
Packit Service a2489d
	       root_mirror->target_set_recursive=target_set.borrow();
Packit Service a2489d
	 }
Packit Service a2489d
	 if(target_set_excluded) {
Packit Service a2489d
	    target_set_excluded->PrependPath(target_relative_dir);
Packit Service a2489d
	    if(root_mirror->target_set_excluded)
Packit Service a2489d
	       root_mirror->target_set_excluded->Merge(target_set_excluded);
Packit Service a2489d
	    else
Packit Service a2489d
	       root_mirror->target_set_excluded=target_set_excluded.borrow();
Packit Service a2489d
	 }
Packit Service a2489d
	 root_mirror->stats.dirs++;
Packit Service a2489d
	 transfer_count++; // parent mirror will decrement it.
Packit Service a2489d
	 goto pre_DONE;
Packit Service a2489d
      }
Packit Service a2489d
Packit Service a2489d
      if(source_set_recursive) {
Packit Service a2489d
	 source_set->Merge(source_set_recursive);
Packit Service a2489d
	 source_set_recursive=0;
Packit Service a2489d
      }
Packit Service a2489d
      if(target_set_recursive) {
Packit Service a2489d
	 target_set->Merge(target_set_recursive);
Packit Service a2489d
	 target_set_recursive=0;
Packit Service a2489d
      }
Packit Service a2489d
      InitSets();
Packit Service a2489d
Packit Service a2489d
      to_transfer->CountBytes(&bytes_to_transfer);
Packit Service a2489d
      if(parent_mirror)
Packit Service a2489d
	 parent_mirror->AddBytesToTransfer(bytes_to_transfer);
Packit Service a2489d
Packit Service a2489d
      to_rm->Count(&stats.del_dirs,&stats.del_files,&stats.del_symlinks,&stats.del_files);
Packit Service a2489d
      to_rm->rewind();
Packit Service a2489d
      to_rm_mismatched->Count(&stats.del_dirs,&stats.del_files,&stats.del_symlinks,&stats.del_files);
Packit Service a2489d
      to_rm_mismatched->rewind();
Packit Service a2489d
Packit Service a2489d
      target_set->Merge(target_set_excluded);
Packit Service a2489d
      target_set_excluded=0;
Packit Service a2489d
Packit Service a2489d
      set_state(TARGET_REMOVE_OLD_FIRST);
Packit Service a2489d
      goto TARGET_REMOVE_OLD_FIRST_label;
Packit Service a2489d
Packit Service a2489d
   pre_TARGET_MKDIR:
Packit Service a2489d
      if(!to_mkdir)
Packit Service a2489d
	 goto pre_WAITING_FOR_TRANSFER;
Packit Service a2489d
      to_mkdir->rewind();
Packit Service a2489d
      set_state(TARGET_MKDIR);
Packit Service a2489d
      m=MOVED;
Packit Service a2489d
      /*fallthrough*/
Packit Service a2489d
   case(TARGET_MKDIR):
Packit Service a2489d
      while((j=FindDoneAwaitedJob())!=0)
Packit Service a2489d
      {
Packit Service a2489d
	 JobFinished(j);
Packit Service a2489d
	 m=MOVED;
Packit Service a2489d
      }
Packit Service a2489d
      if(max_error_count>0 && stats.error_count>=max_error_count)
Packit Service a2489d
	 goto pre_FINISHING;
Packit Service a2489d
      while(transfer_count
Packit Service a2489d
      {
Packit Service a2489d
	 file=to_mkdir->curr();
Packit Service a2489d
	 if(!file)
Packit Service a2489d
	    goto pre_WAITING_FOR_TRANSFER;
Packit Service a2489d
	 to_mkdir->next();
Packit Service a2489d
	 if(!file->TypeIs(file->DIRECTORY))
Packit Service a2489d
	    continue;
Packit Service a2489d
	 if(script)
Packit Service a2489d
	    fprintf(script,"mkdir %s\n",target_session->GetFileURL(file->name).get());
Packit Service a2489d
	 if(!script_only)
Packit Service a2489d
	 {
Packit Service a2489d
	    ArgV *a=new ArgV("mkdir");
Packit Service a2489d
	    a->Append(file->name);
Packit Service a2489d
	    mkdirJob *mkj=new mkdirJob(target_session->Clone(),a);
Packit Service a2489d
	    a->CombineTo(mkj->cmdline);
Packit Service a2489d
	    JobStarted(mkj);
Packit Service a2489d
	    m=MOVED;
Packit Service a2489d
	 }
Packit Service a2489d
      }
Packit Service a2489d
      break;
Packit Service a2489d
Packit Service a2489d
   pre_WAITING_FOR_TRANSFER:
Packit Service a2489d
      to_transfer->rewind();
Packit Service a2489d
      set_state(WAITING_FOR_TRANSFER);
Packit Service a2489d
      m=MOVED;
Packit Service a2489d
      /*fallthrough*/
Packit Service a2489d
   case(WAITING_FOR_TRANSFER):
Packit Service a2489d
      while((j=FindDoneAwaitedJob())!=0)
Packit Service a2489d
      {
Packit Service a2489d
	 TransferFinished(j);
Packit Service a2489d
	 m=MOVED;
Packit Service a2489d
      }
Packit Service a2489d
      if(max_error_count>0 && stats.error_count>=max_error_count)
Packit Service a2489d
	 goto pre_FINISHING;
Packit Service a2489d
      while(transfer_count
Packit Service a2489d
      {
Packit Service a2489d
	 file=to_transfer->curr();
Packit Service a2489d
	 if(!file)
Packit Service a2489d
	 {
Packit Service a2489d
	    // go to the next step only when all transfers have finished
Packit Service a2489d
	    if(waiting_num>0)
Packit Service a2489d
	       break;
Packit Service a2489d
	    if(FlagSet(DEPTH_FIRST))
Packit Service a2489d
	    {
Packit Service a2489d
	       // we have been in the depth, don't go there again
Packit Service a2489d
	       SetFlags(DEPTH_FIRST,false);
Packit Service a2489d
	       SetFlags(NO_RECURSION,true);
Packit Service a2489d
Packit Service a2489d
	       // if we have not created any subdirs and there are only subdirs,
Packit Service a2489d
	       // then the directory would be empty - skip it.
Packit Service a2489d
	       if(FlagSet(NO_EMPTY_DIRS) && stats.dirs==0 && only_dirs)
Packit Service a2489d
		  goto pre_FINISHING_FIX_LOCAL;
Packit Service a2489d
Packit Service a2489d
	       MirrorStarted();
Packit Service a2489d
	       goto pre_MAKE_TARGET_DIR;
Packit Service a2489d
	    }
Packit Service a2489d
	    goto pre_TARGET_REMOVE_OLD;
Packit Service a2489d
	 }
Packit Service a2489d
	 HandleFile(file);
Packit Service a2489d
	 to_transfer->next();
Packit Service a2489d
	 m=MOVED;
Packit Service a2489d
      }
Packit Service a2489d
      break;
Packit Service a2489d
Packit Service a2489d
   pre_TARGET_REMOVE_OLD:
Packit Service a2489d
      if(FlagSet(REMOVE_FIRST))
Packit Service a2489d
	 goto pre_TARGET_CHMOD;
Packit Service a2489d
      set_state(TARGET_REMOVE_OLD);
Packit Service a2489d
      m=MOVED;
Packit Service a2489d
      /*fallthrough*/
Packit Service a2489d
   case(TARGET_REMOVE_OLD):
Packit Service a2489d
   case(TARGET_REMOVE_OLD_FIRST):
Packit Service a2489d
   TARGET_REMOVE_OLD_FIRST_label:
Packit Service a2489d
      while((j=FindDoneAwaitedJob())!=0)
Packit Service a2489d
      {
Packit Service a2489d
	 JobFinished(j);
Packit Service a2489d
	 m=MOVED;
Packit Service a2489d
      }
Packit Service a2489d
      if(max_error_count>0 && stats.error_count>=max_error_count)
Packit Service a2489d
	 goto pre_FINISHING;
Packit Service a2489d
      while(transfer_count
Packit Service a2489d
      {
Packit Service a2489d
	 file=0;
Packit Service a2489d
	 if(!file && state==TARGET_REMOVE_OLD_FIRST)
Packit Service a2489d
	 {
Packit Service a2489d
	    file=to_rm_mismatched->curr();
Packit Service a2489d
	    to_rm_mismatched->next();
Packit Service a2489d
	 }
Packit Service a2489d
	 if(!file && (state==TARGET_REMOVE_OLD || FlagSet(REMOVE_FIRST)))
Packit Service a2489d
	 {
Packit Service a2489d
	    file=to_rm->curr();
Packit Service a2489d
	    to_rm->next();
Packit Service a2489d
	 }
Packit Service a2489d
	 if(!file)
Packit Service a2489d
	 {
Packit Service a2489d
	    if(waiting_num>0)
Packit Service a2489d
	       break;
Packit Service a2489d
	    if(state==TARGET_REMOVE_OLD)
Packit Service a2489d
	       goto pre_TARGET_CHMOD;
Packit Service a2489d
	    goto pre_TARGET_MKDIR;
Packit Service a2489d
	 }
Packit Service a2489d
	 if(!FlagSet(DELETE))
Packit Service a2489d
	 {
Packit Service a2489d
	    if(FlagSet(REPORT_NOT_DELETED))
Packit Service a2489d
	    {
Packit Service a2489d
	       const char *target_name_rel=dir_file(target_relative_dir,file->name);
Packit Service a2489d
	       if(file->TypeIs(file->DIRECTORY))
Packit Service a2489d
		  Report(_("Old directory `%s' is not removed"),target_name_rel);
Packit Service a2489d
	       else
Packit Service a2489d
		  Report(_("Old file `%s' is not removed"),target_name_rel);
Packit Service a2489d
	    }
Packit Service a2489d
	    continue;
Packit Service a2489d
	 }
Packit Service a2489d
	 bool use_rmdir = (file->TypeIs(file->DIRECTORY)
Packit Service a2489d
			   && recursion_mode==RECURSION_NEVER);
Packit Service a2489d
	 if(script)
Packit Service a2489d
	 {
Packit Service a2489d
	    ArgV args(use_rmdir?"rmdir":"rm");
Packit Service a2489d
	    if(file->TypeIs(file->DIRECTORY) && !use_rmdir)
Packit Service a2489d
	       args.Append("-r");
Packit Service a2489d
	    args.Append(target_session->GetFileURL(file->name));
Packit Service a2489d
	    xstring_ca cmd(args.CombineQuoted());
Packit Service a2489d
	    fprintf(script,"%s\n",cmd.get());
Packit Service a2489d
	 }
Packit Service a2489d
	 if(!script_only)
Packit Service a2489d
	 {
Packit Service a2489d
	    ArgV *args=new ArgV(use_rmdir?"rmdir":"rm");
Packit Service a2489d
	    args->Append(dir_file(".",file->name));
Packit Service a2489d
	    args->seek(1);
Packit Service a2489d
	    rmJob *j=new rmJob(target_session->Clone(),args);
Packit Service a2489d
	    args->CombineTo(j->cmdline);
Packit Service a2489d
	    JobStarted(j);
Packit Service a2489d
	    if(file->TypeIs(file->DIRECTORY))
Packit Service a2489d
	    {
Packit Service a2489d
	       if(recursion_mode==RECURSION_NEVER)
Packit Service a2489d
		  j->Rmdir();
Packit Service a2489d
	       else
Packit Service a2489d
		  j->Recurse();
Packit Service a2489d
	    }
Packit Service a2489d
	 }
Packit Service a2489d
	 const char *target_name_rel=dir_file(target_relative_dir,file->name);
Packit Service a2489d
	 if(file->TypeIs(file->DIRECTORY))
Packit Service a2489d
	    Report(_("Removing old directory `%s'"),target_name_rel);
Packit Service a2489d
	 else
Packit Service a2489d
	    Report(_("Removing old file `%s'"),target_name_rel);
Packit Service a2489d
      }
Packit Service a2489d
      break;
Packit Service a2489d
Packit Service a2489d
   pre_TARGET_CHMOD:
Packit Service a2489d
      if(FlagSet(NO_PERMS))
Packit Service a2489d
	 goto pre_FINISHING_FIX_LOCAL;
Packit Service a2489d
Packit Service a2489d
      to_transfer->rewind();
Packit Service a2489d
      if(FlagSet(TARGET_FLAT))
Packit Service a2489d
	 to_transfer->Sort(FileSet::BYNAME_FLAT);
Packit Service a2489d
      set_state(TARGET_CHMOD);
Packit Service a2489d
      m=MOVED;
Packit Service a2489d
      /*fallthrough*/
Packit Service a2489d
   case(TARGET_CHMOD):
Packit Service a2489d
      while((j=FindDoneAwaitedJob())!=0)
Packit Service a2489d
      {
Packit Service a2489d
	 JobFinished(j);
Packit Service a2489d
	 m=MOVED;
Packit Service a2489d
      }
Packit Service a2489d
      if(max_error_count>0 && stats.error_count>=max_error_count)
Packit Service a2489d
	 goto pre_FINISHING;
Packit Service a2489d
      while(transfer_count
Packit Service a2489d
      {
Packit Service a2489d
	 file=to_transfer->curr();
Packit Service a2489d
	 if(!file)
Packit Service a2489d
	    goto pre_FINISHING_FIX_LOCAL;
Packit Service a2489d
	 to_transfer->next();
Packit Service a2489d
	 if(file->TypeIs(file->SYMLINK))
Packit Service a2489d
	    continue;
Packit Service a2489d
	 if(!file->Has(file->MODE))
Packit Service a2489d
	    continue;
Packit Service a2489d
	 mode_t mode_mask=get_mode_mask();
Packit Service a2489d
	 mode_t def_mode=(file->TypeIs(file->DIRECTORY)?0775:0664)&~mode_mask;
Packit Service a2489d
	 if(target_is_local && file->mode==def_mode)
Packit Service a2489d
	 {
Packit Service a2489d
	    struct stat st;
Packit Service a2489d
	    if(!target_is_local || lstat(dir_file(target_dir,file->name),&st)==-1)
Packit Service a2489d
	       continue;
Packit Service a2489d
	    if((st.st_mode&07777)==(file->mode&~mode_mask))
Packit Service a2489d
	       continue;
Packit Service a2489d
	 }
Packit Service a2489d
	 FileInfo *target=target_set->FindByName(file->name);
Packit Service a2489d
	 if(target && target->filetype==file->DIRECTORY && file->filetype==file->DIRECTORY
Packit Service a2489d
	 && target->mode==(file->mode&~mode_mask) && (target->mode&0200))
Packit Service a2489d
	    continue;
Packit Service a2489d
	 if(script)
Packit Service a2489d
	 {
Packit Service a2489d
	    ArgV args("chmod");
Packit Service a2489d
	    args.Append(xstring::format("%03lo",(unsigned long)(file->mode&~mode_mask)));
Packit Service a2489d
	    args.Append(target_session->GetFileURL(file->name));
Packit Service a2489d
	    xstring_ca cmd(args.CombineQuoted());
Packit Service a2489d
	    fprintf(script,"%s\n",cmd.get());
Packit Service a2489d
	 }
Packit Service a2489d
	 if(!script_only)
Packit Service a2489d
	 {
Packit Service a2489d
	    ArgV *a=new ArgV("chmod");
Packit Service a2489d
	    a->Append(dir_file(".",file->name));
Packit Service a2489d
	    a->seek(1);
Packit Service a2489d
	    ChmodJob *cj=new ChmodJob(target_session->Clone(),
Packit Service a2489d
				 file->mode&~mode_mask,a);
Packit Service a2489d
	    a->CombineTo(cj->cmdline);
Packit Service a2489d
	    if(!verbose_report)
Packit Service a2489d
	       cj->BeQuiet(); // chmod is not supported on all servers; be quiet.
Packit Service a2489d
	    JobStarted(cj);
Packit Service a2489d
	    m=MOVED;
Packit Service a2489d
	 }
Packit Service a2489d
      }
Packit Service a2489d
      break;
Packit Service a2489d
Packit Service a2489d
   pre_FINISHING_FIX_LOCAL:
Packit Service a2489d
      if(target_is_local && !script_only)     // FIXME
Packit Service a2489d
      {
Packit Service a2489d
	 const bool flat=FlagSet(TARGET_FLAT);
Packit Service a2489d
	 to_transfer->Sort(FileSet::BYNAME_FLAT);
Packit Service a2489d
	 to_transfer->LocalUtime(target_dir,/*only_dirs=*/true,flat);
Packit Service a2489d
	 if(FlagSet(ALLOW_CHOWN))
Packit Service a2489d
	    to_transfer->LocalChown(target_dir,flat);
Packit Service a2489d
	 if(!FlagSet(NO_PERMS) && same)
Packit Service a2489d
	    same->LocalChmod(target_dir,get_mode_mask(),flat);
Packit Service a2489d
	 if(FlagSet(ALLOW_CHOWN) && same)
Packit Service a2489d
	    same->LocalChown(target_dir,flat);
Packit Service a2489d
      }
Packit Service a2489d
      if(remove_source_files && (same || to_rm_src))
Packit Service a2489d
	 goto pre_SOURCE_REMOVING_SAME;
Packit Service a2489d
   pre_FINISHING:
Packit Service a2489d
      set_state(FINISHING);
Packit Service a2489d
      m=MOVED;
Packit Service a2489d
      /*fallthrough*/
Packit Service a2489d
   case(FINISHING):
Packit Service a2489d
      while((j=FindDoneAwaitedJob())!=0)
Packit Service a2489d
      {
Packit Service a2489d
	 JobFinished(j);
Packit Service a2489d
	 m=MOVED;
Packit Service a2489d
      }
Packit Service a2489d
      if(waiting_num>0)
Packit Service a2489d
	 break;
Packit Service a2489d
Packit Service a2489d
      // all jobs finished.
Packit Service a2489d
      if(remove_this_source_dir) {
Packit Service a2489d
	 // remove source directory once.
Packit Service a2489d
	 remove_this_source_dir=false;
Packit Service a2489d
	 if(script)
Packit Service a2489d
	 {
Packit Service a2489d
	    ArgV args("rmdir");
Packit Service a2489d
	    args.Append(source_session->GetFileURL(source_dir));
Packit Service a2489d
	    xstring_ca cmd(args.CombineQuoted());
Packit Service a2489d
	    fprintf(script,"%s\n",cmd.get());
Packit Service a2489d
	 }
Packit Service a2489d
	 if(!script_only)
Packit Service a2489d
	 {
Packit Service a2489d
	    ArgV *args=new ArgV("rmdir");
Packit Service a2489d
	    args->Append(source_dir);
Packit Service a2489d
	    args->seek(1);
Packit Service a2489d
	    rmJob *j=new rmJob(source_session->Clone(),args);
Packit Service a2489d
	    args->CombineTo(j->cmdline);
Packit Service a2489d
	    j->Rmdir();
Packit Service a2489d
	    JobStarted(j);
Packit Service a2489d
	 }
Packit Service a2489d
	 if(source_relative_dir)
Packit Service a2489d
	    Report(_("Removing source directory `%s'"),source_relative_dir.get());
Packit Service a2489d
	 m=MOVED;
Packit Service a2489d
	 break;
Packit Service a2489d
      }
Packit Service a2489d
Packit Service a2489d
      // all jobs finished and src dir removed, if needed.
Packit Service a2489d
Packit Service a2489d
      transfer_count++; // parent mirror will decrement it.
Packit Service a2489d
      if(parent_mirror)
Packit Service a2489d
	 parent_mirror->stats.Add(stats);
Packit Service a2489d
      else
Packit Service a2489d
      {
Packit Service a2489d
	 if(stats.HaveSomethingDone(flags) && on_change)
Packit Service a2489d
	 {
Packit Service a2489d
	    CmdExec *exec=new CmdExec(source_session->Clone(),0);
Packit Service a2489d
	    AddWaiting(exec);
Packit Service a2489d
	    exec->FeedCmd(on_change);
Packit Service a2489d
	    exec->FeedCmd("\n");
Packit Service a2489d
	    set_state(LAST_EXEC);
Packit Service a2489d
	    break;
Packit Service a2489d
	 }
Packit Service a2489d
      }
Packit Service a2489d
      goto pre_DONE;
Packit Service a2489d
Packit Service a2489d
   pre_SOURCE_REMOVING_SAME:
Packit Service a2489d
      if(!same)
Packit Service a2489d
	 same=to_rm_src.borrow();
Packit Service a2489d
      else if(to_rm_src)
Packit Service a2489d
	 same->Merge(to_rm_src);
Packit Service a2489d
      same->rewind();
Packit Service a2489d
      set_state(SOURCE_REMOVING_SAME);
Packit Service a2489d
      m=MOVED;
Packit Service a2489d
      /*fallthrough*/
Packit Service a2489d
   case(SOURCE_REMOVING_SAME):
Packit Service a2489d
      while((j=FindDoneAwaitedJob())!=0)
Packit Service a2489d
      {
Packit Service a2489d
	 JobFinished(j);
Packit Service a2489d
	 m=MOVED;
Packit Service a2489d
      }
Packit Service a2489d
      if(max_error_count>0 && stats.error_count>=max_error_count)
Packit Service a2489d
	 goto pre_FINISHING;
Packit Service a2489d
      while(transfer_count
Packit Service a2489d
      {
Packit Service a2489d
	 file=same->curr();
Packit Service a2489d
	 same->next();
Packit Service a2489d
	 if(!file)
Packit Service a2489d
	    goto pre_FINISHING;
Packit Service a2489d
	 if(file->TypeIs(file->DIRECTORY))
Packit Service a2489d
	    continue;
Packit Service a2489d
	 if(script)
Packit Service a2489d
	 {
Packit Service a2489d
	    ArgV args("rm");
Packit Service a2489d
	    args.Append(source_session->GetFileURL(file->name));
Packit Service a2489d
	    xstring_ca cmd(args.CombineQuoted());
Packit Service a2489d
	    fprintf(script,"%s\n",cmd.get());
Packit Service a2489d
	 }
Packit Service a2489d
	 if(!script_only)
Packit Service a2489d
	 {
Packit Service a2489d
	    ArgV *args=new ArgV("rm");
Packit Service a2489d
	    args->Append(dir_file(".",file->name));
Packit Service a2489d
	    args->seek(1);
Packit Service a2489d
	    rmJob *j=new rmJob(source_session->Clone(),args);
Packit Service a2489d
	    args->CombineTo(j->cmdline);
Packit Service a2489d
	    JobStarted(j);
Packit Service a2489d
	 }
Packit Service a2489d
	 const char *source_name_rel=dir_file(source_relative_dir,file->name);
Packit Service a2489d
	 Report(_("Removing source file `%s'"),source_name_rel);
Packit Service a2489d
      }
Packit Service a2489d
      break;
Packit Service a2489d
Packit Service a2489d
   case(LAST_EXEC):
Packit Service a2489d
      while((j=FindDoneAwaitedJob())!=0)
Packit Service a2489d
      {
Packit Service a2489d
	 RemoveWaiting(j);
Packit Service a2489d
	 Delete(j);
Packit Service a2489d
	 m=MOVED;
Packit Service a2489d
      }
Packit Service a2489d
      if(waiting_num>0)
Packit Service a2489d
	 break;
Packit Service a2489d
   pre_DONE:
Packit Service a2489d
      set_state(DONE);
Packit Service a2489d
      m=MOVED;
Packit Service a2489d
      bytes_transferred=0;
Packit Service a2489d
      if(!parent_mirror && FlagSet(LOOP) && stats.HaveSomethingDone(flags) && !stats.error_count)
Packit Service a2489d
      {
Packit Service a2489d
	 PrintStatus(0,"");
Packit Service a2489d
	 printf(_("Retrying mirror...\n"));
Packit Service a2489d
	 stats.Reset();
Packit Service a2489d
	 source_set=0;
Packit Service a2489d
	 target_set=0;
Packit Service a2489d
	 goto pre_GETTING_LIST_INFO;
Packit Service a2489d
      }
Packit Service a2489d
      /*fallthrough*/
Packit Service a2489d
   case(DONE):
Packit Service a2489d
      break;
Packit Service a2489d
   }
Packit Service a2489d
   // give direct parent priority over grand-parents.
Packit Service a2489d
   if(transfer_count
Packit Service a2489d
      m|=parent_mirror->Roll();
Packit Service a2489d
   return m;
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
MirrorJob::MirrorJob(MirrorJob *parent,
Packit Service a2489d
   FileAccess *source,FileAccess *target,
Packit Service a2489d
   const char *new_source_dir,const char *new_target_dir)
Packit Service a2489d
 :
Packit Service a2489d
   bytes_transferred(0), bytes_to_transfer(0),
Packit Service a2489d
   source_dir(new_source_dir), target_dir(new_target_dir),
Packit Service a2489d
   transfer_time_elapsed(0), root_transfer_count(0),
Packit Service a2489d
   verbose_report(0),
Packit Service a2489d
   parent_mirror(parent), root_mirror(parent?parent->root_mirror:this)
Packit Service a2489d
{
Packit Service a2489d
Packit Service a2489d
   source_session=source;
Packit Service a2489d
   target_session=target;
Packit Service a2489d
   // TODO: get rid of this.
Packit Service a2489d
   source_is_local=!strcmp(source_session->GetProto(),"file");
Packit Service a2489d
   target_is_local=!strcmp(target_session->GetProto(),"file");
Packit Service a2489d
Packit Service a2489d
   create_target_dir=true;
Packit Service a2489d
   no_target_dir=false;
Packit Service a2489d
   remove_this_source_dir=false;
Packit Service a2489d
Packit Service a2489d
   flags=0;
Packit Service a2489d
   recursion_mode=RECURSION_ALWAYS;
Packit Service a2489d
   max_error_count=0;
Packit Service a2489d
Packit Service a2489d
   exclude=0;
Packit Service a2489d
Packit Service a2489d
   set_state(INITIAL_STATE);
Packit Service a2489d
Packit Service a2489d
   newer_than=NO_DATE;
Packit Service a2489d
   older_than=NO_DATE;
Packit Service a2489d
   size_range=0;
Packit Service a2489d
Packit Service a2489d
   script=0;
Packit Service a2489d
   script_only=false;
Packit Service a2489d
   script_needs_closing=false;
Packit Service a2489d
Packit Service a2489d
   use_cache=false;
Packit Service a2489d
   remove_source_files=false;
Packit Service a2489d
   remove_source_dirs=false;
Packit Service a2489d
   skip_noaccess=false;
Packit Service a2489d
Packit Service a2489d
   parallel=1;
Packit Service a2489d
   pget_n=1;
Packit Service a2489d
   pget_minchunk=0x10000;
Packit Service a2489d
Packit Service a2489d
   source_redirections=0;
Packit Service a2489d
   target_redirections=0;
Packit Service a2489d
Packit Service a2489d
   if(parent_mirror)
Packit Service a2489d
   {
Packit Service a2489d
      bool parallel_dirs=ResMgr::QueryBool("mirror:parallel-directories",0);
Packit Service a2489d
      // If parallel_dirs is true, allow parent mirror to continue
Packit Service a2489d
      // processing other directories, otherwise block it until we
Packit Service a2489d
      // get file sets and start transfers.
Packit Service a2489d
      // See also comment at MirrorJob::MirrorStarted().
Packit Service a2489d
      root_transfer_count=parallel_dirs?1:1024;
Packit Service a2489d
Packit Service a2489d
      // inherit flags and other things
Packit Service a2489d
      SetFlags(parent->flags,1);
Packit Service a2489d
      UseCache(parent->use_cache);
Packit Service a2489d
Packit Service a2489d
      SetExclude(parent->exclude);
Packit Service a2489d
Packit Service a2489d
      verbose_report=parent->verbose_report;
Packit Service a2489d
      newer_than=parent->newer_than;
Packit Service a2489d
      older_than=parent->older_than;
Packit Service a2489d
      size_range=parent->size_range;
Packit Service a2489d
      parallel=parent->parallel;
Packit Service a2489d
      pget_n=parent->pget_n;
Packit Service a2489d
      pget_minchunk=parent->pget_minchunk;
Packit Service a2489d
      remove_source_files=parent->remove_source_files;
Packit Service a2489d
      remove_source_dirs=parent->remove_source_dirs;
Packit Service a2489d
      skip_noaccess=parent->skip_noaccess;
Packit Service a2489d
      no_target_dir=parent->no_target_dir;
Packit Service a2489d
      recursion_mode=parent->recursion_mode;
Packit Service a2489d
Packit Service a2489d
      script=parent->script;
Packit Service a2489d
      script_needs_closing=false;
Packit Service a2489d
      script_name.set(parent->script_name);
Packit Service a2489d
      script_only=parent->script_only;
Packit Service a2489d
Packit Service a2489d
      max_error_count=parent->max_error_count;
Packit Service a2489d
   }
Packit Service a2489d
   MirrorStarted();
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
MirrorJob::~MirrorJob()
Packit Service a2489d
{
Packit Service a2489d
   if(script && script_needs_closing)
Packit Service a2489d
      fclose(script);
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
void MirrorJob::va_Report(const char *fmt,va_list v)
Packit Service a2489d
{
Packit Service a2489d
   if(parent_mirror)
Packit Service a2489d
   {
Packit Service a2489d
      parent_mirror->va_Report(fmt,v);
Packit Service a2489d
      return;
Packit Service a2489d
   }
Packit Service a2489d
Packit Service a2489d
   if(verbose_report)
Packit Service a2489d
   {
Packit Service a2489d
      pid_t p=tcgetpgrp(fileno(stdout));
Packit Service a2489d
      if(p>0 && p!=getpgrp())
Packit Service a2489d
	 return;
Packit Service a2489d
Packit Service a2489d
      vfprintf(stdout,fmt,v);
Packit Service a2489d
      printf("\n");
Packit Service a2489d
      fflush(stdout);
Packit Service a2489d
   }
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
void MirrorJob::Report(const char *fmt,...)
Packit Service a2489d
{
Packit Service a2489d
   va_list v;
Packit Service a2489d
   va_start(v,fmt);
Packit Service a2489d
Packit Service a2489d
   va_Report(fmt,v);
Packit Service a2489d
Packit Service a2489d
   va_end(v);
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
extern "C" {
Packit Service a2489d
#include "parse-datetime.h"
Packit Service a2489d
}
Packit Service a2489d
void MirrorJob::SetNewerThan(const char *f)
Packit Service a2489d
{
Packit Service a2489d
   struct timespec ts;
Packit Service a2489d
   if(parse_datetime(&ts,f,0))
Packit Service a2489d
   {
Packit Service a2489d
      newer_than=ts.tv_sec;
Packit Service a2489d
      return;
Packit Service a2489d
   }
Packit Service a2489d
   struct stat st;
Packit Service a2489d
   if(stat(f,&st)==-1)
Packit Service a2489d
   {
Packit Service a2489d
      perror(f);
Packit Service a2489d
      return;
Packit Service a2489d
   }
Packit Service a2489d
   newer_than=st.st_mtime;
Packit Service a2489d
}
Packit Service a2489d
void MirrorJob::SetOlderThan(const char *f)
Packit Service a2489d
{
Packit Service a2489d
   struct timespec ts;
Packit Service a2489d
   if(parse_datetime(&ts,f,0))
Packit Service a2489d
   {
Packit Service a2489d
      older_than=ts.tv_sec;
Packit Service a2489d
      return;
Packit Service a2489d
   }
Packit Service a2489d
   struct stat st;
Packit Service a2489d
   if(stat(f,&st)==-1)
Packit Service a2489d
   {
Packit Service a2489d
      perror(f);
Packit Service a2489d
      return;
Packit Service a2489d
   }
Packit Service a2489d
   older_than=st.st_mtime;
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
mode_t MirrorJob::get_mode_mask()
Packit Service a2489d
{
Packit Service a2489d
   mode_t mode_mask=0;
Packit Service a2489d
   if(!FlagSet(ALLOW_SUID))
Packit Service a2489d
      mode_mask|=S_ISUID|S_ISGID;
Packit Service a2489d
   if(!FlagSet(NO_UMASK))
Packit Service a2489d
   {
Packit Service a2489d
      if(target_is_local)
Packit Service a2489d
      {
Packit Service a2489d
	 mode_t u=umask(022); // get+set
Packit Service a2489d
	 umask(u);	      // retore
Packit Service a2489d
	 mode_mask|=u;
Packit Service a2489d
      }
Packit Service a2489d
      else
Packit Service a2489d
	 mode_mask|=022;   // sane default.
Packit Service a2489d
   }
Packit Service a2489d
   return mode_mask;
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
void MirrorJob::Fg()
Packit Service a2489d
{
Packit Service a2489d
   Job::Fg();
Packit Service a2489d
   source_session->SetPriority(1);
Packit Service a2489d
   target_session->SetPriority(1);
Packit Service a2489d
}
Packit Service a2489d
void MirrorJob::Bg()
Packit Service a2489d
{
Packit Service a2489d
   source_session->SetPriority(0);
Packit Service a2489d
   target_session->SetPriority(0);
Packit Service a2489d
   Job::Bg();
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
MirrorJob::Statistics::Statistics()
Packit Service a2489d
{
Packit Service a2489d
   Reset();
Packit Service a2489d
   error_count=0;
Packit Service a2489d
   bytes=0;
Packit Service a2489d
   time=0;
Packit Service a2489d
}
Packit Service a2489d
void MirrorJob::Statistics::Reset()
Packit Service a2489d
{
Packit Service a2489d
   tot_files=new_files=mod_files=del_files=
Packit Service a2489d
   tot_symlinks=new_symlinks=mod_symlinks=del_symlinks=
Packit Service a2489d
   dirs=del_dirs=0;
Packit Service a2489d
}
Packit Service a2489d
void MirrorJob::Statistics::Add(const Statistics &s)
Packit Service a2489d
{
Packit Service a2489d
   tot_files   +=s.tot_files;
Packit Service a2489d
   new_files   +=s.new_files;
Packit Service a2489d
   mod_files   +=s.mod_files;
Packit Service a2489d
   del_files   +=s.del_files;
Packit Service a2489d
   tot_symlinks+=s.tot_symlinks;
Packit Service a2489d
   new_symlinks+=s.new_symlinks;
Packit Service a2489d
   mod_symlinks+=s.mod_symlinks;
Packit Service a2489d
   del_symlinks+=s.del_symlinks;
Packit Service a2489d
   dirs        +=s.dirs;
Packit Service a2489d
   del_dirs    +=s.del_dirs;
Packit Service a2489d
   error_count +=s.error_count;
Packit Service a2489d
   bytes       +=s.bytes;
Packit Service a2489d
   time	       +=s.time;
Packit Service a2489d
}
Packit Service a2489d
bool MirrorJob::Statistics::HaveSomethingDone(unsigned flags)
Packit Service a2489d
{
Packit Service a2489d
   bool del=(flags&MirrorJob::DELETE);
Packit Service a2489d
   return new_files|mod_files|(del_files*del)|new_symlinks|mod_symlinks|(del_symlinks*del)|(del_dirs*del);
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
const char *MirrorJob::SetScriptFile(const char *n)
Packit Service a2489d
{
Packit Service a2489d
   script_name.set(n);
Packit Service a2489d
   if(strcmp(n,"-"))
Packit Service a2489d
   {
Packit Service a2489d
      script=fopen(n,"w");
Packit Service a2489d
      if(!script)
Packit Service a2489d
	 return xstring::format("%s: %s",n,strerror(errno));
Packit Service a2489d
      setvbuf(script,NULL,_IOLBF,0);
Packit Service a2489d
      script_needs_closing=true;
Packit Service a2489d
   }
Packit Service a2489d
   else
Packit Service a2489d
   {
Packit Service a2489d
      script=stdout;
Packit Service a2489d
      script_needs_closing=false;
Packit Service a2489d
   }
Packit Service a2489d
   return 0;
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
void MirrorJob::SetOnChange(const char *oc)
Packit Service a2489d
{
Packit Service a2489d
   on_change.set(oc);
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
const char *MirrorJob::AddPattern(Ref<PatternSet>& exclude,char opt,const char *optarg)
Packit Service a2489d
{
Packit Service a2489d
   PatternSet::Type type=
Packit Service a2489d
      (opt=='x'||opt=='X'||opt=='\0'?PatternSet::EXCLUDE:PatternSet::INCLUDE);
Packit Service a2489d
   PatternSet::Pattern *pattern=0;
Packit Service a2489d
   if(opt=='x' || opt=='i')
Packit Service a2489d
   {
Packit Service a2489d
      Ref<PatternSet::Regex> rx(new PatternSet::Regex(optarg));
Packit Service a2489d
      if(rx->Error())
Packit Service a2489d
	 return xstring::get_tmp(rx->ErrorText());
Packit Service a2489d
      pattern=rx.borrow();
Packit Service a2489d
   }
Packit Service a2489d
   else if(opt=='X' || opt=='I')
Packit Service a2489d
   {
Packit Service a2489d
      pattern=new PatternSet::Glob(optarg);
Packit Service a2489d
   }
Packit Service a2489d
   if(!exclude)
Packit Service a2489d
   {
Packit Service a2489d
      const char *default_exclude=ResMgr::Query("mirror:exclude-regex",0);
Packit Service a2489d
      const char *default_include=ResMgr::Query("mirror:include-regex",0);
Packit Service a2489d
Packit Service a2489d
      // don't create default pattern set if not needed
Packit Service a2489d
      if(!pattern && !(default_exclude && *default_exclude))
Packit Service a2489d
	 return NULL;
Packit Service a2489d
Packit Service a2489d
      exclude=new PatternSet;
Packit Service a2489d
      /* Make default_exclude the first pattern so that it can be
Packit Service a2489d
       * overridden by --include later, and do that only when first
Packit Service a2489d
       * explicit pattern is for exclusion - otherwise all files are
Packit Service a2489d
       * excluded by default and no default exclusion is needed. */
Packit Service a2489d
      if(type==PatternSet::EXCLUDE && default_exclude && *default_exclude)
Packit Service a2489d
      {
Packit Service a2489d
	 exclude->Add(type,new PatternSet::Regex(default_exclude));
Packit Service a2489d
	 if(default_include && *default_include)
Packit Service a2489d
	    exclude->Add(PatternSet::INCLUDE,new PatternSet::Regex(default_include));
Packit Service a2489d
      }
Packit Service a2489d
   }
Packit Service a2489d
   if(pattern)
Packit Service a2489d
      exclude->Add(type,pattern);
Packit Service a2489d
Packit Service a2489d
   return NULL; // no error
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
const char *MirrorJob::AddPatternsFrom(Ref<PatternSet>& exclude,char opt,const char *file)
Packit Service a2489d
{
Packit Service a2489d
   FILE *f=fopen(file,"r");
Packit Service a2489d
   if(!f)
Packit Service a2489d
      return xstring::format("%s: %s",file,strerror(errno));
Packit Service a2489d
Packit Service a2489d
   xstring line;
Packit Service a2489d
   const char *err=0;
Packit Service a2489d
   int c;
Packit Service a2489d
   while(!feof(f)) {
Packit Service a2489d
      line.truncate();
Packit Service a2489d
      while((c=getc(f))!=EOF && c!='\n')
Packit Service a2489d
	 line.append(c);
Packit Service a2489d
      if(line.length()>0) {
Packit Service a2489d
	 err=AddPattern(exclude,opt,line);
Packit Service a2489d
	 if(err)
Packit Service a2489d
	    break;
Packit Service a2489d
      }
Packit Service a2489d
   }
Packit Service a2489d
Packit Service a2489d
   fclose(f);
Packit Service a2489d
   return err;
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
const char *MirrorJob::SetRecursionMode(const char *m)
Packit Service a2489d
{
Packit Service a2489d
   struct { const char name[8]; recursion_mode_t mode; } map[]={
Packit Service a2489d
      {"always", RECURSION_ALWAYS},
Packit Service a2489d
      {"never",  RECURSION_NEVER},
Packit Service a2489d
      {"missing",RECURSION_MISSING},
Packit Service a2489d
      {"newer",  RECURSION_NEWER},
Packit Service a2489d
   };
Packit Service a2489d
   unsigned i;
Packit Service a2489d
   for(i=0; i
Packit Service a2489d
      if(!strcasecmp(m,map[i].name)) {
Packit Service a2489d
	 recursion_mode=map[i].mode;
Packit Service a2489d
	 return 0;
Packit Service a2489d
      }
Packit Service a2489d
   }
Packit Service a2489d
   xstring list(map[0].name);
Packit Service a2489d
   for(i=1; i
Packit Service a2489d
      list.append(", ").append(map[i].name);
Packit Service a2489d
   return xstring::format(_("%s must be one of: %s"),"--recursion",list.get());
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
CMD(mirror)
Packit Service a2489d
{
Packit Service a2489d
#define args (parent->args)
Packit Service a2489d
#define eprintf parent->eprintf
Packit Service a2489d
   enum {
Packit Service a2489d
      OPT_ALLOW_CHOWN,
Packit Service a2489d
      OPT_DELETE_FIRST,
Packit Service a2489d
      OPT_IGNORE_SIZE,
Packit Service a2489d
      OPT_IGNORE_TIME,
Packit Service a2489d
      OPT_LOOP,
Packit Service a2489d
      OPT_MAX_ERRORS,
Packit Service a2489d
      OPT_NO_DEREFERENCE,
Packit Service a2489d
      OPT_NO_SYMLINKS,
Packit Service a2489d
      OPT_NO_UMASK,
Packit Service a2489d
      OPT_OLDER_THAN,
Packit Service a2489d
      OPT_ONLY_MISSING,
Packit Service a2489d
      OPT_ONLY_EXISTING,
Packit Service a2489d
      OPT_PERMS,
Packit Service a2489d
      OPT_REMOVE_SOURCE_FILES,
Packit Service a2489d
      OPT_REMOVE_SOURCE_DIRS,
Packit Service a2489d
      OPT_SCRIPT,
Packit Service a2489d
      OPT_SCRIPT_ONLY,
Packit Service a2489d
      OPT_SIZE_RANGE,
Packit Service a2489d
      OPT_USE_CACHE,
Packit Service a2489d
      OPT_USE_PGET_N,
Packit Service a2489d
      OPT_SKIP_NOACCESS,
Packit Service a2489d
      OPT_ON_CHANGE,
Packit Service a2489d
      OPT_NO_EMPTY_DIRS,
Packit Service a2489d
      OPT_DEPTH_FIRST,
Packit Service a2489d
      OPT_ASCII,
Packit Service a2489d
      OPT_SCAN_ALL_FIRST,
Packit Service a2489d
      OPT_OVERWRITE,
Packit Service a2489d
      OPT_NO_OVERWRITE,
Packit Service a2489d
      OPT_RECURSION,
Packit Service a2489d
      OPT_UPLOAD_OLDER,
Packit Service a2489d
      OPT_TRANSFER_ALL,
Packit Service a2489d
      OPT_TARGET_FLAT,
Packit Service a2489d
      OPT_DELETE_EXCLUDED,
Packit Service a2489d
   };
Packit Service a2489d
   static const struct option mirror_opts[]=
Packit Service a2489d
   {
Packit Service a2489d
      {"delete",no_argument,0,'e'},
Packit Service a2489d
      {"allow-suid",no_argument,0,'s'},
Packit Service a2489d
      {"allow-chown",no_argument,0,OPT_ALLOW_CHOWN},
Packit Service a2489d
      {"include",required_argument,0,'i'},
Packit Service a2489d
      {"exclude",required_argument,0,'x'},
Packit Service a2489d
      {"include-glob",required_argument,0,'I'},
Packit Service a2489d
      {"exclude-glob",required_argument,0,'X'},
Packit Service a2489d
      {"include-rx-from",required_argument,0,'i'+'f'},
Packit Service a2489d
      {"exclude-rx-from",required_argument,0,'x'+'f'},
Packit Service a2489d
      {"include-glob-from",required_argument,0,'I'+'f'},
Packit Service a2489d
      {"exclude-glob-from",required_argument,0,'X'+'f'},
Packit Service a2489d
      {"only-newer",no_argument,0,'n'},
Packit Service a2489d
      {"no-recursion",no_argument,0,'r'},
Packit Service a2489d
      {"no-perms",no_argument,0,'p'},
Packit Service a2489d
      {"perms",no_argument,0,OPT_PERMS},
Packit Service a2489d
      {"no-umask",no_argument,0,OPT_NO_UMASK},
Packit Service a2489d
      {"continue",no_argument,0,'c'},
Packit Service a2489d
      {"reverse",no_argument,0,'R'},
Packit Service a2489d
      {"verbose",optional_argument,0,'v'},
Packit Service a2489d
      {"newer-than",required_argument,0,'N'},
Packit Service a2489d
      {"file",required_argument,0,'f'},
Packit Service a2489d
      {"directory",required_argument,0,'F'},
Packit Service a2489d
      {"older-than",required_argument,0,OPT_OLDER_THAN},
Packit Service a2489d
      {"size-range",required_argument,0,OPT_SIZE_RANGE},
Packit Service a2489d
      {"dereference",no_argument,0,'L'},
Packit Service a2489d
      {"no-dereference",no_argument,0,OPT_NO_DEREFERENCE},
Packit Service a2489d
      {"use-cache",no_argument,0,OPT_USE_CACHE},
Packit Service a2489d
      {"Remove-source-files",no_argument,0,OPT_REMOVE_SOURCE_FILES},
Packit Service a2489d
      {"Remove-source-dirs",no_argument,0,OPT_REMOVE_SOURCE_DIRS},
Packit Service a2489d
      {"Move",no_argument,0,OPT_REMOVE_SOURCE_DIRS},
Packit Service a2489d
      {"parallel",optional_argument,0,'P'},
Packit Service a2489d
      {"ignore-time",no_argument,0,OPT_IGNORE_TIME},
Packit Service a2489d
      {"ignore-size",no_argument,0,OPT_IGNORE_SIZE},
Packit Service a2489d
      {"only-missing",no_argument,0,OPT_ONLY_MISSING},
Packit Service a2489d
      {"only-existing",no_argument,0,OPT_ONLY_EXISTING},
Packit Service a2489d
      {"log",required_argument,0,OPT_SCRIPT},
Packit Service a2489d
      {"script",    required_argument,0,OPT_SCRIPT_ONLY},
Packit Service a2489d
      {"just-print",optional_argument,0,OPT_SCRIPT_ONLY},
Packit Service a2489d
      {"dry-run",   optional_argument,0,OPT_SCRIPT_ONLY},
Packit Service a2489d
      {"delete-first",no_argument,0,OPT_DELETE_FIRST},
Packit Service a2489d
      {"use-pget-n",optional_argument,0,OPT_USE_PGET_N},
Packit Service a2489d
      {"no-symlinks",no_argument,0,OPT_NO_SYMLINKS},
Packit Service a2489d
      {"loop",no_argument,0,OPT_LOOP},
Packit Service a2489d
      {"max-errors",required_argument,0,OPT_MAX_ERRORS},
Packit Service a2489d
      {"skip-noaccess",no_argument,0,OPT_SKIP_NOACCESS},
Packit Service a2489d
      {"on-change",required_argument,0,OPT_ON_CHANGE},
Packit Service a2489d
      {"no-empty-dirs",no_argument,0,OPT_NO_EMPTY_DIRS},
Packit Service a2489d
      {"depth-first",no_argument,0,OPT_DEPTH_FIRST},
Packit Service a2489d
      {"ascii",no_argument,0,OPT_ASCII},
Packit Service a2489d
      {"target-directory",required_argument,0,'O'},
Packit Service a2489d
      {"destination-directory",required_argument,0,'O'},
Packit Service a2489d
      {"scan-all-first",no_argument,0,OPT_SCAN_ALL_FIRST},
Packit Service a2489d
      {"overwrite",no_argument,0,OPT_OVERWRITE},
Packit Service a2489d
      {"no-overwrite",no_argument,0,OPT_NO_OVERWRITE},
Packit Service a2489d
      {"recursion",required_argument,0,OPT_RECURSION},
Packit Service a2489d
      {"upload-older",no_argument,0,OPT_UPLOAD_OLDER},
Packit Service a2489d
      {"transfer-all",no_argument,0,OPT_TRANSFER_ALL},
Packit Service a2489d
      {"flat",no_argument,0,OPT_TARGET_FLAT},
Packit Service a2489d
      {"delete-excluded",no_argument,0,OPT_DELETE_EXCLUDED},
Packit Service a2489d
      {0}
Packit Service a2489d
   };
Packit Service a2489d
Packit Service a2489d
   int opt;
Packit Service a2489d
   unsigned flags=0;
Packit Service a2489d
   int max_error_count=0;
Packit Service a2489d
Packit Service a2489d
   bool use_cache=false;
Packit Service a2489d
Packit Service a2489d
   FileAccessRef source_session;
Packit Service a2489d
   FileAccessRef target_session;
Packit Service a2489d
Packit Service a2489d
   int	 verbose=0;
Packit Service a2489d
   const char *newer_than=0;
Packit Service a2489d
   const char *older_than=0;
Packit Service a2489d
   Ref<Range> size_range;
Packit Service a2489d
   bool  remove_source_files=false;
Packit Service a2489d
   bool  remove_source_dirs=false;
Packit Service a2489d
   bool	 skip_noaccess=ResMgr::QueryBool("mirror:skip-noaccess",0);
Packit Service a2489d
   int	 parallel=-1;
Packit Service a2489d
   int	 use_pget=-1;
Packit Service a2489d
   bool	 reverse=false;
Packit Service a2489d
   bool	 script_only=false;
Packit Service a2489d
   bool	 no_empty_dirs=ResMgr::QueryBool("mirror:no-empty-dirs",0);
Packit Service a2489d
   const char *script_file=0;
Packit Service a2489d
   const char *on_change=0;
Packit Service a2489d
   const char *recursion_mode=0;
Packit Service a2489d
   bool single_file=false;
Packit Service a2489d
   bool single_dir=false;
Packit Service a2489d
Packit Service a2489d
   Ref<PatternSet> exclude;
Packit Service a2489d
   Ref<PatternSet> top_exclude;
Packit Service a2489d
Packit Service a2489d
   if(!ResMgr::QueryBool("mirror:set-permissions",0))
Packit Service a2489d
      flags|=MirrorJob::NO_PERMS;
Packit Service a2489d
   if(ResMgr::QueryBool("mirror:dereference",0))
Packit Service a2489d
      flags|=MirrorJob::RETR_SYMLINKS;
Packit Service a2489d
   if(ResMgr::QueryBool("mirror:overwrite",0))
Packit Service a2489d
      flags|=MirrorJob::OVERWRITE;
Packit Service a2489d
Packit Service a2489d
   const char *source_dir=NULL;
Packit Service a2489d
   const char *target_dir=NULL;
Packit Service a2489d
Packit Service a2489d
   args->rewind();
Packit Service a2489d
   while((opt=args->getopt_long("esi:x:I:X:nrpcRvN:LP:af:F:O:",mirror_opts,0))!=EOF)
Packit Service a2489d
   {
Packit Service a2489d
      switch(opt)
Packit Service a2489d
      {
Packit Service a2489d
      case('e'):
Packit Service a2489d
	 flags|=MirrorJob::DELETE;
Packit Service a2489d
	 break;
Packit Service a2489d
      case('s'):
Packit Service a2489d
	 flags|=MirrorJob::ALLOW_SUID;
Packit Service a2489d
	 break;
Packit Service a2489d
      case(OPT_ALLOW_CHOWN):
Packit Service a2489d
	 flags|=MirrorJob::ALLOW_CHOWN;
Packit Service a2489d
	 break;
Packit Service a2489d
      case('a'):
Packit Service a2489d
	 flags|=MirrorJob::ALLOW_SUID|MirrorJob::ALLOW_CHOWN|MirrorJob::NO_UMASK;
Packit Service a2489d
	 break;
Packit Service a2489d
      case('r'):
Packit Service a2489d
	 recursion_mode="never";
Packit Service a2489d
	 break;
Packit Service a2489d
      case('n'):
Packit Service a2489d
	 flags|=MirrorJob::ONLY_NEWER;
Packit Service a2489d
	 break;
Packit Service a2489d
      case('p'):
Packit Service a2489d
	 flags|=MirrorJob::NO_PERMS;
Packit Service a2489d
	 break;
Packit Service a2489d
      case(OPT_PERMS):
Packit Service a2489d
	 flags&=~MirrorJob::NO_PERMS;
Packit Service a2489d
	 break;
Packit Service a2489d
      case('c'):
Packit Service a2489d
	 flags|=MirrorJob::CONTINUE;
Packit Service a2489d
	 break;
Packit Service a2489d
      case('x'):
Packit Service a2489d
      case('i'):
Packit Service a2489d
      case('X'):
Packit Service a2489d
      case('I'):
Packit Service a2489d
      {
Packit Service a2489d
	 const char *err=MirrorJob::AddPattern(exclude,opt,optarg);
Packit Service a2489d
	 if(err)
Packit Service a2489d
	 {
Packit Service a2489d
	    eprintf("%s: %s\n",args->a0(),err);
Packit Service a2489d
	    goto no_job;
Packit Service a2489d
	 }
Packit Service a2489d
	 break;
Packit Service a2489d
      }
Packit Service a2489d
      case('x'+'f'):
Packit Service a2489d
      case('i'+'f'):
Packit Service a2489d
      case('X'+'f'):
Packit Service a2489d
      case('I'+'f'):
Packit Service a2489d
      {
Packit Service a2489d
	 const char *err=MirrorJob::AddPatternsFrom(exclude,opt-'f',optarg);
Packit Service a2489d
	 if(err)
Packit Service a2489d
	 {
Packit Service a2489d
	    eprintf("%s: %s\n",args->a0(),err);
Packit Service a2489d
	    goto no_job;
Packit Service a2489d
	 }
Packit Service a2489d
	 break;
Packit Service a2489d
      }
Packit Service a2489d
      case('R'):
Packit Service a2489d
	 reverse=true;
Packit Service a2489d
	 break;
Packit Service a2489d
      case('L'):
Packit Service a2489d
	 flags|=MirrorJob::RETR_SYMLINKS;
Packit Service a2489d
	 break;
Packit Service a2489d
      case(OPT_NO_DEREFERENCE):
Packit Service a2489d
	 flags&=~MirrorJob::RETR_SYMLINKS;
Packit Service a2489d
	 break;
Packit Service a2489d
      case('v'):
Packit Service a2489d
	 if(optarg)
Packit Service a2489d
	    verbose=atoi(optarg);
Packit Service a2489d
	 else
Packit Service a2489d
	    verbose++;
Packit Service a2489d
	 if(verbose>1)
Packit Service a2489d
	    flags|=MirrorJob::REPORT_NOT_DELETED;
Packit Service a2489d
	 break;
Packit Service a2489d
      case('N'):
Packit Service a2489d
	 newer_than=optarg;
Packit Service a2489d
	 break;
Packit Service a2489d
      case('f'): // mirror for a single file (or glob pattern).
Packit Service a2489d
	 single_file=true;
Packit Service a2489d
	 /*fallthrough*/
Packit Service a2489d
      case('F'): // mirror for a single directory (or glob pattern).
Packit Service a2489d
      {
Packit Service a2489d
	 xstring pattern(basename_ptr(optarg));
Packit Service a2489d
	 if(opt=='F') {
Packit Service a2489d
	    single_dir=true;
Packit Service a2489d
	    if(pattern.last_char()!='/')
Packit Service a2489d
	       pattern.append('/');
Packit Service a2489d
	 }
Packit Service a2489d
	 if(!top_exclude)
Packit Service a2489d
	    top_exclude=new PatternSet();
Packit Service a2489d
	 top_exclude->Add(PatternSet::INCLUDE,new PatternSet::Glob(pattern));
Packit Service a2489d
	 const char *dir=dirname(optarg);
Packit Service a2489d
	 if(source_dir && strcmp(source_dir,dir)) {
Packit Service a2489d
	    eprintf(_("%s: multiple --file or --directory options must have the same base directory\n"),args->a0());
Packit Service a2489d
	    goto no_job;
Packit Service a2489d
	 }
Packit Service a2489d
	 if(!source_dir)
Packit Service a2489d
	    source_dir=alloca_strdup(dir); // save the temp string
Packit Service a2489d
	 break;
Packit Service a2489d
      }
Packit Service a2489d
      case('O'):
Packit Service a2489d
	 target_dir=optarg;
Packit Service a2489d
	 break;
Packit Service a2489d
      case(OPT_OLDER_THAN):
Packit Service a2489d
	 older_than=optarg;
Packit Service a2489d
	 break;
Packit Service a2489d
      case(OPT_SIZE_RANGE):
Packit Service a2489d
	 size_range=new Range(optarg);
Packit Service a2489d
	 if(size_range->Error())
Packit Service a2489d
	 {
Packit Service a2489d
	    eprintf("%s: --size-range \"%s\": %s\n",
Packit Service a2489d
	       args->a0(),optarg,size_range->ErrorText());
Packit Service a2489d
	    goto no_job;
Packit Service a2489d
	 }
Packit Service a2489d
	 break;
Packit Service a2489d
      case(OPT_NO_UMASK):
Packit Service a2489d
	 flags|=MirrorJob::NO_UMASK;
Packit Service a2489d
	 break;
Packit Service a2489d
      case(OPT_USE_CACHE):
Packit Service a2489d
	 use_cache=true;
Packit Service a2489d
	 break;
Packit Service a2489d
      case(OPT_REMOVE_SOURCE_FILES):
Packit Service a2489d
	 remove_source_files=true;
Packit Service a2489d
	 break;
Packit Service a2489d
      case(OPT_REMOVE_SOURCE_DIRS):
Packit Service a2489d
	 remove_source_dirs=true;
Packit Service a2489d
	 break;
Packit Service a2489d
      case(OPT_IGNORE_TIME):
Packit Service a2489d
	 flags|=MirrorJob::IGNORE_TIME;
Packit Service a2489d
	 break;
Packit Service a2489d
      case(OPT_IGNORE_SIZE):
Packit Service a2489d
	 flags|=MirrorJob::IGNORE_SIZE;
Packit Service a2489d
	 break;
Packit Service a2489d
      case(OPT_ONLY_MISSING):
Packit Service a2489d
	 flags|=MirrorJob::IGNORE_TIME|MirrorJob::IGNORE_SIZE;
Packit Service a2489d
	 break;
Packit Service a2489d
      case('P'):
Packit Service a2489d
	 if(optarg)
Packit Service a2489d
	    parallel=atoi(optarg);
Packit Service a2489d
	 else
Packit Service a2489d
	    parallel=3;
Packit Service a2489d
	 break;
Packit Service a2489d
      case(OPT_USE_PGET_N):
Packit Service a2489d
	 if(optarg)
Packit Service a2489d
	    use_pget=atoi(optarg);
Packit Service a2489d
	 else
Packit Service a2489d
	    use_pget=3;
Packit Service a2489d
	 break;
Packit Service a2489d
      case(OPT_SCRIPT_ONLY):
Packit Service a2489d
	 script_only=true;
Packit Service a2489d
      case(OPT_SCRIPT):
Packit Service a2489d
	 script_file=optarg;
Packit Service a2489d
	 if(script_file==0)
Packit Service a2489d
	    script_file="-";
Packit Service a2489d
	 break;
Packit Service a2489d
      case(OPT_DELETE_FIRST):
Packit Service a2489d
	 flags|=MirrorJob::REMOVE_FIRST|MirrorJob::DELETE;
Packit Service a2489d
	 break;
Packit Service a2489d
      case(OPT_NO_SYMLINKS):
Packit Service a2489d
	 flags|=MirrorJob::NO_SYMLINKS;
Packit Service a2489d
	 break;
Packit Service a2489d
      case(OPT_LOOP):
Packit Service a2489d
	 flags|=MirrorJob::LOOP;
Packit Service a2489d
	 break;
Packit Service a2489d
      case(OPT_MAX_ERRORS):
Packit Service a2489d
	 max_error_count=atoi(optarg);
Packit Service a2489d
	 break;
Packit Service a2489d
      case(OPT_SKIP_NOACCESS):
Packit Service a2489d
	 skip_noaccess=true;
Packit Service a2489d
	 break;
Packit Service a2489d
      case(OPT_ON_CHANGE):
Packit Service a2489d
	 on_change=optarg;
Packit Service a2489d
	 break;
Packit Service a2489d
      case(OPT_ONLY_EXISTING):
Packit Service a2489d
	 flags|=MirrorJob::ONLY_EXISTING;
Packit Service a2489d
	 break;
Packit Service a2489d
      case(OPT_NO_EMPTY_DIRS):
Packit Service a2489d
	 no_empty_dirs=true;
Packit Service a2489d
	 break;
Packit Service a2489d
      case(OPT_DEPTH_FIRST):
Packit Service a2489d
	 flags|=MirrorJob::DEPTH_FIRST;
Packit Service a2489d
	 break;
Packit Service a2489d
      case(OPT_SCAN_ALL_FIRST):
Packit Service a2489d
	 flags|=MirrorJob::SCAN_ALL_FIRST|MirrorJob::DEPTH_FIRST;
Packit Service a2489d
	 break;
Packit Service a2489d
      case(OPT_ASCII):
Packit Service a2489d
	 flags|=MirrorJob::ASCII|MirrorJob::IGNORE_SIZE;
Packit Service a2489d
	 break;
Packit Service a2489d
      case(OPT_OVERWRITE):
Packit Service a2489d
	 flags|=MirrorJob::OVERWRITE;
Packit Service a2489d
	 break;
Packit Service a2489d
      case(OPT_NO_OVERWRITE):
Packit Service a2489d
	 flags&=~MirrorJob::OVERWRITE;
Packit Service a2489d
	 break;
Packit Service a2489d
      case(OPT_RECURSION):
Packit Service a2489d
	 recursion_mode=optarg;
Packit Service a2489d
	 break;
Packit Service a2489d
      case(OPT_UPLOAD_OLDER):
Packit Service a2489d
	 flags|=MirrorJob::UPLOAD_OLDER;
Packit Service a2489d
	 break;
Packit Service a2489d
      case(OPT_TRANSFER_ALL):
Packit Service a2489d
	 flags|=MirrorJob::TRANSFER_ALL;
Packit Service a2489d
	 break;
Packit Service a2489d
      case(OPT_TARGET_FLAT):
Packit Service a2489d
	 flags|=MirrorJob::TARGET_FLAT|MirrorJob::SCAN_ALL_FIRST|MirrorJob::DEPTH_FIRST;
Packit Service a2489d
	 break;
Packit Service a2489d
      case(OPT_DELETE_EXCLUDED):
Packit Service a2489d
	 flags|=MirrorJob::DELETE_EXCLUDED;
Packit Service a2489d
	 break;
Packit Service a2489d
      case('?'):
Packit Service a2489d
	 eprintf(_("Try `help %s' for more information.\n"),args->a0());
Packit Service a2489d
      no_job:
Packit Service a2489d
	 return 0;
Packit Service a2489d
      }
Packit Service a2489d
   }
Packit Service a2489d
Packit Service a2489d
   if(exclude && xstrcasecmp(recursion_mode,"never"))
Packit Service a2489d
   {
Packit Service a2489d
      /* Users usually don't want to exclude all directories when recursing */
Packit Service a2489d
      if(exclude->GetFirstType()==PatternSet::INCLUDE)
Packit Service a2489d
	 exclude->AddFirst(PatternSet::INCLUDE,new PatternSet::Regex("/$"));
Packit Service a2489d
   }
Packit Service a2489d
Packit Service a2489d
   /* add default exclusion if no explicit patterns were specified */
Packit Service a2489d
   if(!exclude)
Packit Service a2489d
      MirrorJob::AddPattern(exclude,'\0',0);
Packit Service a2489d
Packit Service a2489d
   args->back();
Packit Service a2489d
Packit Service a2489d
   const char *arg=args->getnext();
Packit Service a2489d
   if(arg)
Packit Service a2489d
   {
Packit Service a2489d
      if(source_dir)
Packit Service a2489d
      {
Packit Service a2489d
	 eprintf(_("%s: ambiguous source directory (`%s' or `%s'?)\n"),args->a0(),
Packit Service a2489d
	    source_dir,arg);
Packit Service a2489d
	 goto no_job;
Packit Service a2489d
      }
Packit Service a2489d
      source_dir=arg;
Packit Service a2489d
      ParsedURL source_url(source_dir);
Packit Service a2489d
      if(source_url.proto && source_url.path)
Packit Service a2489d
      {
Packit Service a2489d
	 source_session=FileAccess::New(&source_url);
Packit Service a2489d
	 if(!source_session)
Packit Service a2489d
	 {
Packit Service a2489d
	    eprintf("%s: %s%s\n",args->a0(),source_url.proto.get(),
Packit Service a2489d
		     _(" - not supported protocol"));
Packit Service a2489d
	    goto no_job;
Packit Service a2489d
	 }
Packit Service a2489d
	 source_dir=alloca_strdup(source_url.path);
Packit Service a2489d
      }
Packit Service a2489d
      arg=args->getnext();
Packit Service a2489d
      if(arg)
Packit Service a2489d
      {
Packit Service a2489d
	 if(target_dir)
Packit Service a2489d
	 {
Packit Service a2489d
	    eprintf(_("%s: ambiguous target directory (`%s' or `%s'?)\n"),args->a0(),
Packit Service a2489d
	       target_dir,arg);
Packit Service a2489d
	    goto no_job;
Packit Service a2489d
	 }
Packit Service a2489d
	 target_dir=arg;
Packit Service a2489d
	 ParsedURL target_url(target_dir);
Packit Service a2489d
	 if(target_url.proto && target_url.path)
Packit Service a2489d
	 {
Packit Service a2489d
	    target_session=FileAccess::New(&target_url);
Packit Service a2489d
	    if(!target_session)
Packit Service a2489d
	    {
Packit Service a2489d
	       eprintf("%s: %s%s\n",args->a0(),target_url.proto.get(),
Packit Service a2489d
			_(" - not supported protocol"));
Packit Service a2489d
	       goto no_job;
Packit Service a2489d
	    }
Packit Service a2489d
	    target_dir=alloca_strdup(target_url.path);
Packit Service a2489d
	 }
Packit Service a2489d
	 if(last_char(arg)=='/' && basename_ptr(arg)[0]!='/' && last_char(source_dir)!='/')
Packit Service a2489d
	 {
Packit Service a2489d
	    // user wants source dir name appended.
Packit Service a2489d
	    const char *base=basename_ptr(source_dir);
Packit Service a2489d
	    if(base[0]!='/' && strcmp(base,basename_ptr(arg))) {
Packit Service a2489d
	       target_dir=xstring::cat(target_dir,base,NULL);
Packit Service a2489d
	       target_dir=alloca_strdup(target_dir); // save the buffer
Packit Service a2489d
	    }
Packit Service a2489d
	 }
Packit Service a2489d
      }
Packit Service a2489d
      else
Packit Service a2489d
      {
Packit Service a2489d
	 target_dir=basename_ptr(source_dir);
Packit Service a2489d
	 if(target_dir[0]=='/')
Packit Service a2489d
	    target_dir=".";
Packit Service a2489d
	 else if(target_dir[0]=='~') {
Packit Service a2489d
	    target_dir=dir_file(".",target_dir);
Packit Service a2489d
	    target_dir=alloca_strdup(target_dir); // save the buffer
Packit Service a2489d
	 }
Packit Service a2489d
      }
Packit Service a2489d
   }
Packit Service a2489d
Packit Service a2489d
   if(!source_dir) {
Packit Service a2489d
      if(ResMgr::QueryBool("mirror:require-source",0)) {
Packit Service a2489d
	 eprintf(_("%s: source directory is required (mirror:require-source is set)\n"),args->a0());
Packit Service a2489d
	 return 0;
Packit Service a2489d
      }
Packit Service a2489d
      source_dir=".";
Packit Service a2489d
   }
Packit Service a2489d
   if(!target_dir)
Packit Service a2489d
      target_dir=".";
Packit Service a2489d
Packit Service a2489d
   if(!reverse)
Packit Service a2489d
   {
Packit Service a2489d
      if(!source_session)
Packit Service a2489d
	 source_session=parent->session->Clone();
Packit Service a2489d
      if(!target_session)
Packit Service a2489d
	 target_session=FileAccess::New("file");
Packit Service a2489d
   }
Packit Service a2489d
   else //reverse
Packit Service a2489d
   {
Packit Service a2489d
      if(!source_session)
Packit Service a2489d
	 source_session=FileAccess::New("file");
Packit Service a2489d
      if(!target_session)
Packit Service a2489d
	 target_session=parent->session->Clone();
Packit Service a2489d
   }
Packit Service a2489d
Packit Service a2489d
   if(no_empty_dirs)
Packit Service a2489d
      flags|=MirrorJob::NO_EMPTY_DIRS|MirrorJob::DEPTH_FIRST;
Packit Service a2489d
Packit Service a2489d
   if(recursion_mode && strcasecmp(recursion_mode,"always")
Packit Service a2489d
   && (flags&MirrorJob::DEPTH_FIRST)) {
Packit Service a2489d
      eprintf("%s: --recursion-mode=%s conflicts with other specified options\n",
Packit Service a2489d
	 args->a0(),recursion_mode);
Packit Service a2489d
      return 0;
Packit Service a2489d
   }
Packit Service a2489d
Packit Service a2489d
   if(parallel<0) {
Packit Service a2489d
      int parallel1=ResMgr::Query("mirror:parallel-transfer-count",source_session->GetHostName());
Packit Service a2489d
      int parallel2=ResMgr::Query("mirror:parallel-transfer-count",target_session->GetHostName());
Packit Service a2489d
      if(parallel1>0)
Packit Service a2489d
	 parallel=parallel1;
Packit Service a2489d
      if(parallel2>0 && (parallel<0 || parallel>parallel2))
Packit Service a2489d
	 parallel=parallel2;
Packit Service a2489d
   }
Packit Service a2489d
   if(use_pget<0) {
Packit Service a2489d
      int use_pget1=ResMgr::Query("mirror:use-pget-n",source_session->GetHostName());
Packit Service a2489d
      int use_pget2=ResMgr::Query("mirror:use-pget-n",target_session->GetHostName());
Packit Service a2489d
      if(use_pget1>0)
Packit Service a2489d
	 use_pget=use_pget1;
Packit Service a2489d
      if(use_pget2>0 && (use_pget<0 || use_pget>use_pget2))
Packit Service a2489d
	 use_pget=use_pget2;
Packit Service a2489d
   }
Packit Service a2489d
Packit Service a2489d
   JobRef<MirrorJob> j(new MirrorJob(0,source_session.borrow(),target_session.borrow(),source_dir,target_dir));
Packit Service a2489d
   j->SetFlags(flags,1);
Packit Service a2489d
   j->SetVerbose(verbose);
Packit Service a2489d
   j->SetExclude(exclude.borrow());
Packit Service a2489d
   j->SetTopExclude(top_exclude.borrow());
Packit Service a2489d
Packit Service a2489d
   if(newer_than)
Packit Service a2489d
      j->SetNewerThan(newer_than);
Packit Service a2489d
   if(older_than)
Packit Service a2489d
      j->SetOlderThan(older_than);
Packit Service a2489d
   if(size_range)
Packit Service a2489d
      j->SetSizeRange(size_range.borrow());
Packit Service a2489d
   j->UseCache(use_cache);
Packit Service a2489d
   if(remove_source_files)
Packit Service a2489d
      j->RemoveSourceFiles();
Packit Service a2489d
   if(remove_source_dirs)
Packit Service a2489d
      j->RemoveSourceDirs();
Packit Service a2489d
   if(skip_noaccess)
Packit Service a2489d
      j->SkipNoAccess();
Packit Service a2489d
   if(parallel<0)
Packit Service a2489d
      parallel=0;
Packit Service a2489d
   if(parallel>64)
Packit Service a2489d
      parallel=64;   // a (in)sane limit.
Packit Service a2489d
   if(parallel)
Packit Service a2489d
      j->SetParallel(parallel);
Packit Service a2489d
   if(use_pget>1 && !(flags&MirrorJob::ASCII))
Packit Service a2489d
      j->SetPGet(use_pget);
Packit Service a2489d
Packit Service a2489d
   if(!recursion_mode && single_file && !single_dir)
Packit Service a2489d
      recursion_mode="never";
Packit Service a2489d
Packit Service a2489d
   if(recursion_mode) {
Packit Service a2489d
      const char *err=j->SetRecursionMode(recursion_mode);
Packit Service a2489d
      if(err) {
Packit Service a2489d
	 eprintf("%s: %s\n",args->a0(),err);
Packit Service a2489d
	 return 0;
Packit Service a2489d
      }
Packit Service a2489d
   }
Packit Service a2489d
   if(script_file)
Packit Service a2489d
   {
Packit Service a2489d
      const char *err=j->SetScriptFile(script_file);
Packit Service a2489d
      if(err)
Packit Service a2489d
      {
Packit Service a2489d
	 eprintf("%s: %s\n",args->a0(),err);
Packit Service a2489d
	 return 0;
Packit Service a2489d
      }
Packit Service a2489d
   }
Packit Service a2489d
   if(script_only)
Packit Service a2489d
   {
Packit Service a2489d
      j->ScriptOnly();
Packit Service a2489d
      if(!script_file)
Packit Service a2489d
	 j->SetScriptFile("-");
Packit Service a2489d
   }
Packit Service a2489d
   j->SetMaxErrorCount(max_error_count);
Packit Service a2489d
   if(on_change)
Packit Service a2489d
      j->SetOnChange(on_change);
Packit Service a2489d
Packit Service a2489d
   return j.borrow();
Packit Service a2489d
Packit Service a2489d
#undef args
Packit Service a2489d
}
Packit Service a2489d
Packit Service a2489d
#include "modconfig.h"
Packit Service a2489d
#ifndef MODULE_CMD_MIRROR
Packit Service a2489d
# define module_init cmd_mirror_module_init
Packit Service a2489d
#endif
Packit Service a2489d
CDECL void module_init()
Packit Service a2489d
{
Packit Service a2489d
   CmdExec::RegisterCommand("mirror",cmd_mirror,0,
Packit Service a2489d
      N_("\n"
Packit Service a2489d
	 "Mirror specified remote directory to local directory\n"
Packit Service a2489d
	 "\n"
Packit Service a2489d
	 " -R, --reverse          reverse mirror (put files)\n"
Packit Service a2489d
	 "Lots of other options are documented in the man page lftp(1).\n"
Packit Service a2489d
	 "\n"
Packit Service a2489d
	 "When using -R, the first directory is local and the second is remote.\n"
Packit Service a2489d
	 "If the second directory is omitted, basename of the first directory is used.\n"
Packit Service a2489d
	 "If both directories are omitted, current local and remote directories are used.\n"
Packit Service a2489d
	 "\n"
Packit Service a2489d
	 "See the man page lftp(1) for a complete documentation.\n"
Packit Service a2489d
      )
Packit Service a2489d
   );
Packit Service a2489d
}