/*ProfiPacket - packet radio terminal program
  Copyright (C) 1999  Alexander Feigl

  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

  Author:

  Alexander Feigl
  Burachstraße 51

  D-88250 Weingarten

  Mail : Alexander.Feigl@gmx.de
*/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#ifdef __GNUC__
#define _storage_text_ __attribute__ ((section(".text")))
#else
#define _storage_text_
#endif

#include "global.h"
#include "Preferences.h"
#include "QSO.h"
#include "GUIProtos.h"
#include "Server.h"
#include "Util.h"
#include "PPVersion.h"

#include "Mail.h"

#include "GUIMain.h"

#ifdef AmigaOS

#include <libraries/mui.h>


#include <dos/exall.h>
#include <dos/dostags.h>
#include <exec/memory.h>

#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/intuition_protos.h>


#ifdef __GNUC__

extern void *DOSBase;
extern void *SysBase;
extern void *IntuitionBase;
extern void *UtilityBase;

#include <inline/dos.h>
#include <inline/exec.h>
#include <inline/intuition.h>
#include <inline/utility.h>

#endif


#else /* AmigaOS*/

#include "amigaemu.h"

#endif /*AmigaOS*/


struct ServerBuffer
 {
  struct Node Node;
  unsigned long InBytes;
  unsigned char Data[4096];
 };


void GUIMAIN_ResetStyles(struct QSO *thisqso);

void FillHook(struct Hook *MyHook,void (*Function)(),void *FunctionData);


ULONG MAIN_RexxEstablishLink(struct Hook *MyHook,unsigned long **Param,void *Object,
                             unsigned long *Data);

ULONG MAIN_RexxStartServer(struct Hook *MyHook,unsigned long **Param,void *Object,
                            unsigned long *Data);

ULONG MAIN_RexxStartForward(struct Hook *MyHook,unsigned long **Param,void *Object,
                            unsigned long *Data);

ULONG MAIN_RexxSetSpy(struct Hook *MyHook,unsigned long **Param,void *Object,
                            unsigned long *Data);

ULONG MAIN_RexxClearSpy(struct Hook *MyHook,unsigned long **Param,void *Object,
                            unsigned long *Data);

ULONG MAIN_RexxGetMailBid(struct Hook *MyHook,unsigned long **Param,void *Object,
                            unsigned long *Data);
ULONG MAIN_RexxGetMailFm(struct Hook *MyHook,unsigned long **Param,void *Object,
                            unsigned long *Data);
ULONG MAIN_RexxGetMailTo(struct Hook *MyHook,unsigned long **Param,void *Object,
                            unsigned long *Data);
ULONG MAIN_RexxGetMailAt(struct Hook *MyHook,unsigned long **Param,void *Object,
                            unsigned long *Data);
ULONG MAIN_RexxGetMailStatus(struct Hook *MyHook,unsigned long **Param,void *Object,
                            unsigned long *Data);
ULONG MAIN_RexxGetMailBytes(struct Hook *MyHook,unsigned long **Param,void *Object,
                            unsigned long *Data);
ULONG MAIN_RexxGetMailTitle(struct Hook *MyHook,unsigned long **Param,void *Object,
                            unsigned long *Data);

ULONG MAIN_RexxGetMailDate(struct Hook *MyHook,unsigned long **Param,void *Object,
                            unsigned long *Data);
ULONG MAIN_RexxKillMail(struct Hook *MyHook,unsigned long **Param,void *Object,
                            unsigned long *Data);


ULONG MAIN_RexxSendMailFile(struct Hook *MyHook,unsigned long **Param,void *Object,
                            unsigned long *Data);

ULONG MAIN_RexxExtractMailFile(struct Hook *MyHook,unsigned long **Param,void *Object,
                            unsigned long *Data);
ULONG MAIN_RexxMarkAsRead(struct Hook *MyHook,unsigned long **Param,void *Object,
                            unsigned long *Data);
ULONG MAIN_RexxMarkAsForward(struct Hook *MyHook,unsigned long **Param,void *Object,
                            unsigned long *Data);

ULONG MAIN_RexxGetBoxCall(struct Hook *MyHook,unsigned long **Param,void *Object,
                            unsigned long *Data);
ULONG MAIN_RexxSearchMailBid(struct Hook *MyHook,unsigned long **Param,void *Object,
                            unsigned long *Data);
ULONG MAIN_RexxTerminateLink(struct Hook *MyHook,unsigned long **Param,void *Object,
                            unsigned long *Data);
ULONG MAIN_RexxGetForwardMails(struct Hook *MyHook,unsigned long **Param,void *Object,
                            unsigned long *Data);
ULONG MAIN_RexxGetForwardBytes(struct Hook *MyHook,unsigned long **Param,void *Object,
                            unsigned long *Data);
ULONG MAIN_RexxSetCMessage(struct Hook *MyHook,unsigned long **Param,void *Object,
                            unsigned long *Data);
ULONG MAIN_RexxSearchChannel(struct Hook *MyHook,unsigned long **Param,void *Object,
                            unsigned long *Data);
ULONG MAIN_RexxSendBeacon(struct Hook *MyHook,unsigned long **Param,void *Object,
                            unsigned long *Data);






static const unsigned char ConvertAmiga2PC[256]=
                       {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
                        0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,
                        0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,
                        0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,
                        0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,
                        0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,
                        0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,
                        0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,
                        0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,
                        0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F,
                        0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,
                        0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F,
                        0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,
                        0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,
                        0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,
                        0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F,
                        0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
                        0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
                        0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
                        0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
                        0x20,0xAD,0x9B,0x9C,0x20,0x9D,0xB3,0x15,
                        0x20,0x63,0xA6,0xAE,0xAA,0xC4,0x72,0xDF,
                        0xF8,0xF1,0xFD,0x33,0x20,0xE6,0x14,0xFA,
                        0x20,0x31,0xA7,0xAF,0xAC,0xAB,0x20,0xA8,
                        0x85,0xA0,0x83,0x41,0x8E,0x8F,0x92,0x80,
                        0x8A,0x90,0x88,0x89,0x8D,0xA1,0x8C,0x8B,
                        0x44,0xA5,0x95,0xA2,0x93,0x4F,0x99,0x78,
                        0x4F,0x97,0xA3,0x96,0x9A,0x59,0x50,0xE1,
                        0x85,0xA0,0x83,0x61,0x84,0x86,0x91,0x87,
                        0x8A,0x82,0x88,0x89,0x8D,0xA1,0x8C,0x8B,
                        0x64,0xA4,0x95,0xA2,0x93,0x6F,0x94,0xF6,
                        0x6F,0x97,0xA3,0x96,0x81,0x79,0x70,0x98};

static const unsigned char ConvertPC2Amiga[256]=
                       {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
                        0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,
                        0x10,0x11,0x12,0x13,0xB6,0xA7,0x16,0x17,
                        0x18,0x19,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,
                        0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,
                        0x28,0x29,0x2A,0x2B,0x2C,0x2D,0x2E,0x2F,
                        0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,
                        0x38,0x39,0x3A,0x3B,0x3C,0x3D,0x3E,0x3F,
                        0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,
                        0x48,0x49,0x4A,0x4B,0x4C,0x4D,0x4E,0x4F,
                        0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,
                        0x58,0x59,0x5A,0x5B,0x5C,0x5D,0x5E,0x5F,
                        0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,
                        0x68,0x69,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,
                        0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,
                        0x78,0x79,0x7A,0x7B,0x7C,0x7D,0x7E,0x7F,
                        0xC7,0xFC,0xE9,0xE2,0xE4,0xE0,0xE5,0xE7,
                        0xEA,0xEB,0xE8,0xEF,0xEE,0xEC,0xC4,0xC5,
                        0xC9,0xE6,0xC6,0xF4,0xF6,0xF2,0xFB,0xF9,
                        0xFF,0xD6,0xDC,0xA2,0xA3,0xA5,0x20,0x66,
                        0xE1,0xED,0xF3,0xFA,0xF1,0xD1,0xAA,0xBA,
                        0xBF,0x20,0xAC,0xBD,0xBC,0xA1,0xAB,0xBB,
                        0x20,0x7F,0x20,0xA6,0x20,0x20,0x20,0x20,
                        0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
                        0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
                        0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
                        0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
                        0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
                        0x20,0xDF,0x20,0x20,0x20,0x20,0xB5,0x20,
                        0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,
                        0x20,0xB1,0x20,0x20,0x20,0x20,0xF7,0x20,
                        0xB0,0xB7,0xB7,0x20,0x6E,0xB2,0x20,0x20};


