/*

STRICQ - An Amiga, MUI Based, ICQ Clone.
Copyright (C) 1998-2000 Douglas F. McLaughlin

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.

Douglas F. McLaughlin - stricq@owlnet.net

*/

#include <sys/types.h>

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include <exec/types.h>

#include <devices/timer.h>

#include <libraries/asl.h>
#include <libraries/asyncio.h>
#include <libraries/icq.h>

#include <bsdsocket/socketbasetags.h>

#include <sys/errno.h>
#include <sys/time.h>

#include <proto/asyncio.h>
#include <proto/dos.h>

#include "STRICQMUI.h"
#include "cmd_codes.h"

#include <proto/socket.h>


LONG __asm SetFileSendStats(REG(a2) APTR obj);
void __asm SkipFile(REG(a2) APTR obj);

void  EndFile(struct Contact *);
ULONG GetSize(struct FileList *files);
void  QueueNextFile(struct Sockets *, struct Contact *);
void  ResetTimer(struct TimeData *);
BOOL  StartFileXFer(struct Contact *, BOOL);

extern void Add2Log(UBYTE, ULONG, UWORD, BOOL, UWORD, char *, ULONG);
extern void AddUser2CList(struct Contact *);
extern void DelSockets(UWORD);
extern struct Contact *GetContact(ULONG);
extern struct Sockets *GetSocketFmContact(struct Contact *, UBYTE);
extern void HandleError(UBYTE, char *, ...);
extern void PrepNewContact(struct Contact *);
extern void SendAMessage(APTR, ULONG, char *, UWORD, UBYTE, int);
extern BOOL SendTCPMessage(ULONG, char *, UWORD, UWORD, UWORD, ULONG, char *, ULONG, UWORD, ULONG);
extern struct Sockets *GetSocketFmSocket(UWORD);
extern void revmemcpy(char *, char *, int);

extern char err_buf[];

extern ULONG AsyncBuf, TCPCodes[], TCP_Seq;

extern struct MUI_CustomClass *mcc_Time;
extern struct ObjApp *app;


void ParseFilePkt(char *buf, int len, UWORD sock)

