/*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

#ifndef IN_CONTRIB
#include "GUIProtos.h"
#include "global.h"

#include "User.h"


#endif

#ifdef AmigaOS

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

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

#ifdef __GNUC__


extern void *DOSBase;
extern void *SysBase;


#include <inline/dos.h>
#include <inline/exec.h>
#endif

#else /*AmigaOS*/

#include "amigaemu.h"

#endif /*AmigaOS*/

#ifndef IN_CONTRIB

#include <exec/memory.h>

#endif

#define FILEVERSION_CURRENT 3


static int QSO_StoreUser(struct User *cuser,BPTR ufile,unsigned long offset);
static int QSO_LoadUser(struct User *cuser,BPTR ufile,unsigned long offset, unsigned long usersize);



#ifndef IN_CONTRIB
static struct User *QSO_OpenUser_General(const unsigned char *call,int mode);
static int QSO_SaveUser(struct User *suser);
#endif

static void QSO_CipherUser(struct User *cuser,int decipher,unsigned long usersize);


struct version2indexhead
        {
         char fileid[8];
         unsigned long fileversion;
         unsigned long indexentries;
        };

struct version2index
        {
         unsigned char call[12];
         unsigned long filepos;
        };

#ifndef IN_CONTRIB
static struct version2index *UserBaseIndex;
static unsigned long UserBaseIndexSize;
static unsigned long UserBaseUsers;
struct version3header *UserBaseHead;
static BPTR UserBaseFile;
#endif

void QSO_ResetUser(struct User *ruser)
{
 ruser->nextuser=NULL;
 ruser->prevuser=NULL;
 ruser->username=(ruser->Call);
 ruser->OpenCnt=0;
 ruser->DataBasePos=0;
 ruser->SaveBackNeeded=FALSE;
 ruser->Call[0]='\0';
 ruser->Name[0]='\0';
 ruser->QTH[0]='\0';
 ruser->Locator[0]='\0';
 ruser->DigiPath[0]='\0';
 ruser->Type=0;
 ruser->RPType=0;
 ruser->LPType=0;
 ruser->RPassword[0]='\0';
 ruser->LPassword[0]='\0';
 ruser->LinkUse=0;
 ruser->MinFrack=400;
 ruser->MaxFrack=10000;
 ruser->CurFrack=750;
 ruser->MinTries=10;
 ruser->MaxTries=30;
 ruser->CurTries=20;
 ruser->MinMaxFrame=1;
 ruser->MaxMaxFrame=4;
 ruser->CurMaxFrame=3;
 ruser->MinPaclen=64;
 ruser->MaxPaclen=196;
 ruser->CurPaclen=128;
 ruser->Language=0;
 ruser->CipherKey[0]=0;
 ruser->CipherKey[1]=0;
 ruser->CipherKey[2]=0;
 ruser->CipherKey[3]=0;
 ruser->PrivateDir[0]='\0';
 ruser->LastNews[0]=0;
 ruser->LastNews[1]=0;
 ruser->LastNews[2]=0;
  {
   ruser->StationID.ProgramName[0]='\0';
   ruser->StationID.Program=SID_Program_Unknown;
   ruser->StationID.Version=0;
   ruser->StationID.Revision=0;
   ruser->StationID.Subrevision=0;
   ruser->StationID.SystemType=SID_SystemType_Chat;
    {
     signed char *xptr;
     unsigned long xlen;
     xptr=& (ruser->StationID.AVersion);
     for (xlen=0;xlen<32;++xlen)
      {
       *(xptr++)=-1;
      }
    }

  }
 ruser->XPKType[0]='I';
 ruser->XPKType[1]='D';
 ruser->XPKType[2]='E';
 ruser->XPKType[3]='A';
 ruser->XPKEfficiency=100;
 ruser->LastBoxcheck=0;
 ruser->LastLogin.ds_Days=0;
 ruser->LastLogin.ds_Minute=0;
 ruser->LastLogin.ds_Tick=0;
}