int StringLength(const unsigned char *string)
 {
  int i;
  const unsigned char *sptr;
  
  sptr=string;
  i=0;
  while (*(sptr++)!='\0') ++i;
  return(i);
 }

unsigned long MAIN_GetMaxChannel(void)
 {
  unsigned long Channels,i;
  Channels=0;

  for (i=0;i<8;++i)
   {
    struct TNCDevice *dev;
    dev=& (Preferences.Devices[i]);
    if ( (dev->ExecDevice[0]!='\0') && (dev->GlobalMycall[0]!='\0') &&
         (dev->Channels!=0) )
     {
      Channels+= ( ((dev->Channels+9) / 10)*10);
     }

   }

  if (Channels==0) Channels=10;
  return (Channels);
 }


void MAIN_InitalizeChannels(void)
 {
  signed long ChannelBase,TNC,i,j,chan;
  struct TNCDevice *dev;
  struct QSO *thisqso;

  for (i=0;i<MaxChannel;++i)
   {
    thisqso=QSOs+(i);
    thisqso->SkipChannel=TRUE;

    thisqso->CallSet=0;
    thisqso->HardwareUnit=0;
    thisqso->HardwareChannel=1;
   }


  ChannelBase=0;
  for (TNC=0;TNC<8;++TNC )
   {
    unsigned long RealCallsetting[8];
    unsigned long CallsetSum;

    dev=&(Preferences.Devices[TNC]);
    if ( (dev->ExecDevice[0]=='\0') || (dev->GlobalMycall[0]=='\0') ||
         (dev->Channels==0) )
     {
      continue;
     }

    for(CallsetSum=0,i=0;i<8;++i)
     {
      RealCallsetting[i]=dev->CallsetChannels[i];
      CallsetSum+=dev->CallsetChannels[i];
     }

    if (CallsetSum!=dev->Channels)
     {
      if (CallsetSum==0)
       {
        RealCallsetting[0]=dev->Channels;
       }
       else
       {
        for (i=0;i<8;++i)
         {
          RealCallsetting[i]= (RealCallsetting[i]*dev->Channels)/CallsetSum;
         }
        for (CallsetSum=0,i=0;i<8;++i) CallsetSum+=RealCallsetting[i];
        if (CallsetSum!=dev->Channels)
         {
          RealCallsetting[0]=dev->Channels-CallsetSum+RealCallsetting[0];
         }
       }

     }

    for (CallsetSum=0,i=0;i<8;++i) CallsetSum+=RealCallsetting[i];

    if (CallsetSum!=dev->Channels) continue;


    chan=ChannelBase;

    for (i=0;i<8;++i)
     {
      for (j=0;j<RealCallsetting[i];++j)
       {
        thisqso=QSOs+(chan);
        thisqso->CallSet=i;
        thisqso->HardwareUnit=TNC;
        thisqso->SkipChannel=FALSE;
        thisqso->HardwareChannel=chan-ChannelBase+1;

        chan++;
       }
     }

    ChannelBase+= (((dev->Channels+9)/10)*10);

   }

  for (i=0;i<MaxChannel;++i)
   {
    thisqso=QSOs+(i);
    thisqso->IncomingMessage=FALSE;
    thisqso->XPacketDelay=NULL;
    MAIN_CleanupChannel(i+1);
    MAIN_HardwareInitChannel(i+1);
    GUIMAIN_ResetStyles(thisqso);
   }

  for (i=0;i<MaxSpy;++i)
   {
    thisqso= ((struct QSO *) Spys)+(i);
    thisqso->IncomingMessage=FALSE;
    MAIN_CleanupChannel( - (i+1) );
    GUIMAIN_ResetStyles(thisqso);

   }
 }



/*ARexx Befehle*/

typedef  const void *constrv;

#define rexxcommand(rca,rcb,rcc,rcd) ((constrv) rca),((constrv) rcb),((constrv) rcc),((constrv) (rcd)),



const static constrv _storage_text_ mycomms[]=
  {
 
   rexxcommand("CLEARSPY","SPYNR/A/N",1,MAIN_RexxClearSpy)
   rexxcommand("ESTABLISHLINK","CALL/A,PATH,HUNIT/N,FROM/K",4,MAIN_RexxEstablishLink)
   rexxcommand("EXTRACTMAILFILE","MAILNR/A/N,FILENAME/A,DISABLECONVERT/S",3,MAIN_RexxExtractMailFile)
   rexxcommand("GETBOXCALL",NULL,0,MAIN_RexxGetBoxCall)
   rexxcommand("GETFORWARDBYTES","BOX/A",1,MAIN_RexxGetForwardBytes)
   rexxcommand("GETFORWARDMAILS","BOX/A",1,MAIN_RexxGetForwardMails)
   rexxcommand("GETMAILAT","MAILNR/A/N",1,MAIN_RexxGetMailAt)
   rexxcommand("GETMAILBID","MAILNR/A/N",1,MAIN_RexxGetMailBid)
   rexxcommand("GETMAILBYTES","MAILNR/A/N",1,MAIN_RexxGetMailBytes)
   rexxcommand("GETMAILDATE","MAILNR/A/N",1,MAIN_RexxGetMailDate)
   rexxcommand("GETMAILFM","MAILNR/A/N",1,MAIN_RexxGetMailFm)
   rexxcommand("GETMAILSTATUS","MAILNR/A/N",1,MAIN_RexxGetMailStatus)
   rexxcommand("GETMAILTITLE","MAILNR/A/N",1,MAIN_RexxGetMailTitle)
   rexxcommand("GETMAILTO","MAILNR/A/N",1,MAIN_RexxGetMailTo)
   rexxcommand("KILLMAIL","MAILNR/A/N",1,MAIN_RexxKillMail)
   rexxcommand("MARKASFORWARD","MAILNR/A/N",1,MAIN_RexxMarkAsForward)
   rexxcommand("MARKASREAD","MAILNR/A/N",1,MAIN_RexxMarkAsRead)
   rexxcommand("SEARCHCHANNEL","CALL/A,CALLSET/N,HUNIT/N,PORT/N",4,MAIN_RexxSearchChannel)
   rexxcommand("SEARCHMAILBID","BID/A",1,MAIN_RexxSearchMailBid)
   rexxcommand("SENDBEACON",NULL,0,MAIN_RexxSendBeacon)
   rexxcommand("SENDMAILFILE","TO/A,AT/K,FROM/K,TITLE/A,FILENAME/A,PERSONAL/S,BINARY/S,BID,FORWARDSOURCE,DISABLECONVERT/S",10,MAIN_RexxSendMailFile)
   rexxcommand("SETSPY","HARDWAREUNIT/A/N,PORT/A/N,NOCOMPRESS/S,CALL1/A,CALL2/A",5,MAIN_RexxSetSpy)
   rexxcommand("SETCMESSAGE","MESSAGENR/A/N",1,MAIN_RexxSetCMessage)
   rexxcommand("STARTSERVER","CHANNEL/A/N,CMD/S,SERVERNAME/A,PARAMETER",4,MAIN_RexxStartServer)
   rexxcommand("STARTFORWARD","BOXCALL/A",1,MAIN_RexxStartForward)
   rexxcommand("TERMINATELINK","CHANNEL/N,ALL/S",2,MAIN_RexxTerminateLink)
   
   (constrv) NULL
   
  };

