/*****************************************************************
* Plugin to collect IDs of players connecting to server.
* It will only store IDs if they don't exist on the server
* already (However, if the same ID but different NAME, then
* ID IS stored. Hence all the players 'fakenicks' will
* be stored along with their IDs).
*
* author: jugger1@blueyonder.co.uk
*
* Fixed bug in welcome message. Added 'greetings' sound on entry.
* Added fix for blank database entries.
* (2/10/2003) ver 1.2.1
*
* Updated to collect Steam tags.
* (30/9/2003) ver 1.2.0
*
* Corrected Linux issue with comments done using '//' when compiling.
* (9/3/2002) ver 1.1.5
*
* Corrected bug when renaming on reaching MAX_NICKS.
* Added a 'new line' character to each output to the database, as
* it sometimes appended all the names/info of several players
* together on a line; rendering the plugin inoperable.
* (25/2/2002) ver 1.1.4
*
* Added error messaging and WelcomeMsg (0/1) to disable/enable
* welcome message when users connect.
* (14/2/02) ver 1.1.3
*
* After the plugin detects MAX_NICKS names stored, it'll auto
* rename the person concerned to the last name used from the
* database. (8/2/2002) ver 1.1.2
*
* Merged the 2 separate database files in to one: "info_id.ini"
* (or "info_ip.ini" for a LAN server)
* (7/2/2002) ver 1.1.1
*
* Added 'admin_showme' command to allow all names attached to
* a ID or IP to be listed (5/2/2002) ver 1.1.0
*
* Added server detection so that it'll record IP address if
* LAN server detected ('sv_lan 1') (4/2/2002) ver 1.0.1
*
******************************************************************/
 
 
#include <core>
#include <console>
#include <string>
#include <admin>
#include <adminlib>
 
 
/*****************************************************************
* Maximum number of Names/IDs/IPs in database. To increase this
* you'll need to recompile the plugin. It does use memory on the
* server, so you might want to experiment with it!
* I know how to do this dynamically in C, but not in the Small
* language. Any suggestons,
* email me please: jugger1@blueyonder.co.uk
*
* Also, this makes the files size for the plugin in large!
* MAX_ENTRIES 4000 makes it above 1 MB
*****************************************************************/
 
 
/* glodabl variable declarations */
#define MAX_ENTRIES	5000
#define MAX_NICKS	10	/* Maximum nicks to allow in database */
 
#define DELAY_TIME	10	/* delay for ID to be gained */
#define MAX_INFO_LENGTH	39	/* maximum length (in chars) of ID or IP */
 
#define MAX_ERRORS	5	/* max errors to be listed by the plugin */
 
 
new STRING_VERSION[MAX_DATA_LENGTH] = "1.2.1";
new PLUGIN_NAME[] = "plugin_userinfo";
 
/* storage for player NAMEs and IDs/IPs in the database */
new DB_NAME[MAX_ENTRIES][MAX_NAME_LENGTH];
new DB_INFO[MAX_ENTRIES][MAX_INFO_LENGTH];
 
/* storage for player Names and IDs/IPs connected to the server */
new NAME[MAX_PLAYERS][MAX_NAME_LENGTH];
new INFO[MAX_PLAYERS][MAX_INFO_LENGTH];
 
/* Greeting and timer flags */
new GREETED[MAX_PLAYERS];
new PL_TIMER[MAX_PLAYERS];
 
/* Database Files */
new FILE[2][] =	{"id_info.ini", "ip_info.ini"};
 
/* others */
new Entries=0, sType;
new Separator[] = ",";
 
/* error handling */
new Error;
new ErrorMsg[MAX_ERRORS][MAX_TEXT_LENGTH];
 
/* options */
new WelcomeMsg = 1;		/* 0 or 1 - greeting on or off */
 
 
/* Functions: Declarations */
forward DatabaseAdd(Index);
forward DatabaseCheck(UserIndex, iMatch);
forward DatabaseLoad();
forward RecordUser(Timer, WaitCount, RepeatCount, HLUser);
forward admin_showme(HLCommand, HLData, HLUserName, UserIndex);
 
 
/* Functions: Events */
 