static int QSO_StoreUser(struct User *cuser,BPTR ufile,unsigned long offset)
{
 char *xptr,*yptr;

 unsigned long xoffset;
 unsigned long xsize;

 xptr= (char *) cuser->Call;
 yptr= (char *) cuser;
 xoffset=xptr-yptr;
 xsize=sizeof(struct User)-xoffset;

 if (!LockRecord(ufile,offset,xsize,REC_EXCLUSIVE,10)) return(FALSE);

 if ( (Seek(ufile,offset,OFFSET_BEGINNING))==-1)
   {
    UnLockRecord(ufile,offset,xsize);
    return (FALSE);
   }
 QSO_CipherUser(cuser,FALSE,sizeof(struct User));
 if ( (Write(ufile,xptr,xsize))!=xsize)
  {
   UnLockRecord(ufile,offset,xsize);
   QSO_CipherUser(cuser,TRUE,sizeof(struct User));
   return (FALSE);
  }
 UnLockRecord(ufile,offset,xsize);
 QSO_CipherUser(cuser,TRUE,sizeof(struct User));
 return (TRUE);
}

static int QSO_LoadUser(struct User *cuser,BPTR ufile,unsigned long offset,
                        unsigned long usersize)
{
 char *xptr,*yptr;

 unsigned long xoffset;
 unsigned long xsize;

 xptr= (char *) cuser->Call;
 yptr= (char *) cuser;
 xoffset=xptr-yptr;
 xsize=usersize-xoffset;


 if (!LockRecord(ufile,offset,xsize,REC_SHARED,10)) return(FALSE);
 if ( (Seek(ufile,offset,OFFSET_BEGINNING))==-1)
  {
   UnLockRecord(ufile,offset,xsize);
   return (FALSE);
  }
 if ( (Read(ufile,xptr,xsize))!=xsize)
  {
   UnLockRecord(ufile,offset,xsize);
   return (FALSE);
  }
 UnLockRecord(ufile,offset,xsize);
 QSO_CipherUser(cuser,TRUE,usersize);
 return (TRUE);
}



static void QSO_CipherUser(struct User *cuser,int decipher,
                           unsigned long usersize)
{
 char *xptr,*yptr;
 unsigned char feedback,feedback2;
 unsigned long xoffset;
 unsigned long xsize,i;

 xptr= (char *) cuser->Call;
 yptr= (char *) cuser;
 xoffset=xptr-yptr;
 xsize=usersize-xoffset;
 feedback=0x47;
 i=0;
 while (xsize>0)
  {
   feedback2=0;
   if (decipher) feedback2=feedback^(0x2f^((!(*xptr))+( (i*5) & 0xff)));
   *(xptr)= (*(xptr))^feedback;
   if (!decipher) feedback2=feedback^(0x2f^((!(*xptr))+( (i*5) & 0xff)));
   --xsize;
   ++xptr;
   ++i;
   feedback=feedback2;
  }
}





#ifndef IN_CONTRIB

void QSO_PresetUser(struct User *ruser)
{
 char *xptr,*yptr;
 unsigned long i;
 xptr=(char *) UserBaseStandard;
 yptr=(char *) ruser;
 for (i=0;i<sizeof(struct User);++i)
  {
   *(yptr++)=*(xptr++);
  }
 ruser->nextuser=NULL;
 ruser->prevuser=NULL;
 ruser->username=ruser->Call;
 ruser->OpenCnt=0;
 ruser->DataBasePos=0;
 ruser->SaveBackNeeded=FALSE;
}

static signed long QSO_UserParseIndex(const unsigned char *call)
{
 const unsigned char *xptr;
 unsigned char *yptr;
 struct version2index *iptr;
 unsigned long left,i;

 iptr=UserBaseIndex;
 if (iptr==NULL) return(-1);
 left=UserBaseUsers;
 i=0;
 while (left>0)
  {
   xptr=call;
   yptr=iptr->call;

   while (*xptr!='\0')
    {
     if (*xptr!=*yptr) break;
     ++xptr;++yptr;
    }
   if ( (*xptr=='\0') && (*yptr=='\0')) return(i);
   ++iptr;++i;--left;
  }

 return(-1);
}

static signed long QSO_GetDiskLocation(const unsigned char *call)
 {
  char *ptr1,*ptr2;
  signed long ientry,floc,xrsize;
  ptr1=NULL;
  ptr2=(char *) (((struct User *) ptr1)->Call);
  xrsize=sizeof(struct User )- (ptr2-ptr1);
  ientry=QSO_UserParseIndex(call);
  if (ientry==-1) return (-1);
  floc=(UserBaseIndex+ientry)->filepos;
  return (sizeof(struct version3header)+floc*xrsize);
 }