struct MUI_Command *MAIN_CreateRexx(void)
{
 const void **commptr;
 struct Hook *hookptr;
 struct MUI_Command *mcptr;
 void (*funcptr)();


 mcptr=GlobalArraysP->RexxCommands;
 hookptr=HooksP->RexxHooks;

 commptr=(void *) mycomms;
 while (*commptr!=NULL)
  {


   mcptr->mc_Name=(char *) (*(commptr++));
   mcptr->mc_Template=(char *) (*(commptr++));
   mcptr->mc_Parameters=(ULONG) *(commptr++);
   funcptr=(void *) *(commptr++);
   FillHook(hookptr,funcptr,NULL);
   mcptr->mc_Hook=hookptr;
   mcptr->mc_Reserved[0]=NULL;
   mcptr->mc_Reserved[1]=NULL;
   mcptr->mc_Reserved[2]=NULL;
   mcptr->mc_Reserved[3]=NULL;
   mcptr->mc_Reserved[4]=NULL;


   mcptr++;
   hookptr++;

  }

 mcptr->mc_Name=NULL;
 mcptr->mc_Template=NULL;
 mcptr->mc_Parameters=NULL;
 mcptr->mc_Hook=NULL;
 mcptr->mc_Reserved[0]=NULL;
 mcptr->mc_Reserved[1]=NULL;
 mcptr->mc_Reserved[2]=NULL;
 mcptr->mc_Reserved[3]=NULL;
 mcptr->mc_Reserved[4]=NULL;
 return(GlobalArraysP->RexxCommands);
}

ULONG MAIN_RexxStartServer(struct Hook *MyHook,unsigned long **Param,void *Object,
                            unsigned long *Data)
 {
  signed long Channel,Iscommand;
  unsigned char *Servername,*Paramsource;
  unsigned char ParString[256];
  signed long rc;

  Channel=**(Param);
  Iscommand=(signed long) *(Param+1);
  Servername=(unsigned char *) (*(Param+2));
  Paramsource=(unsigned char *) (*(Param+3));

  if (Paramsource==NULL)
   {
    rc=QSO_StartExtServer(Channel,Iscommand,Servername,NULL);
   }

   else
   {
    unsigned char *sou,*des;
    unsigned long i;

    sou=Paramsource;
    des=ParString;
    i=0;
    while (*sou!='\0')
     {
      if (i>=255) return(0);
      i++;
      *(des++)=*(sou++);
     }
    *(des++)='\0';
    rc=QSO_StartExtServer(Channel,Iscommand,Servername,ParString);
   }
  return(rc);
 }

ULONG MAIN_RexxStartForward(struct Hook *MyHook,unsigned long **Param,void *Object,
                            unsigned long *Data)
 {
  unsigned char *call;

  call=(unsigned char *) (*Param);

  MAIL_ForwardBox(call);
  return(0);
 }


ULONG MAIN_RexxSetSpy(struct Hook *MyHook,unsigned long **Param,void *Object,
                            unsigned long *Data)
{
 signed long spynr,hunit,port,compr;

 unsigned char *p1,*p2;

 unsigned char call1[256],call2[256];

 hunit=(signed long) **(Param);
 port=(signed long) **(Param+1);
 compr=(signed long) **(Param+2);

 p1=(unsigned char *) *(Param+3);
 p2=(unsigned char *) *(Param+4);

 CopyStringUpper(call1,p1);
 CopyStringUpper(call2,p2);


 compr=(compr==TRUE)?0:1;

 spynr=MAIN_SearchFreeSpy();

 if (spynr==0) return(0);

 if ( SPY_SetSpy(spynr,hunit,port,compr,call1,call2)!=0 ) return(0);

 return(spynr);
}

ULONG MAIN_RexxClearSpy(struct Hook *MyHook,unsigned long **Param,void *Object,
                            unsigned long *Data)

{
 unsigned long spynr;
 spynr=(unsigned long) **(Param);
 if (spynr<=0) return (0);
 if (spynr>MaxSpy) return (0);
 SPY_ClearSpy(spynr);
 return(spynr);
}

int CompareString(const unsigned char *s1,const unsigned char *s2)
{
 const unsigned char *s1p,*s2p;
 s1p=s1;
 s2p=s2;
 do {
      if ( *s1p!=*s2p) return(FALSE);
      s2p++;
    } while (  (*(s1p++)!='\0') );
 return(TRUE);
}

int CompareStringLimit(const unsigned char *s1,const unsigned char *s2,
                       unsigned long len)
{
 const unsigned char *s1p,*s2p;
 unsigned long i;
 s1p=s1;
 s2p=s2;
 for (i=0;i<len;++i) {
      if ( *s1p!=*s2p) return(FALSE);
      s2p++;
      if ( *(s1p++)=='\0') break;
    }
 return(TRUE);
}


void CopyString(unsigned char *dest,const unsigned char *source)
{
 do { *(dest++)=*(source++);} while (*(source-1)!='\0' );
}

void CopyStringLimit(unsigned char *dest,const unsigned char *source,unsigned long len)
{
 unsigned long rlen;
 rlen=len;
 do {
     if (rlen==1)
      {
       *dest='\0';
       return;
      }
     rlen--;
     *(dest++)=*(source++);
   } while (*(source-1)!='\0' );
}



void CopyStringUpper(unsigned char *dest,const unsigned char *source)
{
 do {
      *(dest)=*(source++);
      if ( (*dest>='a') && (*dest<='z') )
       {
        *(dest++)-=32;
       }
       else
       {
        dest++;
       }
    } while (*(source-1)!='\0');
}

int MAIN_ExecuteARexxScript(const unsigned char *script,const unsigned char *parameter,unsigned long asyncflag)
{
 unsigned char commandline[512];
 unsigned char *dptr,*clptr;
 const unsigned char *sptr;

 if (Application==NULL) return(FALSE);

 dptr=commandline;
 sptr=  (const unsigned char *) "sys:rexxc/rx ";
 while ( *(sptr)!='\0') { *(dptr++)=*(sptr++);}
 clptr=dptr;
 sptr=  (const unsigned char *) "ARexx/";
 while ( *(sptr)!='\0') { *(dptr++)=*(sptr++);}
 sptr= script;
 while ( *(sptr)!='\0') { *(dptr++)=*(sptr++);}
 sptr=  (const unsigned char *) ".rexx";
 while ( *(sptr)!='\0') { *(dptr++)=*(sptr++);}
 *(dptr)='\0';

  {
   BPTR tlock;
   if (  (tlock=Lock(clptr,ACCESS_READ))==NULL) return(FALSE);
   UnLock(tlock);
  }
 *(dptr++)=' ';
 get(Application,MUIA_Application_Base,&sptr);
 while ( *(sptr)!='\0') { *(dptr++)=*(sptr++);}
 *(dptr++)=' ';
 sptr=parameter;
 if (sptr!=NULL) while ( *(sptr)!='\0') { *(dptr++)=*(sptr++);}
 *(dptr++)='\0';
  {
   BPTR lolo;
   BPTR lola;
   lolo=Open("NIL:",MODE_OLDFILE);
   lola=Open("NIL:",MODE_NEWFILE);
   SystemTags(commandline,SYS_Input,lolo,SYS_Output,lola,SYS_Asynch,asyncflag,TAG_DONE);
   if (!asyncflag)
    {
     if (lolo!=NULL) Close(lolo);
     if (lola!=NULL) Close(lola);
    }
  }
 return(TRUE);
}


unsigned long MAIN_SearchFreeSpy(void)
{
 unsigned long i;
 struct Spy *cspy;
 cspy=Spys;
 for (i=0;i<MaxSpy;++i)
  {
   if (cspy->LinkStatus==0) return (i+1);
   cspy=(struct Spy *) ( ((struct QSO *) cspy) +1 );
  }
 return(0);
}