/* called once on server start */
public plugin_init()
{
	/* Checks critical Admin Mod values. This plugin will NOT function without these settings */
	new Critical[7][] = {
		"file_access_write", "1",
		"file_access_read", "1",
		"allow_client_exec", "1",
		""};
	Error = 0;	/* error handling */
 
	for (new i=0; strlen(Critical[i]); i+=2 ) {
		if ( getvar(Critical[i])!=strtonum(Critical[i+1]) ) {
				snprintf(ErrorMsg[Error++], MAX_TEXT_LENGTH, "%s: %s MUST be set to %s", PLUGIN_NAME, Critical[i], Critical[i+1]);
		}
	}
	if ( !Error ) {
		plugin_registerinfo("Jugger's ID/IP Collector","Collects connecting player's name and ID (or IP)",STRING_VERSION);
		plugin_registercmd("admin_showme", "admin_showme", ACCESS_ALL, "admin_showme <target or ID or IP>: Shows all names on server for that ID or IP.");
 
		sType = getvar("sv_lan");		/* store type of server */
		DatabaseLoad();	/* load database in to memory */
	}
	return PLUGIN_CONTINUE;
}
 
 
/* called whenever a player connects */
public plugin_connect(HLUserName, HLIP, UserIndex)
{
	if ( Error ) {
		/* Output error messages to each user who connects */
		new Email[MAX_TEXT_LENGTH];
		getstrvar("sv_contact", Email, MAX_TEXT_LENGTH);
 
		for (new i=0; i<Error; i++) {
			selfmessage(ErrorMsg[i]);
		}
		selfmessage("Contact the server admin");
 
		if ( strlen(Email) ) {
			selfmessage(Email);
		}
	} else {
		strinit(INFO[UserIndex]);	/* initialise player data */
		strinit(NAME[UserIndex]);
		GREETED[UserIndex] = 0;
 
		if ( sType==1 ) {	/* LAN Server */
			new Data[MAX_DATA_LENGTH];
			convert_string(HLIP, Data, MAX_DATA_LENGTH);
			strtok(Data, ":", INFO[UserIndex], MAX_DATA_LENGTH);	/* Store IP Without port */
		}
	}
	return PLUGIN_CONTINUE;
}
 
 
/* called whenever a player changes name or connects */
public plugin_info(HLOldName, HLNewName, UserIndex) {
	if ( !Error ) {
		new Temp[MAX_NAME_LENGTH];
 
		convert_string(HLNewName, NAME[UserIndex], MAX_NAME_LENGTH);
		convert_string(HLOldName, Temp, MAX_NAME_LENGTH);
 
		if ( strcasecmp(NAME[UserIndex], Temp) ) {	/* Insert quickly on a name change */
			PL_TIMER[UserIndex] = set_timer("RecordUser", 1, 1, NAME[UserIndex]);
		} else {
			PL_TIMER[UserIndex] = set_timer("RecordUser", DELAY_TIME, 1, NAME[UserIndex]);
		}
	}
	return PLUGIN_CONTINUE;
}
 
 
/* called whenever a player disconnects */
public plugin_disconnect(HLUserName, UserIndex)
{
	kill_timer(PL_TIMER[UserIndex]);
 
	return PLUGIN_CONTINUE;
}
 
 
/* Add Name and Info to database for User */
public DatabaseAdd(Index)
{
	if ( strlen(INFO[Index]) ) {	/* if no IP or ID, no addition to database made */
		new Text[MAX_TEXT_LENGTH];
 
		if ( Entries<MAX_ENTRIES ) {	/* if Database is NOT full, add user to DB and save */
			strcpy(DB_NAME[Entries], NAME[Index], MAX_NAME_LENGTH);
			strcpy(DB_INFO[Entries], INFO[Index], MAX_INFO_LENGTH);
 
			snprintf(Text, MAX_TEXT_LENGTH, "%s%s%s", INFO[Index], Separator, NAME[Index]);
			writefile(FILE[sType], Text);
			Entries++;
		} else {
			snprintf(Text, MAX_TEXT_LENGTH, "%s: Database is full. No more names will be recorded.", PLUGIN_NAME);
			say(Text);
		}
	}
	return PLUGIN_CONTINUE;
}
 
 
/* Checks database to see if name/info has a matching entry
 * iMatch==0 - doesn't list matches of names with INFO
 * iMatch==1 - lists all names matched with INFO
 * Function returns 1 if details already exist in the database
 */
