/************************************************************************/
/*                   DUPESCAN © 1993 Metal Software                     */
/*                   1996-1998 by Kelly Cochran                         */
/************************************************************************/

void  GetOut( void );
BOOL  IsAbort( void );
void  Notify( void );
void  LogToFile( void );
void  CheckNew( void );
void  FirstRun( void );
BOOL  CheckLog( void );
void  ReadDSLog( void );
APTR  save_user_info(struct UserData *info);
void  deallocate_users( void );
void  dupescan_main( void );
void  version( void );

UBYTE *vertag = "\0$VER: DupeScan 2.04 "__AMIGADATE__;

struct MsgPort      *replyp;
struct CPort        *cport;
struct CMessage     cmess;
struct MainPort     *myp;
struct PortData     *z;
struct Library      *CNetBase = NULL;
struct Library      *CNetCBase = NULL;
struct CNetCContext *context = NULL;

struct UsersChecked
{
   int  Account;
   LONG serialID;
   char Handle  [ 21 ];
};

struct UserStuff
{
   short	account;
   short Access;
   LONG	IDNumber;
   char	Handle    [ 21 ];
   char	RealName  [ 26 ];
   char	Address   [ 31 ];
   char	CityState [ 31 ];
   char	ZipCode   [ 11 ];
   char	PhoneNo   [ 17 ];
   char  VoiceNo   [ 17 ];
   struct IsDate Birthdate;
   struct IsDate LastCall;
   struct UserStuff *next;
};

struct UserStuff *First_User     = NULL;
struct UserStuff *UserPointer    = NULL;
struct UserStuff *OldPointer     = NULL;
struct UserStuff *search_pointer = NULL;

struct UsersChecked Checked;

BOOL  duplicate,
      CheckFlag;

BOOL  more_users   = TRUE,
      FirstAccount = TRUE;

short CurrentNum = 0;
int   ramenable  = 0;

int   count,
      err,
      key,
      location,
      search,
      totalaccounts;

char **bm;

char DupeType [ 20 ],
     userfile [ 40 ];
 
char  *logfile   = "SysData:Log/DupeScan";
char  *udatafile = "SysData:bbs.udata4";
char  *cfgfile   = "SysData:DupeScan.config";

/*** End of your GLOBAL variables   ***/

void main( int argc, char **argv )   
{
   if( argc<2 || !(cport = (struct CPort *)FindPort( argv[1] )) )
   {
      Printf("This is a CNet C program.\n");
      exit(0);
   }

   if(!(CNetCBase = OpenLibrary("cnetc.library", 4L)))
   {
      Printf("Unable to open cnetc.library!\n");
      exit(0);
   }

   if(!(context = CNC_InitContext(argv[1])))
   {
      exit(0);
   }

   z   = context->z;
   myp = context->myp;

   if( !(CNetBase = OpenLibrary( "cnet.library", 4 )) )
      GetOut();

   dupescan_main();

   GetOut();
}

void dupescan_main(void)
{
   BPTR  fp;

	totalaccounts = myp->Nums[0];

	if(z->id == 1)    /* if maint NO LOG CHECK, only account 1! */
	{
   	if(CheckLog())
		{
			if(CNC_PutQ("c1Entries in logc4; c3Read now [Yes]?q1 ?1"))
			{
            CNC_DoReturn();
			   ReadDSLog();
			}
		}
		sprintf(z->ABuffer, "\nc7%ld c4accounts total; c7%ld c4active accountsq1\n", totalaccounts, myp->Nums[1]);
		CNC_PutA();
		if(!(CNC_PutQ("c3Process (SCAN) User List now [Yes]?q1 ?1")))
			GetOut();
      CNC_DoReturn();
   }

	/* read config and, if enabled, do processing in ram */
   if(fp = Open(cfgfile, MODE_OLDFILE))
	{
		ramenable = FGetC(fp) - '0';
		Close(fp);
	}
	else
   {
      sprintf(z->ABuffer,"Config file: %s not found!  Not using RAM: for processing!n1",cfgfile);
      CNC_PutA();
      ramenable = FALSE;
   }

	if(ramenable)
	{
		CNC_PutText("c1Ram access enabled c4- c3Copying vital data to RAM:..q1");
		SystemTags("Copy SysData:DupeCheck.UData RAM:", TAG_END);
		strcpy(userfile, "RAM:DupeCheck.UData");
      CNC_DoReturn();
	}
	else
		strcpy(userfile, "SysData:DupeCheck.Udata");

	if(!FileExists(udatafile))
	{
		sprintf(z->ABuffer, "Could not open %s for reading!\n\n", udatafile);
		CNC_PutA();
		GetOut();
	}

	/**************** allocate all users ***************/
	if(fp = Open(udatafile, MODE_OLDFILE))
	{
		CNC_PutText("@4\nAllocating Users...");
		while((Read(fp,(char *)&z->user2, sizeof(struct UserData)) > 0) && (more_users == TRUE))
		{
			CurrentNum++;
			if(*z->user2.Handle != '!')
			{
				if(FirstAccount == TRUE)
				{
					First_User   = (struct UserStuff *) save_user_info(&z->user2);
					FirstAccount = FALSE;
				}
				else
					UserPointer = (struct UserStuff *) save_user_info(&z->user2);
			}
         if(CurrentNum > totalaccounts)
            more_users = FALSE;
		}
		OldPointer->next = NULL;
   	Close(fp);
	}
	else
		GetOut();

	/******************* end of user allocations ******************/


	if (!FileExists(userfile))
	{
		FirstRun();
		deallocate_users();
		if(ramenable)
		{
			SystemTags("Copy RAM:DupeCheck.UData SysData:", TAG_END);
			DeleteFile("RAM:DupeCheck.UData");
		}
		CNC_PutText("c2DupeScan Complete!q1\n");
		GetOut();
	}

	CheckNew();
	deallocate_users();
	if(ramenable)
	{
		SystemTags("Copy RAM:DupeCheck.UData SysData:", TAG_END);
		DeleteFile("RAM:DupeCheck.UData");
	}
	CNC_PutText("c2DupeScan Complete!q1\n");
}