ULONG MAIN_RexxEstablishLink(struct Hook *MyHook,unsigned long **Param,void *Object,
                             unsigned long *Data)
{
 unsigned char *call;
 unsigned char *path;
 unsigned long hunit;
 unsigned char *callset;
 unsigned long csn,i,chan;
 unsigned char rcl[24];

 call=(unsigned char *) *(Param);
 path=(unsigned char *) *(Param+1);
 hunit=(unsigned long) **(Param+2);
 callset=(unsigned char *) *(Param+3);

 csn=0;
 if (callset!=NULL)
  {
   chan=FALSE;
   for (i=0;i<8;++i)
    {
     if (CompareString(Preferences.Callsets[i].MyCall,callset))
      {
       csn=i;
       chan=TRUE;
      }
    }
   if (chan==FALSE) return (1);
  }
 chan=MAIN_EstablishLink(call,path,hunit,csn);
 if (chan==0) return (1);
 GUI_LongToString(rcl,chan,10000000,3);
 set(Application,MUIA_Application_RexxString,(ULONG) rcl);
 return(0);
}


ULONG MAIN_RexxGetMailBid(struct Hook *MyHook,unsigned long **Param,void *Object,
                            unsigned long *Data)

{
 unsigned long mailnr;
 void *mailhandle;

 mailnr=(unsigned long) **(Param);
 if (mailnr<=0) return (1);
 if ( (mailhandle=MAIL_OpenMail(mailnr,NULL))==NULL) return(1);
 if (mailhandle== ((void *)-1 )) return(1);
 set(Application,MUIA_Application_RexxString,(ULONG) MAIL_GetMailBid(mailhandle));
 MAIL_CloseMail(mailhandle);
 return(0);
}

ULONG MAIN_RexxGetMailFm(struct Hook *MyHook,unsigned long **Param,void *Object,
                            unsigned long *Data)

{
 unsigned long mailnr;
 void *mailhandle;

 mailnr=(unsigned long) **(Param);
 if (mailnr<=0) return (1);
 if ( (mailhandle=MAIL_OpenMail(mailnr,NULL))==NULL) return(1);
 if (mailhandle== ((void *)-1 )) return(1);
 set(Application,MUIA_Application_RexxString,(ULONG) MAIL_GetMailFm(mailhandle));
 MAIL_CloseMail(mailhandle);
 return(0);
}


ULONG MAIN_RexxGetMailTo(struct Hook *MyHook,unsigned long **Param,void *Object,
                            unsigned long *Data)

{
 unsigned long mailnr;
 void *mailhandle;

 mailnr=(unsigned long) **(Param);
 if (mailnr<=0) return (1);
 if ( (mailhandle=MAIL_OpenMail(mailnr,NULL))==NULL) return(1);
 if (mailhandle== ((void *)-1 )) return(1);
 set(Application,MUIA_Application_RexxString,(ULONG) MAIL_GetMailTo(mailhandle));
 MAIL_CloseMail(mailhandle);
 return(0);
}


ULONG MAIN_RexxGetMailAt(struct Hook *MyHook,unsigned long **Param,void *Object,
                            unsigned long *Data)

{
 unsigned long mailnr;
 void *mailhandle;

 mailnr=(unsigned long) **(Param);
 if (mailnr<=0) return (1);
 if ( (mailhandle=MAIL_OpenMail(mailnr,NULL))==NULL) return(1);
 if (mailhandle== ((void *)-1 )) return(1);
 set(Application,MUIA_Application_RexxString,(ULONG) MAIL_GetMailAt(mailhandle));
 MAIL_CloseMail(mailhandle);
 return(0);
}


ULONG MAIN_RexxGetMailStatus(struct Hook *MyHook,unsigned long **Param,void *Object,
                            unsigned long *Data)

{
 unsigned long mailnr;
 unsigned long rc;
 unsigned char rcl[24];
 void *mailhandle;

 mailnr=(unsigned long) **(Param);
 if (mailnr<=0) return (1);
 if ( (mailhandle=MAIL_OpenMail(mailnr,NULL))==NULL) return(1);
 if (mailhandle== ((void *)-1 )) return(1);
 rc=MAIL_GetMailStatus(mailhandle);
 GUI_LongToString(rcl,rc,1000000000,3);
 set(Application,MUIA_Application_RexxString,(ULONG) rcl);
 MAIL_CloseMail(mailhandle);
 return(0);
}


ULONG MAIN_RexxGetMailBytes(struct Hook *MyHook,unsigned long **Param,void *Object,
                            unsigned long *Data)

{
 unsigned long mailnr;
 unsigned long rc;
 unsigned char rcl[24];
 void *mailhandle;

 mailnr=(unsigned long) **(Param);
 if (mailnr<=0) return (1);
 if ( (mailhandle=MAIL_OpenMail(mailnr,NULL))==NULL) return(1);
 if (mailhandle== ((void *)-1 )) return(1);
 rc=MAIL_GetMailBytes(mailhandle);
 GUI_LongToString(rcl,rc,10000000,3);
 set(Application,MUIA_Application_RexxString,(ULONG) rcl);
 MAIL_CloseMail(mailhandle);
 return(0);
}


ULONG MAIN_RexxGetMailTitle(struct Hook *MyHook,unsigned long **Param,void *Object,
                            unsigned long *Data)

{
 unsigned long mailnr;
 void *mailhandle;

 mailnr=(unsigned long) **(Param);
 if (mailnr<=0) return (1);
 if ( (mailhandle=MAIL_OpenMail(mailnr,NULL))==NULL) return(1);
 if (mailhandle== ((void *)-1 )) return(1);
 set(Application,MUIA_Application_RexxString,(ULONG) MAIL_GetMailTitle(mailhandle));
 MAIL_CloseMail(mailhandle);
 return(0);
}


ULONG MAIN_RexxGetMailDate(struct Hook *MyHook,unsigned long **Param,void *Object,
                            unsigned long *Data)

{
 unsigned long mailnr;
 unsigned char mailtext[24];
 void *mailhandle;

 mailnr=(unsigned long) **(Param);
 if (mailnr<=0) return (1);
 if ( (mailhandle=MAIL_OpenMail(mailnr,NULL))==NULL) return(1);
 if (mailhandle== ((void *)-1 )) return(1);
 MAIL_GetMailDateTxt(mailhandle,mailtext);
 set(Application,MUIA_Application_RexxString,(ULONG) mailtext);
 MAIL_CloseMail(mailhandle);
 return(0);
}

ULONG MAIN_RexxKillMail(struct Hook *MyHook,unsigned long **Param,void *Object,
                            unsigned long *Data)

{
 unsigned long mailnr;

 mailnr=(unsigned long) **(Param);
 if (mailnr<=0) return (1);
 return(-MAIL_KillMail(mailnr,NULL));

}

ULONG MAIN_RexxSendMailFile(struct Hook *MyHook,unsigned long **Param,void *Object,
                            unsigned long *Data)

{
 unsigned char *toptr,*atptr,*fromptr,*title,*filename,*bid,*fsource;
 unsigned long pmail,binary;
 unsigned long notconv;

 unsigned char tostr[64];
 unsigned char fromstr[64];
 unsigned char atstr[128];


 toptr=(unsigned char *) *(Param);
 atptr=(unsigned char *) *(Param+1);
 fromptr=(unsigned char *) *(Param+2);
 title=(unsigned char *) *(Param+3);
 filename=(unsigned char *) *(Param+4);
 bid=(unsigned char*) *(Param+7);
 fsource=(unsigned char *) *(Param+8);


 pmail=(unsigned long) *(Param+5);
 binary=(unsigned long) *(Param+6);
 notconv=(unsigned long) *(Param+9);

 if (fromptr==NULL) fromptr=(unsigned char *) MAIL_GetBoxCall();

 CopyStringUpper(tostr,toptr);
 toptr=tostr;
 if (atptr!=NULL)
  {
   CopyStringUpper(atstr,atptr);
   atptr=atstr;
  }
 CopyStringUpper(fromstr,fromptr);
 fromptr=fromstr;

 return (-MAIL_SendMailFile(pmail,binary,fromptr,toptr,atptr,title,
                           filename,bid,fsource,notconv));
}