int QSO_SaveHeader(void)
{
 BPTR ufile;

 ufile=UserBaseFile;

 if (!LockRecord(ufile,0,sizeof(*UserBaseHead),REC_EXCLUSIVE,10))
  {
   return(FALSE);
  }
 if (Seek(ufile,0,OFFSET_BEGINNING)==-1)
  {
   UnLockRecord(ufile,0,sizeof(*UserBaseHead));
   return(FALSE);
  }
 if ((Write(ufile,UserBaseHead,sizeof(*UserBaseHead)))!=
         sizeof(*UserBaseHead))
  {
   UnLockRecord(ufile,0,sizeof(*UserBaseHead));
   return(FALSE);
  }
 UnLockRecord(ufile,0,sizeof(*UserBaseHead));
 return(TRUE);
}

static int QSO_LoadBase(void)
{
 BPTR ufile;
 signed long suser;
 unsigned long usiz;
 struct version3header *v3h;

  {
   char *ptr1,*ptr2;
   ptr1=NULL;
   ptr2=( (char *) (((struct User *)ptr1)->Call));
   usiz=sizeof(struct User)-(ptr2-ptr1);
  }

 if ((UserBaseHead=AllocVec(sizeof(struct version3header),MEMF_PUBLIC))
         ==NULL) return (FALSE);
 v3h=UserBaseHead;



 ufile=UserBaseFile;

 if (!LockRecord(ufile,0,sizeof(*v3h),REC_EXCLUSIVE,10))
  {
   return(FALSE);
  }

 if ( Seek(ufile,0,OFFSET_BEGINNING)==-1)
  {
   UnLockRecord(ufile,0,sizeof(*v3h));
   return(FALSE);
  }

 if ( (Read(ufile,v3h,sizeof(*v3h)))!=sizeof(*v3h))
  {
   UnLockRecord(ufile,0,sizeof(*v3h));
   return(FALSE);
  }

 UnLockRecord(ufile,0,sizeof(*v3h));

 if ( (v3h->fileid[0]!='P') || (v3h->fileid[1]!='A') ||
      (v3h->fileid[2]!='C') || (v3h->fileid[3]!='K') ||
      (v3h->fileid[4]!='U') || (v3h->fileid[5]!='S') ||
      (v3h->fileid[6]!='E') || (v3h->fileid[7]!='R') )
  {
   return (FALSE);
  }

 if (v3h->fileversion!=FILEVERSION_CURRENT)
  {
   return(FALSE);
  }

 if ( (v3h->usedusers!=UserBaseUsers) ||
      (v3h->userspace!=UserBaseUsers) ||
      (v3h->usersize!=usiz) )
  {
   return(FALSE);
  }

 suser=QSO_GetDiskLocation("STANDARD");
 if (suser==-1)
  {
   return(FALSE);
  }

 QSO_ResetUser(UserBaseStandard);
 UserBaseStandard->DataBasePos=suser;
 if ( !QSO_LoadUser(UserBaseStandard,ufile,suser,sizeof(struct User)))
  {
   return(FALSE);
  }
 return(TRUE);
}

static int QSO_SaveIndex(void)
{
 BPTR ifile;
 struct version2indexhead v2ih;

 v2ih.fileid[0]='U';
 v2ih.fileid[1]='S';
 v2ih.fileid[2]='E';
 v2ih.fileid[3]='R';
 v2ih.fileid[4]='I';
 v2ih.fileid[5]='N';
 v2ih.fileid[6]='D';
 v2ih.fileid[7]='X';
 v2ih.fileversion=FILEVERSION_CURRENT;
 v2ih.indexentries=UserBaseUsers;

 if ( (ifile=Open("data/users.idx",MODE_NEWFILE))==NULL)
  {
   return (FALSE);
  }
 if ( (Write(ifile,&v2ih,sizeof(v2ih)))!=sizeof(v2ih))
  {
   Close(ifile);
   DeleteFile("data/users.idx");
   return(FALSE);
  }
 if ( (Write(ifile,UserBaseIndex,sizeof(struct version2index)*
                                 UserBaseUsers))!=
                                 sizeof(struct version2index)*
                                 UserBaseUsers)
  {
   Close(ifile);
   DeleteFile("data/users.idx");
   return(FALSE);
  }
 Close(ifile);
 return(TRUE);
}

