/* apccomm_pc.c
   Part of "APCComm" 
   Copyright (C) 2001 Ralf Hoffmann
   Contact: ralf.hoffmann@epost.de

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  
*/
/* $Id: apccomm_pc.c,v 1.8 2002/06/11 00:11:39 ralf Exp $ */

#include "apccomm_pc.h"
#include "apccomm_pc_tr.h"
#include "apccomm_all.h"

#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/stat.h>

struct transfer_log tfl;

int sendRec(const char *dirstr)
{
  /* fullname is not really the full name, but the (relative) path */
  struct dirent *namelist;
  DIR *dir;
  struct stat buf;
  char *fullname,*filepart,*tstr;
  int ret=0;
  int sended=0;
  apc_command_t com;
  int nm;

  if(stat(dirstr,&buf)==0) {
    if(S_ISDIR(buf.st_mode)) {
      dir=opendir(dirstr);
      if(dir!=NULL) {
	nm = 0;
	if ( buf.st_mode & S_IRUSR ) nm |= 0400;
	if ( buf.st_mode & S_IWUSR ) nm |= 0200;
	if ( buf.st_mode & S_IXUSR ) nm |= 0100;
	if ( buf.st_mode & S_IRGRP ) nm |= 0040;
	if ( buf.st_mode & S_IWGRP ) nm |= 0020;
	if ( buf.st_mode & S_IXGRP ) nm |= 0010;
	if ( buf.st_mode & S_IROTH ) nm |= 0004;
	if ( buf.st_mode & S_IWOTH ) nm |= 0002;
	if ( buf.st_mode & S_IXOTH ) nm |= 0001;

	/* Send EnterDir with filepart */
	filepart=strdup(dirstr);
	if(filepart[strlen(filepart)-1]=='/') filepart[strlen(filepart)-1]='\0';
	tstr=strrchr(filepart,'/');
	if(tstr!=NULL) {
	  tstr++;
	  if(strlen(tstr)>0) {
	    ret=sendcommand(APC_COMMAND_ENTERDIR,tstr,nm);
	    com = (apc_command_t) getint(inblock);
	    if ( com == APC_COMMAND_ABORT ) ret = -1;
	    
	    sended=1;
	  }
	} else {
	  if(strlen(filepart)>0) {
	    ret=sendcommand(APC_COMMAND_ENTERDIR,filepart,nm);
	    com = (apc_command_t) getint(inblock);
	    if ( com == APC_COMMAND_ABORT ) ret = -1;

	    sended=1;
	  }
	}

	if ( ret == 0 ) {
	  while((namelist=readdir(dir))!=NULL) {
	    if( (strcmp(namelist->d_name,".")!=0) &&
		(strcmp(namelist->d_name,"..")!=0) ) {
	      if(dirstr[strlen(dirstr)-1]=='/') {
		fullname=(char*)malloc(strlen(dirstr)+strlen(namelist->d_name)+1);
		sprintf(fullname,"%s%s",dirstr,namelist->d_name);
	      } else {
		fullname=(char*)malloc(strlen(dirstr)+1+strlen(namelist->d_name)+1);
		sprintf(fullname,"%s/%s",dirstr,namelist->d_name);
	      }
	      ret = sendRec(fullname);
	      free(fullname);
	    }
	    if ( ret != 0 ) break;
	  }
	}
	closedir(dir);
	/* Send LeaveDir */
	if ( ( sended == 1 ) && ( ret == 0 ) ) {
	  ret=sendcommand(APC_COMMAND_LEAVEDIR,NULL,0);
	  com = (apc_command_t) getint(inblock);
	  if ( com == APC_COMMAND_ABORT ) ret = -1;
	}
	free(filepart);
      } else {
	fprintf( stderr, "can't open dir %s!\n", dirstr );
      }
    } else if(S_ISREG(buf.st_mode)) {
      nm = 0;
      if ( buf.st_mode & S_IRUSR ) nm |= 0400;
      if ( buf.st_mode & S_IWUSR ) nm |= 0200;
      if ( buf.st_mode & S_IXUSR ) nm |= 0100;
      if ( buf.st_mode & S_IRGRP ) nm |= 0040;
      if ( buf.st_mode & S_IWGRP ) nm |= 0020;
      if ( buf.st_mode & S_IXGRP ) nm |= 0010;
      if ( buf.st_mode & S_IROTH ) nm |= 0004;
      if ( buf.st_mode & S_IWOTH ) nm |= 0002;
      if ( buf.st_mode & S_IXOTH ) nm |= 0001;
      
      ret=sendcommand(APC_COMMAND_FILETRANSFER,dirstr,nm);
    }
  } else {
    fprintf( stderr, "no dir information for %s!\n", dirstr );
  }
  return ret;
}

