/* Copyright (c) 1998,1999   Alexander Yukhimets. All rights reserved. */
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<sys/stat.h>
#include<setjmp.h>

#include"multi.h"

#include"axyftp.h"
#include"progress_dialog.h"
#include"read_init.h"
#include"fileinfo.h"
#include"dirinfo.h"
#include"functions.h"
#include"utils.h"
#include"ftp.h"

#include"prompt_dialogs.h"

sigjmp_buf jmp_down_env;

static struct _dirs {
  char* dir;
  struct _dirs *next;
} *dirs;

void destroy_dirs(struct _dirs* d){
  if(d==NULL)return;
  WXfree(d->dir);
  destroy_dirs(d->next);
  WXfree((char*)d);
}

static int append_dirs(char* dir){
  struct _dirs *d;

  if(dirs==NULL){
    dirs=(struct _dirs*)WXmalloc(sizeof(struct _dirs));
    dirs->dir=NULL;
    dirs->next=NULL;
  }

  for(d=dirs;d->next!=NULL;d=d->next){
    if(strcmp(d->next->dir,dir)==0){
      return 1;
    }
  }

  d->next=(struct _dirs*)WXmalloc(sizeof(struct _dirs));
  d=d->next;
  d->next=NULL;
  d->dir=WXnewstring(dir);

  return 0;
}

#ifndef SIZE
#define SIZE 256
#endif

void get_dir_files(dirinfo*,char);
void put_dir_files(dirinfo*,char);

static int process_down_directory(fileinfo* fi,char type){
  dirinfo *di;
  struct stat statbuf;
  char* buf;
  int size;

  if(appdata.interrupt) return -1;

  if(!stat(fi->name,&statbuf)){
    if(!S_ISDIR(statbuf.st_mode)){
      return 1;
    }
  } else {
    if(mkdir(fi->name,0755)==-1)return 2;
  }

  size=SIZE;
  while(1) {
    buf=WXmalloc(size);
    if(getcwd(buf,size)==NULL){
      if(errno==ERANGE){
        WXfree(buf);
        size*=2;
      } else {
        WXfree(buf);
        return 3;
      }
    } else {
      break;
    }
  }


  if(chdir(fi->name)==-1){
    WXfree(buf);
    return 4;
  }

  di=create_remote_dirinfo(NULL);

  if(appdata.interrupt){
    destroy_dirinfo(di);
    return -1;
  }
  
  if(!append_dirs(di->dir))get_dir_files(di,type);
  destroy_dirinfo(di);
  chdir(buf);
  WXfree(buf);
  return 0;
}


void get_dir_files(dirinfo* di, char type){
  fileinfo *fi;

  if(appdata.interrupt) return;
  /*printf("%s\n",di->dir);*/

  fi=di->files[0];
  while(fi->next!=NULL){
    fi=fi->next;
    if(fi->perms[0]=='-'){
      download_file(fi,type);
      /*{
        char* p;
	p=get_fileinfo_string(fi);
	printf("%d: %s",ret,p);
	WXfree(p);
      }*/
      if(appdata.interrupt) return;
    }
  }

  fi=di->files[0];
  while(fi->next!=NULL){
    fi=fi->next;
    if(fi->perms[0]=='d'){
      if(strcmp(fi->name,".") && strcmp(fi->name,"..") &&
	  !ftp_chdir(fi->name,&appdata.connect,logfile,check_for_interrupt)){
        process_down_directory(fi,type);
	ftp_chdir(di->dir,&appdata.connect,logfile,check_for_interrupt);
	if(appdata.interrupt) return;
      }
    }
  }

  fi=di->files[0];
  while(fi->next!=NULL){
    fi=fi->next;
    if(fi->perms[0]=='l'){
      if(!ftp_chdir(fi->name,&appdata.connect,logfile,check_for_interrupt)){
        process_down_directory(fi,type);
	ftp_chdir(di->dir,&appdata.connect,logfile,check_for_interrupt);
	if(appdata.interrupt) return;
      } else {
	download_file(fi,type);
	/*{
	  char* p;
	  p=get_fileinfo_string(fi);
	  printf("%d: %s",ret,p);
	  WXfree(p);
	}*/
	if(appdata.interrupt) return;
      }
    }
  }
}