ULONG MAIN_RexxExtractMailFile(struct Hook *MyHook,unsigned long **Param,void *Object,
                            unsigned long *Data)

{
 unsigned long mailnr;
 unsigned char *mailfname;
 unsigned long disableconvert;


 mailnr=(unsigned long) **(Param);
 mailfname=(unsigned char *) *(Param+1);
 disableconvert=(unsigned long) *(Param+2);

 if (mailnr<=0) return (1);

 return(MAIL_ExtractMailFile(mailfname,mailnr,disableconvert));
}


ULONG MAIN_RexxSearchMailBid(struct Hook *MyHook,unsigned long **Param,void *Object,
                            unsigned long *Data)

{
 unsigned char *bid;
 unsigned long mailnr;
 unsigned char rcl[24];

 bid=(unsigned char *) *(Param);
 mailnr=MAIL_SearchMailBid(bid);
 GUI_LongToString(rcl,mailnr,10000000,3);
 set(Application,MUIA_Application_RexxString,(ULONG)rcl);
 return((mailnr==0)?1:0);
}

ULONG MAIN_RexxGetBoxCall(struct Hook *MyHook,unsigned long **Param,void *Object,
                            unsigned long *Data)

{
 set(Application,MUIA_Application_RexxString,(ULONG) MAIL_GetBoxCall());
 return(0);
}

ULONG MAIN_RexxMarkAsRead(struct Hook *MyHook,unsigned long **Param,void *Object,
                            unsigned long *Data)

{
 unsigned long mailnr;
 void *mailhandle;

 mailnr=(unsigned long) **(Param);
 if (mailnr<=0) return (1);
 if ( (mailhandle=MAIL_OpenMail(mailnr,NULL))==NULL) return(1);
 if (mailhandle== ((void *)-1 )) return(1);
 MAIL_MarkAsRead(mailhandle);
 MAIL_CloseMail(mailhandle);
 return(0);
}

ULONG MAIN_RexxMarkAsForward(struct Hook *MyHook,unsigned long **Param,void *Object,
                             unsigned long *Data)

{
 unsigned long mailnr;
 void *mailhandle;

 mailnr=(unsigned long) **(Param);
 if (mailnr<=0) return (1);
 if ( (mailhandle=MAIL_OpenMail(mailnr,NULL))==NULL) return(1);
 if (mailhandle== ((void *)-1 )) return(1);
 MAIL_MarkAsForward(mailhandle);
 MAIL_CloseMail(mailhandle);
 return(0);
}


ULONG MAIN_RexxTerminateLink(struct Hook *MyHook,unsigned long **Param,void *Object,
                             unsigned long *Data)
{
 unsigned long channelnr;
 unsigned long allchannel;

 channelnr=0;
 if (*(Param)!=NULL) channelnr=**(Param);
 allchannel=(unsigned long ) *(Param+1);

 if (allchannel)
  {
   unsigned long i;
   for (i=1;i<=MaxChannel;++i)
    {
     QSO_ForceTransmit(i);
     QSO_SendCommandUser(1,i,"D");
    }
  }
  else
  {
   if ((channelnr<1) || (channelnr>MaxChannel) ) return(0);
   QSO_ForceTransmit(channelnr);
   QSO_SendCommandUser(1,channelnr,"D");
  }
 return(0);
}


ULONG MAIN_RexxSetCMessage(struct Hook *MyHook,unsigned long **Param,void *Object,
                             unsigned long *Data)
{
 unsigned long messagenr;



 messagenr=**(Param);

 if (messagenr<0) return(0);
 if (messagenr>16) return(0);

 CMessageNr=**(Param);
 LastSysopActivity=CurrentDateStamp;
 GUI_RefreshWindowTitle();


 return(0);
}




ULONG MAIN_RexxGetForwardMails(struct Hook *MyHook,unsigned long **Param,void *Object,
                               unsigned long *Data)
{
 unsigned char *fwdbox;
 unsigned char rcl[24];
 unsigned long rc;

 fwdbox=(unsigned char *) *Param;
 rc=MAIL_GetForwardMails(fwdbox);
 GUI_LongToString(rcl,rc,10000000,3);
 set(Application,MUIA_Application_RexxString,(ULONG) rcl);
 return(0);
}

ULONG MAIN_RexxGetForwardBytes(struct Hook *MyHook,unsigned long **Param,void *Object,
                               unsigned long *Data)
{
 unsigned char *fwdbox;
 unsigned char rcl[24];
 unsigned long rc;

 fwdbox=(unsigned char *) *Param;
 rc=MAIL_GetForwardBytes(fwdbox);
 GUI_LongToString(rcl,rc,10000000,3);
 set(Application,MUIA_Application_RexxString,(ULONG) rcl);
 return(0);

}

ULONG MAIN_RexxSearchChannel(struct Hook *MyHook,unsigned long **Param,void *Object,
                               unsigned long *Data)
{
 unsigned char *call;
 unsigned char rcl[24];
 unsigned long callset,hunit,port,rc,i;
 struct QSO *qsos;

 call=(unsigned char *) *Param;
 callset=123456;
 hunit=123456;
 port=123456;
 if (*(Param+1)!=NULL) callset=(**(Param+1))-1;
 if (*(Param+2)!=NULL) hunit=(**(Param+2))-1;
 if (*(Param+3)!=NULL) port=**(Param+3);
 rc=0;

 qsos=QSOs;
 for (i=1;i<=MaxChannel;++i,++qsos)
  {
   if (qsos->SkipChannel) continue;
   if (!CompareString(call,qsos->Call)) continue;
   if ( (hunit!=123456) && (hunit!=qsos->HardwareUnit)) continue;
   if ( (port!=123456) && (port!=qsos->HardwarePort) ) continue;
   if ( (callset!=123456) && (callset!=qsos->CallSet)) continue;
   rc=i;
  }
 GUI_LongToString(rcl,rc,10000000,3);
 set(Application,MUIA_Application_RexxString,(ULONG)rcl);
 return(0);

}

ULONG MAIN_RexxSendBeacon(struct Hook *MyHook,unsigned long **Param,void *Object,
                            unsigned long *Data)
{

 MAIN_SendBeacon();
 return(0);
}



int MAIN_EstablishLink(unsigned char *call,unsigned char *path,
                       unsigned long hardunit,unsigned long callset)
{
 unsigned long channel;

 channel=QSO_FindFreeChannel(hardunit,callset);

 if (channel==0) return(0);

   {
    unsigned char line [320];
    unsigned char *sptr,*dptr;
    struct Callset *cset;

    dptr=line;
    cset=&Preferences.Callsets[callset];
    sptr=cset->ServerName;
    while (*sptr!='\0') *(dptr++)=*(sptr++);
    *(dptr++)=' ';
    sptr=path;
    while (*sptr!='\0') *(dptr++)=*(sptr++);
    *(dptr++)=' ';
    sptr=call;
    while (*sptr!='\0') *(dptr++)=*(sptr++);
    *(dptr++)='\0';

    if (QSO_StartExtServer(channel,FALSE,"Connection",line)!=0) return(channel);

    dptr=line;
    *(dptr++)='C';
    *(dptr++)=' ';
    sptr=call;
    if (*sptr=='\0') return(TRUE);
    while (*sptr!='\0') *(dptr++)=*(sptr++);
    *(dptr++)=' ';
    sptr=path;
    if (sptr!=NULL) while (*sptr!='\0') *(dptr++)=*(sptr++);
    *(dptr)='\0';
    QSO_SendCommandUser(dptr-line,channel,line);

   }
 return(channel);
}



