
/*
 * FAME WHO Door
 * ~~~~~~~~~~~~~
 *
 * $VER: FAMEWHO.c v1.2
 *
 * FAMEWHO.FIM is a replacement door for MainPart's internal WHO!
 *
 * Door coded by David 'Strider/tRSi' Wettig e-mail: strider@trsi.de
 *
 * Design and idea by sCANDIC/dCN.
 *
 * Code by: David 'Strider/tRSi' Wettig
 * E-Mail:  strider@trsi.de
 * URL:     http://www.trsi.de/inno/strider/index.html
 *
 * Used tab size: 2
 */

/*
 * Includes:
 */

#include <FAME/FAMEPublic.h>

/*
 * Disable Lattice CTRL-C handling:
 */

#ifdef LATTICE
int CXBRK(void) {return(0);}
int chkabort(void) {return(0);}
#endif

/*
 * Define our version string for the "version" command:
 */

char *VerStr={"$VER: FAMEWHO.FIM v1.2 "__AMIGADATE__};

/*
 * _ProgramName contains our filename:
 */

extern char	*_ProgramName;

/*
 * Prototypes:
 */

short int WHOAddList( struct FAMEInfoList *MyFAMEInfoList );
void StartIntWHO( void );
void ShutDown( long ErrCode );

/*
 * No CON window please, quit immediately.
 */

void __autoopenfail(void) { _XCEXIT(0);}

/*
 * Variables for ReadArgs and Template.
 */

char 	ArgStr[]		= "NODENR/N/A";
long	ArgArray[]	= {0L};

/* 
 * Structures:
 */

struct	WHOList {
struct	WHOList	*whol_Next;
char						whol_UserName[32],
								whol_Location[32],
								whol_SVAction[22],
								whol_FileUpload[32],
								whol_FileDownload[32],
								whol_XferProt[32];
long						whol_SVNodeNumber,
								whol_UploadCPS,
								whol_DownloadCPS,
								whol_BaudRate,
								whol_InitBaud;
};

/*
 * Global variables:
 */

struct	FAMELibrary		*FAMEBase			= NULL;
struct	Library				*UtilityBase	= NULL;
struct	WHOList				*FirstWHOList	= NULL,
											*LastWHOList	= NULL;
struct	FAMEInfoList	*TNFInfoList	= NULL;
short		int						ForceWhoFlag	= 0;
long									NodeNr;

char	WorkStr[256],
			DummyBuf[256],
			NodeNrBuf[12],
			BaudBuffer[22];

/*
 * void main( void );
 *
 * main() entry point:
 */