{

char cbuf[128];

int off = 0;

ULONG uin;

UWORD pktlen = 0;

struct Contact *user, User;
struct Sockets *socks;

if (socks = GetSocketFmSocket(sock)) user = GetContact(socks->UIN);

revmemcpy((char *)&pktlen,buf+off,2); off += 2;

switch(buf[off++]) {
  case (char)0xff: {
    PrintHex(DBG_TCP_PKTS,"Recv file packet 0xFF",buf,len);
    off += 8;
    revmemcpy((char *)&uin,buf+off,4); off += 4;
    if (!(user = GetContact(uin))) {
      PrepNewContact(&User);
      User.UIN = uin;
      sprintf(cbuf,"%ld",uin);
      User.Nick = cbuf;
      User.Status = STAT_NEWUIN;
      User.CanTCP = 4;
      AddUser2CList(&User);
      user = GetContact(uin);
    }
    socks->UIN = user->UIN;
    socks->HadInit = TRUE;
    socks->Timehack = time(NULL);
    DoMethod(app->LV_SockList,MUIM_NList_Redraw,MUIV_NList_Redraw_All);
    break;
  }
  case (char)0x00: {
    char *nick, *mynick, *dldir;
    UBYTE hs = 0x01;
    ULONG tot_files = 0, tot_size = 0, speed = 0;
    UWORD nick_len = 0, plen = 0, revlen = 0;
    PrintHex(DBG_TCP_PKTS,"Recv packet 0x00",buf,len);
    off += 4;
    revmemcpy((char *)&tot_files,buf+off,4); off += 4;
    revmemcpy((char *)&tot_size,buf+off,4); off += 4;
    revmemcpy((char *)&speed,buf+off,4); off += 4;
    off += 2;
    nick = buf+off;
    if (!StartFileXFer(user,TRUE)) {
      DelSockets(sock);
      return;
    }
    if (tot_files == 1) set(user->FWin->BT_Skip,MUIA_Disabled,TRUE);
    user->FWin->TotAllFiles = user->FWin->CurrFiles = 0;
    user->FWin->TotalFiles = tot_files;
    user->FWin->TotFilesSize = tot_size;
    sprintf(cbuf,"0/%ld",tot_files);
    set(user->FWin->TX_FileCount,MUIA_Text_Contents,cbuf);
    sprintf(cbuf,"0 Bytes of %ld",tot_size);
    set(user->FWin->TX_BatchStat,MUIA_Text_Contents,cbuf);
    set(user->FWin->GA_BatchXfer,MUIA_Gauge_Current,0);
    set(user->FWin->GA_BatchXfer,MUIA_Gauge_Max,(tot_size>65535?tot_size/1024:tot_size));
    sprintf(user->FWin->Title,"FileDirect Session With %s",nick);
    set(user->FWin->WI_FileWin,MUIA_Window_Title,user->FWin->Title);
    get(app->STR_DownloadDir,MUIA_String_Contents,&dldir);
    set(user->FWin->TX_StoreDir,MUIA_Text_Contents,dldir);
    get(app->STR_Nick,MUIA_String_Contents,&mynick);
    nick_len = strlen(mynick)+1;
    get(user->FWin->NU_Speed,MUIA_Numeric_Value,&speed);
    revmemcpy(cbuf+plen,(char *)&hs,1); plen++;
    revmemcpy(cbuf+plen,(char *)&speed,4); plen += 4;
    revmemcpy(cbuf+plen,(char *)&nick_len,2); plen += 2;
    strcpy(cbuf+plen,mynick); plen += nick_len;
    PrintHex(DBG_TCP_PKTS,"Send packet 0x01",cbuf,plen);
    revmemcpy((char *)&revlen,(char *)&plen,2);
    send(sock,(char *)&revlen,2,0);
    send(sock,cbuf,plen,0);
    break;
  }
  case (char)0x01: {
    char *nick;
    PrintHex(DBG_TCP_PKTS,"Recv packet 0x01",buf,len);
    off += 6;
    nick = buf+off;
    sprintf(user->FWin->Title,"FileDirect Session With %s",nick);
    set(user->FWin->WI_FileWin,MUIA_Window_Title,user->FWin->Title);
    QueueNextFile(socks,user);
    break;
  }
  case (char)0x02: {
    char *filename, *X1, *dldir, fullname[MAXNAMLEN];
    LONG access = MODE_WRITE, ret;
    UBYTE hs = 0x03;
    ULONG filesize = 0, X2 = 0, X3 = 0, speed = 0, size;
    UWORD text_len = 0, plen = 0, revlen = 0;
    struct FileList File;
    PrintHex(DBG_TCP_PKTS,"Recv packet 0x02",buf,len);
    off++;
    revmemcpy((char *)&text_len,buf+off,2); off += 2;
    filename = buf+off; off += text_len;
    revmemcpy((char *)&text_len,buf+off,2); off += 2;
    X1 = buf+off; off += text_len;
    revmemcpy((char *)&filesize,buf+off,4); off += 4;
    revmemcpy((char *)&X2,buf+off,4); off += 4;
    revmemcpy((char *)&speed,buf+off,4);
    user->FWin->ThisFile = 0;
    user->FWin->ThisFileSize = filesize;
    set(user->FWin->TX_Filename,MUIA_Text_Contents,filename);
    sprintf(cbuf,"%ld/%ld",++user->FWin->CurrFiles,user->FWin->TotalFiles);
    set(user->FWin->TX_FileCount,MUIA_Text_Contents,cbuf);
    set(user->FWin->GA_FileXfer,MUIA_Gauge_Current,0);
    set(user->FWin->GA_FileXfer,MUIA_Gauge_Max,(filesize>65535?filesize/1024:filesize));
    sprintf(cbuf,"0 Bytes of %ld",filesize);
    set(user->FWin->TX_FileStat,MUIA_Text_Contents,cbuf);
    get(app->STR_DownloadDir,MUIA_String_Contents,&dldir);
    if (LASTCHAR(dldir) == ':' || LASTCHAR(dldir) == '/') sprintf(fullname,"%s%s",dldir,filename);
    else sprintf(fullname,"%s/%s",dldir,filename);
    if (user->FWin->File) CloseAsync(user->FWin->File);
    strcpy(File.Filename,fullname);
    if (size = GetSize(&File)) {
      if ((ret = MUI_Request(app->App,user->FWin->WI_FileWin,0,NULL,"Resume|Overwrite|Cancel","%s Already Exists!\n\n Here:  %ld\nThere:  %ld",filename,size,filesize)) == 0) {
        set(user->FWin->TX_Status,MUIA_Text_Contents,"User Abort.");
        DelSockets(sock);
        return;
      }
      if (ret == 1) {
        access = MODE_APPEND;
        user->FWin->ThisFile = size;
        user->FWin->TotAllFiles += size;
      }
      else size = 0;
    }
    if (!(user->FWin->File = OpenAsync(fullname,access,AsyncBuf))) {
      set(user->FWin->TX_Status,MUIA_Text_Contents,"Unable to Open File");
      DelSockets(sock);
      return;
    }
    strcpy(user->FWin->Status,"Receiving");
    set(user->FWin->TX_Status,MUIA_Text_Contents,user->FWin->Status);
    if (user->FWin->StartTime == 0) user->FWin->StartTime = time(NULL);
    get(user->FWin->NU_Speed,MUIA_Numeric_Value,&speed);
    revmemcpy(cbuf+plen,(char *)&hs,1); plen++;
    revmemcpy(cbuf+plen,(char *)&size,4); plen += 4;
    revmemcpy(cbuf+plen,(char *)&X3,4); plen += 4;
    revmemcpy(cbuf+plen,(char *)&speed,4); plen += 4;
    PrintHex(DBG_TCP_PKTS,"Send packet 0x03",cbuf,plen);
    revmemcpy((char *)&revlen,(char *)&plen,2);
    send(sock,(char *)&revlen,2,0);
    send(sock,cbuf,plen,0);
    break;
  }
  case (char)0x03: {
    ULONG resume, X1;
    struct FileList *files;
    PrintHex(DBG_TCP_PKTS,"Recv packet 0x03",buf,len);
    revmemcpy((char *)&resume,buf+off,4); off += 4;
    revmemcpy((char *)&X1,buf+off,4); off += 4;
    DoMethod(app->LV_FileList,MUIM_NList_GetEntry,0,&files);
    if (user->FWin->File) CloseAsync(user->FWin->File);
    if (!(user->FWin->File = OpenAsync(files->Filename,MODE_READ,AsyncBuf))) {
      set(user->FWin->TX_Status,MUIA_Text_Contents,"Unable to Open File");
      SkipFile(user->FWin->BT_Skip);
      break;
    }
    if (resume) {
      SeekAsync(user->FWin->File,resume,MODE_START);
      user->FWin->ThisFile = resume;
      user->FWin->TotAllFiles += resume;
    }
    strcpy(user->FWin->Status,"Sending");
    set(user->FWin->TX_Status,MUIA_Text_Contents,user->FWin->Status);
    if (user->FWin->StartTime == 0) user->FWin->StartTime = time(NULL);
    socks->FileMode = user;
    break;
  }
  case (char)0x04: {
    ULONG entries;
    PrintHex(DBG_TCP_PKTS,"Recv packet 0x04",buf,len);
    set(user->FWin->TX_Status,MUIA_Text_Contents,"Skipping");
    user->FWin->TotAllFiles += ((user->FWin->ThisFileSize)-(user->FWin->ThisFile));
    if (user->FWin->File) {
      CloseAsync(user->FWin->File);
      user->FWin->File = NULL;
      socks->FileMode = NULL;
    }
    if (user->FWin->CC_Timer) {
      DoMethod(app->LV_FileList,MUIM_NList_Remove,0);
      SetFileSendStats(NULL);
      get(app->LV_FileList,MUIA_NList_Entries,&entries);
      if (entries > 0) QueueNextFile(socks,user);
    }
    break;
  }
  case (char)0x05: {
    ULONG speed = 0, secs = 0, micros;
    PrintHex(DBG_TCP_PKTS,"Recv packet 0x05",buf,len);
    revmemcpy((char *)&speed,buf+off,4);
    DoMethod(user->FWin->NU_Speed,MUIM_NoNotifySet,MUIA_Numeric_Value,speed);
    micros = (100-speed)*50000;
    while(micros > 1000000) {
      micros -= 1000000;
      secs++;
    }
    if (speed == 100) micros = 25000;
    set(user->FWin->CC_Timer,TIME_SetSecs,secs);
    set(user->FWin->CC_Timer,TIME_SetMicro,micros);
    if (speed == 0) set(user->FWin->TX_Status,MUIA_Text_Contents,"Paused");
    else set(user->FWin->TX_Status,MUIA_Text_Contents,user->FWin->Status);
    break;
  }
  case (char)0x06: {
    int hours, mins, secs, cps;
    ULONG interim, remain;
    PrintHex(DBG_TCP_PKTS,"Recv packet 0x06",buf,16);
    user->FWin->PktCnt++;
    user->FWin->ThisFile += (pktlen-1);
    user->FWin->TotAllFiles += (pktlen-1);
    set(user->FWin->GA_FileXfer,MUIA_Gauge_Current,(user->FWin->ThisFileSize>65535?user->FWin->ThisFile/1024:user->FWin->ThisFile));
    set(user->FWin->GA_BatchXfer,MUIA_Gauge_Current,(user->FWin->TotFilesSize>65535?user->FWin->TotAllFiles/1024:user->FWin->TotAllFiles));
    sprintf(cbuf,"%ld Bytes of %ld",user->FWin->ThisFile,user->FWin->ThisFileSize);
    set(user->FWin->TX_FileStat,MUIA_Text_Contents,cbuf);
    sprintf(user->FWin->FilePcnt,"%2.0f%%",100.0*user->FWin->ThisFile/user->FWin->ThisFileSize);
    set(user->FWin->GA_FileXfer,MUIA_Gauge_InfoText,user->FWin->FilePcnt);
    sprintf(cbuf,"%ld Bytes of %ld",user->FWin->TotAllFiles,user->FWin->TotFilesSize);
    set(user->FWin->TX_BatchStat,MUIA_Text_Contents,cbuf);
    sprintf(user->FWin->BatchPcnt,"%2.0f%%",100.0*user->FWin->TotAllFiles/user->FWin->TotFilesSize);
    set(user->FWin->GA_BatchXfer,MUIA_Gauge_InfoText,user->FWin->BatchPcnt);
    interim = time(NULL)-(user->FWin->StartTime);
    hours = interim/60/60;
    mins = (interim-(hours*60*60))/60;
    secs = interim-(hours*60*60)-(mins*60);
    sprintf(cbuf,"%02d:%02d:%02d",hours,mins,secs);
    set(user->FWin->TX_Elapsed,MUIA_Text_Contents,cbuf);
    cps = (interim>0?user->FWin->TotAllFiles/interim:0);
    cps = ((user->FWin->CPS)+=cps)/user->FWin->PktCnt;
    if (cps != 0) {
      sprintf(cbuf,"%d",cps);
      set(user->FWin->TX_CPS,MUIA_Text_Contents,cbuf);
      remain = (user->FWin->TotFilesSize/cps)-interim;
      hours = remain/60/60;
      mins = (remain-(hours*60*60))/60;
      secs = remain-(hours*60*60)-(mins*60);
      sprintf(cbuf,"%02d:%02d:%02d",hours,mins,secs);
      set(user->FWin->TX_Remain,MUIA_Text_Contents,cbuf);
    }
    if (user->FWin->File) {
      WriteAsync(user->FWin->File,buf+off,pktlen-1);
      if (pktlen < 0x0801) {
        CloseAsync(user->FWin->File);
        user->FWin->File = NULL;
        strcpy(user->FWin->Status,"Finished");
        set(user->FWin->TX_Status,MUIA_Text_Contents,user->FWin->Status);
      }
    }
    break;
  }
  default:
    PrintHex(DBG_TCP_ERRS,"Unknown FileDirect packet",buf,len);
    CloseAsync(user->FWin->File);
    user->FWin->File = NULL;
    strcpy(user->FWin->Status,"Error Abort");
    set(user->FWin->TX_Status,MUIA_Text_Contents,user->FWin->Status);
}

socks->Timehack = time(NULL);

return;

}