static int QSO_LoadIndex(void)
{
 BPTR ifile;
 struct version2indexhead v2ih;
 if ( (ifile=Open("data/users.idx",MODE_OLDFILE))==NULL)
  {
   return (FALSE);
  }
 if ( (Read(ifile,&v2ih,sizeof(v2ih)))!=sizeof(v2ih))
  {
   Close(ifile);
   return (FALSE);
  }
 if ((v2ih.fileid[0]!='U') || (v2ih.fileid[1]!='S') ||
     (v2ih.fileid[2]!='E') || (v2ih.fileid[3]!='R') ||
     (v2ih.fileid[4]!='I') || (v2ih.fileid[5]!='N') ||
     (v2ih.fileid[6]!='D') || (v2ih.fileid[7]!='X') )
  {
   Close(ifile);
   return (FALSE);
  }
 if (v2ih.fileversion!=FILEVERSION_CURRENT)
  {
   Close(ifile);
   return (FALSE);
  }
 UserBaseIndexSize=v2ih.indexentries+(v2ih.indexentries>>4)+16;
 UserBaseUsers=v2ih.indexentries;
 if ( (UserBaseIndex=AllocVec(UserBaseIndexSize*
                              sizeof(struct version2index),MEMF_PUBLIC))
                              ==NULL)
  {
   Close(ifile);
   return (FALSE);
  }

 if (UserBaseUsers==0)
  {
   Close(ifile);
   return(TRUE);
  }
 if ( (Read(ifile,UserBaseIndex,sizeof(struct version2index)*
                                UserBaseUsers))!=
                                sizeof(struct version2index)*
                                UserBaseUsers)
  {
   FreeVec(UserBaseIndex);
   UserBaseIndex=NULL;
   Close(ifile);
   return (FALSE);
  }
 Close(ifile);
 return (TRUE);
}


static int QSO_SaveUser(struct User *suser)
{
 BPTR sfile;

 sfile=UserBaseFile;

 if (!QSO_StoreUser(suser,sfile,suser->DataBasePos))
  {
   return(FALSE);
  }
 suser->SaveBackNeeded=FALSE;
 return (TRUE);
}




int QSO_InitalizeUsers(void)
{

 UserBaseStandard=AllocVec(sizeof(struct User),MEMF_PUBLIC);
 if (UserBaseStandard==NULL)
   {
    GUI_HandleRequest(0,126,16,NULL);
    return(1);
   }
 QSO_ResetUser(UserBaseStandard);


 if ( (UserBaseFile=Open("data/users.dat",MODE_READWRITE))
         ==NULL)
  {
   GUI_HandleRequest(0,475,16,NULL);
   return(1);
  }


 if (!QSO_LoadIndex())
  {
   GUI_HandleRequest(0,475,16,NULL);
   return(1);
  }

 if (!QSO_LoadBase())
  {
   GUI_HandleRequest(0,475,16,NULL);
   return(1);
  }

 return(0);
}

void QSO_CleanupUsers(void)
{
 while (((struct User * )&UserBaseList)->nextuser->nextuser!=NULL)
  {
   struct User *ruser;
   ruser=((struct User *)&UserBaseList)->nextuser;
   Remove( (void *)ruser);
   if (ruser->SaveBackNeeded) QSO_SaveUser(ruser);
   FreeVec(ruser);
  }
 if (UserBaseStandard!=NULL)
  {
   FreeVec(UserBaseStandard);
   UserBaseStandard=NULL;
  }
 if (UserBaseIndex!=NULL)
  {
   FreeVec(UserBaseIndex);
   UserBaseIndex=NULL;
  }

 if (UserBaseFile!=NULL)
  {
   Close(UserBaseFile);
   UserBaseFile=NULL;
  }
}

void QSO_ChangeUser(struct User *cuser)
{
 cuser->SaveBackNeeded=TRUE;
}

int QSO_RemoveUser(struct User *ruser)
{
 char calli;
 signed long pxi;
 if (ruser==UserBaseStandard) return(1);
 if (ruser->OpenCnt!=1) return (1);
 calli=ruser->Call[0];
 ruser->Call[0]='\0';
 if (!QSO_SaveUser(ruser))
  {
   ruser->Call[0]=calli;
   return(1);
  }
 ruser->Call[0]=calli;
 pxi=QSO_UserParseIndex(ruser->Call);
 if (pxi!=-1)
  {
   (UserBaseIndex+pxi)->call[0]='\0';
   QSO_SaveIndex();
  }
 Remove((void *) ruser);
 FreeVec(ruser);
 return(0);
}

void QSO_CloseUser(struct User *cuser)
{
 cuser->OpenCnt--;
 if (cuser->OpenCnt>0) return;
 if (cuser->SaveBackNeeded)
  {
   if ( (QSO_SaveUser(cuser)) && (cuser!=UserBaseStandard) )
    {
     Remove((void *) cuser);
     FreeVec(cuser);
    }
  }
}






struct User *QSO_OpenStandardUser(void)
{
 UserBaseStandard->OpenCnt++;
 return (UserBaseStandard);
}