void main( void )
{
	struct RDArgs *rda = NULL;

	/*
	 * FAMESemaphore:
	 */

	struct  FAMESemaphore *MyFAMESemaphore;

	/*
	 * Use ReadArgs() for arguments.
	 */

	if(rda = ReadArgs(ArgStr,ArgArray,NULL))
	{
		/*
		 * Read the Node number:
		 */

		if(ArgArray[0])
		{
			long *LDummy = (long *)ArgArray[0];
			NodeNr = (*LDummy);
		}

		FreeArgs(rda);
	}
	else
	{
		/*
		 * Something failed with the argument, exit the door:
		 */

    Printf("\nSorry, %s is a Door and must be called from FAME BBS!\n\n",_ProgramName);

		SetIoErr( ERROR_REQUIRED_ARG_MISSING );
		PrintFault(IoErr(),"FAMEWHO.FIM");

    exit( RETURN_FAIL );
  }

	/*
	 * Now we have to open the FAME.library.
	 *
	 * From now on we have to call our ShutDown() function if something fails
	 * on our own resource initialisation where we have to release all our
	 * Door resources in ShutDown(). We are allowed to use ShutDown() just to
	 * the point where we call FIMStart(). From then we *HAVE* to call
	 * FIMEnd(0) to end the door!
	 *
	 * FIMEnd(0) will do our ShutDown() call then for us.
	 */

	if( ! (FAMEBase = (struct FAMELibrary *) OpenLibrary("FAME.library",0L)))
	{
		ShutDown( RETURN_FAIL );
	}

	/*
	 * Now we have to open the utility.library, needed for Stricmp:
	 */

	if( ! (UtilityBase = (struct Library *) OpenLibrary("utility.library",37L)))
	{
		ShutDown( RETURN_FAIL );
	}

	/*
	 * Ok time to continue with the Door initialisation code.
	 *
	 * Now we have to build the MessagePort name string of the Door we
	 * are called from:
	 */

  SPrintf(FAMEDoorPort,"FAMEDoorPort%ld",NodeNr);

	/*
	 * Now we start the Door initialising code.
	 *
	 * Here we can define the buffer size for the Global String Pointer
	 * if we need more than the default of 1024 bytes. If we call FIMEnd()
	 * with a value < 1024 bytes (FIM_MINBUFSIZE) like here (0) FIMStart()
	 * will use automatically the default value of FIM_MINBUFSIZE (1024 bytes).
	 *
	 * With the second argument we can set some flags we might need for our
	 * Door to tell the FIM DoorStartUp code how to handle some things we
	 * need. Following flags are available (bit-flags you can combine them by
	 * separating them with the OR sign: |
	 * i.e.: FIMF_USERSIGMODE | FIMF_NOCHECK.
	 *
	 * - FIMF_USERSIGMODE to enable the user signal check.
	 * - FIMF_NOCHECK to disable the error check of FIM commands send to
	 *   the door. Some doors might need to continue work also if there is
	 *   a problem with the port or a command like a lost carrier.
	 *   If FIMF_NOCHECK is set *YOU* have to check the return code of the
	 *   support commands you are using yourself!
	 *
	 * Example: (FIM_CMDSUCCESSFULL = 0)
	 *
	 * if(GetString(StrBuffer,50) < FIM_CMDSUCCESSFULL)
	 * {
	 *   ...
	 *
	 *   FIMEnd(0);
	 * }
	 *
	 *
	 * From now on you *HAVE* to call FIMEnd(0) to end the door!
	 * FIMEnd(0) calls the from the door supported function ShutDown() itself.
	 * This is also the reason why if FIMStart() fails no longer ShutDown()
	 * will be called from here. FIMStart() has already called it if something
	 * failed during the Door initialisation so if FIMStart() fails just
	 * exit() is needed here:
	 */

  if(FIMStart( 0, FIMF_USERSIGMODE ))
	{
		exit( RETURN_ERROR );
	}

  /*
	 * Place your own code in here:
	 */

	// ****************************************************************

	/*
	 * Version check:
	 */

	GetCommand(WorkStr,0,0,0,SR_FAMEVersion);

	if(MyFAMEDoorMsg -> fdom_Data2 < 1 || (MyFAMEDoorMsg -> fdom_Data2 == 1 && MyFAMEDoorMsg -> fdom_Data3 < 13))
	{
		PutString("\r\n[33mSoory[m, [33myou need at least MainPart v[33m1[m.[33m13 to use FAMEWHO.FIM v[33m1[m.[33m2 [m!\r\n\n[36mStarting internal WHO instead[m...",1);

		Delay(100);

		StartIntWHO();
	}

	/*
	 * Get some arguments:
	 */

	GetCommand(WorkStr,0,0,0,NR_GetArgument1);

	if( ! Stricmp(WorkStr,"FORCE"))
	{
		ForceWhoFlag = 1;
	}
	else if( ! Stricmp(WorkStr,"U"))
	{
		StartIntWHO();
	}

	GetCommand(WorkStr,0,0,0,NR_GetArgument2);

	if( ! Stricmp(WorkStr,"FORCE"))
	{
		ForceWhoFlag = 1;
	}
	else if( ! Stricmp(WorkStr,"U"))
	{
    StartIntWHO();
  }

	/*
	 * Try to find and obtain the FAMESemaphore:
	 * The name is always 'FAMESemaphore'.
	 */

	Forbid();

	if(MyFAMESemaphore = (struct FAMESemaphore *)FindSemaphore("FAMESemaphore"))
	{
		struct	FAMEInfoList	*MyFAMEInfoList;

		ObtainSemaphore((struct SignalSemaphore *)MyFAMESemaphore);
		Permit();

		/*
		 * Now let's try to get our own pointer:
		 */

		if(TNFInfoList = MyFAMESemaphore -> fsem_FirstNode)
		{
			long NodeCounter = 0;

			while(TNFInfoList && NodeCounter < NodeNr)
			{
				TNFInfoList = TNFInfoList -> fili_Next;

				NodeCounter ++;
			}

			/*
			 * Got our Node ?
			 */

			if(NodeCounter != NodeNr)
			{
				TNFInfoList = NULL;
			}
		}

		/*
		 * Now lets read it's data into our own list to lock the FAMESemaphore
		 * as short as possible:
		 */

		if(MyFAMEInfoList = MyFAMESemaphore -> fsem_FirstNode)
		{
			do
			{
				/*
				 * Now give the FAMEInfoList structure to our WHOAddList():
				 */

				if( ! WHOAddList(MyFAMEInfoList))
				{
					break;
				}
			}
			while(MyFAMEInfoList = MyFAMEInfoList -> fili_Next);
		}

		/* 
		 * We now can/should/must release the FAMESemaphore:
		 */

		ReleaseSemaphore((struct SignalSemaphore *)MyFAMESemaphore);

		/*
		 * Now let's do the WHO:
		 *
		 * Note that PutString uses AR_SendStr which will not use the
		 * struct FAMEDoorMsg fdom_IOString[202] !
		 * It will use the fdom_StringPtr and because of this we can have endless
		 * length strings here.
		 * Beware! Do not use strings longer than 201 bytes with PutStringFormat!
		 * PutStringFormat uses fdom_IOString[202] to format into it!
		 */

		PutString("\f\r\n[33m  Node  [mHandle[35m/[36mLocation        [mAction[35m([ms[35m)        [1m_[m                     Baud\r\n[1;34m.----------------------------.-----------------[35m\\[36m/[34m------------------.---------.",0);

		/*
		 * We need LastWHOList, because it's no longer needed.
		 * Beware! If you must call WHOAddList later again use a new temporäry
		 * struct WHOList pointer instead!!!
		 */

		LastWHOList = FirstWHOList;

		while(LastWHOList)
		{
			/* 
			 * Upload instead action?
			 */

			SPrintf(NodeNrBuf,"%ld",LastWHOList -> whol_SVNodeNumber);

			if(strlen(NodeNrBuf) < 2)
			{
				SPrintf(NodeNrBuf,"0%ld",LastWHOList -> whol_SVNodeNumber);
			}

			FAMENumToStr(LastWHOList -> whol_InitBaud,FNSF_GROUPING|FNSF_NUMLOCALE,0,WorkStr);
			FAMENumToStr(LastWHOList -> whol_BaudRate,FNSF_GROUPING|FNSF_NUMLOCALE,0,BaudBuffer);

			if(LastWHOList -> whol_FileUpload[0] || LastWHOList -> whol_FileDownload[0])
			{
				if(LastWHOList -> whol_FileUpload[0])
				{
					PutStringFormat("\r\n| [0;33m %-4s [m%-20s [1;34m| [m%s %-16s  [32m%5ld cps [1;34m| [m%7s [1;34m|",NodeNrBuf,LastWHOList -> whol_UserName,LastWHOList -> whol_XferProt,LastWHOList -> whol_FileUpload,LastWHOList -> whol_UploadCPS,WorkStr);

					if(LastWHOList -> whol_FileDownload[0])
					{
						PutStringFormat("\r\n| [0;36m%-26s [1;34m| [m%s %-16s  [32m%5ld cps [1;34m| [m%7s [1;34m|",LastWHOList -> whol_Location,LastWHOList -> whol_XferProt,LastWHOList -> whol_FileDownload,LastWHOList -> whol_DownloadCPS,BaudBuffer);
					}
					else
					{
						PutStringFormat("\r\n| [0;36m%-26s [1;34m|[37C| [m%7s [1;34m|",LastWHOList -> whol_Location,BaudBuffer);
					}
				}
				else 
				{
					PutStringFormat("\r\n| [0;33m %-4s [m%-20s [1;34m| [m%s %-16s  [32m%5ld cps [1;34m| [m%7s [1;34m|",NodeNrBuf,LastWHOList -> whol_UserName,LastWHOList -> whol_XferProt,LastWHOList -> whol_FileDownload,LastWHOList -> whol_DownloadCPS,WorkStr);
					PutStringFormat("\r\n| [0;36m%-26s [1;34m|[37C| [m%7s [1;34m|",LastWHOList -> whol_Location,BaudBuffer);
				}
			}
			else
			{
				PutStringFormat("\r\n| [0;33m %-4s [m%-20s [1;34m| [m%-35s [1;34m| [m%7s [1;34m|",NodeNrBuf,LastWHOList -> whol_UserName,LastWHOList -> whol_SVAction,WorkStr);
				PutStringFormat("\r\n| [0;36m%-26s [1;34m|[37C| [m%7s [1;34m|",LastWHOList -> whol_Location,BaudBuffer);
			}

			/*
			 * Ok, next one:
			 */

			if(LastWHOList = LastWHOList -> whol_Next)
			{
				PutString("\r\n|----------------------------+-------------------------------------+---------|",0);
			}
		}

		PutString("\r\n`----------------------------·-----------------------------[35m\\[36m/[34m------·---------'[m\r\n\n",0);

	}
	else
	{
		Permit();
	}

	// ****************************************************************

	/*
	 * End the Door:
	 */

	FIMEnd(0);

} // void main( void );