BOOL StartFileXFer(struct Contact *user, BOOL open)

{

struct FileWin *FWin;

if (user->FWin) {
  user->FWin->Skip = FALSE;
  user->FWin->PktCnt = 0;
  user->FWin->StartTime = 0;
  user->FWin->Speed = user->FWin->ThisFile = user->FWin->ThisFileSize = user->FWin->CurrFiles = user->FWin->TotalFiles = user->FWin->TotAllFiles = user->FWin->TotFilesSize = user->FWin->CPS = 0;
  set(user->FWin->NU_Speed,MUIA_Disabled,FALSE);
  set(user->FWin->BT_Skip,MUIA_Disabled,FALSE);
  set(user->FWin->BT_Abort,MUIA_Disabled,FALSE);
  if (open) set(user->FWin->WI_FileWin,MUIA_Window_Open,TRUE);
  return(TRUE);
}

if (!(FWin = AllocVec(sizeof(struct FileWin),MEMF_CLEAR))) {
  HandleError(DBG_OTH_FATL,"Failed to AllocVec %d bytes!",sizeof(struct FileWin));
  return(FALSE);
}

FWin->DoNotDelete = FALSE;

strcpy(FWin->Status,"Listening");

strcpy(FWin->Title,"Initiating FileDirect");

FWin->WI_FileWin = WindowObject,
  MUIA_Window_Title, FWin->Title,
  MUIA_Window_ID, user->UIN | 0x30000000,
  MUIA_Window_Activate, FALSE,
  MUIA_UserData, user,
  WindowContents, VGroup,
    Child, VGroup,
      MUIA_Group_Columns, 4,
      Child, Label("FileName"),
      Child, FWin->TX_Filename = TextObject,
        MUIA_Background, MUII_TextBack,
        MUIA_Frame, MUIV_Frame_Text,
        MUIA_Text_Contents, NULL,
      End,
      Child, Label("Files"),
      Child, FWin->TX_FileCount = TextObject,
        MUIA_Background, MUII_TextBack,
        MUIA_Frame, MUIV_Frame_Text,
        MUIA_Text_Contents, NULL,
      End,
      Child, Label("Directory"),
      Child, FWin->TX_StoreDir = TextObject,
        MUIA_Background, MUII_TextBack,
        MUIA_Frame, MUIV_Frame_Text,
        MUIA_Text_Contents, NULL,
      End,
      Child, Label("Status"),
      Child, FWin->TX_Status = TextObject,
        MUIA_Background, MUII_TextBack,
        MUIA_Frame, MUIV_Frame_Text,
        MUIA_Text_Contents, FWin->Status,
      End,
      Child, Label("File"),
      Child, FWin->GA_FileXfer = GaugeObject,
        MUIA_Background, MUII_TextBack,
        MUIA_Frame, MUIV_Frame_Gauge,
        MUIA_Gauge_Horiz, TRUE,
      End,
      Child, Label("Size"),
      Child, FWin->TX_FileStat = TextObject,
        MUIA_Background, MUII_TextBack,
        MUIA_Frame, MUIV_Frame_Text,
        MUIA_Text_Contents, NULL,
      End,
      Child, Label("Batch"),
      Child, FWin->GA_BatchXfer = GaugeObject,
        MUIA_Background, MUII_TextBack,
        MUIA_Frame, MUIV_Frame_Gauge,
        MUIA_Gauge_Horiz, TRUE,
      End,
      Child, Label("Size"),
      Child, FWin->TX_BatchStat = TextObject,
        MUIA_Background, MUII_TextBack,
        MUIA_Frame, MUIV_Frame_Text,
        MUIA_Text_Contents, NULL,
      End,
      Child, Label("Elapsed"),
      Child, HGroup,
        Child, FWin->TX_Elapsed = TextObject,
          MUIA_Background, MUII_TextBack,
          MUIA_Frame, MUIV_Frame_Text,
          MUIA_Text_PreParse, "\033c",
          MUIA_Text_Contents, "00:00:00",
        End,
        Child, Label("Remaining"),
        Child, FWin->TX_Remain = TextObject,
          MUIA_Background, MUII_TextBack,
          MUIA_Frame, MUIV_Frame_Text,
          MUIA_Text_PreParse, "\033c",
          MUIA_Text_Contents, "N/A",
        End,
      End,
      Child, Label("CPS"),
      Child, FWin->TX_CPS = TextObject,
        MUIA_Background, MUII_TextBack,
        MUIA_Frame, MUIV_Frame_Text,
        MUIA_Text_Contents, "N/A",
      End,
      Child, Label("Speed"),
      Child, FWin->NU_Speed = SliderObject,
        MUIA_CycleChain, 1,
        MUIA_UserData, user,
        MUIA_Numeric_Default, 100,
        MUIA_Numeric_Max, 100,
        MUIA_Numeric_Value, 100,
      End,
      Child, HSpace(0),
      Child, HGroup,
        Child, FWin->BT_Skip = SimpleButton("Skip File"),
        Child, FWin->BT_Abort = SimpleButton("Abort"),
      End,
    End,
  End,
End;

if (!FWin->WI_FileWin) {
  HandleError(DBG_OTH_FATL,"Could not create a file window.");
  FreeVec(FWin);
  FWin = NULL;
  return(FALSE);
}

user->FWin = FWin;

DoMethod(app->App,OM_ADDMEMBER,FWin->WI_FileWin);

set(FWin->BT_Skip,MUIA_CycleChain,1);
set(FWin->BT_Skip,MUIA_UserData,user);
set(FWin->BT_Abort,MUIA_CycleChain,1);
set(FWin->BT_Abort,MUIA_UserData,user);

set(FWin->WI_FileWin,MUIA_Window_ActiveObject,FWin->BT_Abort);

DoMethod(FWin->WI_FileWin, MUIM_Notify, MUIA_Window_CloseRequest, TRUE, FWin->WI_FileWin, 2,
         MUIM_CallHook, &KillXferWinHook);

DoMethod(FWin->NU_Speed, MUIM_Notify, MUIA_Numeric_Value, MUIV_EveryTime, FWin->NU_Speed, 2,
         MUIM_CallHook, &SetXferSpeedHook);

DoMethod(FWin->BT_Skip, MUIM_Notify, MUIA_Pressed, FALSE, FWin->BT_Skip, 2,
         MUIM_CallHook, &SkipFileHook);

DoMethod(FWin->BT_Abort, MUIM_Notify, MUIA_Pressed, FALSE, FWin->BT_Abort, 2,
         MUIM_CallHook, &AbortXferHook);

if (open) set(FWin->WI_FileWin,MUIA_Window_Open,TRUE);

return(TRUE);

}


