/* apccomm_am.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_am.c,v 1.10 2002/06/11 01:22:18 ralf Exp $ */

#include "apccomm_am.h"
#include "apccomm_am_tr.h"
#include "apccomm_all.h"
#include "apccomm_gui_includes.h"
#include "apccomm_gui.h"

struct transfer_log tfl;
struct sendlist_elem *sendlist_head = NULL;
struct sendlist_elem *sendlist_tail = NULL;
short connect_cancel = 0, send_cancel = 0, receive_cancel = 0;

ULONG rttags[] = { RT_PubScrName, (ULONG)0, TAG_END };

int sendRec(const char *fullname, struct FileInfoBlock *fib, int root)
{
  struct FileInfoBlock rfib;
  BPTR lck,plck;
  char *rfn;
  int ret=0;
  apc_command_t com;
  LONG prot;

  if(fib->fib_DirEntryType < 0) {
    /* a file
       send it with name fib_FileName (not needed)
       open it with fullname */
    ret=sendcommand(APC_COMMAND_FILETRANSFER,fullname,fib->fib_Protection);
  } else {
    /* Lock the dir for Examine */
    lck = Lock(fullname, ACCESS_READ);
    if(lck != NULL) {
      if(Examine(lck, &rfib)) {

        /* Verzeichnis "fullname" wird geentert
           Also "Create&Enter Senden
           Wenn aber root==1, dann stellt "fullname" das Basisverz.
           dar */

	/* Send "Create&Enter" for rfib.fib_FileName */
	
	/* If root==1 it's possibly that this dir is an root-dir (device)
	 * to detect I try to get a parent lock
	 * I know that fib_DirEntryType should be 1 in this case but this
	 * doesn't work correctly for some (many devices) (f.i. disk drives)
	 *
	 * I do this because for root-dirs the fib_Protection doesn't have
	 * useful values
	 */
	if ( root == 1 ) {
	  plck = ParentDir( lck );
	  if ( plck != NULL ) {
	    /* not a root dir */
	    prot = rfib.fib_Protection;
	    UnLock( plck );
	  } else prot = 0; /* normal dir protection */
	} else prot = rfib.fib_Protection;
	ret = sendcommand( APC_COMMAND_ENTERDIR, rfib.fib_FileName, prot );
	com = (apc_command_t) getint(inblock);
	if ( com == APC_COMMAND_ABORT ) ret = -1;

	if(ret==0) {
	  while(ExNext(lck, &rfib)) {
	    rfn = (char*)malloc( strlen(fullname)+1+
				 strlen(rfib.fib_FileName)+1);
	    if(rfn!=NULL) {
	      if(fullname[strlen(fullname)-1] == ':') {
		sprintf(rfn, "%s%s", fullname, rfib.fib_FileName);
	      } else {
		sprintf(rfn, "%s/%s", fullname, rfib.fib_FileName);
	      }
	      ret=sendRec(rfn, &rfib, 0);
	      free(rfn);
	      if(ret!=0) break;
	    } else {
#ifdef GUI
              rtEZRequest( "No mem!", "Okay", NULL, (struct TagItem *)rttags );
#else
	      printf("No mem!\n");
#endif
	    }
	  }
	  if ( ret == 0 ) {
	    /* Send: cd.. (nur wenn root==0?) */
	    ret = sendcommand( APC_COMMAND_LEAVEDIR, NULL , 0 );
	    com = (apc_command_t) getint(inblock);
	    if ( com == APC_COMMAND_ABORT ) ret = -1;
	  }
	}
      }
      UnLock(lck);
    } else {
#ifdef GUI
      rtEZRequest( "No lock for %s, skipping!", "Okay", NULL, (struct TagItem *)rttags, fullname );
#else
      printf("No lock for %s, skipping!\n",fullname);
#endif
    }
  }
  return ret;
}