/*
 * short int WHOAddList( struct FAMEInfoList *MyFAMEInfoList );
 *
 * Generate our list:
 */

short int WHOAddList( struct FAMEInfoList *MyFAMEInfoList )
{
	if(MyFAMEInfoList)
	{
	  if( ! FirstWHOList)
		{
			if( ! (FirstWHOList = AllocVec(sizeof(struct WHOList),MEMF_PUBLIC | MEMF_CLEAR)))
			{
				return(FALSE);
			}

			LastWHOList = FirstWHOList;
		}
		else
		{
			if( ! (LastWHOList -> whol_Next = AllocVec(sizeof(struct WHOList),MEMF_PUBLIC | MEMF_CLEAR)))
			{
				return(FALSE);
			}

			LastWHOList = LastWHOList -> whol_Next;
		}

		/*
		 * Node must be online, else some pointers are invalid!!!
		 */

		if(MyFAMEInfoList -> fili_SVNodeOnline)
		{
			/*
			 * User online?
			 */

			if(MyFAMEInfoList -> fili_UserOnline == 2)	// Online
			{
				/* 
				 * Transfer in progress?
				 */

				/*
				 * Bi-directional Hydra in usage?
				 */

				if(MyFAMEInfoList -> fili_SVActionCmd == FAME_HYDRA)
				{
					Forbid();
					FAMEStrCopy(MyFAMEInfoList -> fili_FileUpload,WorkStr,12);
					Permit();
					strcpy(LastWHOList -> whol_FileUpload,"UL: ");
					strcat(LastWHOList -> whol_FileUpload,WorkStr);

					Forbid();
					FAMEStrCopy(MyFAMEInfoList -> fili_FileDownload,WorkStr,12);
					Permit();
					strcpy(LastWHOList -> whol_FileDownload,"DL: ");
					strcat(LastWHOList -> whol_FileDownload,WorkStr);

					strcpy(LastWHOList -> whol_XferProt,"  HYDRA");

					/* 
					 * Note the (* ) !!! We need this, because fili_UploadCPS is a pointer!
					 */

					LastWHOList -> whol_UploadCPS			= (*MyFAMEInfoList -> fili_UploadCPS);

					/*
					 * Note the (* ) !!! We need this, because fili_DownloadCPS  is a pointer!
					 */

					LastWHOList -> whol_DownloadCPS		= (*MyFAMEInfoList -> fili_DownloadCPS);

				}
				else if(MyFAMEInfoList -> fili_SVActionCmd == FAME_UPLOAD || MyFAMEInfoList -> fili_SVActionCmd == FAME_DOWNLOAD)
				{
					/* 
					 * We have a uni-directional xpr transfer:
					 */

					char *Temp;

					if(MyFAMEInfoList -> fili_SVActionCmd == FAME_UPLOAD)
					{
						Forbid();
						FAMEStrCopy(MyFAMEInfoList -> fili_FileUpload,WorkStr,12);
						Permit();
						strcpy(LastWHOList -> whol_FileUpload,"UL: ");
						strcat(LastWHOList -> whol_FileUpload,WorkStr);

						/* 
						 * Note the (* ) !!! We need this, because fili_UploadCPS is a pointer!
						 */

						LastWHOList -> whol_UploadCPS			= (*MyFAMEInfoList -> fili_UploadCPS);
					}
					else 
					{
						Forbid();
						FAMEStrCopy(MyFAMEInfoList -> fili_FileDownload,WorkStr,12);
						Permit();
						strcpy(LastWHOList -> whol_FileDownload,"DL: ");
						strcat(LastWHOList -> whol_FileDownload,WorkStr);

						/* 
						 * Note the (* ) !!! We need this, because fili_DownloadCPS  is a pointer!
						 */

						LastWHOList -> whol_DownloadCPS		= (*MyFAMEInfoList -> fili_DownloadCPS);
					}

					/* 
					 * Cut the .library and uppercase:
					 */

					Forbid();
					strcpy(WorkStr,MyFAMEInfoList -> fili_XferProt);
					Permit();

					if(Temp = FAMEStrChrCase(WorkStr,'.'))
					{
						*Temp = '\0';
					}

					FAMEStrCopy(strupr(WorkStr),LastWHOList -> whol_XferProt,7);
				}

				Forbid();
				strcpy(LastWHOList -> whol_UserName,MyFAMEInfoList -> fili_NodeRUser -> UserName);
				strcpy(LastWHOList -> whol_Location,MyFAMEInfoList -> fili_NodeRUser -> UserLocation);
				strcpy(LastWHOList -> whol_SVAction,MyFAMEInfoList -> fili_SVAction);
				Permit();

				LastWHOList -> whol_UserName[20] = '\0';
				LastWHOList -> whol_Location[26] = '\0';

				/*
				 * Note the (* ) !!! We need this, because fili_BaudRate is a pointer!
				 */

				LastWHOList -> whol_BaudRate			= (*MyFAMEInfoList -> fili_BaudRate);

			}
			else 
			{
				/* 
				 * Else Awaiting Call or Connecting, we left whol_FileUpload,
				 * whol_FileDownload and whol_XferProt blank.
				 * They are cleared from allocation with MEMF_CLEAR.
				 */

				strcpy(LastWHOList -> whol_UserName,"Not available");
				strcpy(LastWHOList -> whol_Location,"Not available");

				if(MyFAMEInfoList -> fili_UserOnline == 1)
				{
					strcpy(LastWHOList -> whol_SVAction,"Connecting");
				}
				else if(MyFAMEInfoList -> fili_UserOnline == 3)
				{
					strcpy(LastWHOList -> whol_SVAction,"Reseting Node");
				}
				else
				{
					strcpy(LastWHOList -> whol_SVAction,"Node awaits a Call");
				}
			}

			/*
			 * Note the (* ) !!! We need this, because fili_InitBaud is a pointer!
			 */

			LastWHOList -> whol_InitBaud			= (*MyFAMEInfoList -> fili_InitBaud);

			/*
			 * Check for ForceWHO:
			 */

			if(TNFInfoList)
			{
				if((TNFInfoList -> fili_BitFlags & FILI_FORCEWHO) && (ForceWhoFlag || (TNFInfoList -> fili_NodeRUser -> UserFlags1 & UD_AUTOFORCEONWHO)))
				{
					ForceWhoFlag = 1;
				}
				else
				{
					ForceWhoFlag = 0;
				}
			} 
			else
			{
				ForceWhoFlag = 0;
			}

			if( ! ForceWhoFlag)
			{
				/* 
				 * Check for (real) hidden user:
				 */

				if(((MyFAMEInfoList -> fili_BitFlags & FILI_QUIETNODE) || (MyFAMEInfoList -> fili_BitFlags & FILI_USERHIDE)) && Stricmp(MyFAMEInfoList -> fili_NodeRUser -> UserName,"-"))
				{
					if(MyFAMEInfoList -> fili_BitFlags & FILI_USERREALHIDE)
					{
						strcpy(LastWHOList -> whol_UserName,"Not available");
						strcpy(LastWHOList -> whol_Location,"Not available");
						strcpy(LastWHOList -> whol_SVAction,"Node awaits a Call");
						LastWHOList -> whol_BaudRate = 0;
					}
					else
					{
						strcpy(LastWHOList -> whol_UserName,"Hidden");
						strcpy(LastWHOList -> whol_Location,"Do not disturb!");
					}
				}
			}
		}
		else
		{
			/*
			 * Node not online:
			 */

			strcpy(LastWHOList -> whol_UserName,"Not available");
			strcpy(LastWHOList -> whol_Location,"Not available");
			strcpy(LastWHOList -> whol_SVAction,"Node not online");
		}

		/*
		 * fili_SVNodeNumber will be in every case available.
		 */

		LastWHOList -> whol_SVNodeNumber	= MyFAMEInfoList -> fili_SVNodeNumber;

	  return(TRUE);
	}
	return(FALSE);

} // short int WHOAddList( struct FAMEInfoList *MyFAMEInfoList );