void MAIN_SendBeacon(void)
{
 unsigned long hunit;
 unsigned char thisbak[256];

 CopyString(thisbak,Preferences.BeaconLine);
 if (MAIL_LookLocalMail()!=0)
  {
   unsigned char *sptr;
   sptr=thisbak;
   while (*sptr!='\0') ++sptr;
   *(sptr++)=13;
   CopyString(sptr,Preferences.BeaconMailLine);
  }
 MAIN_ConvertToPC(thisbak);
 for (hunit=0;hunit<8;++hunit)
  {
   struct TNCDevice *tncd;
   tncd=&Preferences.Devices[hunit];
   if (tncd->ExecDevice[0]=='\0') continue;
   if (tncd->Channels==0) continue;

   if (tncd->SendBeacon==SENDBEACON_NEVER) continue;

   if (tncd->SendBeacon==SENDBEACON_NOTCONNECTED)
    {
     struct QSO *tqso;
     unsigned long i,f;
     tqso=QSOs;
     f=FALSE;
     for (i=0;i<MaxChannel;++i,++tqso)
      {
       if (tqso->SkipChannel) continue;
       if (tqso->LinkStatus==0) continue;
       if (tqso->HardwareUnit!=hunit) continue;
       f=TRUE;
       break;
      }
     if (f) continue;
    }


   PACK_SetMonitorUnit(hunit);
    {
     unsigned char bpath[128];
     unsigned char *sptr;
     unsigned long slen;
     bpath[0]='C';
     bpath[1]=' ';
     CopyString(bpath+2,tncd->BeaconPath);
     sptr=bpath;
     slen=0;
     while (*(sptr++)!='\0') ++slen;
     QSO_SendCommandUser(slen,0,bpath);
    }

   QSO_TransmitText(thisbak,0,FALSE,TRUE);
  }
 PACK_SetMonitorUnit(-1);
}



void MAIN_NewMinute(void)
 {


  if ((CurrentDateStamp.ds_Minute)%(Preferences.BeaconMinutes)==0)
   {
    if ( (!(Preferences.BeaconOnlyMail)) || (MAIL_LookLocalMail()!=0))
     {
      if ( (CMessageNr==0) || (!(Preferences.BeaconNoMessage)))
       {
        MAIN_SendBeacon();
       }

     }
   }

  MAIL_NewMinute();
 }

void MAIN_HardwareInitChan(unsigned long channel)
 {
  struct QSO *qso;
  unsigned long i;
  qso=QSOs+(channel-1);
  CopyStringUpper(qso->MyCall,Preferences.Callsets[qso->CallSet].MyCall);
  qso->LinkStatus=0;
  qso->NotDisplayedLink=0;
  qso->NotDisplayed=0;
  qso->NotSent=0;
  qso->Unack=0;
  qso->Tries=0;
  qso->SelfConnect=0;
  qso->HardCall[0]='\0';
  qso->HardwarePort=1;
  for (i=0;i<8;++i)
   {
    qso->HardPath[i][0]='\0'; 
   }    
  qso->StartupFlag=0;
  qso->StartupMsg=0;
 }
 
void MAIN_LongToString(unsigned char *target,unsigned long dezi)
  {
   RawDoFmt("%ld",&dezi,PutRawDoFmt,target); 
  }
  
void MAIN_AddTimeStamp(const struct DateStamp *source,struct DateStamp *target)
  {
   target->ds_Tick+=source->ds_Tick;
   while (target->ds_Tick>= (60*50) ) 
    {
     target->ds_Tick-=60*50;
     target->ds_Minute++;
    }
  
   target->ds_Minute+=source->ds_Minute;
   while (target->ds_Minute>= (60*60))
    {
     target->ds_Minute-=60*60;
     target->ds_Days++;
    }
   target->ds_Days+=source->ds_Days;
  }
  
signed long MAIN_TestTimeStamp(const struct DateStamp *date1,const struct DateStamp *date2)
 {
  if (date2->ds_Days>date1->ds_Days) return (-1);
  if (date2->ds_Days<date1->ds_Days) return (1);
  if (date2->ds_Minute>date1->ds_Minute) return (-1);
  if (date2->ds_Minute<date1->ds_Minute) return (1);
  if (date2->ds_Tick>date1->ds_Tick) return(-1);
  if (date2->ds_Tick<date1->ds_Tick) return(1);
  return(0);
 }
 
 
 
void MAIN_ConvertToPC(unsigned char *string)
 {
  unsigned char *cptr;
  
  cptr=string;
  if (cptr==NULL) return;
  while (*cptr!='\0') 
   {
    *(cptr)=ConvertAmiga2PC[*cptr];
    cptr++;
   }
 }
 
void MAIN_ConvertToAmiga(unsigned char *string)
 {
  unsigned char *cptr;
  
  cptr=string;
  if (cptr==NULL) return;
  while (*cptr!='\0')
   {
    *(cptr)=ConvertPC2Amiga[*cptr];
    cptr++;
   }
 }

void MAIN_ConvertToPCLen(unsigned char *string,unsigned long length)
 {
  unsigned char *cptr;
  unsigned long i;

  cptr=string;
  if ( (cptr==NULL) || (length==0)) return;
  for (i=0;i<length;++i)
   {
    *(cptr)=ConvertAmiga2PC[*cptr];
    cptr++;
   }
 }

void MAIN_ConvertToAmigaLen(unsigned char *string,unsigned long length)
 {
  unsigned char *cptr;
  unsigned long i;

  cptr=string;
  if ( (cptr==NULL) || (length==0)) return;
  for (i=0;i<length;++i)
   {
    *(cptr)=ConvertPC2Amiga[*cptr];
    cptr++;
   }
 }


void MAIN_RecalcFileTransfer(unsigned long channel)
 {
  struct QSO *qso;
  unsigned long precisepercent;


  precisepercent=0;

  qso=QSOs+(channel-1);

  qso->TransferElapsed=MAIN_GetSecondsAgo(&qso->TransferStart);
  if (qso->TransferElapsed<0) qso->TransferElapsed=0;
  if (qso->TransferElapsed!=0)
   {
    if (qso->TransferOffset<qso->TransferRBytes)

       {
        if (qso->TransferElapsed<8)
         {
          qso->TransferBaud=(qso->TransferRBytes-qso->TransferOffset)/
                            (qso->TransferElapsed)*8;
         }
         else
         {
          qso->TransferBaud=(qso->TransferRBytes-qso->TransferOffset)/
                            (qso->TransferElapsed/8);
         }
       }
      else
       {
         qso->TransferBaud=123;
       }
    if (qso->TransferTotBytes!=0)
      {
       if (qso->TransferRBytes>4000000)
        {
         if (qso->TransferTotBytes>=128)
          {
           precisepercent= ((qso->TransferRBytes+127)>>7)*1000 /
                               ( (qso->TransferTotBytes+127)>>7 );
          }
          else
          {
           qso->TransferPercent=0;
           qso->TransferTotal=0;
           qso->TransferEstimated=0;
           return;
          }
        }
        else
        {
         if (qso->TransferTotBytes>0)
          {
           precisepercent=qso->TransferRBytes*1000/qso->TransferTotBytes;
          }
          else
          {
           qso->TransferPercent=0;
           qso->TransferTotal=0;
           qso->TransferEstimated=0;
           return;
          }
        }
       qso->TransferPercent=precisepercent/10;

      }
      else
      {
       qso->TransferEstimated=-1;
       qso->TransferTotal=-1;
       qso->TransferPercent=-1;
       return;
      }

    if (qso->TransferRBytes>qso->TransferOffset)
     {
      if ((qso->TransferRBytes-qso->TransferOffset)>4000000)
        {
         if (qso->TransferTotBytes>=128)
          {
           precisepercent= (((qso->TransferRBytes-qso->TransferOffset)+127)>>7)*1000 /
                               ( (qso->TransferTotBytes+127)>>7 );
          }
          else
          {
           qso->TransferTotal=0;
           qso->TransferEstimated=0;
           return;
          }
        }
        else
        {
         if (qso->TransferTotBytes>0)
          {
           precisepercent=(qso->TransferRBytes-qso->TransferOffset)
                          *1000/qso->TransferTotBytes;
          }
          else
          {
           qso->TransferTotal=0;
           qso->TransferEstimated=0;
           return;
          }
        }
      if (precisepercent!=0)
       {
        qso->TransferTotal=(qso->TransferElapsed*1000)/precisepercent;
       }
       else
       {
        qso->TransferTotal=0;
        qso->TransferEstimated=0;
        return;
       }
      qso->TransferEstimated=qso->TransferTotal-qso->TransferElapsed;
      return;
     }
     else
     {
      qso->TransferEstimated=0;
      qso->TransferTotal=0;
      return;
     }
   }
   else
   {
    qso->TransferBaud=0;
    qso->TransferEstimated=0;
    qso->TransferTotal=0;
    qso->TransferPercent=0;
    return;
   }

 }