int get_files(int* list,char type){
  dirinfo *di;
  
  dirs=NULL;

  di=select_dirinfo(appdata.remote.list,list);
  append_dirs(di->dir);

  get_dir_files(di,type);

  /*
  printf("%s %d\n",di->dir,di->total);
  for(i=0;i<di->total;i++){
    char* p;
    p=get_fileinfo_string(di->files[i+1]);
    printf("%s",p);
    WXfree(p);
  }
  printf("\n");
  */

  destroy_dirinfo(di);
  destroy_dirs(dirs);

  
  if(appdata.progress_shown){
    hide_progress_dialog(appdata.progress);
    appdata.progress_shown=0;
  }

  appdata.interrupt=0;

  return 0;
}

static int process_up_directory(fileinfo* fi,char type){
  dirinfo *di;
  char* buf;

  if(appdata.interrupt) return -1;

  di=create_local_dirinfo(NULL);
  if(append_dirs(di->dir)){
    destroy_dirinfo(di);
    return 1;
  }
  if(ftp_pwd(&buf,&appdata.connect,logfile,check_for_small_interrupt)){
    return 2;
  }
  if(appdata.interrupt){
    destroy_dirinfo(di);
    return -1;
  }
  ftp_mkdir(fi->name,&appdata.connect,logfile,check_for_small_interrupt);
  if(appdata.interrupt){
    destroy_dirinfo(di);
    return -1;
  }
  if(ftp_chdir(fi->name,&appdata.connect,logfile,check_for_small_interrupt)){
    destroy_dirinfo(di);
    return 1;
  }
  put_dir_files(di,type);
  destroy_dirinfo(di);
  ftp_chdir(buf,&appdata.connect,logfile,check_for_small_interrupt);
  WXfree(buf);

  return 0;
}

void put_dir_files(dirinfo* di, char type){
  fileinfo *fi;

  if(appdata.interrupt) return;
  /*printf("%s\n",di->dir);*/

  fi=di->files[0];
  while(fi->next!=NULL){
    fi=fi->next;
    if(fi->perms[0]=='-'){
      upload_file(fi,type);
      /*{
        char* p;
	p=get_fileinfo_string(fi);
	printf("%d: %s",ret,p);
	WXfree(p);
      }*/
      if(appdata.interrupt) return;
    }
  }

  fi=di->files[0];
  while(fi->next!=NULL){
    fi=fi->next;
    if(fi->perms[0]=='d'){
      if(strcmp(fi->name,".") && strcmp(fi->name,"..") && chdir(fi->name)!=-1){
        process_up_directory(fi,type);
	chdir(di->dir);
	if(appdata.interrupt) return;
      }
    }
  }

  fi=di->files[0];
  while(fi->next!=NULL){
    fi=fi->next;
    if(fi->perms[0]=='l'){
      if(chdir(fi->name)!=-1){
        process_up_directory(fi,type);
	chdir(di->dir);
	if(appdata.interrupt) return;
      } else {
	upload_file(fi,type);
	/*{
	  char* p;
	  p=get_fileinfo_string(fi);
	  printf("%d: %s",ret,p);
	  WXfree(p);
	}*/
	if(appdata.interrupt) return;
      }
    }
  }
}


int put_files(int* list,char type){
  dirinfo *di;
  
  dirs=NULL;

  di=select_dirinfo(appdata.local.list,list);
  append_dirs(di->dir);

  put_dir_files(di,type);

  destroy_dirinfo(di);
  destroy_dirs(dirs);

  if(appdata.progress_shown){
    hide_progress_dialog(appdata.progress);
    appdata.progress_shown=0;
  }

  appdata.interrupt=0;

  return 0;
}

static int verify_deletions;
static int verify_local_deletions;

static void delete_remote_link(fileinfo *fi){
  delete_remote(fi->name);
  if(appdata.connect.lastline[0]=='5'){
    ftp_put('I',fi->link,&appdata.connect,logfile,check_for_interrupt);
    ftp_close_data(&appdata.connect,logfile,check_for_interrupt);
    delete_remote(fi->name);
    delete_remote(fi->link);
  }
}