void __asm SetXferSpeed(REG(a2) APTR obj)

{

char buf[10];

ULONG speed = 0, secs = 0, micros;

UWORD len = 0, revlen;

struct Contact *user;
struct Sockets *socks;
struct TCP_FileSpeed Speed;

get(obj,MUIA_UserData,&user);

if (!(socks = GetSocketFmContact(user,SOCK_FILE))) return;

get(user->FWin->NU_Speed,MUIA_Numeric_Value,&speed);

Speed.Speed = speed;

len = TCP_CreateFilePacket(TCP_FILE_SPEED,&Speed,buf,&revlen);

send(socks->Socket,(char *)&revlen,2,0);
send(socks->Socket,buf,len,0);

micros = (100-speed)*50000;

while(micros > 1000000) {
  micros -= 1000000;
  secs++;
}

if (speed == 100) micros = 25000;

set(user->FWin->CC_Timer,TIME_SetSecs,secs);
set(user->FWin->CC_Timer,TIME_SetMicro,micros);

if (speed == 0) set(user->FWin->TX_Status,MUIA_Text_Contents,"Paused");
else set(user->FWin->TX_Status,MUIA_Text_Contents,user->FWin->Status);

return;

}


void __asm SkipFile(REG(a2) APTR obj)

{

char buf[10];

ULONG entries;

UWORD len = 0, revlen;

struct Contact *user;
struct Sockets *socks;

get(obj,MUIA_UserData,&user);

if (!(socks = GetSocketFmContact(user,SOCK_FILE))) return;

buf[0] = (char)0x04; len++;

revmemcpy(buf+1,(char *)&user->FWin->CurrFiles,4); len += 4;

PrintHex(DBG_TCP_PKTS,"Send packet 0x04",buf,5);

revmemcpy((char *)&revlen,(char *)&len,2);

send(socks->Socket,(char *)&revlen,2,0);
send(socks->Socket,buf,len,0);

set(user->FWin->TX_Status,MUIA_Text_Contents,"Skipping");
user->FWin->TotAllFiles += ((user->FWin->ThisFileSize)-(user->FWin->ThisFile));

if (user->FWin->File) {
  CloseAsync(user->FWin->File);
  user->FWin->File = NULL;
  socks->FileMode = NULL;
}

DoMethod(app->LV_FileList,MUIM_NList_Remove,0);

SetFileSendStats(NULL);

get(app->LV_FileList,MUIA_NList_Entries,&entries);

if (entries > 0) QueueNextFile(socks,user);

return;

}