void MAIN_ResetFileTransfer(unsigned long channel)
 {
  struct QSO *qso;

  qso=QSOs+(channel-1);

  if (qso->FileID!=NULL)
   {
    Close(qso->FileID);
    qso->FileID=NULL;
    if (qso->WriteMode)
     {
      DeleteFile(qso->FileName);
     }
   }
  qso->FileName[0]='\0';
  qso->TransferMode=0;
  qso->TransferStart=CurrentDateStamp;
  qso->TransferRBytes=0;
  qso->TransferBytes=0;
  qso->TransferTotBytes=0;
  qso->TransferOffset=0;
  qso->TransferFileDate.ds_Days=0;
  qso->TransferFileDate.ds_Minute=0;
  qso->TransferFileDate.ds_Tick=0;
  qso->TransferCRC=-1;
  qso->OnlyPrepared=FALSE;
  qso->WriteMode=FALSE;
  MAIN_RecalcFileTransfer(channel);
 }


void MAIN_CleanupChannel(signed long channel)
 {
  if (channel==0) return;
  if (channel<0)
   {
    /* cleanup spy */
    struct Spy *spy;
    unsigned long i;

    spy=(struct Spy *)(((struct QSO *) Spys)+(-channel-1));
    CopyString(spy->MyCall,"------");
    CopyString(spy->Call,"------");
    spy->LinkStatus=0;
    if (spy->FrameCollector!=NULL)
     {
      FreeVec(spy->FrameCollector);
      spy->FrameCollector=NULL;
     }
    for (i=0;i<260;++i)
     {
      spy->XPDecompress1[i]=0;
      spy->XPDecompress2[i]=0;
     }
    for (i=0;i<8;++i)
     {
      spy->Path[i][0]='\0';
     }
   }
   else
   {
    /* cleanup QSO */
    struct QSO *qso;
    unsigned long i;

    qso=QSOs+(channel-1);

    CopyString(qso->Call,"------");
    for (i=0;i<8;++i)
     {
      qso->Path[i][0]='\0';
     }
    qso->Name[0]='\0';


    while (qso->Server!=NULL) SERV_CleanupServer(qso);

    MAIN_ResetFileTransfer(channel);
    GUI_RedisplayFileTransfer(channel);

    if (qso->User!=NULL)
     {
      QSO_CloseUser(qso->User);
      qso->User=NULL;
     }

    if (qso->PrPaPointer!=NULL)
     {
      FreeVec(qso->PrPaPointer);
      qso->PrPaPointer=NULL;
     }
    qso->PrivateDir=FALSE;

    while (qso->ServerBuffer.lh_TailPred!=((struct Node *) &qso->ServerBuffer ))
     {
      struct Node *remmi;
      remmi=qso->ServerBuffer.lh_Head;
      Remove(remmi);
      FreeVec(remmi);
     }

     {
      struct ServerBuffer *myservbuf;

      myservbuf=AllocVec(sizeof(*myservbuf),1);
      if (myservbuf!=NULL)
       {
        myservbuf->InBytes=0;
        AddTail(&qso->ServerBuffer,(struct Node *) myservbuf);
       }
     }

    qso->CurrentDir[0]='\0';
    qso->CompressedMode=0;
    qso->PasswordRemote=0;
    qso->SysopMode=FALSE;

    qso->ConnectTime=CurrentDateStamp;
    qso->ActivityTime=CurrentDateStamp;

    if (qso->XPKLibrary!=NULL)
     {
      CloseLibrary(qso->XPKLibrary);
      qso->XPKLibrary=NULL;
     }
    qso->XPKType[0]=-1;
    qso->XPKType[1]=-1;
    qso->XPKType[2]=-1;
    qso->XPKType[3]=-1;

    qso->XPKEfficiency=-1;
    qso->XPKSublibrary=-1;

     {
      unsigned long i;
      for (i=0;i<260;++i)
       {
        qso->XPDecompress[i]='\0';
       }
      for (i=0;i<128;++i)
       {
        qso->XPacketKey[i]='\0';
       }
     }

    while (qso->XPacketDelay!=NULL)
     {
      void **free;

      free=(void *) qso->XPacketDelay;
      qso->XPacketDelay=*free;
      FreeVec(free);
     }

   }

 }
 

void MAIN_WriteServBuffer(struct QSO *qso,const unsigned char *chars,unsigned long length)
 {
  struct ServerBuffer *lastbuffer;
  unsigned long remainsize;
  const unsigned char *remainptr;
  if  (length==NULL) return;

  remainsize=length;
  remainptr=chars;

  while (remainsize>0)
   {
    unsigned long jobbytes;
    unsigned char *jobtarget;
    lastbuffer=(struct ServerBuffer *) qso->ServerBuffer.lh_TailPred;
    if  ( (lastbuffer==((struct ServerBuffer *) &qso->ServerBuffer)) || (lastbuffer->InBytes>=4096) )
     {
      lastbuffer=AllocVec(sizeof(*lastbuffer),1);
      if (lastbuffer==NULL) return;
      lastbuffer->InBytes=0;
      AddTail(&qso->ServerBuffer,(struct Node *) lastbuffer);
     }
    if ( (remainsize+lastbuffer->InBytes)>4096)
     {
      jobbytes=lastbuffer->InBytes-4096;
     }
     else
     {
      jobbytes=remainsize;
     }

    jobtarget=lastbuffer->Data+lastbuffer->InBytes;
    lastbuffer->InBytes+=jobbytes;
    while (jobbytes>0)
     {
      *(jobtarget++)=*(remainptr++);
      remainsize--;jobbytes--;
     }

   }
 }

unsigned long MAIN_TestServBuffer(const struct QSO *qso)
 {
  struct ServerBuffer *buffer;
  unsigned long insize;


  buffer=(struct ServerBuffer *) qso->ServerBuffer.lh_Head;
  insize=0;

  while (buffer->Node.ln_Succ!=NULL)
   {
    insize+=buffer->InBytes;
    buffer=(struct ServerBuffer *) buffer->Node.ln_Succ;
   }
  return(insize);
 }

unsigned long MAIN_TestServBufferLin(const struct QSO *qso)
 {
  struct ServerBuffer *buffer;
  unsigned long insize;


  buffer=(struct ServerBuffer *) qso->ServerBuffer.lh_Head;
  insize=0;

  while (buffer->Node.ln_Succ!=NULL)
   {
    unsigned char *inptr;
    unsigned long i;
    int abortme=FALSE;
    inptr=buffer->Data;
    for (i=0;i<buffer->InBytes;++i,++inptr)
     {
      ++insize;
      if (*inptr=='\r')
       {
        abortme=TRUE;
        break;
       }
     }
    if (abortme) break;
    buffer=(struct ServerBuffer *) buffer->Node.ln_Succ;
   }
  return(insize>256?256:insize);
 }

