#include <amxmodx>
#include <amxmisc>
// my logs directory function
stock get_logsdir(output[], len) {
return get_localinfo("amxx_logs", output, len);
}
#define DEBUG
#define TASK_ID_REMOVE 14325
new gCaseSensitive;
#define IsCaseSensitive(%1) (gCaseSensitive & (1 << (%1 & 31)))
#define SetCaseSensitive(%1) gCaseSensitive |= (1 << (%1 & 31))
#define ClearCaseSensitive(%1) gCaseSensitive &= ~(1 << (%1 & 31))
// admin data stored
enum _:AdminData {
Admin_Auth[44],
Admin_Password[32],
Admin_Access,
Admin_Flags
};
// array holding admin data
new Array:gAdminData;
// auth key pointing to index of array
new Trie:gAuthIndex;
// size of array
new gNumAdmins;
// file where admins are loaded
new gAdminFile[64];
// default amxx cvars
new amx_mode;
new amx_password_field;
new amx_default_access;
// kick command
new gKickCommand[32];
#if defined DEBUG
new gLogFile[64];
#define DebugLog(%1) log_to_file(gLogFile, %1)
new const gSeparator[] = "===========================================================";
#else
stock DebugLog(any:...) { }
stock gSeparator;
#endif
public plugin_init() {
register_plugin("Admin Custom", "0.0.2", "Exolent");
#if defined DEBUG
// locate log file directory
get_logsdir(gLogFile, charsmax(gLogFile));
new l = add(gLogFile, charsmax(gLogFile), "/admin_custom");
// check if log directory exists
if(!dir_exists(gLogFile)) {
// make directory
mkdir(gLogFile);
}
// get the log file
get_time("/%Y-%m-%d.log", gLogFile[l], charsmax(gLogFile) - l);
#endif
// grab default amxx cvars
// registering will grab pointer if exists
amx_mode = register_cvar("amx_mode", "1");
amx_password_field = register_cvar("amx_password_field", "_pw");
amx_default_access = register_cvar("amx_default_access", "");
// register kick command
formatex(gKickCommand, charsmax(gKickCommand), "amxauthcustom%c%c%c%c", random_num('A', 'Z'), random_num('A', 'Z'), random_num('A', 'Z'), random_num('A', 'Z'));
register_clcmd(gKickCommand, "CmdKick");
// locate admin file
get_configsdir(gAdminFile, charsmax(gAdminFile));
add(gAdminFile, charsmax(gAdminFile), "/users_custom.ini");
// create array and trie
gAdminData = ArrayCreate(AdminData);
gAuthIndex = TrieCreate();
// load admins
LoadAdmins();
// grab current time
new hour, minute, second;
time(hour, minute, second);
// subtract current time from day length to get time left for today
// add 5 seconds into the next day to be sure the day changed
new timeLeft = 86400 - (hour * 3600) - (minute * 60) - second + 5;
// set task to refresh admins when tomorrow starts for expiration checking and day checking
set_task(float(timeLeft), "TaskRefreshAdmins");
}
public TaskRefreshAdmins() {
#if defined DEBUG
// grab last '/' position
new slash, last = -1;
while((slash = contain(gLogFile[last + 1], "/")) >= 0) {
last = slash;
}
// get the log file
get_time("/%Y-%m-%d.log", gLogFile[last], charsmax(gLogFile) - last);
#endif
// reload admins
LoadAdmins();
// grab all players
new players[32], pnum;
get_players(players, pnum);
// loop through all players
while(pnum--) {
// check admin for player
checkAdmin(players[pnum]);
}
// refresh admins next day
set_task(86400.0, "TaskRefreshAdmins");
}
public plugin_end() {
// clear the memory
ArrayDestroy(gAdminData);
TrieDestroy(gAuthIndex);
}
public client_connect(id) {
// set that name checks are case insensitive
ClearCaseSensitive(id);
}
public client_authorized(id) {
// check if admin is turned on
if(get_pcvar_num(amx_mode)) {
DebugLog("%s", gSeparator);
DebugLog("User authorized %d", id);
// check admin for this user
checkAdmin(id);
}
}
public client_putinserver(id) {
// for listen servers, check host access
if(get_pcvar_num(amx_mode) && !is_dedicated_server() && id == 1) {
DebugLog("%s", gSeparator);
DebugLog("Host connected %d", id);
// check admin for host
checkAdmin(id);
}
}
public client_infochanged(id) {
// check if player is connected and admin is turned on
if(is_user_connected(id) && get_pcvar_num(amx_mode)) {
// grab new and old name
new oldName[32], newName[32];
get_user_name(id, oldName, charsmax(oldName));
get_user_info(id, "name", newName, charsmax(newName));
// check if names changed based on case sensitive flag
if(strcmp(oldName, newName, !IsCaseSensitive(id)) == 0) {
DebugLog("%s", gSeparator);
DebugLog("Changed name (%d) case sensitive: %d", id, !!IsCaseSensitive(id));
// name changed, check admin
checkAdmin(id, newName);
}
}
}
public CmdKick(id) {
// kick player from server
server_cmd("kick #%d", get_user_userid(id));
// hide command from console
return PLUGIN_HANDLED;
}
public TaskRemoveAuth(auth[]) {
// grab index of admins where auth is
new index;
if(!TrieGetCell(gAuthIndex, auth, index)) {
return;
}
// delete from admins
ArrayDeleteItem(gAdminData, index);
TrieDeleteKey(gAuthIndex, auth);
gNumAdmins--;
// loop through all admins and update indexes
new admin[AdminData];
while(index < gNumAdmins) {
// grab auth from this index
ArrayGetArray(gAdminData, index, admin);
// update index for this admin
TrieSetCell(gAuthIndex, admin[Admin_Auth], index);
}
// grab all players
new players[32], pnum;
get_players(players, pnum);
// loop through all players
while(pnum--) {
// check admin for player
checkAdmin(players[pnum]);
}
}
checkAdmin(id, name[32] = "") {
DebugLog("Checking admin for %d", id);
// remove any existing flags
remove_user_flags(id);
// check if no name was passed
if(!name[0]) {
// grab current name
get_user_name(id, name, charsmax(name));
}
// set name to not be case sensitive
ClearCaseSensitive(id);
// grab SteamID and IP as well
new steamID[35], ip[32];
get_user_authid(id, steamID, charsmax(steamID));
get_user_ip(id, ip, charsmax(ip), 1);
DebugLog("Grabbed all player data for admin check: ^"%s^" ^"%s^" ^"%s^"", name, steamID, ip);
// create variables we need for admin checking
new admin[AdminData];
new temp;
new bool:found = false;
DebugLog("Checking normal admin list");
// loop through normal admin list before checking custom
for(new i = admins_num() - 1; i >= 0; i--) {
DebugLog("Checking normal admin index #%d", i);
// grab the auth, password, access, and flags
admins_lookup(i, AdminProp_Auth , admin[Admin_Auth ], charsmax(admin[Admin_Auth ]));
admins_lookup(i, AdminProp_Password, admin[Admin_Password], charsmax(admin[Admin_Password]));
admin[Admin_Access] = admins_lookup(i, AdminProp_Access);
admin[Admin_Flags ] = admins_lookup(i, AdminProp_Flags );
// check if player matches this admin
if((found = adminMatch(id, name, steamID, ip, admin))) {
break;
}
}
// check if player was not found in the normal admin list
if(!found) {
DebugLog("Not found in normal admin list, checking custom");
// loop through custom admin list
for(new i = 0; i < gNumAdmins; i++) {
// grab admin data
ArrayGetArray(gAdminData, i, admin);
// check if player matches this admin
if((found = adminMatch(id, name, steamID, ip, admin))) {
break;
}
}
}
// check if player was found for any admin at all
if(found) {
// check if this requires a password
if(~admin[Admin_Flags] & FLAG_NOPASS) {
DebugLog("Admin requires a password");
// grab password field and player's password
new field[32], password[32];
get_pcvar_string(amx_password_field, field, charsmax(field));
get_user_info(id, field, password, charsmax(password));
// check if passwords don't match
if(!equal(admin[Admin_Password], password)) {
DebugLog("Passwords don't match");
// check if this should kick players
if(admin[Admin_Flags] & FLAG_KICK) {
DebugLog("Admin flags specify to kick player");
// kick player
client_cmd(id, "%s", gKickCommand);
}
// don't give access
return;
}
}
new flags[27];
get_flags(admin[Admin_Access], flags, charsmax(flags));
DebugLog("Player authorized as admin: %s", flags);
// give player admin access
set_user_flags(id, admin[Admin_Access]);
}
// check if non-admins should be kicked
else if(get_pcvar_num(amx_mode) == 2) {
DebugLog("Not found in any admin list");
DebugLog("amx_mode is 2, kicking player");
// kick player
client_cmd(id, "%s", gKickCommand);
}
// give default flags
else {
DebugLog("Not found in any admin list");
// get default flags
new flags[27];
get_pcvar_string(amx_default_access, flags, charsmax(flags));
temp = read_flags(flags);
// check if no flags are given
if(!temp) {
// give user flag
temp = ADMIN_USER;
}
get_flags(temp, flags, charsmax(flags));
DebugLog("Giving default flags: %s", flags);
// give player flags
set_user_flags(id, temp);
}
}
bool:adminMatch(id, const name[], const steamID[], const ip[], const admin[AdminData]) {
// create variables we need
new temp;
new bool:found = false;
// check if this is a SteamID
if(admin[Admin_Flags] & FLAG_AUTHID) {
DebugLog("Admin flags specify SteamID");
// check if SteamIDs match
if(equal(steamID, admin[Admin_Auth])) {
DebugLog("SteamIDs match");
// we found the admin
found = true;
}
}
// check if this is an IP
else if(admin[Admin_Flags] & FLAG_IP) {
DebugLog("Admin flags specify IP");
// grab length of ip in list
temp = strlen(admin[Admin_Auth]);
// check if ends in a '.' for range checks
if(admin[Admin_Auth][temp - 1] != '.') {
DebugLog("Full IP given, no range");
// set length to 0 to match whole string
temp = 0;
} else {
DebugLog("IP Range given");
}
// check if ip's match
if(equal(ip, admin[Admin_Auth], temp)) {
DebugLog("IPs match");
// we found the admin
found = true;
}
}
// check if this is a tag
else if(admin[Admin_Flags] & FLAG_TAG) {
DebugLog("Admin flags specify Tag");
// cache if this is case sensitive admin name
temp = admin[Admin_Flags] & FLAG_CASE_SENSITIVE;
DebugLog("Case sensitive: %d", !!temp);
// check if tag is in name based on case sensitivity flag from admin list
if(strfind(name, admin[Admin_Auth], !temp) >= 0) {
DebugLog("Tag found inside name");
// set case sensitive flag if admin list has it
if(temp) {
SetCaseSensitive(id);
}
// we found the admin
found = true;
}
}
// then this should be an admin name
else {
DebugLog("Admin flags specify Name");
// cache if this is case sensitive admin name
temp = admin[Admin_Flags] & FLAG_CASE_SENSITIVE;
DebugLog("Case sensitive: %d", !!temp);
// check if names match based on case sensitivity flag from admin list
if(strcmp(name, admin[Admin_Auth], !temp) == 0) {
DebugLog("Names match");
// set case sensitive flag if admin list has it
if(temp) {
SetCaseSensitive(id);
}
// we found the admin
found = true;
}
}
// return if we found admin
return found;
}
LoadAdmins() {
DebugLog("%s", gSeparator);
DebugLog("Loading admins");
// check if admins have been loaded already
if(gNumAdmins) {
// clear out old stored data
ArrayClear(gAdminData);
TrieClear(gAuthIndex);
gNumAdmins = 0;
DebugLog("Cleared out existing admins");
}
// calculate lines in admin file
new fileSize = file_size(gAdminFile, 1);
// check if no lines exist
if(fileSize < 1) {
DebugLog("No lines inside admin file");
// don't read file
return;
}
// grab current day of the week
new data[256];
get_time("%w", data, charsmax(data));
// store current day as a bit
new currentDay = 1 << str_to_num(data);
// prepare variables for reading the admin file
new admin[AdminData];
new accessString[27];
new flagString[27];
new activityString[8];
new expireString[32];
new activity;
new expireTime;
new temp;
new currentTime = get_systime();
// iterate through all lines
for(new line = 0; line < fileSize; line++) {
// read current line
read_file(gAdminFile, line, data, charsmax(data), expireTime);
// trim any white space
trim(data);
DebugLog("Found line: #%d -> %s", line, data);
// check if this is a valid line
if(!data[0] || data[0] == ';' || data[0] == '/' && data[1] == '/') {
DebugLog("Line is empty");
continue;
}
// parse out all the pieces of the line
parse(data,
admin[Admin_Auth], charsmax(admin[Admin_Auth]),
admin[Admin_Password], charsmax(admin[Admin_Password]),
accessString, charsmax(accessString),
flagString, charsmax(flagString),
activityString, charsmax(activityString),
expireString, charsmax(expireString)
);
// convert access and flags to bits and init activity to all days
admin[Admin_Access] = read_flags(accessString);
admin[Admin_Flags] = read_flags(flagString);
activity = 0;
DebugLog("Parsed access (%d) and flags (%d)", admin[Admin_Access], admin[Admin_Flags]);
// using expireTime as an index for activity string
expireTime = 0;
// loop through all characters in activity string
while((temp = activityString[expireTime])) {
// check if this is a valid weekday number
if('1' <= temp <= '7') {
// add to activity bitsum
activity |= (1 << (temp - '1'));
}
// increase index for activity string
expireTime++;
}
DebugLog("Parsed activity: %d", activity);
// check if this admin has specific days set and cannot have admin for today
if(activity && (~activity & currentDay)) {
DebugLog("Admin not enabled for today (%d)", currentDay);
// don't add admin to list
continue;
}
// check if expiration date is set
if(expireString[0]) {
DebugLog("Found expiration date");
// parse out "day.month.year" format
// using accessString for day, flagString for month, expireString for year
strtok(expireString, accessString, charsmax(accessString), expireString, charsmax(expireString), '.');
strtok(expireString, flagString, charsmax(flagString), expireString, charsmax(expireString), '.');
// convert parsed values to integers
activity = str_to_num(accessString); // day
expireTime = str_to_num(flagString); // month
temp = str_to_num(expireString); // year
DebugLog("Parsed expiration date: day (%d) month (%d) year (%d)", activity, expireTime, temp);
// grab this expiration date's timestamp for when the day starts
expireTime = TimeToUnix(temp, expireTime, activity, 0, 0, 0);
DebugLog("Parsed expiration timestamp: %d", expireTime);
// calculate the time left before this expires
expireTime -= currentTime;
DebugLog("Seconds before expiration: %d", expireTime);
// if time is 0 or negative, then it already expired
if(expireTime <= 0) {
DebugLog("Expired, commenting out line");
// expired, so set line to be a comment and add a comment on the end saying it expired
format(data, charsmax(data), ";%s ; Expired already", data);
// replace current line with commented data
write_file(gAdminFile, data, line);
// don't add to admin list
continue;
}
// set a task for this admin to expire
set_task(float(expireTime), "TaskRemoveAuth", TASK_ID_REMOVE, admin[Admin_Auth], sizeof(admin[Admin_Auth]));
}
DebugLog("Added to admin list");
// add to admin list
ArrayPushString(gAdminData, admin);
// keep track of where it is in the list
TrieSetCell(gAuthIndex, admin[Admin_Auth], gNumAdmins);
// increase array size
gNumAdmins++;
}
DebugLog("Loaded %d admin%s", gNumAdmins, (gNumAdmins == 1) ? "" : "s");
}
// Code from Bugsy's unixtime.inc
stock const YearSeconds[2] =
{
31536000, //Normal year
31622400 //Leap year
};
stock const MonthSeconds[12] =
{
2678400, //January 31
2419200, //February 28
2678400, //March 31
2592000, //April 30
2678400, //May 31
2592000, //June 30
2678400, //July 31
2678400, //August 31
2592000, //September 30
2678400, //October 31
2592000, //November 30
2678400 //December 31
};
stock const DaySeconds = 86400;
stock const HourSeconds = 3600;
stock const MinuteSeconds = 60;
stock TimeToUnix( const iYear , const iMonth , const iDay , const iHour , const iMinute , const iSecond )
{
new i;
new iTimeStamp;
for ( i = 1970 ; i < iYear ; i++ )
iTimeStamp += YearSeconds[ IsLeapYear(i) ];
for ( i = 1 ; i < iMonth ; i++ )
iTimeStamp += SecondsInMonth( iYear , i );
iTimeStamp += ( ( iDay - 1 ) * DaySeconds );
iTimeStamp += ( iHour * HourSeconds );
iTimeStamp += ( iMinute * MinuteSeconds );
iTimeStamp += iSecond;
return iTimeStamp;
}
stock SecondsInMonth( const iYear , const iMonth )
{
return ( ( IsLeapYear( iYear ) && ( iMonth == 2 ) ) ? ( MonthSeconds[iMonth - 1] + DaySeconds ) : MonthSeconds[iMonth - 1] );
}
stock IsLeapYear( const iYear )
{
return ( ( (iYear % 4) == 0) && ( ( (iYear % 100) != 0) || ( (iYear % 400) == 0 ) ) );
}