/*
 * void StartIntWHO( void );
 *
 * Start internal WHO:
 */

void StartIntWHO( void )
{
	GetCommand(DummyBuf,0,0,0,NR_GetFullArg);

	SPrintf(WorkStr,"WHO %s",DummyBuf);

	PutCommand(WorkStr,0,0,0,CF_InternalCmd);

	PutString("",1);

	/*
	 * End the Door:
	 */

	FIMEnd(0);

} // void StartIntWHO( void );

/*
 * void ShutDown( long ErrCode );
 *
 * This function has to be supplied by the door and will be called if any
 * error occures. It's up to the door to exit or continue the work, but
 * remember that the DoorPort and therefor the communication to FAME is
 * CLOSED ! The errcode contains the errorcode supplied by FAME, you may
 * use it to your own requirements.
 */

void ShutDown( long ErrCode )
{
	/* Free our list:
	 * Wee used LastWHOList also, because it's no longer used here.
	 */

	while(FirstWHOList)
	{
		LastWHOList = FirstWHOList -> whol_Next;
		FreeVec(FirstWHOList);
		FirstWHOList = LastWHOList;
	}

	/*
	 * Now close the utility.library.
	 *
	 * First check if the resource is really allocated:
	 */

	if(UtilityBase)
	{
		CloseLibrary((struct Library *)UtilityBase);
	}

	/*
	 * To NULL our pointers after freeing the resource of it is *VERY*
	 * important to avoid problems if ShutDown() gets called more the one time!
	 */

	UtilityBase	= NULL;

	/*
	 * Now close the FAME.library.
	 *
	 * First check if the resource is really allocated:
	 */

	if(FAMEBase)
	{
		CloseLibrary((struct Library *)FAMEBase);
	}

	/*
	 * To NULL our pointers after freeing the resource of it is *VERY*
	 * important to avoid problems if ShutDown() gets called more the one time!
	 */

	FAMEBase = NULL;

	/*
	 * Now do some standard AmigaOS error handling if needed:
	 */

	if(ErrCode < RETURN_OK )
	{
		SetIoErr( ERROR_OBJECT_NOT_FOUND );
		PrintFault(IoErr(),"FAMEWHO.FIM");

		ErrCode = RETURN_ERROR;
	}

	/*
	 * Exit the Door with a standard and defined AmigaOS exit code:
	 */

	exit( ErrCode );

} // void ShutDown( long ErrCode );