int delete_remote_dir_files(dirinfo* volatile di){
  dirinfo * volatile di_new;
  fileinfo * volatile fi;
  int volatile ret;

  fi=di->files[0];
  while(fi->next!=NULL){
    fi=fi->next;
    if(fi->perms[0]=='l'){
      if(verify_deletions){
	if(!(ret=sigsetjmp(jmp_down_env,1))){
	  (void)init_delete_dialog(appdata.delete_dialog,fi->name);
	  LOOP();
	}
	if(ret==1){
	  delete_remote_link(fi);
	} else if(ret==2){
	  verify_deletions=0;
	  delete_remote_link(fi);
	}
      } else {
	delete_remote_link(fi);
      }
    }
  }
  fi=di->files[0];
  while(fi->next!=NULL){
    fi=fi->next;
    if(fi->perms[0]!='d'){
      if(verify_deletions){
	if(!(ret=sigsetjmp(jmp_down_env,1))){
	  (void)init_delete_dialog(appdata.delete_dialog,fi->name);
	  LOOP();
	}
	if(ret==1){
	  delete_remote(fi->name);
	} else if(ret==2){
	  verify_deletions=0;
	  delete_remote(fi->name);
	}
      } else {
	delete_remote(fi->name);
      }
    } else if(strcmp(".",fi->name) && strcmp("..",fi->name)){
      if( appdata.odata->recurse_del &&
	  !ftp_chdir(fi->name,&appdata.connect,logfile,check_for_interrupt)){
	di_new=create_remote_dirinfo(".* *");
	delete_remote_dir_files(di_new);
	destroy_dirinfo(di_new);
	ftp_chdir(di->dir,&appdata.connect,logfile,check_for_interrupt);
      }
      if(verify_deletions){
	if(!(ret=sigsetjmp(jmp_down_env,1))){
	  (void)init_delete_dialog(appdata.delete_dialog,fi->name);
	  LOOP();
	}
	if(ret==1){
	  rmdir_remote(fi->name);
	} else if(ret==2){
	  verify_deletions=0;
	  rmdir_remote(fi->name);
	}
      } else {
	rmdir_remote(fi->name);
      }
    }
  }

  return 0;
}


int delete_remote_files(int* list){

  dirinfo *di;
  
  dirs=NULL;

  di=select_dirinfo(appdata.remote.list,list);
  verify_deletions=appdata.odata->deletions;

  delete_remote_dir_files(di);
  destroy_dirinfo(di);

  return 0;
}

int delete_local_dir_files(dirinfo* volatile di){
  dirinfo * volatile di_new;
  fileinfo * volatile fi;
  int volatile ret;

  fi=di->files[0];
  while(fi->next!=NULL){
    fi=fi->next;
    if(fi->perms[0]!='d'){
      if(verify_local_deletions){
	if(!(ret=sigsetjmp(jmp_down_env,1))){
	  (void)init_delete_dialog(appdata.delete_dialog,fi->name);
	  LOOP();
	}
	if(ret==1){
	  delete_local(fi->name);
	} else if(ret==2){
	  verify_local_deletions=0;
	  delete_local(fi->name);
	}
      } else {
	delete_local(fi->name);
      }
    } else if(strcmp(".",fi->name) && strcmp("..",fi->name)){
      if( appdata.odata->recurse_del &&
	  !chdir(fi->name)){
	di_new=create_local_dirinfo(".* *");
	delete_local_dir_files(di_new);
	destroy_dirinfo(di_new);
	chdir(di->dir);
      }
      if(verify_local_deletions){
	if(!(ret=sigsetjmp(jmp_down_env,1))){
	  (void)init_delete_dialog(appdata.delete_dialog,fi->name);
	  LOOP();
	}
	if(ret==1){
	  rmdir_local(fi->name);
	} else if(ret==2){
	  verify_local_deletions=0;
	  rmdir_local(fi->name);
	}
      } else {
	rmdir_local(fi->name);
      }
    }
  }

  return 0;
}


int delete_local_files(int* list){

  dirinfo *di;
  
  dirs=NULL;

  di=select_dirinfo(appdata.local.list,list);
  verify_local_deletions=appdata.odata->deletions;

  delete_local_dir_files(di);
  destroy_dirinfo(di);

  return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1