void GetOut( void )              /* exit pfile with no spawn */
{
	if(FileExists("RAM:DupeCheck.UData"))
		DeleteFile("RAM:DupeCheck.UData");

   if(CNetCBase)
   {
   	CNC_ShutDown( NULL );
      CNC_DisposeContext();
      CloseLibrary(CNetCBase);
   }

   if(CNetBase)
      CloseLibrary(CNetBase);

	exit(0);
}

void Notify( void )
{
   BPTR  fp;

	char TempDate [ 2 ][ 30 ];

   if(!(fp = OpenAppend(logfile)))
	{
		sprintf(z->ABuffer, "Err %ld: Could not open %s\n", IoErr(), logfile);
		CNC_PutA();
      GetOut();
	}

   sprintf(z->ABuffer, "Dupe w / account [#%4ld]: Handle: %-21s Type: %-13s!\n", search_pointer->account, search_pointer->Handle, DupeType);
	CNC_PutA();
	FPrintf(fp, "\nc1Possible Duplicate account:  Type: %s\n", DupeType);
	FPrintf(fp, "c6Account  :c3 %-30ld %-30ld\n", UserPointer->account, search_pointer->account);
   FPrintf(fp, "c6Access   :c3 %-15.15s [%2ld]           %-15.15s [%2ld]\n", myp->AGC[UserPointer->Access].Name, UserPointer->Access, myp->AGC[search_pointer->Access].Name, search_pointer->Access);
	FPrintf(fp, "c6Handle   :c3 %-30.30s %-30.30s\n", UserPointer->Handle, search_pointer->Handle);
	FPrintf(fp, "c6Name     :c3 %-30.30s %-30.30s\n", UserPointer->RealName, search_pointer->RealName);
	FPrintf(fp, "c6Address  :c3 %-30.30s %-30.30s\n", UserPointer->Address, search_pointer->Address);
   FPrintf(fp, "c6Voice Ph#:c3 %-30.30s %-30.30s\n", UserPointer->VoiceNo, search_pointer->VoiceNo);
	FPrintf(fp, "c6Data Ph# :c3 %-30.30s %-30.30s\n", UserPointer->PhoneNo, search_pointer->PhoneNo);
	FPrintf(fp, "c6City     :c3 %-30.30s %-30.30s\n", UserPointer->CityState, search_pointer->CityState);
	CNC_MakeDate(&UserPointer->LastCall, TempDate[0]);
	CNC_MakeDate(&search_pointer->LastCall, TempDate[1]);
	FPrintf(fp, "c6LastCall :c3 %-30.30s %-30.30s\n", TempDate[0], TempDate[1]);
   CNC_MakeDate(&UserPointer->Birthdate, TempDate[0]);
   CNC_MakeDate(&search_pointer->Birthdate, TempDate[1]);
   FPrintf(fp, "c6BirthDate:c3 %-30.30s %-30.30s\n", TempDate[0], TempDate[1]);
	Close(fp);

	return;
}

void LogToFile( void )
{
   BPTR  fp;

   if(fp = OpenAppend(userfile))
   {
      Write(fp,(char *)&Checked,sizeof(struct UsersChecked));
   	Close(fp);
   }
}

