/*
	PalNFO
	Copyright (C) 2005-2006 Paul Taylor

	PalNFO 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, or (at your option) any later
	version.

	The PalNFO plugin and plugin source 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 be able to get a copy of the GNU General Public License at:
	*  http://www.gnu.org/copyleft/gpl.html
	Or, if these sites do not work, write to the Free Software Foundation,
	59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

#ifndef __PALNFO_C__
#define __PALNFO_C__

#include "palnfo.h"

  AnyType initializeServerPlugin( PluginID pluginID, int argc, char *argv[ ] )
{
	sCfg.uptime = time( NULL );
	sCfg.cfg = ( ServerConfiguration * ) malloc( sizeof( ServerConfiguration ) );
	sCfg.pthrd = ( PThrd * ) malloc( sizeof( PThrd ) );
	LoadConfig( NULL, ( argc > 1 ? argv[ 1 ] : NULL ) );
	scheduleTimerEvent( pluginID, 0, 0 );
	FormattedLogMessage( NULL, INT_PLGNAME, "v%d.%d Build %d %s", vMajor, vMinor, vBuild, INT_PLGCOPY );
	pluginRegisterCommand( "palnfo/", "<argument>", 1, 2, &PalNFO, 0, "Arguments: about, reload <file>." );
	return ( AnyType ) pluginID;
}

  void shutdownServerPlugin( ServerState *state, AnyType pluginDat )
{
	uint32 j;
	for ( j = 0; *Templates[ j ].cmd; j++ )
		freeVar( ( void * ) &Templates[ j ].ptr, NULL, 0 );
	for ( j = 0; *Flags[ j ].cmd; j++ )
	{
		freeVar( ( void * ) &Flags[ j ].ptn, NULL, 0 );
		freeVar( ( void * ) &Flags[ j ].off, NULL, 0 );
		freeVar( ( void * ) &Flags[ j ].on, NULL, 0 );
	}
	free( sCfg.pthrd );
	free( sCfg.cfg );
}

  void handleTimerEvent( ServerState *state, AnyType pluginDat, AnyType timerDat )
{
	if ( state && svrStrtd )
		svrStrtd = ( state->nbrRooms == svrStrtd ? FALSE : state->nbrRooms );
	else if ( Switches[ timerDat = ( AnyType ) getIndexbyName( CMD_DUMPSTARTUP, &Switches, sizeof( Switch ), 0 ) ].bit ) {
		Switches[ timerDat ].bit = 0;
		createThread( state, CMD_DUMPSTARTUP );
	} else if ( timer && ! --timer ) {
		createThread( state, CMD_TIMER );
		timer = Numerics[ getIndexbyName( CMD_TIMER, &Numerics, sizeof( Numeric ), 0 ) ].num;
	}
	scheduleTimerEvent( ( PluginID ) pluginDat, 0, 1 );
}

  void noteRoomCreation( ServerState *state, AnyType pluginDat, ServerRoomRec *room )
{
	if ( room )
		if ( Switches[ getIndexbyName( CMD_RMMAKE, &Switches, sizeof( Switch ), 0 ) ].bit )
			createThread( state, CMD_RMMAKE );
}

  void noteRoomDeletion( ServerState *state, AnyType pluginDat, ServerRoomRec *room )
{
	if ( room )
		if ( Switches[ getIndexbyName( CMD_RMDEL, &Switches, sizeof( Switch ), 0 ) ].bit )
			createThread( state, CMD_RMDEL );
}

  void noteUserCreation( ServerState *state, AnyType pluginDat, ServerUserRec *user )
{
	if ( user )
		if ( Switches[ getIndexbyName( CMD_URLOGON, &Switches, sizeof( Switch ), 0 ) ].bit && ! Switches[ getIndexbyName( CMD_URENTR, &Switches, sizeof( Switch ), 0 ) ].bit )
			createThread( state, CMD_URLOGON );
}

  void noteUserDeletion( ServerState *state, AnyType pluginDat, ServerUserRec *user )
{
	if ( user )
		if ( Switches[ getIndexbyName( CMD_URLOGOFF, &Switches, sizeof( Switch ), 0 ) ].bit )
			createThread( state, CMD_URLOGOFF );
}

  void noteUserRoomEntry( ServerState *state, AnyType pluginDat, ServerUserRec *user, ServerRoomRec *room )
{
	if ( user && room )
		if ( Switches[ getIndexbyName( CMD_URENTR, &Switches, sizeof( Switch ), 0 ) ].bit )
			createThread( state, CMD_URENTR );
}

  static CommandResult PalNFO( ServerUserRec *user, int argc, char *argv[ ] )
{
	if ( ! strcmp( NFO_ABOUT, argv[ 1 ] ) )
		UserMessage( user, "%s v%d.%d.%d (%s)\n%s", INT_PLGNAME, vMajor, vMinor, vBuild, INT_PLGHOME, INT_PLGCOPY );
	else if ( ! strcmp( NFO_RELOAD, argv[ 1 ] ) ) {
		if ( UserRank( user ) < 3 )
			return 0;
		if ( LoadConfig( user, ( argc > 1 ? argv[ 2 ] : NULL ) ) )
			UserMessage( user, "PalNFO Configuration Reloaded!" );
	}
	return 1;
}

  static void createThread( const ServerState *state, const uint8 *ptr )
{
	if ( svrStrtd || gQuitFlag )
		return;
	else if ( state )
	{
		sCfg.users = state->userList;
		sCfg.rooms = state->roomList;
		if ( state->entrance )
			sCfg.entrance = state->entrance->room.roomID;
	}
	if ( Switches[ getIndexbyName( CMD_THREAD, &Switches, sizeof( Switch ), 0 ) ].bit )
	{
		pthread_cancel( sCfg.pthrd->id );
		if ( ! pthread_attr_init( &sCfg.pthrd->attr ) )
			if ( ! pthread_attr_setdetachstate( &sCfg.pthrd->attr, PTHREAD_CREATE_DETACHED ) )
				if ( ! pthread_create( &sCfg.pthrd->id, &sCfg.pthrd->attr, ( void * ) &createTemplate, NULL ) )
					return;
		rprtErr( NULL, ERR_THREAD, ptr );
	}
	createTemplate( );
}

  static void createTemplate( )
{
	uint8 *ptr;
	uint32 x, y, z;
	x = time( NULL );
	if ( sCfg.time )
	{
		sCfg.freqt += abs( x - sCfg.time );
		sCfg.freqc++;
	}
	sCfg.time = x;
	snprintf( sCfg.date, sizeof( sCfg.date ), rmvchr( ctime( &sCfg.time ), "\r\n" ) );
	for ( memset( &sCfg.nbrRooms, 0, sizeof( sCfg.nbrRooms ) ), ptr = ( uint8 * ) sCfg.rooms; ptr; ptr = ( uint8 * ) ( ( ServerRoomRec * ) ptr )->nextRoom )
		sCfg.nbrRooms[ ( ( ( ServerRoomRec * ) ptr )->memberOwner ? 2 : 0 ) + ( ( ( ServerRoomRec * ) ptr )->room.roomFlags & RF_Hidden ? 1 : 0 ) ]++;
	for ( memset( &sCfg.nbrUsers, 0, sizeof( sCfg.nbrUsers ) ), ptr = ( uint8 * ) sCfg.users; ptr; ptr = ( uint8 * ) ( ( ServerUserRec * ) ptr )->nextUser )
		if ( ( ( ServerUserRec * ) ptr )->loggedOn )
		{
			if ( Switches[ getIndexbyName( CMD_DUPIP, &Switches, sizeof( Switch ), 0 ) ].bit )
			{
				uint8 *obj;
				for ( obj = ( uint8 * ) ( ( ServerUserRec * ) ptr )->nextUser; obj; obj = ( uint8 * ) ( ( ServerUserRec * ) obj )->nextUser )
					if ( ( ( ServerUserRec * ) obj )->feIPAddr == ( ( ServerUserRec * ) ptr )->feIPAddr && isGuest( ( ServerUserRec * ) obj ) == isGuest( ( ServerUserRec * ) ptr ) )
						break;
				if ( obj )
					continue;
			}
			sCfg.nbrUsers[ ( isGuest( ( ServerUserRec * ) ptr ) ? 2 : 0 ) + ( ( ( ServerUserRec * ) ptr )->flags & U_Hide ? 1 : 0 ) ]++;
		}
	getServerConfiguration( sCfg.cfg );
	for ( x = 0; *Templates[ x ].cmd; x++ )
		if ( Templates[ x ].io == IO_OUTPUT && Templates[ x ].ptr )
		{
			FILE *fPtrO = ( FILE * ) ( ptr = NULL );
			uint8 shl = ( Templates[ x ].tpl & ATR_SHELL ? 1 : 0 );
			if ( ! shl )
				fPtrO = fopen( Templates[ x ].ptr, "wt" );
			if ( fPtrO || shl )
			{
				if ( ! strcmp( Templates[ x ].cmd, CMD_RMTPL ) )
					ptr = CMD_RMTPLWRAP;
				else if ( ! strcmp( Templates[ x ].cmd, CMD_SVRTPL ) )
					ptr = CMD_SVRTPLWRAP;
				else if ( ! strcmp( Templates[ x ].cmd, CMD_URTPL ) )
					ptr = CMD_URTPLWRAP;
				uint8 bit = ( ptr ? Switches[ getIndexbyName( ptr, &Switches, sizeof( Switch ), 0 ) ].bit : 1 );
				for ( y = 0; Struct[ y ]; y++ )
					for ( sCfg.inc = 0, z = 0; *Templates[ z ].cmd; z++ )
						if ( Templates[ z ].io == IO_INPUT && Templates[ z ].ptr )
						{
							if ( ( Templates[ z ].tpl & ( ATR_HEAD |  ATR_FOOT ) ) && ! bit )
								continue;
							else if ( Struct[ y ] != Templates[ z ].tpl && ( Struct[ y ] | TPL_SRVR ) != Templates[ z ].tpl && Struct[ y ] != ( Templates[ z ].tpl | ATR_LOOP ) )
								continue;
							else if ( ! ( Struct[ y ] & Templates[ x ].tpl ) )
								continue;
							if ( Struct[ y ] & ATR_LOOP )
							{
								for ( ptr = ( Templates[ z ].tpl & TPL_ROOM ? ( uint8 * ) sCfg.rooms : ( uint8 * ) sCfg.users ); ptr; sCfg.inc++, ptr = ( Templates[ z ].tpl & TPL_ROOM ? ( uint8 * ) ( ( ServerRoomRec * ) ptr )->nextRoom : ( uint8 * ) ( ( ServerUserRec * ) ptr )->nextUser ) )
								{
									if ( Templates[ z ].tpl & TPL_USER )
										if ( ! ( ( ServerUserRec * ) ptr )->loggedOn )
											continue;
									if ( shl )
										writeTemplate( NULL, Templates[ x ].ptr, ptr, ( Templates[ z ].tpl | ATR_SHELL ) );
									else
										readTemplate( fPtrO, ptr, z );
								}
							} else
								if ( shl )
									writeTemplate( NULL, Templates[ x ].ptr, NULL, ( Templates[ z ].tpl | ATR_SHELL ) );
								else
									readTemplate( fPtrO, NULL, z );
						}
				if ( fPtrO )
					fclose( ( FILE * ) fPtrO );
			} else
				rprtErr( NULL, ERR_MAKETPL, ( Templates[ x ].tpl & ATR_SHELL ? Templates[ x ].cmd : Templates[ x ].ptr ) );
		}
	if ( Switches[ getIndexbyName( CMD_RESET, &Switches, sizeof( Switch ), 0 ) ].bit )
		timer = Numerics[ getIndexbyName( CMD_TIMER, &Numerics, sizeof( Numeric ), 0 ) ].num;
}

  static void readTemplate( const FILE *fPtrO, const uint8 *obj, const uint32 idx )
{
	FILE *fPtrI;
	if ( fPtrI = fopen( Templates[ idx ].ptr, "rt" ) )
	{
		uint8 bfr[ INT_INPTTPL ];
		while ( 1 )
		{
			fgets( bfr, sizeof( bfr ), fPtrI );
			if ( feof( fPtrI ) )
				break;
			if ( strlen ( bfr ) )
				writeTemplate( fPtrO, bfr, obj, Templates[ idx ].tpl );
		}
		fclose( fPtrI );
	} else
		writeTemplate( fPtrO, Templates[ idx ].ptr, obj, Templates[ idx ].tpl );
}

  static void writeTemplate( const FILE *fPtrO, const uint8 *str, const uint8 *obj, const uint8 tpl )
{
	if ( ( tpl & ATR_SHELL ) && ( ( tpl & ATR_HEAD ) || ( tpl & ATR_FOOT ) ) )
		return;
	uint32 j;
	uint8 bfr[ INT_INPTTPL ], tmp[ INT_INPTTPL ], *ptr;
	snprintf( bfr, sizeof( bfr ), str );
	for ( j = 0; *Flags[ j ].cmd; j++ )
		if ( ( Flags[ j ].tpl & tpl ) || ! Flags[ j ].tpl )
		{
			ptr = NULL;
			switch ( Flags[ j ].tpl )
			{
				case TPL_NONE:
					switch ( Flags[ j ].var )
					{
						case VAR_STRING:
							if ( ! strcmp( CMD_DATETIME, Flags[ j ].cmd ) )
								ptr = sCfg.date;
							else if ( ! strcmp( CMD_PLGNCOPY, Flags[ j ].cmd ) )
								ptr = INT_PLGCOPY;
							else if ( ! strcmp( CMD_PLGNHOME, Flags[ j ].cmd ) )
								ptr = INT_PLGHOME;
							else if ( ! strcmp( CMD_PLGNNAME, Flags[ j ].cmd ) )
								ptr = INT_PLGNAME;
							break;
						case VAR_INTEGER:
							if ( ! strcmp( CMD_INCREMENT, Flags[ j ].cmd ) )
								snprintf( tmp, sizeof( tmp ), "%ld", sCfg.inc );
							else if ( ! strcmp( CMD_PLGNVER, Flags[ j ].cmd ) )
								snprintf( tmp, sizeof( tmp ), "%ld.%ld.%ld", vMajor, vMinor, vBuild );
							else if ( ! strcmp( CMD_TIMESTAMP, Flags[ j ].cmd ) )
								snprintf( tmp, sizeof( tmp ), "%u", sCfg.time );
							else if ( ! strcmp( CMD_UPDATEFREQ, Flags[ j ].cmd ) )
								snprintf( tmp, sizeof( tmp ), "%0.2f", ( float ) ( sCfg.freqc ? ( float ) ( ( float ) sCfg.freqt / ( float ) sCfg.freqc ) : 0 ) );
							break;
						case VAR_SWITCH:
							break;
					}
					break;
				case TPL_SRVR:
					switch ( Flags[ j ].var )
					{
						case VAR_STRING:
							if ( ! strcmp( CMD_SVRAVS, Flags[ j ].cmd ) )
								ptr = sCfg.cfg->avatarServerURL;
							else if ( ! strcmp( CMD_SVRMEDIA, Flags[ j ].cmd ) )
								ptr = sCfg.cfg->mediaServerURL;
							else if ( ! strcmp( CMD_SVRNAME, Flags[ j ].cmd ) )
								ptr = sCfg.cfg->serverName;
							else if ( ! strcmp( CMD_SVRUPDATE, Flags[ j ].cmd ) )
								ptr = rmvchr( ctime( &sCfg.uptime ), "\r\n" );
							else if ( ! strcmp( CMD_SVRURL, Flags[ j ].cmd ) )
								ptr = sCfg.cfg->url;
							break;
						case VAR_INTEGER:
							if ( ! strcmp( CMD_SVRENTRMID, Flags[ j ].cmd ) )
								snprintf( tmp, sizeof( tmp ), "%ld", sCfg.entrance );
							else if ( ! strcmp( CMD_SVRMAXOCC, Flags[ j ].cmd ) )
								snprintf( tmp, sizeof( tmp ), "%ld", sCfg.cfg->maxOccupancy );
							else if ( ! strcmp( CMD_SVRRMMAXOCC, Flags[ j ].cmd ) )
								snprintf( tmp, sizeof( tmp ), "%ld", sCfg.cfg->roomOccupancy );
							else if ( ! strcmp( CMD_SVRNBRGURS, Flags[ j ].cmd ) )
								snprintf( tmp, sizeof( tmp ), "%ld", sCfg.nbrUsers[ 2 ] );
							else if ( ! strcmp( CMD_SVRNBRHGURS, Flags[ j ].cmd ) )
								snprintf( tmp, sizeof( tmp ), "%ld", sCfg.nbrUsers[ 3 ] );
							else if ( ! strcmp( CMD_SVRNBRHMRMS, Flags[ j ].cmd ) )
								snprintf( tmp, sizeof( tmp ), "%ld", sCfg.nbrRooms[ 3 ] );
							else if ( ! strcmp( CMD_SVRNBRHRMS, Flags[ j ].cmd ) )
								snprintf( tmp, sizeof( tmp ), "%ld", sCfg.nbrRooms[ 1 ] );
							else if ( ! strcmp( CMD_SVRNBRHURS, Flags[ j ].cmd ) )
								snprintf( tmp, sizeof( tmp ), "%ld", sCfg.nbrUsers[ 1 ] );
							else if ( ! strcmp( CMD_SVRNBRMRMS, Flags[ j ].cmd ) )
								snprintf( tmp, sizeof( tmp ), "%ld", sCfg.nbrRooms[ 2 ] );
							else if ( ! strcmp( CMD_SVRNBRRMS, Flags[ j ].cmd ) )
								snprintf( tmp, sizeof( tmp ), "%ld", sCfg.nbrRooms[ 0 ] );
							else if ( ! strcmp( CMD_SVRNBRURS, Flags[ j ].cmd ) )
								snprintf( tmp, sizeof( tmp ), "%ld", sCfg.nbrUsers[ 0 ] );
							else if ( ! strcmp( CMD_SVRUPTIME, Flags[ j ].cmd ) )
								snprintf( tmp, sizeof( tmp ), "%u", sCfg.uptime );
								break;
						case VAR_SWITCH:
							if ( ! strcmp( CMD_CUSTOMPRPS, Flags[ j ].cmd ) )
								str = ( uint8 * ) ( sCfg.cfg->permissions & PM_AllowCustomProps );
							else if ( ! strcmp( CMD_CYBORGS, Flags[ j ].cmd ) )
								str = ( uint8 * ) ( sCfg.cfg->permissions & PM_AllowCyborgs );
							else if ( ! strcmp( CMD_GUESTS, Flags[ j ].cmd ) )
								str = ( uint8 * ) ( sCfg.cfg->permissions & PM_AllowGuests );
							else if ( ! strcmp( CMD_MRMS, Flags[ j ].cmd ) )
								str = ( uint8 * ) ( sCfg.cfg->permissions & PM_MemberCreatedRooms );
							else if ( ! strcmp( CMD_PAINTING, Flags[ j ].cmd ) )
								str = ( uint8 * ) ( sCfg.cfg->permissions & PM_AllowPainting );
							else if ( ! strcmp( CMD_SPOOFING, Flags[ j ].cmd ) )
								str = ( uint8 * ) ! ( sCfg.cfg->permissions & PM_NoSpoofing );
							break;
					}
					break;
				case TPL_ROOM:
					if ( tpl & ( ATR_HEAD | ATR_FOOT ) )
						continue;
					else if ( ! obj )
						return;
					else if ( ( ( ServerRoomRec * ) obj )->memberOwner && ! Switches[ getIndexbyName( CMD_LMRMS, &Switches, sizeof( Switch ), 0 ) ].bit )
						return;
					else if ( ( ( ( ServerRoomRec * ) obj )->room.roomFlags & RF_Hidden ) && ! Switches[ getIndexbyName( CMD_LHRMS, &Switches, sizeof( Switch ), 0 ) ].bit )
						return;
					switch ( Flags[ j ].var )
					{
						case VAR_STRING:
							if ( ! strcmp( CMD_RMARTIST, Flags[ j ].cmd ) )
								ptr = ( ( ( ServerRoomRec * ) obj )->room.artistNameOfst ? ( uint8 * ) CvtToCString( &( ( ServerRoomRec * ) obj )->varBuf[ ( ( ServerRoomRec * ) obj )->room.artistNameOfst ] ) : NULL );
							else if ( ! strcmp( CMD_RMNAME, Flags[ j ].cmd ) )
								ptr = ( ( ( ServerRoomRec * ) obj )->room.roomNameOfst ? ( uint8 * ) CvtToCString( &( ( ServerRoomRec * ) obj )->varBuf[ ( ( ServerRoomRec * ) obj )->room.roomNameOfst ] ) : NULL );
							break;
						case VAR_INTEGER:
							if ( ! strcmp( CMD_RMID, Flags[ j ].cmd ) )
								snprintf( tmp, sizeof( tmp ), "%ld", ( ( ServerRoomRec * ) obj )->room.roomID );
							else if ( ! strcmp( CMD_RMMAXOCC, Flags[ j ].cmd ) )
								snprintf( tmp, sizeof( tmp ), "%ld", ( ( ServerRoomRec * ) obj )->maxOccupancy );
							else if ( ! strcmp( CMD_RMOWNURID, Flags[ j ].cmd ) )
								snprintf( tmp, sizeof( tmp ), "%u", ( ( ( ServerRoomRec * ) obj )->memberOwner ? ( ( str = ( uint8 * ) GetServerUserByCounter( ( ( ServerRoomRec * ) obj )->memberOwner ) ) ? ( ( ServerUserRec * ) str )->user.userID : 0 ) : 0 ) );
							else if ( ! strcmp( CMD_RMURCNT, Flags[ j ].cmd ) )
								snprintf( tmp, sizeof( tmp ), "%ld", ( ( ServerRoomRec * ) obj )->room.nbrPeople );
							break;
						case VAR_SWITCH:
							if ( ! strcmp( CMD_RMCLOSED, Flags[ j ].cmd ) )
								str = ( uint8 * ) ( ( ( ServerRoomRec * ) obj )->room.roomFlags & RF_Closed );
							else if ( ! strcmp( CMD_RMCYBORG, Flags[ j ].cmd ) )
								str = ( uint8 * ) ! ( ( ( ServerRoomRec * ) obj )->room.roomFlags & RF_CyborgFreeZone );
							else if ( ! strcmp( CMD_RMDROPZONE, Flags[ j ].cmd ) )
								str = ( uint8 * ) ( ( ( ServerRoomRec * ) obj )->room.roomFlags & RF_DropZone );
							else if ( ! strcmp( CMD_RMGUESTS, Flags[ j ].cmd ) )
								str = ( uint8 * ) ! ( ( ( ServerRoomRec * ) obj )->room.roomFlags & RF_NoGuests );
							else if ( ! strcmp( CMD_RMH, Flags[ j ].cmd ) )
								str = ( uint8 * ) ( ( ( ServerRoomRec * ) obj )->room.roomFlags & RF_Hidden );
							else if ( ! strcmp( CMD_RMLPRPS, Flags[ j ].cmd ) )
								str = ( uint8 * ) ! ( ( ( ServerRoomRec * ) obj )->room.roomFlags & RF_NoLooseProps );
							else if ( ! strcmp( CMD_RMM, Flags[ j ].cmd ) )
								str = ( uint8 * ) ( ( ServerRoomRec * ) obj )->memberOwner;
							else if ( ! strcmp( CMD_RMPAINT, Flags[ j ].cmd ) )
								str = ( uint8 * ) ! ( ( ( ServerRoomRec * ) obj )->room.roomFlags & RF_NoPainting );
							else if ( ! strcmp( CMD_RMPRI, Flags[ j ].cmd ) )
								str = ( uint8 * ) ( ( ( ServerRoomRec * ) obj )->room.roomFlags & RF_Private );
							else if ( ! strcmp( CMD_RMWIZONLY, Flags[ j ].cmd ) )
								str = ( uint8 * ) ( ( ( ServerRoomRec * ) obj )->room.roomFlags & RF_WizardsOnly );
							break;
					}
					break;
				case TPL_USER:
					if ( tpl & ( ATR_HEAD | ATR_FOOT ) )
						continue;
					else if ( ! obj )
						return;
					else if ( isGuest( ( ServerUserRec * ) obj ) && ! Switches[ getIndexbyName( CMD_LGS, &Switches, sizeof( Switch ), 0 ) ].bit )
						return;
					else if ( ( ( ( ServerUserRec * ) obj )->flags & U_Hide ) && ! Switches[ getIndexbyName( CMD_LHURS, &Switches, sizeof( Switch ), 0 ) ].bit )
						return;
					switch ( Flags[ j ].var )
					{
						case VAR_STRING:
							if ( ! strcmp( CMD_URLACTDATE, Flags[ j ].cmd ) )
								ptr = rmvchr( ctime( &( ( ServerUserRec * ) obj )->lastActive ), "\r\n" );
							else if ( ! strcmp( CMD_URLOGONDATE, Flags[ j ].cmd ) )
								ptr = rmvchr( ctime( &( ( ServerUserRec * ) obj )->signonTime ), "\r\n" );
							else if ( ! strcmp( CMD_URNAME, Flags[ j ].cmd ) )
								ptr = CvtToCString( ( uint8 * ) &( ( ServerUserRec * ) obj )->user.name );
							else if ( ! strcmp( CMD_UROS, Flags[ j ].cmd ) )
								ptr = ( uint8 * ) ( ( ( ServerUserRec * ) obj )->machine );
							else if ( ! strcmp( CMD_URRMNAME, Flags[ j ].cmd ) )
								ptr = ( uint8 * ) ( ( str = ( uint8 * ) GetRoom( ( ( ServerUserRec * ) obj )->user.roomID ) ) ? ( ( ( ServerRoomRec * ) str )->room.roomNameOfst ? CvtToCString( &( ( ServerRoomRec * ) str )->varBuf[ ( ( ServerRoomRec * ) str )->room.roomNameOfst ] ) : NULL ) : NULL );
							break;
						case VAR_INTEGER:
							if ( ! strcmp( CMD_URID, Flags[ j ].cmd ) )
								snprintf( tmp, sizeof( tmp ), "%u", ( ( ServerUserRec * ) obj )->user.userID );
							else if ( ! strcmp( CMD_URLACTTIME, Flags[ j ].cmd ) )
								snprintf( tmp, sizeof( tmp ), "%u", ( ( ServerUserRec * ) obj )->lastActive );
							else if ( ! strcmp( CMD_URLOGONTIME, Flags[ j ].cmd ) )
								snprintf( tmp, sizeof( tmp ), "%u", ( ( ServerUserRec * ) obj )->signonTime );
							else if ( ! strcmp( CMD_URRANK, Flags[ j ].cmd ) )
								snprintf( tmp, sizeof( tmp ), "%u", ( isGuest( ( ServerUserRec * ) obj ) ? 0 : UserRank( ( ServerUserRec * ) obj ) ) );
							else if ( ! strcmp( CMD_URRMID, Flags[ j ].cmd ) )
								snprintf( tmp, sizeof( tmp ), "%ld", ( ( ServerUserRec * ) obj )->user.roomID );
							break;
						case VAR_SWITCH:
							if ( ! strcmp( CMD_URGAG, Flags[ j ].cmd ) )
								str = ( uint8 * ) ( ( ( ServerUserRec * ) obj )->flags & U_Gag );
							else if ( ! strcmp( CMD_URH, Flags[ j ].cmd ) )
								str = ( uint8 * ) ( ( ( ServerUserRec * ) obj )->flags & U_Hide );
							else if ( ! strcmp( CMD_URPIN, Flags[ j ].cmd ) )
								str = ( uint8 * ) ( ( ( ServerUserRec * ) obj )->flags & U_Pin );
							else if ( ! strcmp( CMD_URPRPGAG, Flags[ j ].cmd ) )
								str = ( uint8 * ) ( ( ( ServerUserRec * ) obj )->flags & U_PropGag );
							else if ( ! strcmp( CMD_URRJCTESP, Flags[ j ].cmd ) )
								str = ( uint8 * ) ( ( ( ServerUserRec * ) obj )->flags & U_RejectESP );
							else if ( ! strcmp( CMD_URRJCTPRI, Flags[ j ].cmd ) )
								str = ( uint8 * ) ( ( ( ServerUserRec * ) obj )->flags & U_RejectPrivate );
							break;
					}
					break;
			}
			switch ( Flags[ j ].var )
			{
				case VAR_STRING:
					if ( ptr && ( tpl & ATR_SHELL ) )
					{
						snprintf( tmp, sizeof( tmp ), ptr );
						if ( Switches[ getIndexbyName( CMD_ESCSHELL, &Switches, sizeof( Switch ), 0 ) ].bit )
							ptr = escstr( tmp, sizeof( tmp ) );
					}
					break;
				case VAR_INTEGER:
					if ( strlen( tmp ) )
						ptr = tmp;
					break;
				case VAR_SWITCH:
					ptr = ( str ? Flags[ j ].on : Flags[ j ].off );
					break;
			}
			fndrpl( bfr, Flags[ j ].ptn, ptr, sizeof( bfr ), 1 );
		}
	if ( tpl & ATR_SHELL )
	{
		snprintf( tmp, sizeof( tmp ), "%s >& /dev/null 2>&1", bfr );
		system( tmp );
	} else if ( fPtrO ) {
		for ( j = 0; *escChr[ j ][ 0 ]; j++ )
			fndrpl( bfr, escChr[ j ][ 0 ], escChr[ j ][ 1 ], sizeof( bfr ), 1 );
		fprintf( ( FILE * ) fPtrO, bfr );
	}
}

  static void rprtErr( const ServerUserRec *user, const uint8 *err, const uint8 *arg )
{
	FormattedLogMessage( ( ServerUserRec * ) user, INT_PLGNAME, ( uint8 * ) err, ( uint8 * ) arg );
	if ( user )
		UserMessage( ( ServerUserRec * ) user, ( uint8 * ) err, ( uint8 * ) arg );
}

  static uint8 freeVar( void **dest, const uint8 *src, const uint32 max )
{

	if ( *dest )
		free( *dest );
	if ( src )
	{
		uint32 len;
		if ( len = ( ( len = strlen( src ) ) > max ? max : len ) )
		{
			snprintf( *dest = malloc( ++len ), ++len, src );
			return 1;
		}
	}
	*dest = NULL;
	return 0;
}

  static uint8 LoadConfig( const ServerUserRec *user, const uint8 *file )
{
	sint32 idx, len;
	static uint8 *INIFile;
	uint8 *ptr, *var, *fPtr, bfr[ INT_INPTBFR ];
	if ( file )
		INIFile = ( uint8 * ) file;
	if ( INIFile )
		fPtr = ( uint8 * ) fopen( INIFile, "rt" );
	else
		fPtr = ( uint8 * ) fopen( INT_INIFILE, "rt" );
	if ( fPtr )
	{
		while ( 1 )
		{
			fgets( bfr, sizeof( bfr ), ( FILE * ) fPtr );
			if ( feof( ( FILE * ) fPtr ) )
				break;
			if ( strlen( ptr = rmvchr( bfr, "\r\n" ) ) )
			{
				while ( isspace( *ptr ) )
					ptr++;
				if ( strchr( "#;", *ptr ) )
					continue;
				if ( ! isMode( ptr ) )
				{
					var = strtok( ptr, "=" );
					if ( ( idx = getIndexbyName( var, &Flags, sizeof( Flag ), 1 ) ) != -1 )
					{
						if ( freeVar( ( void * ) &Flags[ idx ].ptn, ( isOff( ptr = strtok( NULL, ( Flags[ idx ].var & VAR_SWITCH ? "," : "\0" ) ) ) ? NULL : ptr ), INT_STRBASE ) )
							if ( Flags[ idx ].var & VAR_SWITCH )
							{
								freeVar( ( void * ) &Flags[ idx ].off, ( isNull( ptr = strtok( NULL, "," ) ) ? NULL : ptr ), INT_PTNBASE );
								freeVar( ( void * ) &Flags[ idx ].on, ( isNull( ptr = strtok( NULL, "\0" ) ) ? NULL : ptr ), INT_PTNBASE );
							}
					} else if ( ptr = strtok( NULL, "\0" ) )
						if ( ( idx = getIndexbyName( var, &Switches, sizeof( Switch ), 1 ) ) != -1 )
						{
							if ( isOn( ptr ) )
								Switches[ idx ].bit = 1;
							else if ( isOff( ptr ) )
								Switches[ idx ].bit = 0;
							else if ( atoi( ptr ) )
								Switches[ idx ].bit = 1;
						} else if ( ( idx = getIndexbyName( var, &Numerics, sizeof( Numeric ), 1 ) ) != -1 )
							Numerics[ idx ].num = ( isOff( ptr ) ? 0 : ( uint16 ) atoi( ptr ) );
						else if ( ( idx = getIndexbyName( var, &Templates, sizeof( Template ), 1 ) ) != -1 )
							freeVar( ( void * ) &Templates[ idx ].ptr, ( isOff( ptr ) ? NULL : ptr ), INT_INPTMAX );
						else
							rprtErr( user, ERR_UNRECVAR, var );
					else
						rprtErr( user, ERR_EMPTYVAL, var );
				}
			}
		}
		fclose( ( FILE * ) fPtr );
		timer = Numerics[ getIndexbyName( CMD_TIMER, &Numerics, sizeof( Numeric ), 0 ) ].num;
	} else {
		rprtErr( user, ERR_NOCFG, INIFile );
		return 0;
	}
	return 1;
}

  static uint8 isOn( uint8 *ptr )
{
	return ( getIndexbyName( ptr, &OnSwitches, sizeof( Str7 ), 1 ) == ( sint32 ) -1 ? 0 : 1 );
}

  static uint8 isOff( uint8 *ptr )
{
	if ( isNull( ptr ) )
		return 1;
	return ( getIndexbyName( ptr, &OffSwitches, sizeof( Str7 ), 1 ) == ( sint32 ) -1 ? 0 : 1 );
}

  static uint8 isNull( uint8 *ptr )
{
	return ( getIndexbyName( ptr, &NullSwitches, sizeof( Str7 ), 1 ) == ( sint32 ) -1 ? 0 : 1 );
}

  static uint8 isMode( uint8 *ptr )
{
	uint8 len = strlen( ptr );
	if ( len > 2 )
		if ( *ptr == '[' && ptr[ --len ] == ']' )
			return 1;
	return 0;
}

  static uint8 isGuest( ServerUserRec *user )
{
	return ( user ? ( user->flags & U_Guest || ( user->crc == INT_GUESTCRC && user->counter == INT_GUESTCTR ) ? 1 : 0 ) : 0 );
}

  static uint8 * strlwr( const uint8 *var )
{
	uint8 *ptr = ( uint8 * ) var;
	if ( ptr )
		while ( *ptr )
		{
			if ( isalpha( *ptr ) )
				*ptr = tolower( *ptr );
			ptr++;
		}
	return ( uint8 * ) var;
}

  static uint8 * rmvchr( const uint8 *var, const uint8 *chr )
{
	uint8 *ptr = ( uint8 * ) var;
	if ( ptr )
		while ( *ptr )
		{
			while ( *ptr && strchr( chr, *ptr ) )
				memmove( --ptr, ++ptr, strlen( ptr ) );
			ptr++;
		}
	return ( uint8 * ) var;
}

  static uint8 * fndrpl( const uint8 *var, const uint8 *fnd, const uint8 *rpl, const uint32 limit, uint8 all )
{
	if ( fnd )
	{
		uint8 *ptr, lnFd = strlen( fnd ), lnRl = ( rpl ? strlen( rpl ) : 0 );
		while ( ptr = strstr( ( uint8 * ) var, ( uint8 * ) fnd ) )
		{
			if ( ( strlen( var ) + ( lnRl - lnFd ) ) < limit )
			{
				memmove( &ptr[ lnRl ], &ptr[ lnFd ], strlen( ptr ) );
				memmove( ptr, rpl, lnRl );
				if ( all )
					continue;
			}
			break;
		}
	}
	return ( uint8 * ) var;
}

  static uint8 * escstr( const uint8 *var, const uint32 limit )
{
	if ( var )
	{
		uint8 *ptr = ( uint8 * ) var, chr[ 2 ], bfr[ INT_STRBASE ];
		while ( *ptr )
		{
			snprintf( chr, sizeof( chr ), ptr );
			snprintf( bfr, sizeof( bfr ), "\\x%-2.2X", *ptr );
			fndrpl( ptr, chr, bfr, limit, 0 );
			ptr += strlen( bfr );
		}
	}
	return ( uint8 * ) var;
}

  static sint32 getIndexbyName( uint8 *ptr, void *fIdx, const uint8 iSize, const uint8 cSen )
{
	if ( ! ptr )
		return ( sint32 ) -1;
	Str31 bfr;
	if ( cSen )
	{
		snprintf( bfr, sizeof( bfr ), ptr );
		ptr = strlwr( bfr );
	}
	sint32 j = 0;
	while ( ( ( uint8 * ) fIdx )[ j * iSize ] )
	{
		if ( ! strcmp( ptr, &( ( uint8 * ) fIdx )[ j * iSize ] ) )
			return j;
		j++;
	}
	return ( sint32 ) -1;
}

#endif /* __PALNFO_C__ */