void __asm AbortXfer(REG(a2) APTR obj)

{

struct Contact *user;
struct Sockets *socks;

get(obj,MUIA_UserData,&user);

if (!(socks = GetSocketFmContact(user,SOCK_FILE))) return;

DelSockets(socks->Socket);

if (user->FWin->File) {
  CloseAsync(user->FWin->File);
  user->FWin->File = NULL;
  socks->FileMode = NULL;
}

set(user->FWin->TX_Status,MUIA_Text_Contents,"Aborted");

set(user->FWin->NU_Speed,MUIA_Disabled,TRUE);
set(user->FWin->BT_Skip,MUIA_Disabled,TRUE);
set(user->FWin->BT_Abort,MUIA_Disabled,TRUE);

EndFile(user);

return;

}


void EndFile(struct Contact *user)

{

if (user->FWin) {
  if (user->FWin->File) {
    CloseAsync(user->FWin->File);
    user->FWin->File = NULL;
  }
  if (user->FWin->CC_Timer) DoMethod(user->FWin->CC_Timer,TIME_RemApp,app->App);
  user->FWin->CC_Timer = NULL;
  if (user->FWin->TotAllFiles < user->FWin->TotFilesSize) set(user->FWin->TX_Status,MUIA_Text_Contents,"Aborted");
  set(user->FWin->NU_Speed,MUIA_Disabled,TRUE);
  set(user->FWin->BT_Skip,MUIA_Disabled,TRUE);
  set(user->FWin->BT_Abort,MUIA_Disabled,TRUE);
}

return;

}


void __asm KillXferWin(REG(a2) APTR obj)

{

struct Contact *user;

get(obj,MUIA_UserData,&user);

if (!user->FWin) return;

AbortXfer(obj);

set(user->FWin->WI_FileWin,MUIA_Window_Open,FALSE);

//DoMethod(app->App,OM_REMMEMBER,user->FWin->WI_FileWin);

//MUI_DisposeObject(user->FWin->WI_FileWin);

//FreeVec(user->FWin);

//user->FWin = NULL;

return;

}


APTR __asm AddFile(REG(a2) APTR pool, REG(a1) struct FileList *files)

{

struct FileList *newfiles = NULL;

if (!(newfiles = AllocPooled(pool,sizeof(struct FileList)))) return(NULL);

strcpy(newfiles->Filename,files->Filename);

newfiles->Filesize = files->Filesize;

return(newfiles);

}


void __asm RemFile(REG(a2) APTR pool, REG(a1) struct FileList *newfiles)

{

FreePooled(pool,newfiles,sizeof(struct FileList));

return;

}


LONG __asm CmpFile(REG(a1) struct FileList *f1, REG(a2) struct FileList *f2)