struct User *QSO_OpenUser(const unsigned char *call)
{
 return QSO_OpenUser_General(call,0);
}

struct User *QSO_OpenUser_SSID(const unsigned char *call)
{
 return QSO_OpenUser_General(call,1);
}

struct User *QSO_OpenUser_Try(const unsigned char *call)
{
 return QSO_OpenUser_General(call,2);
}

static struct User *QSO_OpenUser_General(const unsigned char *call,int mode)
{
 unsigned char withoutssid[16];
 unsigned char *yptr;
 const unsigned char *xptr;
 struct User *myuser;
 signed long dpos;

 xptr=call;
 yptr=withoutssid;
 do {
   *yptr=*(xptr++);
   if (*yptr=='-')
    {
     *yptr='\0';
     break;
    }
   yptr++;
    } while ( *(xptr-1)!='\0');

 if ((myuser=(void *)FindName(&UserBaseList,(unsigned char *) call))!=NULL)
  {
   myuser->OpenCnt++;
   return (myuser);
  }



 dpos=QSO_GetDiskLocation(call);
 if (dpos==-1)
  {
   if (mode!=1)
    {
     if ((myuser=(void *)FindName(&UserBaseList,withoutssid))!=NULL)
      {
       myuser->OpenCnt++;
       return(myuser);
      }
     dpos=QSO_GetDiskLocation(withoutssid);
    }
  }

 if ( (dpos==-1) && (mode==2) ) return(NULL);

 myuser=AllocVec(sizeof(struct User),MEMF_PUBLIC);
 if (myuser==NULL) return (NULL);

 QSO_PresetUser(myuser);

 if ( dpos==-1)
  {
   if (QSO_UserParseIndex("")==-1)
    {
     struct version2index *v2i;
     if (UserBaseIndexSize==UserBaseUsers)
      {
       void *ubi;
       unsigned long ubl;

       ubi=UserBaseIndex;
       ubl=UserBaseIndexSize;
       UserBaseIndex=NULL;



       if (!QSO_LoadIndex())
        {
         UserBaseIndex=ubi;
         UserBaseIndexSize=ubl;
         FreeVec(myuser);
         return(NULL);
        }
       FreeVec(ubi);
      }

     v2i=UserBaseIndex+UserBaseUsers;
     v2i->call[0]='\0';
     v2i->filepos=UserBaseUsers;
     UserBaseHead->userspace++;
     UserBaseHead->usedusers++;
     UserBaseUsers++;
     if (!QSO_SaveIndex())
      {
       FreeVec(myuser);
       return(NULL);
      }
     if (!QSO_SaveHeader())
      {
       FreeVec(myuser);
       return(NULL);
      }
    }

   dpos=QSO_GetDiskLocation("");
   if (dpos==-1)
    {
     FreeVec(myuser);
     return(NULL);
    }
    {
     signed long ipos;
     ipos=QSO_UserParseIndex("");
     if (ipos==-1)
      {
       FreeVec(myuser);
       return(NULL);
      }
     yptr=(UserBaseIndex+ipos)->call;
     xptr=(mode==1)?call:withoutssid;
     do { *(yptr++)=*(xptr++);} while (*(xptr-1)!='\0');
    }

   myuser->DataBasePos=dpos;
    {
     xptr=(mode!=1)?withoutssid:call;
     yptr=myuser->Call;
     do { *(yptr++)=*(xptr++); } while (*(xptr-1)!='\0');
    }

   AddTail(&UserBaseList,(void *) myuser);
   if (!QSO_SaveUser(myuser))
    {
     return(NULL);
    }
   if (!QSO_SaveIndex())
    {
     return(NULL);
    }
   myuser->OpenCnt++;
   return(myuser);

  }
  else
  {
   BPTR ufile;

   ufile=UserBaseFile;

   if (!QSO_LoadUser(myuser,ufile,dpos,sizeof(struct User)))
    {
     FreeVec(myuser);
     return(NULL);
    }

   myuser->DataBasePos=dpos;
   myuser->OpenCnt++;
   AddTail(&UserBaseList,(void *)myuser);
   return(myuser);
  }

 return (NULL);
}

unsigned char *USER_GetSysopName(void)
 {
  return(UserBaseHead->ownerfirstname);

 }

unsigned char *USER_GetSysopLocator(void)
 {
  return(UserBaseHead->ownerlocator);
 }

unsigned char *USER_GetSysopQTH(void)

 {
  return(UserBaseHead->ownerqth);
 }



#endif