unsigned long MAIN_ReadServBuffer(struct QSO *qso,unsigned char *data,unsigned long length)
 {
  unsigned char *remainptr;
  unsigned long remainsize,readsize;
  struct ServerBuffer *buffer;

  if (length==NULL) return(0);

  remainptr=data;
  remainsize=length;
  readsize=0;

  buffer=(struct ServerBuffer *) qso->ServerBuffer.lh_Head;

  while (remainsize>0)
   {
    unsigned char *jobptr,*jobptr2;
    unsigned long jobsize,i;
    struct ServerBuffer *oldbuffer;
    if (buffer->Node.ln_Succ==NULL) break;

    jobsize=(buffer->InBytes>remainsize)?remainsize:buffer->InBytes;

    jobptr=buffer->Data;
    for (i=0;i<jobsize;++i)
     {
      *(remainptr++)=*(jobptr++);
     }
    remainsize-=jobsize;
    readsize+=jobsize;

    jobptr=buffer->Data+jobsize;
    jobptr2=buffer->Data;

    buffer->InBytes-=jobsize;

    for (i=0;i<buffer->InBytes;++i)
     {
      *(jobptr2++)=*(jobptr++);
     }



    oldbuffer=buffer;
    buffer=(struct ServerBuffer *) buffer->Node.ln_Succ;
    if ( (oldbuffer->InBytes==0) &&
         (  ( qso->ServerBuffer.lh_Head !=(struct Node *) oldbuffer) ||
            ( qso->ServerBuffer.lh_TailPred!=(struct Node *) oldbuffer) ) )
     {
      Remove((struct Node *) oldbuffer);
      FreeVec(oldbuffer);
     }

   }
  return(readsize);
 }

unsigned long MAIN_GetConnectMsgNr(void)
 {
  if (CMessageNr!=0) return(CMessageNr);

  if (Preferences.ConnectMsgAutoTime==0) return(0);
  if (Preferences.ConnectMsgAutoNr==0) return(0);
  if (  MAIN_GetSecondsAgo(&LastSysopActivity)<
           Preferences.ConnectMsgAutoTime) return(0);
  return(Preferences.ConnectMsgAutoNr);
 }

unsigned char *MAIN_GetConnectMsg()
 {
  unsigned long cmsgn;

  cmsgn=MAIN_GetConnectMsgNr();
  if (cmsgn==0) return(NULL);
  return (Preferences.ConnectMsg[cmsgn-1]);
 }


void MAIN_KillAllServers()
 {
  unsigned long i;
  struct QSO *qso;
  for (i=0,qso=QSOs;i<MaxChannel;++i,++qso)
   {
    MAIN_ResetFileTransfer(i+1);
    while (qso->Server!=NULL) SERV_CleanupServer(qso);

   }
 }

void MAIN_FreeAllBuffers()
 {
  MAIN_KillAllServers();
   {
    unsigned long i;
    struct QSO *qso;
    for (i=0,qso=QSOs;i<MaxChannel;++i,++qso)
     {
      while (qso->ServerBuffer.lh_TailPred!=((struct Node *) &qso->ServerBuffer ))
       {
        struct Node *remmi;
        remmi=qso->ServerBuffer.lh_Head;
        Remove(remmi);
        FreeVec(remmi);
       }
      if (qso->PrPaPointer!=NULL)
       {
        FreeVec(qso->PrPaPointer);
        qso->PrPaPointer=NULL;
       }
      while (qso->ChannelInPackets.lh_TailPred!=
                ((struct Node *) &qso->ChannelInPackets))
       {
        struct Node *remmi;
        remmi=qso->ChannelInPackets.lh_Head;
        Remove(remmi);
        FreeVec(remmi);
       }
      while (qso->ChannelOutPackets.lh_TailPred!=
                ((struct Node *) &qso->ChannelOutPackets ))
       {
        struct Node *remmi;
        remmi=qso->ChannelOutPackets.lh_Head;
        Remove(remmi);
        FreeVec(remmi);
       }
      if (qso->XPKLibrary!=NULL)
       {
        CloseLibrary(qso->XPKLibrary);
        qso->XPKLibrary=NULL;
       }

      if (qso->User!=NULL)
       {
        QSO_CloseUser(qso->User);
        qso->User=NULL;
       }
     }
   }
   {
    unsigned long i;
    struct Spy *spy;

    for (i=0,spy=Spys;i<MaxSpy;++i,spy=((struct Spy *)(((struct QSO *) spy)+1)))
     {
      if (spy->FrameCollector!=NULL)
       {
        FreeVec(spy->FrameCollector);
        spy->FrameCollector=NULL;
       }
     }
   }

 }


void MAIN_Cleanup(void)
 {
  MAIN_FreeAllBuffers();
  if (QSOs!=NULL)
   {
    FreeVec(QSOs);
    QSOs=NULL;
   }
  if (Spys!=NULL)
   {
    FreeVec(Spys);
    Spys=NULL;
   }
 }


void MAIN_Init(void)
 {
  struct QSO *qso;
  unsigned long i;


  UTIL_InitRandom();
  if (GUI_InitLowLevel()) Clean_Up();

  MAIN_SetDefaultPrefs();
  PREF_LoadPreferences();

  MaxChannel=MAIN_GetMaxChannel();
  MaxSpy=Preferences.MaxSpy;
  ActiveChannel=1;

  QSOs=AllocVec(sizeof(struct QSO)*MaxChannel,0x10001);
  if (QSOs==NULL)
   {
    GUI_GeneralError(19);
    Clean_Up();
   }
  for (i=0,qso=QSOs;i<MaxChannel;++i,++qso)
   {
    qso->ChannelInPackets.lh_Head=(struct Node *) &qso->ChannelInPackets.lh_Tail;
    qso->ChannelInPackets.lh_Tail=NULL;
    qso->ChannelInPackets.lh_TailPred=(struct Node *) &qso->ChannelInPackets.lh_Head;

    qso->ChannelOutPackets.lh_Head=(struct Node *) &qso->ChannelOutPackets.lh_Tail;
    qso->ChannelOutPackets.lh_Tail=NULL;
    qso->ChannelOutPackets.lh_TailPred=(struct Node *) &qso->ChannelOutPackets.lh_Head;

    qso->ServerBuffer.lh_Head=(struct Node *) &qso->ServerBuffer.lh_Tail;
    qso->ServerBuffer.lh_Tail=NULL;
    qso->ServerBuffer.lh_TailPred=(struct Node *) &qso->ServerBuffer.lh_Head;

   }
  if (MaxSpy!=0)
   {
    Spys=AllocVec(sizeof(struct QSO)*MaxSpy,0x10001);
    if (Spys==NULL)
     {
      GUI_GeneralError(19);
      Clean_Up();
     }
   }

  MAIN_InitalizeChannels();
  ActiveChannel=0;
  MonitorSaveChannel=1;

  DateStamp(&CurrentDateStamp);
  LastSysopActivity=CurrentDateStamp;

  CMessageNr=0;

  AUDIO_Init();
  if (GUI_Init()) Clean_Up();
  if (QSO_Init()) Clean_Up();
  if (PACK_Init()) Clean_Up();
  if (!MAIL_Init()) Clean_Up();

  QSO_FreeHoldArray();

  GUI_CloseLaunchWindow();
  GUI_ChangeChannel(1);

  while (1)
   {
    GUI_MainPoll(FALSE);
   }


 }

void MAIN_WaitMoment(void)
 {
  unsigned long i;
  for (i=0;i<25;++i) GUI_MainPoll(TRUE);


 }


_storage_text_ const char Ver2_Export[]=PPVersion_VersionString;
_storage_text_ const char ProgramVersion[]=PPVersion_ProgramVersion;