void FirstRun( void )
{
   version();

	for (UserPointer = First_User;UserPointer != NULL;UserPointer=UserPointer->next)		/* start at accnt 1 */
	{
		sprintf(z->ABuffer,"c3Checking account [#%4ld]: Handle: c1%-21sq1\n",UserPointer->account,UserPointer->Handle);
		CNC_PutA();

		if(!IsAbort())
         return;

		strcpy(Checked.Handle, UserPointer->Handle);	/* assign check to structure */

		Checked.serialID = UserPointer->IDNumber;
		Checked.Account  = UserPointer->account;					/*       end of assigns      */

		for (search_pointer = UserPointer->next;search_pointer != NULL;search_pointer=search_pointer->next)
		{
			duplicate = FALSE;
			if(search_pointer != UserPointer)
			{
				if( (!strcmp(UserPointer->RealName, search_pointer->RealName)) && (!duplicate) )
				{
					/* duplicate real name */
					strcpy(DupeType, "Real Name");
					duplicate = TRUE;
					Notify();
				}
				if( (!strcmp(UserPointer->Address, search_pointer->Address)) && (!duplicate) )
				{
					/* duplicate address */
					strcpy(DupeType, "Address");
					duplicate = TRUE;
					Notify();
				}
				if( (!strcmp(UserPointer->PhoneNo, search_pointer->PhoneNo)) && (!duplicate) )
				{
					/* duplicate phone number */
					strcpy(DupeType, "Data Phone");
					duplicate = TRUE;
					Notify();
				}
            if((!strcmp(UserPointer->VoiceNo, search_pointer->VoiceNo)) && (!duplicate))
            {
               /* dupe data phone */
               strcpy(DupeType, "Voice Phone");
               duplicate = TRUE;
               Notify();
            }
            if((!strcmp(UserPointer->VoiceNo, search_pointer->PhoneNo)) && (!duplicate))
            {
               /* dupe voice -> data phone */
               strcpy(DupeType, "Voice-Data");
               duplicate = TRUE;
               Notify();
            }
            if((!strcmp(UserPointer->PhoneNo, search_pointer->VoiceNo)) && (!duplicate))
            {
               /* dupe data -> voice phone */
               strcpy(DupeType, "Data-Voice");
               duplicate = TRUE;
               Notify();
            }
			}
		}
		LogToFile();
	}
	return;
}

void CheckNew( void )
{
   BPTR  fp;

	err = FALSE;
   version();
	CNC_PutText("c7Scanning for New/Unchecked users..q1\n");
	for (UserPointer=First_User;UserPointer != NULL;UserPointer=UserPointer->next)		/* start at accnt 1 */
	{
		if (!FileExists(userfile))
		{
			err = TRUE;	
			return;
		}
		CheckFlag = TRUE;
		if(fp = Open(userfile, MODE_OLDFILE))
		{
			while(!(Read(fp,&Checked,sizeof(struct UsersChecked))))
			{
				if (Checked.serialID == UserPointer->IDNumber)
					CheckFlag = FALSE;
			}
			Close(fp);
		}
		
		if (CheckFlag)
		{
			sprintf(z->ABuffer, "c6   %s has been checked already..q1\n", UserPointer->Handle);
			CNC_PutA();
			if(!IsAbort())
            return;
		}
      else
		{
			/* compare UserPointer with all current users(search_user) */
			sprintf(z->ABuffer,"c3Checking account [#c1%4ldc3]: Handle: %-21sq1\n", UserPointer->account, UserPointer->Handle);
			CNC_PutA();
			strcpy(Checked.Handle, UserPointer->Handle);	/* assign user to structure */
			Checked.serialID = UserPointer->IDNumber;
			Checked.Account = UserPointer->account;					/*       end of assigns      */
			if(!IsAbort())
            return;
			for (search_pointer = First_User;search_pointer != NULL;search_pointer=search_pointer->next)
			{
            /* THE ACTUAL COMPARISON */
				duplicate = FALSE;
				if(search_pointer != UserPointer)
				{
					if( (!strcmp(UserPointer->RealName, search_pointer->RealName)) && (!duplicate) )
					{
						/* duplicate real name */
						strcpy(DupeType, "Real Name");
						duplicate = TRUE;
						Notify();
					}
					if( (!strcmp(UserPointer->Address, search_pointer->Address)) && (!duplicate) )
					{
						/* duplicate address */
						strcpy(DupeType, "Address");
						duplicate = TRUE;
						Notify();
					}
					if( (!strcmp(UserPointer->PhoneNo, search_pointer->PhoneNo)) && (!duplicate) )
					{
			   		/* duplicate phone number */
						strcpy(DupeType, "Data Phone");
						duplicate = TRUE;
						Notify();
					}
               if((!strcmp(UserPointer->VoiceNo, search_pointer->VoiceNo)) && (!duplicate))
               {
                  /* dupe data phone */
                  strcpy(DupeType, "Voice Phone");
                  duplicate = TRUE;
                  Notify();
               }
               if((!strcmp(UserPointer->VoiceNo, search_pointer->PhoneNo)) && (!duplicate))
               {
                  /* dupe voice -> data phone */
                  strcpy(DupeType, "Voice-Data");
                  duplicate = TRUE;
                  Notify();
               }
               if((!strcmp(UserPointer->PhoneNo, search_pointer->VoiceNo)) && (!duplicate))
               {
                  /* dupe data -> voice phone */
                  strcpy(DupeType, "Data-Voice");
                  duplicate = TRUE;
                  Notify();
               }
				}
			}
		   LogToFile();
		}
	}
	return;
}