{

return(stricmp(f1->Filename,f2->Filename));

}


void __asm DisplayFile(REG(a2) char **array, REG(a1) struct FileList *files)

{

static char buf[32];

if (files) {
  sprintf(buf,"%ld",files->Filesize);
  *array++ = files->Filename;
  *array   = buf;
}
else {
  *array++ = "Filename";
  *array   = "Filesize";
}

return;

}


void __asm OpenFileDirectWin(REG(a2) APTR obj)

{

ULONG uin;

get(obj,MUIA_UserData,&uin);

set(app->BT_SendFile,MUIA_UserData,uin);

DoMethod(app->LV_FileList,MUIM_NList_Clear);

set(app->TX_TotalFiles,MUIA_Text_Contents,"No Files");
set(app->TX_TotalSize,MUIA_Text_Contents,"Zero Bytes");

set(app->GP_FDescGroup,MUIA_Disabled,FALSE);
set(app->BT_AddFile,MUIA_Disabled,FALSE);
set(app->BT_RemFile,MUIA_Disabled,FALSE);

set(app->WI_FileSendOut,MUIA_Window_Open,TRUE);

return;

}


void __asm AddFiles2Send(REG(a2) APTR obj)

{

char title[] = "Choose Files To Send:";

char *dldir;

int i;

struct FileList       Files;
struct FileRequester *myasl;
struct WBArg         *files;
struct Window        *mywin = NULL;

struct TagItem MyASLTags[] = {
  {ASLFR_TitleText,    NULL},
  {ASLFR_Window,       NULL},
  {ASLFR_InitialDrawer,NULL},
  {ASLFR_DoMultiSelect,TRUE},

  {TAG_DONE,NULL},
};

get(app->WI_Main,MUIA_Window_Window,&mywin);
get(app->STR_DownloadDir,MUIA_String_Contents,&dldir);

MyASLTags[0].ti_Data = (ULONG)title;
MyASLTags[1].ti_Data = (ULONG)mywin;
MyASLTags[2].ti_Data = (ULONG)dldir;

if (!(myasl = MUI_AllocAslRequest(ASL_FileRequest,MyASLTags))) return;

if (!MUI_AslRequest(myasl,NULL)) return;

if (myasl->rf_NumArgs) {
  files = myasl->rf_ArgList;
  for(i = 0; i < myasl->rf_NumArgs; i++) {
    if (LASTCHAR(myasl->fr_Drawer) != ':' && LASTCHAR(myasl->fr_Drawer) != '/') sprintf(Files.Filename,"%s/%s",myasl->fr_Drawer,files[i].wa_Name);
    else sprintf(Files.Filename,"%s%s",myasl->fr_Drawer,files[i].wa_Name);
    GetSize(&Files);
    DoMethod(app->LV_FileList,MUIM_NList_InsertSingle,&Files,MUIV_NList_Insert_Sorted);
  }
}
else {
  if (LASTCHAR(myasl->fr_Drawer) != ':' && LASTCHAR(myasl->fr_Drawer) != '/') sprintf(Files.Filename,"%s/%s",myasl->fr_Drawer,myasl->fr_File);
  else sprintf(Files.Filename,"%s%s",myasl->fr_Drawer,myasl->fr_File);
  GetSize(&Files);
  DoMethod(app->LV_FileList,MUIM_NList_InsertSingle,&Files,MUIV_NList_Insert_Sorted);
}

MUI_FreeAslRequest(myasl);

SetFileSendStats(NULL);

return;

}


ULONG GetSize(struct FileList *files)

{

__aligned struct FileInfoBlock *myFIB;

BPTR mylock;

if (!(myFIB = (struct FileInfoBlock *)AllocVec(sizeof(struct FileInfoBlock),MEMF_PUBLIC|MEMF_CLEAR))) {
  HandleError(DBG_OTH_FATL,"Could not allocate FileInfoBlock for filesize check.\n");
  return(0);
}

if ((mylock = Lock(files->Filename,SHARED_LOCK)) == NULL) {
  FreeVec(myFIB);
  return(0);
}

if (Examine(mylock,myFIB) == NULL) {
  HandleError(DBG_OTH_FATL,"Couldn't Examine '%s'.\n",files->Filename);
  FreeVec(myFIB);
  UnLock(mylock);
  return(0);
}

files->Filesize = myFIB->fib_Size;

UnLock(mylock);
FreeVec(myFIB);

return(files->Filesize);

}


LONG __asm SetFileSendStats(REG(a2) APTR obj)

{

char buf[32];

int i;

LONG entries, totalbytes = 0, totalfiles = 0;

struct FileList *files;

get(app->LV_FileList,MUIA_NList_Entries,&entries);

if (entries < 1) {
  set(app->TX_TotalFiles,MUIA_Text_Contents,"No Files");
  set(app->TX_TotalSize,MUIA_Text_Contents,"Zero Bytes");
  return(0);
}

for(i = 0; i < entries; i++) {
  DoMethod(app->LV_FileList,MUIM_NList_GetEntry,i,&files);
  if (files) {
    totalbytes += files->Filesize;
    totalfiles++;
  }
}

sprintf(buf,"%ld Files",totalfiles);
set(app->TX_TotalFiles,MUIA_Text_Contents,buf);

sprintf(buf,"%ld Bytes",totalbytes);
set(app->TX_TotalSize,MUIA_Text_Contents,buf);

return(totalbytes);

}


void __asm SendFileSend(REG(a2) APTR obj)