int send( int argc, char **argv, short *realargs )
{
  int i, ret = 0;
  unsigned long secs, usecs;
  double d1;

  /* init transfer log */
  tfl.files = 0;
  tfl.bytes = 0;
  tfl.tvs.tv_sec = tfl.tvs.tv_usec = 0;

  /* for all args */
  for( i = 1; i < argc; i++ ) {
    /* only if this arg should be used
     * determined by the options-checker in main
     */
    if ( realargs[i] == 1 ) {
      ret = sendRec( argv[i] );
      if ( ret < 0 ) break;
    }
  }
  if ( ret == 0 ) {
    sendcommand( APC_COMMAND_FINISHED, NULL , 0);
    if ( ! BE_QUIET ) printf( "Whole transfer completed!\n" );

    if ( tfl.files > 1 ) {
      secs = tfl.tvs.tv_sec;
      usecs = tfl.tvs.tv_usec;
      d1=tfl.bytes;
      d1*=1000000;
      d1/=(secs*1000000.0+usecs);
      d1/=1024;
      if ( ! BE_QUIET ) {
	printf( "  Files sended: %ld\n", tfl.files );
	printf( "  Bytes with average rate: %ld @ %.2g KB/s\n", tfl.bytes, d1 );
      }
    }
  } else {
    printf( "Transfer aborted!\n" );
  }
  return 0;
}


int receive()
{
  char *arg,*tstr,*tstr2;
  apc_command_t com;
  int ret=0;
  struct stat buf;
  unsigned long secs, usecs;
  double d1;
  int mode, tm;

  /* init transfer log */
  tfl.files = 0;
  tfl.bytes = 0;
  tfl.tvs.tv_sec = tfl.tvs.tv_usec = 0;

  basedir=NULL;
  do {
    if(recvcommand(&com,&arg,&mode)==0) {
      if(com==APC_COMMAND_ENTERDIR) {
	if(arg!=NULL) {
	  if(basedir==NULL) {
	    basedir=(char*)malloc(strlen(arg)+1+1);
	    sprintf(basedir,"%s/",arg);
	    free(arg);
	  } else {
	    if(strlen(basedir)<2) {
	      tstr=(char*)malloc(strlen(arg)+2+1);
	      sprintf(tstr,"/%s/",arg);
	      free(basedir);
	      basedir=tstr;
	    } else {
	      tstr=(char*)malloc(strlen(basedir)+strlen(arg)+1+1);
	      sprintf(tstr,"%s%s/",basedir,arg);
	      free(basedir);
	      basedir=tstr;
	    }
	    free(arg);
	  }
	  /* now create basedir */
	  tstr2=strrchr(basedir,'/');
	  *tstr2='\0';
	  if(strlen(basedir)!=0) {
	    if(stat(basedir,&buf)!=0) {
	      if(mkdir(basedir,0755)!=0) {
		fprintf( stderr, "Can't create dir %s, aborting!\n", basedir );
		ret=1;
		break;
	      }
	    } else {
	      if(!S_ISDIR(buf.st_mode)) {
		fprintf( stderr, "Want to create dir %s but there is already a file, aborting!\n", basedir );
		ret=1;
		break;
	      }
	    }
	    /* Apply mode
	     * mode is the unix number but to be sure:
	     */
	    tm = 0;
	    if ( mode & 0400 ) tm |= S_IRUSR;
	    if ( mode & 0200 ) tm |= S_IWUSR;
	    if ( mode & 0100 ) tm |= S_IXUSR;
	    if ( mode & 0040 ) tm |= S_IRGRP;
	    if ( mode & 0020 ) tm |= S_IWGRP;
	    if ( mode & 0010 ) tm |= S_IXGRP;
	    if ( mode & 0004 ) tm |= S_IROTH;
	    if ( mode & 0002 ) tm |= S_IWOTH;
	    if ( mode & 0001 ) tm |= S_IXOTH;

	    /* dir should be read and searchable */
	    tm |= S_IRUSR;
	    tm |= S_IXUSR;
	    
	    if ( ! ignore_prot )
	      if ( chmod( basedir, tm ) != 0 ) {
		fprintf( stderr, "Can't restore file protection to %s\n", basedir);
	    }
	  }
	  *tstr2='/';
	}
      } else if(com==APC_COMMAND_LEAVEDIR) {
	if(basedir!=NULL) {
	  tstr=strrchr(basedir,'/');
	  *tstr='\0';
	  tstr=strrchr(basedir,'/');
	  if(tstr!=NULL) {
	    *(tstr+1)='\0';
	    if(strlen(basedir)<1) {
	      free(basedir);
	      basedir=NULL;
	    }
	  } else {
	    free(basedir);
	    basedir=NULL;
	  }
	}
      } else if(com==APC_COMMAND_FILETRANSFER) {
	/* Nothing has to be done because recvcommand handle this already */
      } else if(com==APC_COMMAND_ERROR) {
	break;
      }
    } else {
      fprintf( stderr, "Transfer aborted\n" );
      break;
    }
  } while(com!=APC_COMMAND_FINISHED);
  
  if ( ret == 0 ) {
    if ( ! BE_QUIET ) printf( "Whole transfer completed!\n" );
    
    if ( tfl.files > 1 ) {
      secs = tfl.tvs.tv_sec;
      usecs = tfl.tvs.tv_usec;
      d1=tfl.bytes;
      d1*=1000000;
      d1/=(secs*1000000.0+usecs);
      d1/=1024;
      if ( ! BE_QUIET ) {
	printf( "  Files received: %ld\n", tfl.files );
	printf( "  Bytes with average rate: %ld @ %d KB/s\n", tfl.bytes, (int)d1 );
      }
    }
  } else if ( ret > 1 ) {
    sendcommand( APC_COMMAND_ABORT, NULL , 0 );
  }

  return ret;
}