BOOL CheckLog( void )
{
   CNC_DoReturn();
	if(FileExists(logfile))
	{
		CNC_PutText("c1Log contains possible Duplicate accounts!q1n2");
		return TRUE;
	}
	CNC_PutText("\nc7DupeScan Log Empty!q1n1");
	return FALSE;
}

void ReadDSLog( void )
{
	if(CNC_ReadFile(logfile, TRUE))
   {   
   	if(CNC_PutQ(bm[188]))
	   {
		   if(!DeleteFile(logfile))
			{
			   sprintf(z->ABuffer, "c1Error deleting file 'c7%sc1'q1\n", logfile);
			   CNC_PutA();
			}
		}
   }
}


/* Allocates memory for the new user, and fills the structure		*/
/* with information.  Returns a memory pointer to the allocated	*/
/* "UserStuff" structure, or NULL.											*/
APTR save_user_info(struct UserData *info)
{
	struct UserStuff *pointer;

	if((pointer = (struct UserStuff *)AllocMem(sizeof(struct UserStuff), MEMF_CLEAR))==NULL)
	{
		/* We could NOT allocate memory for the structure! */
		CNC_PutText("NOT enough memory to allocate all users!n2"); /* Inform the user. */
		more_users = FALSE; /* Do not read any more users. */
		return(NULL);
	}
	else
	{
		if(OldPointer)
			OldPointer->next = pointer;
		pointer->account  = CurrentNum;
      pointer->Access   = info->Access;
		pointer->IDNumber = info->IDNumber;

		CopyMem(&info->LastCall, &pointer->LastCall, sizeof(struct IsDate));
      CopyMem(&info->Birthdate, &pointer->Birthdate, sizeof(struct IsDate));

		strcpy(pointer->Handle,    info->Handle);
		strcpy(pointer->RealName,  info->RealName);
		strcpy(pointer->Address,   info->Address);
		strcpy(pointer->CityState, info->CityState);
		strcpy(pointer->ZipCode,   info->ZipCode);
		strcpy(pointer->PhoneNo,   info->PhoneNo);
      strcpy(pointer->VoiceNo,   info->VoiceNo);
		OldPointer = pointer;
	}
  
	/* Return the address of the allocated structure: */
	return( (APTR) pointer);
}

   /* Deallocate the memory we have dynamically allocated for users: */ 
void deallocate_users( void )
{
	struct UserStuff *pointer, *temp_pointer;

	CNC_PutText("Deallocating users...");

	/* Does the first pointer point to an allocated structure? */   
	if(First_User)
	{
		/* Save the address of the next structure: */
		pointer=First_User->next;
    
		/* Deallocate the first structure: */
		FreeMem( First_User, sizeof(struct UserStuff));

		/* As long as pointer points to an allocated structure: */
		while(pointer)
		{
			/* Save the address of the next structure: */    
			temp_pointer = pointer->next;
			FreeMem( pointer, sizeof(struct UserStuff));
			pointer = temp_pointer;
		}
	}
  
	/* Clear first_pointer: */
	First_User = NULL;

	/* Next time we try to list the files, we start with the first_file: */
	FirstAccount = TRUE;
   CNC_DoReturn();
}

void version(void)
{
	CNC_PutText("\nc3DupeScan v2.03 by Kelly Cochran (Trolan).\n");
   CNC_PutText("Originally by Ray Akey (Heavy Metal).\n");
   CNC_PutText("c2Press '/' or [SPACE] at any time to abort processingq1\n\n");
}

BOOL IsAbort( void )
{
	if (z->PrintFlags == 1)
	{
		CNC_PutText(bm[1065]);
		return FALSE;
	}
	if (z->PrintFlags > 1)
	{
		CNC_PutText(bm[1064]);
	   return FALSE;
	}
	return TRUE;
}
