But it does also need someone else to test it, because it's Sooooooo Painful for one person to do all the testing.
It's also something I wouldn't use on my worst enemy - Well, not unless he was also a pain in the butt!
And I could nearly swear that MG had this in place for some of us with the last server he was using!
Grrrrrrrrrr!
Give it a spin as a troll-user, and if it passes all expectations; I'll release it as a MOD.
Download: -> Troll Mod for Icy Phoenix
Btw, I renamed / recoded the instructions for miserable_user.php and renamed it to auth_validate.php because IF there are timeout's, the error message gives the game away by stating "Blah Blah" in miserable_user.php
I was going to call it ctracker_something.php - But thought maybe those files get enough complaints now!
And I've only posted the code here if MG wants to run an eye over it before this is officially released - The install is also in the .zip
Spoiler: [ Show ]
Spoiler: [ Hide ]
Code: [Show]
Package Icy Phoenix Modifications
File $Id: install_troll_v0-1.txt
Version for Icy Phoenix: 1.3.0.*
Author: Merlin Sythove < Merlin@silvercircle.org >
Author: Kalipo < N/A >
Copyright: (c) 2008 Merlin Sythove
Email: < N/A >
License: http://opensource.org/licenses/gpl-license.php GNU Public License
Extra credits for this file:
Modified for Icy Phoenix by Lopalong. < http://www.lopalong.com > < webmaster@lopalong.com > 27th October 2009
* Dr. Doom & TERMINATRIX - original Miserable User script.
* Paddic - Ban Control page Add-on.
* Civphp - borrowed code for the ACP display.
* JKeats - borrowed code for the ACP controls.
Title:
Troll Mod
Title2: (Merged by Lopalong) Troll in Ban Users Page
Author: Paddic < Addic@hamppu.net > (N/A)
Description:
This is a combination of Miserable User Mod and extra features plus a cookie system, basically to make life miserable for trolls.
What happens (adaptation of the Miserable User Mod):
In all cases everything is really slowed down. I've chosen between 5 and 30 seconds but that is already infuriating. You can easily change that time. 50% of the time the forum software "messes up", by sending them mysteriously to the index page, giving a blank screen, "hanging" the computer orgiving strange error messages. The other 50% of the time they can do what they want (still delayed of course)
The Troll Mod adds extra trouble within the 50% of pages that do work:
Login: This only succeeds half the time, the other half of the time error messages are shown (i.e. only 1 in 4 logins succeed)
Posting, PM and Email: This gives error messages half the time, the other times it only appears to succeed but in fact nothing is posted or sent. (i.e. only 25% of the attempts seem to work)
Cookie system
A troll who logs in will get a cookie. If there is a cookie, that person is a troll, whether that person is logged in or not. If the cookie no longer matches the database (i.e. the admin has re-set the troll flag) the cookie is removed.
* A troll who is just browsing as guest, is still treated as a troll
* A troll who comes to your house and uses your computer to log in, will set the cookie, so the next time it will look like he messed up your computer
* A troll who has made a new account will still be seen as troll based on the cookie of the old account
* The ADMIN can un-set the troll flag to remove cookies when the ex-troll (or misfortunate friend) visits the board. They do not need to log in for the cookie to be removed, a visit is enough.
The only way to get round the system, is to remove cookies, and make a new account and behave (to prevent the ADMIN from setting the new account to troll).
Usage
Users can be marked as Trolls via the User Management in the ACP. Further, Trolls are identified on the ACP Index (names and total number) below the Users Online table.
Configuration location: ACP > User Admin - User Ban
Files To Edit: 11
login_ip.php,
privmsg.php,
adm/admin_users.php,
adm/index.php,
includes/functions_post.php,
includes/page_header.php,
includes/sessions.php,
includes/usercp_email.php,
templates/common/acp/index_body.tpl,
templates/common/acp/user_ban_body.tpl
templates/common/acp/user_edit_body.tpl
Included Files: 1
includes/auth_validate.php - Previously known as miserable_user.php
Included Folders: None
Disclaimer:
There is no guarantee that there are no security problems within the MOD.
Comments:
Author Notes:
WARNING - WARNING - WARNING:
Before Adding This MOD To Your Forum, You Should Back Up All Files Related To This MOD
#
#-----[ BEGIN MODIFICATION ]------------------------------------------
#
#
#-----[ SQL ]------------------------------------------
#
ALTER TABLE `ip_users` ADD `user_troll` TINYINT;
#
#-----[ COPY ]----------------------------------------------------------
#
copy includes/auth_validate.php to includes/auth_validate.php
#
#-----[ OPEN ]------------------------------------------
#
login_ip.php
#
#-----[ FIND ]------------------------------------------
#
$sql = "SELECT user_id, username, user_password, user_active, user_level, user_login_tries, user_last_login_try, ct_login_count
#
#-----[ IN-LINE FIND ]------------------------------------------
#
ct_login_count
#
#-----[ IN-LINE AFTER, ADD ]------------------------------------------
#
, user_troll
#
#-----[ FIND ]------------------------------------------
#
if((md5($password) == $row['user_password']) && $row['user_active'])
{
#
#-----[ AFTER, ADD ]------------------------------------------
#
//START MOD troll
if ($row['user_troll'])
{
switch (rand (1,2)) //case 3 is done by miserable_user.php
{
case 1: //Give obfuscating message
message_die(GENERAL_MESSAGE, $lang['Troll_error_login_' . rand (1,3) ]);
break;
case 2: //Allow login
break;
case 3: //Abort further processing, computer "hangs"
exit;
break;
}
}
//END MOD troll
#
#-----[ OPEN ]------------------------------------------
#
privmsg.php
#
#-----[ FIND ]------------------------------------------
#
message_die(GENERAL_MESSAGE, $lang['Flood_Error']);
}
}
// End Flood control
}
#
#-----[ AFTER, ADD ]------------------------------------------
#
//START MOD troll
if ($userdata['user_troll'])
{
switch (rand (1,2)) //case 3 is done by miserable_user.php
{
case 1: //Give obfuscating message
message_die(GENERAL_MESSAGE, $lang['Troll_error_privmsg_' . rand (1,3) ]);
break;
case 2: //Pretend it worked
$template->assign_vars(array(
' META' => '<meta http-equiv="refresh" content="3;url=' . append_sid('privmsg.' . PHP_EXT . '?folder=inbox') . '">')
);
$msg = $lang['Message_sent'] . '<br /><br />' . sprintf($lang['Click_return_inbox'], '<a href="' . append_sid('privmsg.' . PHP_EXT . '?folder=inbox') . '">', '</a> ') . '<br /><br />' . sprintf($lang['Click_return_index'], '<a href="' . append_sid('index_ip.' . PHP_EXT) . '">', '</a>');
message_die(GENERAL_MESSAGE, $msg);
break;
case 3: //Abort further processing, computer "hangs"
exit;
break;
}
}
//END MOD troll
#
#-----[ OPEN ]------------------------------------------
#
adm/admin_users.php
#
#-----[ FIND ]------------------------------------------
#
$user_allowavatar = (!empty($_POST['user_allowavatar'])) ? intval($_POST['user_allowavatar']) : 0;
#
#-----[ AFTER, ADD ]------------------------------------------
#
//START MOD troll
$user_troll = (!empty($_POST['user_troll'])) ? intval($_POST['user_troll']) : 0;
//END MOD troll
#
#-----[ FIND ]------------------------------------------
#
$sql = "UPDATE " . USERS_TABLE . "
SET " . $username_sql . $passwd_sql . "user_email =
#
#-----[ IN-LINE FIND ]------------------------------------------
#
, user_allowavatar = $user_allowavatar
#
#-----[ IN-LINE AFTER, ADD ]------------------------------------------
#
, user_troll = $user_troll
#
#-----[ FIND ]------------------------------------------
#
$user_allowavatar = $this_userdata['user_allowavatar'];
#
#-----[ AFTER, ADD ]------------------------------------------
#
//START MOD troll
$user_troll = $this_userdata['user_troll'];
//END MOD troll
#
#-----[ FIND ]------------------------------------------
#
$s_hidden_fields .= '<input type="hidden" name="user_allowavatar" value="' . $user_allowavatar . '" />';
#
#-----[ AFTER, ADD ]------------------------------------------
#
//START MOD troll
$s_hidden_fields .= '<input type="hidden" name="user_troll" value="' . $user_troll . '" />';
//END MOD troll
#
#-----[ FIND ]------------------------------------------
#
'ALLOW_AVATAR_YES' => ($user_allowavatar) ? 'checked="checked"' : '',
'ALLOW_AVATAR_NO' => (!$user_allowavatar) ? 'checked="checked"' : '',
#
#-----[ AFTER, ADD ]------------------------------------------
#
//START MOD troll
'TROLL_YES' => ($user_troll) ? 'checked="checked"' : '',
'TROLL_NO' => (!$user_troll) ? 'checked="checked"' : '',
//END MOD troll
#
#-----[ FIND ]------------------------------------------
#
'L_ALLOW_AVATAR' => $lang['User_allowavatar'],
#
#-----[ AFTER, ADD ]------------------------------------------
#
//START MOD troll
'L_TROLL' => $lang['User_troll'],
//END MOD troll
#
#-----[ OPEN ]------------------------------------------
#
adm/index.php
#
#-----[ FIND ]------------------------------------------
#
'L_GZIP_COMPRESSION' => $lang['Gzip_compression']
#
#-----[ BEFORE, ADD ]------------------------------------------
#
//START MOD troll
'L_TROLL_INFO' => $lang['Troll_info'],
'L_NUMBER_TROLLS' => $lang['Numberof_Trolls'],
'L_NAME_TROLLS' => $lang['Troll_users'],
//END MOD troll
#
#-----[ FIND ]------------------------------------------
#
$total_topics = get_db_stat('topiccount');
#
#-----[ AFTER, ADD ]------------------------------------------
#
//START MOD troll
$total_trolls = 0;
$troll_names = '';
$sql = "SELECT username
FROM " . USERS_TABLE . "
WHERE user_troll = 1
AND user_id <> " . ANONYMOUS . "
ORDER BY username";
if (!($result = $db->sql_query($sql)))
{
message_die(GENERAL_ERROR, "Couldn't get statistic data.", "", __LINE__, __FILE__, $sql);
}
while ($row = $db->sql_fetchrow($result))
{
$troll_names .= (($troll_names == '') ? '' : ', ') . $row['username'];
$total_trolls++;
}
//END MOD troll
#
#-----[ FIND ]------------------------------------------
#
'GZIP_COMPRESSION' => ($board_config['gzip_compress']) ? $lang['ON'] : $lang['OFF']
#
#-----[ BEFORE, ADD ]------------------------------------------
#
//START MOD troll
'NUMBER_OF_TROLLS' => $total_trolls,
'NAMES_OF_TROLLS' => htmlspecialchars($troll_names),
//END MOD troll
#
#-----[ OPEN ]------------------------------------------
#
adm/admin_user_ban.php
#
#-----[ FIND ]------------------------------------------
#
if ( !empty($_POST['username']) )
#
#-----[ REPLACE WITH ]------------------------------------------
#
if (!empty($_POST['usernamez']))
#
#-----[ FIND ]------------------------------------------
#
$this_userdata = get_userdata($_POST['username'], true);
#
#-----[ REPLACE WITH ]------------------------------------------
#
$this_userdata = get_userdata($_POST['usernamez'], true);
#
#-----[ FIND ]------------------------------------------
#
$where_sql = '';
#
#-----[ AFTER, ADD ]------------------------------------------
#
//START Mod Troll
if (!empty($_POST['trollname']))
{
$this_userdata = get_userdata($_POST['trollname'], true);
if(!$this_userdata)
{
message_die(GENERAL_MESSAGE, $lang['No_user_id_specified']);
}
$sql = "UPDATE " . USERS_TABLE . " SET user_troll = 1 WHERE user_id = ".$this_userdata['user_id'];
if (!$db->sql_query($sql))
{
message_die(GENERAL_ERROR, "Couldn't delete ban info from database", "", __LINE__, __FILE__, $sql);
}
}
if (isset($_POST['untroll_user']))
{
$user_list = $_POST['untroll_user'];
for($i = 0; $i < count($user_list); $i++)
{
if ($user_list[$i] != -1)
{
$where_sql .= (($where_sql != '') ? ', ' : '') . intval($user_list[$i]);
}
}
if($where_sql != '')
{
$sql = "UPDATE " . USERS_TABLE . " SET user_troll = 0 WHERE user_id IN ($where_sql)";
$where_sql = '';
if (!$db->sql_query($sql))
{
message_die(GENERAL_ERROR, "Couldn't delete troll info from database", "", __LINE__, __FILE__, $sql);
}
}
}
//END Mod Troll
#
#-----[ FIND ]------------------------------------------
#
'L_EMAIL_ADDRESS' => $lang['Email_address'],
#
#-----[ AFTER, ADD ]------------------------------------------
#
//START Mod Troll
'L_TROLL' => $lang['User_troll'], // troll+banlist
'L_TROLL_EXPLAIN' => $lang['User_troll_explain'],
'L_UNTROLL' => $lang['User_untroll'], // troll+banlist
'L_UNTROLL_EXPLAIN' => $lang['User_untroll_explain'],
//END Mod Troll
#
#-----[ FIND ]------------------------------------------
#
$userban_count = 0;
$ipban_count = 0;
$emailban_count = 0;
#
#-----[ AFTER, ADD ]------------------------------------------
#
//START Mod Troll
$sql = "SELECT username, user_id FROM " . USERS_TABLE . " WHERE user_troll > 0 ORDER BY user_id ASC";
if (!($result = $db->sql_query($sql)))
{
message_die(GENERAL_ERROR, 'Could not select current troll list', '', __LINE__, __FILE__, $sql);
}
$user_list = $db->sql_fetchrowset($result);
$db->sql_freeresult($result);
$select_trolllist = '';
for($i = 0; $i < count($user_list); $i++)
{
$select_trolllist .= '<option value="' . $user_list[$i]['user_id'] . '">' . $user_list[$i]['username'] . '</option>';
}
if($select_trolllist == '')
{
$select_trolllist = '<option value="-1">' . $lang['No_trolls'] . '</option>';
}
$select_trolllist = '<select name="untroll_user[]" multiple="multiple" size="5">' . $select_trolllist . '</select>';
//END Mod Troll
#
#-----[ FIND ]------------------------------------------
#
'S_UNBAN_USERLIST_SELECT' => $select_userlist,
#
#-----[ AFTER, ADD ]------------------------------------------
#
//START Mod Troll
'S_UNTROLL_USERLIST_SELECT' =>$select_trolllist,
//END Mod Troll
#
#-----[ OPEN ]------------------------------------------
#
includes/functions_post.php
#
#-----[ FIND ]------------------------------------------
#
$error_msg .= (!empty($error_msg)) ? '<br />' . $result['error_msg'] : $result['error_msg'];
}
}
else
{
$username = '';
}
}
#
#-----[ AFTER, ADD ]------------------------------------------
#
//START MOD troll
if (($mode == 'newtopic') || (($mode == 'reply') || (($mode == 'editpost') && $userdata['user_troll'])))
{
switch (rand (1,2)) //case 3 is done by miserable_user.php
{
case 1: //Give obfuscating message
message_die(GENERAL_MESSAGE, $lang['Troll_error_posting_' . rand (1,3) ]);
break;
case 2: //Pretend it worked
$meta = '<meta http-equiv="refresh" content="3;url=' . append_sid('viewtopic.' . PHP_EXT . '?' . POST_POST_URL . '=' . $post_id) . '#' . $post_id . '>';
//MOD Moderate_user
$message = $lang['Stored'];
if ($post_moderated) $message .= '<br />' . $lang['moderate_user_notify'];
$message .= '<br /><br />' . sprintf($lang['Click_view_message'], '<a href="' . append_sid('viewtopic.' . PHP_EXT . '?' . POST_POST_URL . '=' . $post_id) . '#' . $post_id . '>', '</a>') . '<br /><br />' . sprintf($lang['Click_return_forum'], '<a href="' . append_sid('viewforum.' . PHP_EXT . '?' . POST_FORUM_URL . '=$forum_id') . '">', '</a>');
message_die(GENERAL_MESSAGE, $message);
break;
case 3: //Abort further processing, computer "hangs"
exit;
break;
}
}
//END MOD troll
#
#-----[ OPEN ]------------------------------------------
#
includes/page_header.php
#
#-----[ FIND ]------------------------------------------
#
$template->pparse('overall_header');
#
#-----[ BEFORE, ADD ]------------------------------------------
#
if($userdata['user_troll'])
{
include_once(IP_ROOT_PATH . 'includes/auth_validate.' . PHP_EXT);
}
#
#-----[ OPEN ]------------------------------------------
#
includes/sessions.php
#
#-----[ FIND ]------------------------------------------
#
// Initial ban check against user id, IP and email address
//
preg_match('/(..)(..)(..)(..)/', $user_ip, $user_ip_parts);
#
#-----[ AFTER, ADD ]------------------------------------
#
//START MOD Troll
//Get troll cookie settings.
$troll_cookie = '';
$troll_id = isset($_COOKIE[$board_config['cookie_name'].'_tr_id']) ? intval($_COOKIE[$board_config['cookie_name'].'_tr_id']) : '';
//Yes, troll cookie present. See if it matches the database. If not, delete cookie.
if ($troll_id)
{
// Note: If another person uses the troll's computer, they are seen as troll too.
// If a troll makes a new account, they are still listed as troll via this cookie
// If the troll doesn't log in, they are still seen as troll.
// Once a troll has visited your house and used your computer and logged in,
// you too are seen as a troll. USER SOLUTION: Remove the cookies from your computer.
// If a troll removes cookies, everything will be fine until they log in again,
// bingo, he will have a new cookie and be troll again.
//
// EX-TROLLS: The ex-troll does NOT have to log in to remove the troll cookie, visiting is enough.
// So you could un-set the troll flag and wait until the troll or another user has
// visited to remove possible troll cookies on their computer, and then set the troll flag again.
$sql = "SELECT user_id
FROM " . USERS_TABLE . "
WHERE user_id = $troll_id";
if (!($result = $db->sql_query($sql)))
{
message_die(CRITICAL_ERROR, 'Could not obtain user information', '', __LINE__, __FILE__, $sql);
}
if ($troll_info = $db->sql_fetchrow($result))
{
$troll_cookie = ($troll_info['user_id']);
}
//There was a cookie but no match in the database:
//delete the cookie by setting the expiry time 1 hour ago
if (! $troll_cookie)
{
setcookie($board_config['cookie_name'].'_tr_id',$troll_id, time()-3600);
}
}
//Have $troll_cookie, if not empty, the user is a troll.
//If empty, then there was no cookie, or there was no LONGER a database match so the cookie was deleted
//Check if user (if logged in) is troll. Fill in the troll_id if there is no id known yet.
$troll_database = '';
if ($user_id != ANONYMOUS)
{
$troll_database = ($userdata['user_troll']);
if (! $troll_id) $troll_id = $user_id;
}
//User is a troll in some way?
if ($troll_cookie || $troll_database)
{
//Set the troll cookie, time it for 1 year. The time restarts every time the user comes here
setcookie($board_config['cookie_name'].'_tr_id',$troll_id, time()+365*24*3600);
//Set the user variable too (so a troll-as-guest is treated as a troll)
$userdata['user_troll'] = true;
}
//END MOD Troll
#
#-----[ OPEN ]------------------------------------------
#
includes/usercp_email.php
#
#-----[ FIND ]------------------------------------------
#
$sql = 'UPDATE ' . USERS_TABLE . '
SET user_emailtime = ' . time() . ', ct_last_mail = ' . $new_mailtime . ' WHERE user_id = ' . $userdata['user_id'];
// CrackerTracker v5.x
#
#-----[ BEFORE, ADD ]------------------------------------------
#
//START MOD troll
if ($userdata['user_troll'])
{
switch (rand (1,2)) //case 3 is done by miserable_user.php
{
case 1: //Give obfuscating message
message_die(GENERAL_MESSAGE, $lang['Troll_error_email_' . rand (1,3) ]);
break;
case 2: //Pretend it worked
$template->assign_vars(array(
'META' => '<meta http-equiv="refresh" content="5;url=' . append_sid('index_ip.' . PHP_EXT) . '">')
);
$message = $lang['Email_sent'] . '<br /><br />' . sprintf($lang['Click_return_index'], '<a href="' . append_sid('index_ip.' . PHP_EXT) . '">', '</a>');
message_die(GENERAL_MESSAGE, $message);
break;
case 3: //Abort further processing, computer "hangs"
exit;
break;
}
}
//END MOD troll
#
#-----[ OPEN ]------------------------------------------
#
templates/common/acp/index_body.tpl
#
#-----[ FIND ]------------------------------------------
#
<tr>
<td class="row3" nowrap width="10"> </td>
<td class="row3" nowrap width="10"> </td>
<td class="row1" nowrap="nowrap">{L_NAME_DEACTIVATED_USERS}:</td>
<td class="row2" colspan="3">{NAMES_OF_DEACTIVATED} </td>
</tr>
#
#-----[ AFTER, ADD ]------------------------------------------
#
<tr>
<td class="row3" nowrap width="10"> </td>
<td class="row1" nowrap="nowrap"colspan="2">{L_NUMBER_TROLLS}:</td>
<td class="row2" colspan="3">{NUMBER_OF_TROLLS} </td>
</tr>
<tr>
<td class="row3" nowrap width="10"> </td>
<td class="row3" nowrap width="10"> </td>
<td class="row1" nowrap="nowrap">{L_NAME_TROLLS}:</td>
<td class="row2" colspan="3">{NAMES_OF_TROLLS} </td>
</tr>
#
#-----[ OPEN ]------------------------------------------
#
templates/common/acp/user_ban_body.tpl
#
#-----[ FIND ]------------------------------------------
#
<td class="row2"><input class="post" type="text" class="post" name="username" maxlength="50" size="20" /> <input type="hidden" name="mode" value="edit" />{S_HIDDEN_FIELDS} <input type="submit" name="usersubmit" value="{L_FIND_USERNAME}" class="liteoption" onclick="window.open('{U_SEARCH_USER}','_phpbbsearch','width=400,height=250,resizable=yes');" /></td>
#
#-----[ REPLACE WITH ]------------------------------------------
#
<td class="row2"><input type="text" class="post" name="usernamez" id="uz" maxlength="50" size="20" /> <input type="hidden" name="mode" value="edit" />{S_HIDDEN_FIELDS} <input type="submit" name="usersubmit" value="{L_FIND_USERNAME}" class="liteoption" onClick="document.forms['post'].trollname.id = 'ut';document.forms['post'].usernamez.id = 'username';var w=window.open('{U_SEARCH_USER}', '_phpbbsearch', 'height=250,resizable=yes,width=400');w.focus();return false;" /></td>
#
#-----[ FIND ]------------------------------------------
#
<td class="row2">{S_UNBAN_EMAILLIST_SELECT}</td>
</tr>
#
#-----[ AFTER, ADD ]------------------------------------------
#
<tr><th colspan="2">{L_TROLL}</th>
</tr>
<tr>
<td class="row1">{L_USERNAME}: <br /><span class="gensmall">{L_TROLL_EXPLAIN}</span></td>
<td class="row2"><input type="text" class="post" name="trollname" id="ut" maxlength="50" size="20" /> <input type="hidden" name="mode" value="edit" />{S_HIDDEN_FIELDS} <input type="submit" name="usersubmit" value="{L_FIND_USERNAME}" class="liteoption" onClick="document.forms['post'].usernamez.id = 'uz';document.forms['post'].trollname.id = 'username';var w=window.open('{U_SEARCH_USER}', '_phpbbsearch', 'HEIGHT=250,resizable=yes,WIDTH=400');w.focus();return false;" /></td>
</tr>
<tr><th class="thHead" colspan="2">{L_UNTROLL}</th>
</tr>
<tr>
<td class="row1">{L_USERNAME}: <br /><span class="gensmall">{L_UNTROLL_EXPLAIN}</span></td>
<td class="row2">{S_UNTROLL_USERLIST_SELECT}</td>
</tr>
#
#-----[ OPEN ]------------------------------------------
#
templates/common/acp/user_edit_body.tpl
#
#-----[ FIND ]------------------------------------------
#
<tr>
<td class="row1"><span class="gen">{L_DELETE_USER}?</span></td>
<td class="row2"><input type="checkbox" name="deleteuser" /> {L_DELETE_USER_EXPLAIN}</td>
</tr>
#
#-----[ BEFORE, ADD ]------------------------------------------
#
<tr>
<td class="row1"><span class="gen">{L_TROLL}</span></td>
<td class="row2">
<input type="radio" name="user_troll" value="1" {TROLL_YES} />
<span class="gen">{L_YES}</span>
<input type="radio" name="user_troll" value="0" {TROLL_NO} />
<span class="gen">{L_NO}</span></td>
</tr>
#
#-----[ SAVE/CLOSE ALL FILES ]------------------------------------------
#
# EoM
Edit: Missed some changes. File updated.