{

char *msg, filename[MAXNAMLEN];

ULONG id = 0, index = 0, status, totalbytes, entries;

struct Contact *user;
struct FileList *files;

get(obj,MUIA_UserData,&id);

if (!(user = GetContact(id))) return;

if (!user->UWin) return;

get(app->CY_MyStatus,MUIA_Cycle_Active,&index);

status = TCPCodes[index];

get(app->TI_FileReason,MUIA_Textinput_Contents,&msg);

get(app->LV_FileList,MUIA_NList_Entries,&entries);

if (entries < 1) return;

set(app->GP_FDescGroup,MUIA_Disabled,TRUE);
set(app->BT_AddFile,MUIA_Disabled,TRUE);
set(app->BT_RemFile,MUIA_Disabled,TRUE);

if (entries == 1) {
  DoMethod(app->LV_FileList,MUIM_NList_GetEntry,0,&files);
  strcpy(filename,FilePart(files->Filename));
  totalbytes = files->Filesize;
}
else {
  sprintf(filename,"%ld Files",entries);
  totalbytes = SetFileSendStats(NULL);
}

if (!SendTCPMessage(id,msg,TCP_MESSAGE,TCP_MSG_FILE,TCP_MSG_REAL,TCP_Seq,filename,totalbytes,status,0)) {
  sprintf(err_buf,"Could not send FileDirect Request to %s.",user->Nick);
  HandleError(DBG_TCP_ERRS,"Could not send FileDirect Request to %s.",user->Nick);
  return;
}

SendAMessage(NULL,id,msg,TCP_MSG_MSG,MSG_LOCAL,MSG_VIA_BEST);

set(app->WI_FileSendOut,MUIA_Window_Open,FALSE);
set(app->TI_FileReason,MUIA_Textinput_Contents,NULL);

Add2Log(LOG_CLIENT,TCP_Seq--,TCP_MESSAGE,FALSE,0,NULL,0);

return;

}


void SendFileInit(UWORD sock, struct Contact *user)

{

char buf[128], *nick;

UWORD len = 0, revlen;

struct TCP_FileBegin Begin;

if (!StartFileXFer(user,TRUE)) {
  DelSockets(sock);
  return;
}

if (!user->FWin->CC_Timer) {
  if (!(user->FWin->CC_Timer = NewObject(mcc_Time->mcc_Class,NULL,TIME_SetSecs,0,TIME_SetMicro,25000,TIME_SetTrg,TIME_Trigger2,TAG_DONE))) {
    HandleError(DBG_OTH_FATL,"Could not create the FileDirect timer object.");
    DelSockets(sock);
    return;
  }
}

DoMethod(user->FWin->CC_Timer,TIME_AddApp,app->App);

set(user->FWin->CC_Timer,TIME_SetSock,sock);

get(app->LV_FileList,MUIA_NList_Entries,&Begin.TotalFiles);
get(user->FWin->NU_Speed,MUIA_Numeric_Value,&Begin.Speed);
get(app->STR_Nick,MUIA_String_Contents,&nick);

Begin.TotalSize = SetFileSendStats(NULL);

strcpy(Begin.Nick,nick);

if (Begin.TotalFiles == 1) set(user->FWin->BT_Skip,MUIA_Disabled,TRUE);

user->FWin->TotAllFiles = user->FWin->CurrFiles = 0;
user->FWin->TotalFiles = Begin.TotalFiles;
user->FWin->TotFilesSize = Begin.TotalSize;

sprintf(buf,"0/%ld",Begin.TotalFiles);

set(user->FWin->TX_FileCount,MUIA_Text_Contents,buf);

sprintf(buf,"0 Bytes of %ld",Begin.TotalSize);

set(user->FWin->TX_BatchStat,MUIA_Text_Contents,buf);
set(user->FWin->GA_BatchXfer,MUIA_Gauge_Current,0);
set(user->FWin->GA_BatchXfer,MUIA_Gauge_Max,(Begin.TotalSize>65535?Begin.TotalSize/1024:Begin.TotalSize));

Begin.X1 = 0x00000000;

len = TCP_CreateFilePacket(TCP_FILE_BEGIN,&Begin,buf,&revlen);

send(sock,(char *)&revlen,2,0);
send(sock,buf,len,0);

return;

}


ULONG SendAFile(struct IClass *cl, Object *obj, Msg msg)