/*
 * void UserSigHandle( ULONG UserSigs );
 *
 * This function has to be supplied by the door and will be called if any
 * user signal occures. User signals will be set via FIMHandlePort() in the
 * second argument "ULONG UserSigs". Use NULL here if you don't have any
 * signals to be supported. UserSigHandle() will be called if any user
 * signals are set and if FIMHandlePort() receives them. In this case
 * check the argument "UserSigs" in your UserSigHandle() function.
 *
 * You can also use the global variable "GlobUserSigs" to inform
 * FIMHandlePort() about your own additional signal bits.
 * In this case FIMHandlePort()'s second argument "ULONG UserSigs" has to
 * be always NULL, because this argument has the higher priority and
 * "GlobUserSigs" will not be used then.
 * "GlobUserSigs" is to handle your own signal bits more easy.
 * All support functions will pass NULL to FIMHandlePort() so usage of
 * "GlobUserSigs" is possible without doing any changes to the doorstartup
 * code.
 *
 *
 * FAMEMsgHeader needs no user signals so UserSigHandle() will just return.
 *
 * It's only implemented for FAMEDoorStartUp codes compiled with the
 * FIM_USERSIGMODE define.
 */

void UserSigHandle( ULONG UserSigs )
{
	// Do nothing - just return.

} // void UserSigHandle( ULONG UserSigs );