public DatabaseCheck(UserIndex, iMatch)
{
	new Count=0, Found=0;
	new Nick[MAX_NICKS][MAX_NAME_LENGTH];
	new Text[MAX_TEXT_LENGTH];
 
	for (new i=0; i<Entries && Count<MAX_NICKS; i++) {		/* Collect in 'Nick' array, all previous names used */
		if ( strcmp(DB_INFO[i], INFO[UserIndex])==0 ) {
			if ( strcasecmp(DB_NAME[i], NAME[UserIndex]) ) {
				strcpy(Nick[Count++], DB_NAME[i], MAX_NAME_LENGTH);
			} else {
				Found = 1;
			}
		}
	}
	if ( iMatch==0 ) {
		if ( Count>=MAX_NICKS && Found==0 ) {	/* if maximum number of names reached, rename to first one used */
			snprintf(Text, MAX_TEXT_LENGTH, "%s: Maximum nicknames exceeded on: %s", PLUGIN_NAME, NAME[UserIndex]);
			say(Text);
			say("Renaming to first used name on the server.");
			snprintf(Text, MAX_TEXT_LENGTH, "name ^"%s^"", Nick[0]);
			execclient(NAME[UserIndex], Text);
			Found = 1;
		}
	} else {
		if ( Count ) {
			snprintf(Text, MAX_TEXT_LENGTH, "%s is also known as:", NAME[UserIndex]);
			selfmessage(Text);
 
			for (new i=0; i<Count; i++) {
				selfmessage(Nick[i]);
			}
		} else {
			snprintf(Text, MAX_TEXT_LENGTH, "%s has only this name on the database", NAME[UserIndex]);
			selfmessage(Text);
		}
	}
	return Found;
}
 
 
/* load database from server */
public DatabaseLoad()
{
	new Comment[] = ";";
	new i=0, Line=0;
	new Text[MAX_TEXT_LENGTH];
	new cl;
 
	cl = strlen(Comment);
 
	if ( fileexists(FILE[sType]) ) {
		while ( readfile(FILE[sType], Text, Line++, MAX_NAME_LENGTH) && i<MAX_ENTRIES ) {
			if ( strncmp(Text, Comment, cl)!=0 ) {
				if ( strtok(Text, Separator, DB_INFO[i], MAX_INFO_LENGTH)!=-1 ) {
					if ( strtokrest(DB_NAME[i], MAX_NAME_LENGTH)!=-1 ) {
						i++;
					}
				}
			}
		}
	} else {
		snprintf(Text, MAX_TEXT_LENGTH, "%s: file %s not found.", PLUGIN_NAME, FILE[sType]);
		say(Text);
	}
	Entries = i;
 
	return PLUGIN_CONTINUE;
}
 
 
/*
 * This function is delayed to allow for Authentication.
 * It is to greet the connected player on entry, then record
 * details.
 */
public RecordUser(Timer, WaitCount, RepeatCount, HLUser)
{
	new UserIndex;
	new ID[MAX_INFO_LENGTH];
	new User[MAX_NAME_LENGTH];
	new Text[MAX_TEXT_LENGTH];
 
	convert_string(HLUser, User, MAX_NAME_LENGTH);
	get_userindex(User, UserIndex);
 
	if ( sType==0 ) {	/* if INTERNET server, record ID */
		get_userAuthID(User, ID, MAX_INFO_LENGTH);
 
		if ( strlen(ID)==0 ) {	/* if ID not assigned, delay again */
			PL_TIMER[UserIndex] = set_timer("RecordUser", DELAY_TIME, 1, NAME[UserIndex]);
 
			return PLUGIN_CONTINUE;
		} else {
			strcpy(INFO[UserIndex], ID, MAX_INFO_LENGTH);
		}
	}
	if ( DatabaseCheck(UserIndex, 0)==0 ) {	/* if details do not exist in database, add them */
		DatabaseAdd(UserIndex);
	}
	if ( WelcomeMsg==1 && GREETED[UserIndex]==0 ) {	/* display welcome message */
		playsound(User, "scientist/greetings2.wav");
		snprintf(Text, MAX_TEXT_LENGTH, "Greetings, %s", User);
		messageex(User, Text, print_tty);
	}
	GREETED[UserIndex] = 1;	/* greet only once per connect */
 
	return PLUGIN_CONTINUE;
}
 
 
/* Functions: User commands */
 
/* function for handling admin_showme command */
public admin_showme(HLCommand, HLData, HLUserName, UserIndex)
{
	new Command[MAX_COMMAND_LENGTH];
	new Data[MAX_DATA_LENGTH];
	new TargetName[MAX_NAME_LENGTH];
	new Text[MAX_TEXT_LENGTH];
	new User[MAX_NAME_LENGTH];
	new Index;
 
	convert_string(HLCommand, Command, MAX_COMMAND_LENGTH);
	convert_string(HLData, Data, MAX_DATA_LENGTH);
	convert_string(HLUserName, User, MAX_NAME_LENGTH);
 
	if ( check_user(Data)==1 ) {
		get_username(Data, TargetName, MAX_NAME_LENGTH);
 
		if ( check_immunity(TargetName)!=0 ) {
			snprintf(Text, MAX_TEXT_LENGTH, "Laf. %s is an admin. That's all private!", TargetName);
			selfmessage(Text);
		} else {
			get_userindex(TargetName, Index);
			strcpy(NAME[Index], TargetName, MAX_NAME_LENGTH);
			DatabaseCheck(Index, 1);
		}
	} else {
		selfmessage("Unrecognized player: ");
		selfmessage(Data);
	}
	return PLUGIN_HANDLED;
}