{

char sbuf[FILE_PKT_SIZE+3], cbuf[128];

int hours, mins, secs, cps;

ULONG entries, interim, remain, speed, err;

UWORD len, revlen;

struct Contact *user;
struct Sockets *socks;
struct TimeData *data = INST_DATA(cl,obj);

if (CheckIO((struct IORequest *)data->req_hbeat)) WaitIO((struct IORequest *)data->req_hbeat);
else return(FALSE);

//printf("In SendAFile.\n");

if (socks = GetSocketFmSocket(data->Socket)) socks->Timehack = time(NULL);

if (!socks->FileMode) {
  ResetTimer(data);
  return(FALSE);
}

user = socks->FileMode;

if (!user->FWin->File) {
  ResetTimer(data);
  return(FALSE);
}

get(user->FWin->NU_Speed,MUIA_Numeric_Value,&speed);

if (speed == 0) {
  ResetTimer(data);
  return(FALSE);
}

if (user->FWin->FileData.DataLen == 0) user->FWin->FileData.DataLen = ReadAsync(user->FWin->File,user->FWin->FileData.Data,FILE_PKT_SIZE);

if (user->FWin->FileData.DataLen > 0) {
  len = TCP_CreateFilePacket(TCP_FILE_DATA,&user->FWin->FileData,sbuf,&revlen);
  if (send(socks->Socket,sbuf,len+2,0) == -1) {
    if ((err = Errno()) != EWOULDBLOCK) {
      SocketBaseTags(SBTM_GETREF(SBTC_ERRNOSTRPTR),&err,TAG_DONE);
      sprintf(err_buf,"Error on file send socket %d: %s\n",socks->Socket,(char *)err);
      HandleError(DBG_TCP_ERRS,"Error on file send socket %d: %s\n",socks->Socket,(char *)err);
      DelSockets(socks->Socket);
      return(FALSE);
    }
    ResetTimer(data);
    return(TRUE);
  }
  user->FWin->FileData.DataLen = 0;
  user->FWin->PktCnt++;
  user->FWin->ThisFile += (len-1);
  user->FWin->TotAllFiles += (len-1);
  set(user->FWin->GA_FileXfer,MUIA_Gauge_Current,(user->FWin->ThisFileSize>65535?user->FWin->ThisFile/1024:user->FWin->ThisFile));
  set(user->FWin->GA_BatchXfer,MUIA_Gauge_Current,(user->FWin->TotFilesSize>65535?user->FWin->TotAllFiles/1024:user->FWin->TotAllFiles));
  sprintf(cbuf,"%ld Bytes of %ld",user->FWin->ThisFile,user->FWin->ThisFileSize);
  set(user->FWin->TX_FileStat,MUIA_Text_Contents,cbuf);
  sprintf(user->FWin->FilePcnt,"%2.0f%%",100.0*user->FWin->ThisFile/user->FWin->ThisFileSize);
  set(user->FWin->GA_FileXfer,MUIA_Gauge_InfoText,user->FWin->FilePcnt);
  sprintf(cbuf,"%ld Bytes of %ld",user->FWin->TotAllFiles,user->FWin->TotFilesSize);
  set(user->FWin->TX_BatchStat,MUIA_Text_Contents,cbuf);
  sprintf(user->FWin->BatchPcnt,"%2.0f%%",100.0*user->FWin->TotAllFiles/user->FWin->TotFilesSize);
  set(user->FWin->GA_BatchXfer,MUIA_Gauge_InfoText,user->FWin->BatchPcnt);
  interim = time(NULL)-(user->FWin->StartTime);
  hours = interim/60/60;
  mins = (interim-(hours*60*60))/60;
  secs = interim-(hours*60*60)-(mins*60);
  sprintf(cbuf,"%02d:%02d:%02d",hours,mins,secs);
  set(user->FWin->TX_Elapsed,MUIA_Text_Contents,cbuf);
  cps = (interim>0?user->FWin->TotAllFiles/interim:0);
//cps = ((user->FWin->CPS)+=cps)/user->FWin->PktCnt;
  if (cps != 0) {
    sprintf(cbuf,"%d",cps);
    set(user->FWin->TX_CPS,MUIA_Text_Contents,cbuf);
    remain = (user->FWin->TotFilesSize/cps)-interim;
    hours = remain/60/60;
    mins = (remain-(hours*60*60))/60;
    secs = remain-(hours*60*60)-(mins*60);
    sprintf(cbuf,"%02d:%02d:%02d",hours,mins,secs);
    set(user->FWin->TX_Remain,MUIA_Text_Contents,cbuf);
  }
}

if (len < FILE_PKT_SIZE+1) {
  CloseAsync(user->FWin->File);
  user->FWin->File = NULL;
  set(user->FWin->TX_Status,MUIA_Text_Contents,"Finished");
  socks->FileMode = NULL;
  DoMethod(app->LV_FileList,MUIM_NList_Remove,0);
  SetFileSendStats(NULL);
  get(app->LV_FileList,MUIA_NList_Entries,&entries);
  if (entries > 0) QueueNextFile(socks,user);
  else EndFile(user);
}

ResetTimer(data);

return(TRUE);

}


void ResetTimer(struct TimeData *data)

{

data->req_hbeat->tr_node.io_Command = TR_ADDREQUEST;
data->req_hbeat->tr_time.tv_secs = data->Seconds;
data->req_hbeat->tr_time.tv_micro = data->Micros;
SendIO((struct IORequest *)data->req_hbeat);

return;

}


void QueueNextFile(struct Sockets *socks, struct Contact *user)

{

char *path, c, sbuf[128];

ULONG speed;

UWORD len, revlen;

struct FileList *files;
struct TCP_FileReady Ready;

DoMethod(app->LV_FileList,MUIM_NList_GetEntry,0,&files);

user->FWin->ThisFile = 0;
user->FWin->ThisFileSize = files->Filesize;

set(user->FWin->TX_Filename,MUIA_Text_Contents,FilePart(files->Filename));

sprintf(sbuf,"%ld/%ld",++user->FWin->CurrFiles,user->FWin->TotalFiles);

set(user->FWin->TX_FileCount,MUIA_Text_Contents,sbuf);

set(user->FWin->GA_FileXfer,MUIA_Gauge_Current,0);

set(user->FWin->GA_FileXfer,MUIA_Gauge_Max,(files->Filesize>65535?files->Filesize/1024:files->Filesize));

sprintf(sbuf,"0 Bytes of %ld",files->Filesize);

set(user->FWin->TX_FileStat,MUIA_Text_Contents,sbuf);

path = PathPart(files->Filename);

c = *path;

*path = '\0';

set(user->FWin->TX_StoreDir,MUIA_Text_Contents,files->Filename);

*path = c;

get(user->FWin->NU_Speed,MUIA_Numeric_Value,&speed);

Ready.X1 = 0x00;
Ready.Filename = FilePart(files->Filename);
Ready.Text = LINE;
Ready.Filesize = files->Filesize;
Ready.X2 = 0x00000000;
Ready.Speed = speed;

len = TCP_CreateFilePacket(TCP_FILE_READY,&Ready,sbuf,&revlen);

send(socks->Socket,(char *)&revlen,2,0);
send(socks->Socket,sbuf,len,0);

return;

}