int send( void )
{
  struct AnchorPath *ap;
  int err,ret=0;
  ULONG secs, usecs;
  double d1;
  struct sendlist_elem *temp;
#ifdef GUI
  char tstr[1024];

  OpenSend_filesWindow();
#endif

  /* Get the space for the structure plus enough for full pathname
     provided by AmigaOS when ap_Strlen>0 */
  ap = (struct AnchorPath*)malloc(sizeof(struct AnchorPath)+1048);
  if(ap == NULL) return 1;

  /* clear the structure */
  memset(ap, 0, sizeof(struct AnchorPath));

  ap->ap_Strlen = 1024;
  /*ap->ap_BreakBits = 0;*/

  /* init transfer log */
  tfl.files = 0;
  tfl.bytes = 0;
  tfl.secs = tfl.usecs = 0;

  /* for all args */
  for ( temp = sendlist_head; temp != NULL; temp = temp->next ) {
    /* Now find all files (for pattern matching) */
    if((err=MatchFirst( temp->filename,ap))==0) {
      do {
        /* now send the entry (can be a file or a dir) */
        ret = sendRec( ap->ap_Buf, &(ap->ap_Info), 1 );
        if ( ret < 0 ) break;
      } while(MatchNext(ap)==0);
      MatchEnd(ap);
    } else {
      if(err==ERROR_OBJECT_NOT_FOUND) {
#ifdef GUI
        rtEZRequest( "No matching files found!", "Okay", NULL, (struct TagItem *)rttags );
#else
        printf("No matching files found!\n");
#endif
      } else {
#ifdef GUI
        rtEZRequest( "Error %ld while matching given pattern!", "Okay", NULL, (struct TagItem *)rttags, err );
#else
        printf("Error %d while matching given pattern!\n",err);
#endif
      }
    }
    if ( ret < 0 ) break;
  }
  free(ap);
  if ( ret == 0 ) {
    sendcommand(APC_COMMAND_FINISHED,NULL,0);
    
#ifdef GUI
    strcpy( tstr, "" );
#endif
    if ( ! BE_QUIET ) {
#ifdef GUI
      strcat( tstr, "Whole transfer completed!" );
#else
      printf("Whole transfer completed!\n");
#endif
    }

#ifdef GUI
    if ( tfl.files > 0 ) {
#else
    if ( tfl.files > 1 ) {
#endif
      secs = tfl.secs;
      usecs = tfl.usecs;
      if ( ( secs == 0 ) && ( usecs == 0 ) ) d1 = 0.0;
      else {
	d1=tfl.bytes;
	d1*=1000000;
	d1/=(secs*1000000.0+usecs);
	d1/=1024;
      }
      if ( ! BE_QUIET ) {
#ifdef GUI
        strcat( tstr, "\nFiles sended: %ld\n" );
        strcat( tstr, "Bytes with average rate: %ld @ %ld KB/s" );
        rtEZRequest( tstr, "Okay", NULL, (struct TagItem *)rttags, tfl.files, tfl.bytes, (long)d1 );
#else
	printf( "  Files sended: %ld\n", tfl.files );
	printf( "  Bytes with average rate: %ld @ %d KB/s\n", tfl.bytes, (int)d1 );
#endif
      }
    } else {
#ifdef GUI
      if ( ! BE_QUIET ) {
        rtEZRequest( tstr, "Okay", NULL, (struct TagItem *)rttags );
      }
#endif
    }
  } else {
#ifdef GUI
    rtEZRequest( "Transfer aborted!", "Okay", NULL, (struct TagItem *)rttags );
#else
    printf( "Transfer aborted!\n" );
#endif
  }
#ifdef GUI
  CloseSend_filesWindow();
#endif
  return 0;
}

int receive( void )
{
  char *arg,*tstr,*tstr2;
  apc_command_t com;
  int ret=0;
  BPTR lock;
  struct FileInfoBlock infoBlock;
  ULONG secs, usecs;
  double d1;
  int mode;

#ifdef GUI
  char buf[1024];

  OpenReceive_filesWindow();
#endif

  /* init transfer log */
  tfl.files = 0;
  tfl.bytes = 0;
  tfl.secs = tfl.usecs = 0;

  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 {
	    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) {
	    /* test for existing entry */
	    lock=Lock(basedir,ACCESS_READ);
	    if(lock==NULL) {
	      /* no? then try to create a dir */
	      lock=CreateDir(basedir);
	      if(lock==NULL) {
		/* No sucess? -> Return */
#ifdef GUI
                rtEZRequest( "Can't create dir %s!", "Okay", NULL, (struct TagItem *)rttags, basedir );
#else
		printf( "Can't create dir %s!\n", basedir );
#endif
		ret=1;
		break;
	      } else UnLock(lock); /* Succes, just unlock */
	    } else {
	      /* Something exists there, get some info about it */
	      if(Examine(lock,&infoBlock)==0) {
		/* no info? -> return */
#ifdef GUI
                rtEZRequest( "Can't get information about %s, aborting!", "Okay", NULL, (struct TagItem *)rttags, basedir );
#else
		printf( "Can't get information about %s, aborting!\n", basedir );
#endif
		ret=1;
		UnLock(lock);
		break;
	      } else {
		if(infoBlock.fib_DirEntryType<0) {
		  /* this is a file -> return */
#ifdef GUI
                  rtEZRequest( "Want to create dir %s, but it's already a file, aborting!", "Okay", NULL, (struct TagItem *)rttags, basedir );
#else
		  printf( "Want to create dir %s, but it's already a file, aborting!\n", basedir );
#endif
		  ret=1;
		  UnLock(lock);
		  break;
		}
	      }
	      /* Ok, there is already a dir */
	      UnLock(lock);
	    }
	    /* Apply mode to dir */
	    if ( ! ignore_prot )
	      if ( SetProtection( basedir, mode ) == 0 )
#ifdef GUI
                rtEZRequest( "Can't restore file protection to %s", "Okay", NULL, (struct TagItem *)rttags, basedir );
#else
		printf( "Can't restore file protection to %s\n", basedir );
#endif
	  }
	  *tstr2='/';
	}
      } else if(com==APC_COMMAND_LEAVEDIR) {
	if(basedir!=NULL) {
	  if(basedir[strlen(basedir)-1]==':') {
	    free(basedir);
	    basedir=NULL;
	  } else {
	    tstr=strrchr(basedir,'/');
	    *tstr='\0';
	    tstr=strrchr(basedir,'/');
	    if(tstr!=NULL) {
	      *(tstr+1)='\0';
	    } else {
	      tstr=strrchr(basedir,':');
	      if(tstr!=NULL) {
		*(tstr+1)='\0';
	      } else {
		free(basedir);
		basedir=NULL;
	      }
	    }
	    if(basedir!=NULL)
	      if(strlen(basedir)<1) {
		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 if ( com == APC_COMMAND_ABORT ) {
	break;
      }
    } else {
#ifdef GUI
      rtEZRequest( "Transfer aborted", "Okay", NULL, (struct TagItem *)rttags );
#else
      printf( "Transfer aborted\n" );
#endif
      break;
    }
  } while(com!=APC_COMMAND_FINISHED);

  if ( ret == 0 ) {

#ifdef GUI
    strcpy( buf, "" );
#endif
    if ( ! BE_QUIET ) {
#ifdef GUI
      strcat( buf, "Whole transfer completed!" );
#else
      printf("Whole transfer completed!\n");
#endif
    }

#ifdef GUI
    if ( tfl.files > 0 ) {
#else
    if ( tfl.files > 1 ) {
#endif
      secs = tfl.secs;
      usecs = tfl.usecs;
      if ( ( secs == 0 ) && ( usecs == 0 ) ) d1 = 0.0;
      else {
	d1=tfl.bytes;
	d1*=1000000;
	d1/=(secs*1000000.0+usecs);
	d1/=1024;
      }
      if ( ! BE_QUIET ) {
#ifdef GUI
        strcat( buf, "\nFiles received: %ld\n" );
        strcat( buf, "Bytes with average rate: %ld @ %ld KB/s\n" );
        rtEZRequest( buf, "Okay", NULL, (struct TagItem *)rttags, tfl.files, tfl.bytes, (long)d1 );
#else
	printf( "  Files received: %ld\n", tfl.files );
	printf( "  Bytes with average rate: %ld @ %d KB/s\n", tfl.bytes, (int)d1 );
#endif
      }
    } else {
#ifdef GUI
      if ( ! BE_QUIET ) {
        rtEZRequest( buf, "Okay", NULL, (struct TagItem *)rttags );
      }
#endif
    }
  } else if ( ret > 0 ) {
    sendcommand( APC_COMMAND_ABORT, NULL , 0 );
  }

#ifdef GUI
  CloseReceive_filesWindow();
#endif
  return ret;
}

int permAmiga2PC(int perm)
{
  int pcperm=0;
  
  /* For DELETE/EXECUTE/WRITE/READ:
   * If set it's forbidden!
   */

  if ( ! ( perm & FIBF_DELETE ) ) pcperm |= 0040;
  if ( ! ( perm & FIBF_EXECUTE ) ) pcperm |= 0100;
  if ( ! ( perm & FIBF_WRITE ) ) pcperm |= 0200;
  if ( ! ( perm & FIBF_READ ) ) pcperm |= 0400;
  if ( perm & FIBF_PURE ) pcperm |= 0001;
  if ( perm & FIBF_SCRIPT ) pcperm |= 0004;
  if ( perm & FIBF_ARCHIVE ) pcperm |= 0010;
  
  return pcperm;
}

int permPC2Amiga(int perm)
{
  int amperm=0;
  
  if ( ! ( perm & 0400 ) ) amperm |= FIBF_READ;
  if ( ! ( perm & 0200 ) ) amperm |= FIBF_WRITE;
  if ( ! ( perm & 0100 ) ) amperm |= FIBF_EXECUTE;
  if ( ! ( perm & 0040 ) ) amperm |= FIBF_DELETE;
  if ( perm & 0010 ) amperm |= FIBF_ARCHIVE;
  if ( perm & 0004 ) amperm |= FIBF_SCRIPT;
  if ( perm & 0001 ) amperm |= FIBF_PURE;

  return amperm;
}

void freeSendList( void )
{
  struct sendlist_elem *temp, *temp2;
  
  temp = sendlist_head;
  while ( temp != NULL ) {
    temp2 = temp->next;
    free( temp->filename );
    free( temp );
    temp = temp2;
  }
  sendlist_head = NULL;
  sendlist_tail = NULL;
}

void addSendListElem( const char *name )
{
  struct sendlist_elem *temp;

  if ( name == NULL ) return;
  temp = (struct sendlist_elem*)malloc( sizeof( struct sendlist_elem ) );
  if ( temp == NULL ) return;
  temp->filename = strdup( name );
  if ( temp->filename == NULL ) {
    free( temp );
    return;
  }
  temp->next = NULL;
  
  if ( sendlist_tail == NULL ) {
    sendlist_head = sendlist_tail = temp;
  } else {
    sendlist_tail->next = temp;
    sendlist_tail = temp;
  }
}

short SendListIsEmpty( void )
{
  if ( sendlist_head == NULL ) return 1;
  return 0;
}
