[ABD] Disable links for new users & guests

Any abandoned MODs will be moved to this forum.

WARNING: MODs in this forum are not currently being supported or maintained by the original MOD author. Proceed at your own risk.
Forum rules
IMPORTANT: MOD Development Forum rules

WARNING: MODs in this forum are not currently being supported nor updated by the original MOD author. Proceed at your own risk.
Locked
Philthy
Registered User
Posts: 210
Joined: Tue Dec 27, 2005 10:05 am
Location: Dawlish, Devon
Contact:

Re: [RC] Disable links for new users & guests

Post by Philthy »

Very nice work Dp :ugeek:
I checked again this morning on your site, and it worked perfectly.
Go on ! it's not as steep as it looks.....
dangerousprototypes
Registered User
Posts: 91
Joined: Fri Feb 11, 2011 5:53 am
Contact:

Re: [RC] Disable links for new users & guests

Post by dangerousprototypes »

This is probably close to my final version:
*Protects PMs, allows non-spam PMs for sleeper agents to get help if needed
*All options are available from the ACP
*updated to v03 link search terms
*optimized to prevent repeated calls of the same functions
*tested with no spam in over 72 hours :)

I added screenshots, detailed config documentation, and install instructions on our wiki (see link in source, I guess I can't post it?).

To do:
*Add button or cron job for zombie registration removal. (Don;t have a clue how to add it to ACP)
*Add sliding window option based on first post so admins have time to catch sneaky scripts (see corner cases/weaknesses on the wiki)

Also, the first time I update the values after adding the fields to the database it doesn't show the defaults I entered, and the first update has a duplicate key SQL error that goes away when you refresh it again.

Code: Select all

<?php
/**
*
* functions_link_filter.php
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
* Modified by Ian Lesnet (http://dangerousprototypes.com)
* Documentation and install info here: 
*  http://dangerousprototypes.com/docs/PhpBB3_MOD:_Disable_links_for_new_users
*
*/

/**
* @ignore
*/
if (!defined('IN_PHPBB'))
{
   exit;
}

class link_filter{

	//--settings variables--//
	// An array of no-nos. Add whatever you need...
   private $no_link_strings=array('http://', 'www.', '.com', '.net', '.org', '.uk', '.ly', '.me', '.ru', '.biz', '.info', 'dot com', 'dot net', 'dot org', 'dotcom', 'dotnet', 'dotorg', '_com', '_org', '_co_uk', '_ru', 'dot ru');


	//a secondary spam words filter
   private $no_word_strings=array();
	private $show_trigger_word=true; //show the user the word that triggered the error
	
	//URLS to ALLOW always. In additon to own-site urls
	private $whitelist_urls=array('http://code.google.com', 'http://sourceforge.net',);
	
	//a regex to filter a unicode character range. 
	//Will throw an error if any of these characters are included in a post. 
	//leave blank to disable this feature
	//for unicode blocks see: http://www.fileformat.info/info/unicode/block/index.htm
	//0400-04FF - Cyrillic (Russian)
	//0600-06FF - Arabic
	//3100-312F - Mandarin Bopomofo block
	//example of chaining together: '/([\x{0400}-\x{04FF}]|[\x{0600}-\x{06FF}])+/u';
	private $unicode_filter='/([\x{0400}-\x{04FF}]|[\x{0600}-\x{06FF}])+/u';
	
	//what percentage of the post must be non-unicode character (mosly useful for english language sites)
	//leave blank to disable this feature
	private $minimum_nonunicode_text=0.95; //percent of post that must be non-unicode
	
	//where can the user get more help about the filter? Could be a forum post or web page
	//if blank the link will NOT be added to the error messages
	//leave blank to no use this features
	private $help_url = 'http://dangerousprototypes.com/forum/viewtopic.php?f=2&t=1846&p=17886#p17886';
	
	private $load_values_from_db=true; //enable to use with DB, or use the values below
	private $minimum_days=1; //minimum days as member to post links
	private $minimum_posts=1; //minimum posts for member to post links
	
	private $sleeper_check=true; //users with 0 posts who try to post after minimum_days will be prohibited
	
	//-- Reporting variables--//
	public $filter_user=false; //we decided to filter this user (they met our criteria)
	
	public $found_stuff=false; //did we find anything? (any item below)
	public $found_sleeper=false;//did we determine this user to be a sleeper agent?
	public $found_links=false; //we found links
	public $found_words=false;//we found bad words
	public $found_unicode=false;
	
	public $error=array(); //holds text error array

	
/**
*  Test if user can have a profile yet
*  returns false if they do NOT need to be filtered
*/
function link_filter_test_profile(){
	global $user;
	
	//do we need to check this user?
	if(!$this->link_filter_check()) return false; //don't check, no error
	
	if($this->link_filter_sleeper_check()) return true; //if it is a sleeper agent just return error, don;t do the check
		
	//If there isn't a phpbb3 no_link message add one
	if (empty($user->lang['NO_LINK_FOR_YOU'])){
		$user->lang['NO_PROFILE_FOR_YOU']='Antispam: You can\'t have a profile yet. You need to post a few times first.';
	}
	
	$this->error[]=$user->lang['NO_PROFILE_FOR_YOU'].' '.$this->link_filter_add_help_link();
	
	return true;
}	

/**
*  Test a submitted signiture for links and words
*  Returns true if bad things detected
*/
function link_filter_test_signiture($signature){
	global $user;

	//do we need to check this user?
	if(!$this->link_filter_check()) return false; //don't check, no error
	
	if($this->link_filter_sleeper_check()) return true; //if it is a sleeper agent just return error, don;t do the check
	
	if($this->load_values_from_db) $this->link_filter_load_list_from_db(); //load list values from DB if configured	
	
	//If there isn't a phpbb3 no_link message add one
	if (empty($user->lang['NO_LINK_FOR_YOU'])){
		$user->lang['NO_LINK_FOR_YOU']='Antispam: You can\'t have off-site URLs in your sig until you post a few times. ';
	}
	//If there isn't a phpbb3 no_word message add one
	if (empty($user->lang['NO_WORD_FOR_YOU'])){
		$user->lang['NO_WORD_FOR_YOU']='Do you kiss your mom with that mouth? We don\'t want to read that! ';
	}
	
	//make a version of the post and subject
	//need the trailing space or it can hang forever in the while loop if only using a local URL
	return $this->link_filter_test(' '.trim($signature).' ');
	
}

/**
*  Test a submitted PM for links and words
*  Returns true if bad things detected
*/
function link_filter_test_pm($message, $subject){
	global $user;
	
	//do we need to check this user?
	if(!$this->link_filter_check()) return false; //don't check, no error
	
	//if($this->found_sleeper) return true; //if it is a sleeper agent we still allow PMs to contact an admin, but we still filter them

	if($this->load_values_from_db) $this->link_filter_load_list_from_db(); //load list values from DB if configured	

	//If there isn't a phpbb3 no_link message add one
	if (empty($user->lang['NO_LINK_FOR_YOU'])){
		$user->lang['NO_LINK_FOR_YOU']='Your message looks too spamy for a new user, please remove off-site URLs.';
	}
	//If there isn't a phpbb3 no_word message add one
	if (empty($user->lang['NO_WORD_FOR_YOU'])){
		$user->lang['NO_WORD_FOR_YOU']='Your message looks too spamy for a new user, please remove bad words or non-english text.';
	}
	
	//make a version of the post and subject
	return $this->link_filter_test(' '.trim($message.' '.$subject).' ');
}

/**
*  Test a submitted post for links and words
*  Returns true if bad things detected
*/
function link_filter_test_post($message, $subject){
	global $user;
	
	//do we need to check this user?
	if(!$this->link_filter_check()) return false; //don't check, no error
	
	if($this->link_filter_sleeper_check()) return true; //if it is a sleeper agent just return error, don;t do the check

	if($this->load_values_from_db) $this->link_filter_load_list_from_db(); //load list values from DB if configured	

	//If there isn't a phpbb3 no_link message add one
	if (empty($user->lang['NO_LINK_FOR_YOU'])){
		$user->lang['NO_LINK_FOR_YOU']='Your post looks too spamy for a new user, please remove off-site URLs.';
	}
	//If there isn't a phpbb3 no_word message add one
	if (empty($user->lang['NO_WORD_FOR_YOU'])){
		$user->lang['NO_WORD_FOR_YOU']='Your post looks too spamy for a new user, please remove bad words or non-english text.';
	}
	
	//make a version of the post and subject
	return $this->link_filter_test(' '.trim($message.' '.$subject).' ');
}

/**
* 	Do we need to check this user?
* 
*/
function link_filter_check()
{
	global $user, $config;

	if($this->load_values_from_db){ //use MOD setting from database
		$this->minimum_days=$config['links_after_num_days'];
		$this->minimum_posts=$config['links_after_num_posts'];
		if($config['links_disable_sleepers']=='1'){
			$this->sleeper_check=true;
		}else{
			$this->sleeper_check=false;
		}
	}

	//check if the user meets filter criteria
	$this->filter_user=((!$user->data['session_admin']) && (($user->data['user_type']==USER_IGNORE) || ($user->data['user_id']==ANONYMOUS) || ($user->data['user_posts']<=($this->minimum_posts-1)) || ($user->data['user_regdate']>((time())-(86400*$this->minimum_days)))));

	//this MIGHT be used in a bigger filter to only apply to new users.
	//$this->filter_user=((!$user->data['session_admin']) && (($user->data['user_type']==USER_IGNORE) || ($user->data['user_id']==ANONYMOUS) || ($user->data['user_new']==1 &&(($user->data['user_posts']<=($this->minimum_posts-1)) || ($user->data['user_regdate']>((time())-(86400*$this->minimum_days)))))));
	
	//If you're not special, we filter you
	return $this->filter_user;
}

function link_filter_sleeper_check(){
	global $user;
	
	//check for spammers who come back later and  try to post (sleeper agents, or aged accounts)
	if(($this->sleeper_check) && ($user->data['user_posts']==0) && ($user->data['user_regdate']<=((time())-(86400*$this->minimum_days)))){
		$this->found_sleeper=$this->found_stuff=true;
		
		//If there isn't a phpbb3 sleeper agent message add one
		if (empty($user->lang['NO_SLEEPER_SPAM_FOR_YOU'])){
			$user->lang['NO_SLEEPER_SPAM_FOR_YOU']='Antispam: account disabled, please contact an admin.';
			//could delete the user automatically here
		}
		$this->error[]=$user->lang['NO_SLEEPER_SPAM_FOR_YOU'].' '.$this->link_filter_add_help_link();
		return true;
	}
	return false;
}

/**
* 	Add a help link if it exists
*  
*/
function link_filter_add_help_link(){
	if(!empty($this->help_url)){
	
		//If there isn't a phpbb3 message add one
		if (empty($user->lang['HELP_LINK'])){
			$user->lang['HELP_LINK']='Click for help';
		}
		
		return '<a href="'.$this->help_url.'">'.$user->lang['HELP_LINK'].'</a>.';
	}
}

/**
* 	if database is enabled this will explore the filter lists into the arrays
*  We move it here so this only happen when we need to filter the user
*/
function link_filter_load_list_from_db(){
	global $config;
	//use MOD setting from database
	$this->whitelist_urls=explode(",", $config['links_allow_always']);
	$this->no_link_strings=explode(",", $config['links_link_strings']);
	$this->no_word_strings=explode(",", $config['links_word_strings']);		
	$this->whitelist_urls=explode(",", $config['links_allow_always']);		
	$this->unicode_filter=$config['links_unicode_filter'];
	$this->minimum_nonunicode_text=(float)$config['links_nonunicode_percent'];
	$this->help_url = $config['links_help_url'];
}

/**
* 	Search the text for forbidden URLs and text. 
*	Add an error to the local error array if found
*	returns true for bad stuff, false for no flags found
* 
*/
function link_filter_test($no_link_message){
	global $user, $config;

	//filter the looozers
	//remove line feeds and stuff
	$no_link_message=str_replace('\n', ' ',$no_link_message);
	$no_link_message=str_replace('\r', ' ', $no_link_message);
	//replace double spaces with single spaces (not sure why, white space?)
	while (strpos($no_link_message, '  ')){
		$no_link_message=str_replace('  ', ' ', $no_link_message);
	}
	
	//remove any own-site references, these are ok
	//first change http://mysite.com to mysite.com so we only have to look once below
	$no_link_message=str_replace($config['server_protocol'].$config['server_name'], $config['server_name'], $no_link_message);

	//whitelist other common domains too
	//we do this by relacing them with our own domain so we only have to run the search once below
	for ($x=0;$x<sizeof($this->whitelist_urls);$x++){
		if(stripos($no_link_message, $this->whitelist_urls[$x])){
			$no_link_message=str_ireplace($this->whitelist_urls[$x], $config['server_name'], $no_link_message);	
		}
	}
	
	//look at all instances of mysite.com
	while ($ok_start=stripos($no_link_message, $config['server_name'])){ //start of mysite.com
		$ok_end=strpos($no_link_message, '[', $ok_start); //find next [ (bbcode?)
		if (!$ok_end){ //if not bbcode
			$ok_end=strpos($no_link_message, ' ', $ok_start); //end is position of next space
		}
		if ($ok_end){
			$no_link_message=substr($no_link_message, 0, $ok_start).substr($no_link_message, $ok_end);//remove own URL
		}
	}
		

	//search for each link element, throw an error when found
	for ($x=0;$x<sizeof($this->no_link_strings);$x++){
		if (stripos($no_link_message, $this->no_link_strings[$x])){
			
			$this->found_links=$this->found_stuff=true;
						
			$this->error[]=$user->lang['NO_LINK_FOR_YOU'].' '.$this->link_filter_add_help_link();
			
			//$x=sizeof($no_link_strings);
			break;//no reason to go further
		}
	}
	
	//search each word, throw an error when found
	for ($x=0;$x<sizeof($this->no_word_strings);$x++){
		if (stripos($no_link_message, $this->no_word_strings[$x])){
		
			$this->found_words=$this->found_stuff=true;
		
			if($this->show_trigger_word){//show the cause so the user isn't stumped 
				$this->error[]=$user->lang['NO_WORD_FOR_YOU'].' ('.$this->no_word_strings[$x].') '.$this->link_filter_add_help_link();
			}else{//don't show the cause
				$this->error[]=$user->lang['NO_WORD_FOR_YOU'].' '.$this->link_filter_add_help_link();
			}
			
			//$x=sizeof($no_link_strings);
			break;//no reason to search further
		}
	}
	
	if($this->found_stuff==true) return true; //don't continue checking below
	
	//make a smaller subset for preg_match to save time and cycles
	if(strlen($no_link_message) > 512){
		$no_link_message = substr($no_link_message, 0, 512);
	}
	
	//Check for unicode characters that don't belong in the language of this forum	
	if(!empty($this->unicode_filter)){
		if(preg_match($this->unicode_filter, $no_link_message, $m)==1){ //test for unicode character ranged defined by user
		
			$this->found_unicode=$this->found_stuff=true;

			$this->error[]=$user->lang['NO_WORD_FOR_YOU'].' '.$this->link_filter_add_help_link();
			
			return true; //don't continue checking below
		}
	}
	
	//check the percentage of the text that is NOT unicode
	//see http://www.mawhorter.net/web-development/easily-detecting-if-a-block-of-text-is-written-in-english-non-unicode-languages
	if((!empty($this->minimum_nonunicode_text))){

		//found the length in unicode mode
		$ulen = preg_match_all("#.#u", $no_link_message, $m);
		//find length without unicode
		$len  = preg_match_all('#.#', $no_link_message, $m);
		
		//determine if % of non-unicode is enough
		if(($ulen/$len)<(float)$this->minimum_nonunicode_text){
			$this->found_unicode=$this->found_stuff=true;
			$this->error[]=$user->lang['NO_WORD_FOR_YOU'].' ('.$ulen.'/'.$len.') '.$this->link_filter_add_help_link();
		}	
	}

	return $this->found_stuff;

}

function link_filter_purge_zombies(){
	global $db, $user;
	
	if($this->minimum_days<1) return; //don't delete if there is no days setting

	// Get bot ids
	$sql = 'SELECT user_id
		FROM ' . BOTS_TABLE;
	$result = $db->sql_query($sql);

	$bot_ids = array();
	while ($row = $db->sql_fetchrow($result))
	{
		$bot_ids[] = $row['user_id'];
	}
	$db->sql_freeresult($result);

	// Select the group of users to delete
	$sql = 'SELECT user_id, username
		FROM ' . USERS_TABLE . '
		WHERE user_id <> ' . ANONYMOUS . '
			AND user_type <> ' . USER_FOUNDER .'
			AND user_regdate < ' . gmmktime(0, 0, 0, date("m"), (date("d")-$this->minimum_days), date("Y")).'
			AND user_posts = 0'; 
	$result = $db->sql_query($sql);

	$usernames = array();

	//this would be simpler with a SQL statment, but recycling the ppBB prune functon makes it more robust
	while ($row = $db->sql_fetchrow($result))
	{
		// Do not prune bots and the user currently pruning.
		if ($row['user_id'] != $user->data['user_id'] && !in_array($row['user_id'], $bot_ids))
		{
			//user_delete('remove', $row['user_id']);//delete the user and all posts (there should be none though)
			user_delete('retain', $row['user_id'],$row['username']); //delete users but not posts, safer just in case
			$usernames[]=$row['username'];//keep the list for the log
		}
	}
	$db->sql_freeresult($result);
	
	//add log message
	add_log('admin', 'LOG_PRUNE_USER_DEL_DEL', implode(', ', $usernames));

}

}//class

?>
Please do not PM or mail with questions. Ask in the forum where everyone can share the answer.
dangerousprototypes
Registered User
Posts: 91
Joined: Fri Feb 11, 2011 5:53 am
Contact:

Re: [RC] Disable links for new users & guests

Post by dangerousprototypes »

My final version:
*Tested zombie registration purge, added to cron and should run every two hours, see updated install instructions on the wiki
*Added class source to SVN for version tracking

To do (not pressing):
1. Sliding window based on first post time
2. Better "cron" integration for zombie purge, the current method is a total hack
3. Apply only filter to the new user group so trusted users get links asap (one commented line should already do this, but is totally untested)

I'm happy with it for now, so I'll test it for a while and wait for tester feedback (would be much appreciated).

Have not had a spam since installing the filter, and I have been able to re-enable the member list (was spammy), new user signatures, and even guest posting! Lots of zombie registrations though, but no links. I'll know in a few hours if the zombie purge is working in the live forum instead of just my test system.

Code: Select all

<?php
/**
*
* functions_link_filter.php version r672
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
* Modified by Ian Lesnet (http://dangerousprototypes.com)
* Documentation and install info here: 
*  http://dangerousprototypes.com/docs/PhpBB3_MOD:_Disable_links_for_new_users
*
*/

/**
* @ignore
*/
if (!defined('IN_PHPBB'))
{
   exit;
}

class link_filter{

	//--settings variables--//
	// An array of no-nos. Add whatever you need...
   private $no_link_strings=array('http://', 'www.', '.com', '.net', '.org', '.uk', '.ly', '.me', '.ru', '.biz', '.info', 'dot com', 'dot net', 'dot org', 'dotcom', 'dotnet', 'dotorg', '_com', '_org', '_co_uk', '_ru', 'dot ru');


	//a secondary spam words filter
   private $no_word_strings=array('porn', 'xxx', 'interracial', 'sex','gangbang','slut', 'cum', 'penis','orgy', 'girlfriend', 
							'vagina', 'cunt', 'bitch', 'fuck', 'dick', 'hottest','suck', 'pussy',
							'viagra', 'casino', 'mortgage', 'debt','poker','games','vioxx', 'xanax','blackjack',
							'all natural', 'buy direct', 'home business', 'premium', 'betting', 'players', 'gamble', 'gambling',
							//'thanks for sharing',  'new member', 'nice web site', 'nice site',
							);
	private $show_trigger_word=true; //show the user the word that triggered the error
	
	//URLS to ALLOW always. In additon to own-site urls
	private $whitelist_urls=array('http://code.google.com', 'http://sourceforge.net',);
	
	//a regex to filter a unicode character range. 
	//Will throw an error if any of these characters are included in a post. 
	//leave blank to disable this feature
	//for unicode blocks see: http://www.fileformat.info/info/unicode/block/index.htm
	//0400-04FF - Cyrillic (Russian)
	//0600-06FF - Arabic
	//3100-312F - Mandarin Bopomofo block
	//example of chaining together: '/([\x{0400}-\x{04FF}]|[\x{0600}-\x{06FF}])+/u';
	private $unicode_filter='/([\x{0400}-\x{04FF}]|[\x{0600}-\x{06FF}])+/u';
	
	//what percentage of the post must be non-unicode character (mosly useful for english language sites)
	//leave blank to disable this feature
	private $minimum_nonunicode_text=0.95; //percent of post that must be non-unicode
	
	//where can the user get more help about the filter? Could be a forum post or web page
	//if blank the link will NOT be added to the error messages
	//leave blank to no use this features
	private $help_url = 'http://dangerousprototypes.com/forum/viewtopic.php?f=2&t=1846&p=17886#p17886';
	
	private $load_values_from_db=true; //enable to use with DB, or use the values below
	private $minimum_days=1; //minimum days as member to post links
	private $minimum_posts=1; //minimum posts for member to post links
	
	private $sleeper_check=true; //users with 0 posts who try to post after minimum_days will be prohibited
	
	//-- Reporting variables--//
	public $filter_user=false; //we decided to filter this user (they met our criteria)
	
	public $found_stuff=false; //did we find anything? (any item below)
	public $found_sleeper=false;//did we determine this user to be a sleeper agent?
	public $found_links=false; //we found links
	public $found_words=false;//we found bad words
	public $found_unicode=false;
	
	public $error=array(); //holds text error array

	
/**
*  Test if user can have a profile yet
*  returns false if they do NOT need to be filtered
*/
function link_filter_test_profile(){
	global $user;
	
	//do we need to check this user?
	if(!$this->link_filter_check()) return false; //don't check, no error
	
	if($this->link_filter_sleeper_check()) return true; //if it is a sleeper agent just return error, don;t do the check
		
	//If there isn't a phpbb3 no_link message add one
	if (empty($user->lang['NO_LINK_FOR_YOU'])){
		$user->lang['NO_PROFILE_FOR_YOU']='Antispam: You can\'t have a profile yet. You need to post a few times first.';
	}
	
	$this->error[]=$user->lang['NO_PROFILE_FOR_YOU'].' '.$this->link_filter_add_help_link();
	
	return true;
}	

/**
*  Test a submitted signiture for links and words
*  Returns true if bad things detected
*/
function link_filter_test_signiture($signature){
	global $user;

	//do we need to check this user?
	if(!$this->link_filter_check()) return false; //don't check, no error
	
	if($this->link_filter_sleeper_check()) return true; //if it is a sleeper agent just return error, don;t do the check
	
	if($this->load_values_from_db) $this->link_filter_load_list_from_db(); //load list values from DB if configured	
	
	//If there isn't a phpbb3 no_link message add one
	if (empty($user->lang['NO_LINK_FOR_YOU'])){
		$user->lang['NO_LINK_FOR_YOU']='Antispam: You can\'t have off-site URLs in your sig until you post a few times. ';
	}
	//If there isn't a phpbb3 no_word message add one
	if (empty($user->lang['NO_WORD_FOR_YOU'])){
		$user->lang['NO_WORD_FOR_YOU']='Do you kiss your mom with that mouth? We don\'t want to read that! ';
	}
	
	//make a version of the post and subject
	//need the trailing space or it can hang forever in the while loop if only using a local URL
	return $this->link_filter_test(' '.trim($signature).' ');
	
}

/**
*  Test a submitted PM for links and words
*  Returns true if bad things detected
*/
function link_filter_test_pm($message, $subject){
	global $user;
	
	//do we need to check this user?
	if(!$this->link_filter_check()) return false; //don't check, no error
	
	//if($this->found_sleeper) return true; //if it is a sleeper agent we still allow PMs to contact an admin, but we still filter them

	if($this->load_values_from_db) $this->link_filter_load_list_from_db(); //load list values from DB if configured	

	//If there isn't a phpbb3 no_link message add one
	if (empty($user->lang['NO_LINK_FOR_YOU'])){
		$user->lang['NO_LINK_FOR_YOU']='Your message looks too spamy for a new user, please remove off-site URLs.';
	}
	//If there isn't a phpbb3 no_word message add one
	if (empty($user->lang['NO_WORD_FOR_YOU'])){
		$user->lang['NO_WORD_FOR_YOU']='Your message looks too spamy for a new user, please remove bad words or non-english text.';
	}
	
	//make a version of the post and subject
	return $this->link_filter_test(' '.trim($message.' '.$subject).' ');
}

/**
*  Test a submitted post for links and words
*  Returns true if bad things detected
*/
function link_filter_test_post($message, $subject){
	global $user;
	
	//do we need to check this user?
	if(!$this->link_filter_check()) return false; //don't check, no error
	
	if($this->link_filter_sleeper_check()) return true; //if it is a sleeper agent just return error, don;t do the check

	if($this->load_values_from_db) $this->link_filter_load_list_from_db(); //load list values from DB if configured	

	//If there isn't a phpbb3 no_link message add one
	if (empty($user->lang['NO_LINK_FOR_YOU'])){
		$user->lang['NO_LINK_FOR_YOU']='Your post looks too spamy for a new user, please remove off-site URLs.';
	}
	//If there isn't a phpbb3 no_word message add one
	if (empty($user->lang['NO_WORD_FOR_YOU'])){
		$user->lang['NO_WORD_FOR_YOU']='Your post looks too spamy for a new user, please remove bad words or non-english text.';
	}
	
	//make a version of the post and subject
	return $this->link_filter_test(' '.trim($message.' '.$subject).' ');
}

/**
* 	Do we need to check this user?
* 
*/
function link_filter_check()
{
	global $user, $config;

	if($this->load_values_from_db){ //use MOD setting from database
		$this->minimum_days=$config['links_after_num_days'];
		$this->minimum_posts=$config['links_after_num_posts'];
		if($config['links_disable_sleepers']=='1'){
			$this->sleeper_check=true;
		}else{
			$this->sleeper_check=false;
		}
	}

	//check if the user meets filter criteria
	$this->filter_user=((!$user->data['session_admin']) && (($user->data['user_type']==USER_IGNORE) || ($user->data['user_id']==ANONYMOUS) || ($user->data['user_posts']<=($this->minimum_posts-1)) || ($user->data['user_regdate']>((time())-(86400*$this->minimum_days)))));

	//this MIGHT be used in a bigger filter to only apply to new users.
	//$this->filter_user=((!$user->data['session_admin']) && (($user->data['user_type']==USER_IGNORE) || ($user->data['user_id']==ANONYMOUS) || ($user->data['user_new']==1 &&(($user->data['user_posts']<=($this->minimum_posts-1)) || ($user->data['user_regdate']>((time())-(86400*$this->minimum_days)))))));
	
	//If you're not special, we filter you
	return $this->filter_user;
}

/**
* 	check for spammers who come back later and  try to post (sleeper agents, or aged accounts)
* 
*/
function link_filter_sleeper_check(){
	global $user;
	
	//check for spammers who come back later and  try to post (sleeper agents, or aged accounts)
	if(($this->sleeper_check) && ($user->data['user_id']!=ANONYMOUS) && ($user->data['user_posts']==0) && ($user->data['user_regdate']<=((time())-(86400*$this->minimum_days)))){
		$this->found_sleeper=$this->found_stuff=true;
		
		//If there isn't a phpbb3 sleeper agent message add one
		if (empty($user->lang['NO_SLEEPER_SPAM_FOR_YOU'])){
			$user->lang['NO_SLEEPER_SPAM_FOR_YOU']='Antispam: account disabled, please contact an admin.';
			//could delete the user automatically here
		}
		$this->error[]=$user->lang['NO_SLEEPER_SPAM_FOR_YOU'].' '.$this->link_filter_add_help_link();
		return true;
	}
	return false;
}

/**
* 	Add a help link if it exists
*  
*/
function link_filter_add_help_link(){
	if(!empty($this->help_url)){
	
		//If there isn't a phpbb3 message add one
		if (empty($user->lang['HELP_LINK'])){
			$user->lang['HELP_LINK']='Click for help';
		}
		
		return '<a href="'.$this->help_url.'">'.$user->lang['HELP_LINK'].'</a>.';
	}
}

/**
* 	if database is enabled this will explore the filter lists into the arrays
*  We move it here so this only happen when we need to filter the user
*/
function link_filter_load_list_from_db(){
	global $config;
	//use MOD setting from database
	$this->whitelist_urls=explode(",", $config['links_allow_always']);
	$this->no_link_strings=explode(",", $config['links_link_strings']);
	$this->no_word_strings=explode(",", $config['links_word_strings']);		
	$this->whitelist_urls=explode(",", $config['links_allow_always']);		
	$this->unicode_filter=$config['links_unicode_filter'];
	$this->minimum_nonunicode_text=(float)$config['links_nonunicode_percent'];
	$this->help_url = $config['links_help_url'];
}

/**
* 	Search the text for forbidden URLs and text. 
*	Add an error to the local error array if found
*	returns true for bad stuff, false for no flags found
* 
*/
function link_filter_test($no_link_message){
	global $user, $config;

	//filter the looozers
	//remove line feeds and stuff
	$no_link_message=str_replace('\n', ' ',$no_link_message);
	$no_link_message=str_replace('\r', ' ', $no_link_message);
	//replace double spaces with single spaces (not sure why, white space?)
	while (strpos($no_link_message, '  ')){
		$no_link_message=str_replace('  ', ' ', $no_link_message);
	}
	
	//remove any own-site references, these are ok
	//first change http://mysite.com to mysite.com so we only have to look once below
	$no_link_message=str_replace($config['server_protocol'].$config['server_name'], $config['server_name'], $no_link_message);

	//whitelist other common domains too
	//we do this by relacing them with our own domain so we only have to run the search once below
	for ($x=0;$x<sizeof($this->whitelist_urls);$x++){
		if(stripos($no_link_message, $this->whitelist_urls[$x])){
			$no_link_message=str_ireplace($this->whitelist_urls[$x], $config['server_name'], $no_link_message);	
		}
	}
	
	//look at all instances of mysite.com
	while ($ok_start=stripos($no_link_message, $config['server_name'])){ //start of mysite.com
		$ok_end=strpos($no_link_message, '[', $ok_start); //find next [ (bbcode?)
		if (!$ok_end){ //if not bbcode
			$ok_end=strpos($no_link_message, ' ', $ok_start); //end is position of next space
		}
		if ($ok_end){
			$no_link_message=substr($no_link_message, 0, $ok_start).substr($no_link_message, $ok_end);//remove own URL
		}
	}
		

	//search for each link element, throw an error when found
	for ($x=0;$x<sizeof($this->no_link_strings);$x++){
		if (stripos($no_link_message, $this->no_link_strings[$x])){
			
			$this->found_links=$this->found_stuff=true;
						
			$this->error[]=$user->lang['NO_LINK_FOR_YOU'].' '.$this->link_filter_add_help_link();
			
			//$x=sizeof($no_link_strings);
			break;//no reason to go further
		}
	}
	
	//search each word, throw an error when found
	for ($x=0;$x<sizeof($this->no_word_strings);$x++){
		if (stripos($no_link_message, $this->no_word_strings[$x])){
		
			$this->found_words=$this->found_stuff=true;
		
			if($this->show_trigger_word){//show the cause so the user isn't stumped 
				$this->error[]=$user->lang['NO_WORD_FOR_YOU'].' ('.$this->no_word_strings[$x].') '.$this->link_filter_add_help_link();
			}else{//don't show the cause
				$this->error[]=$user->lang['NO_WORD_FOR_YOU'].' '.$this->link_filter_add_help_link();
			}
			
			//$x=sizeof($no_link_strings);
			break;//no reason to search further
		}
	}
	
	if($this->found_stuff==true) return true; //don't continue checking below
	
	//make a smaller subset for preg_match to save time and cycles
	if(strlen($no_link_message) > 512){
		$no_link_message = substr($no_link_message, 0, 512);
	}
	
	//Check for unicode characters that don't belong in the language of this forum	
	if(!empty($this->unicode_filter)){
		if(preg_match($this->unicode_filter, $no_link_message, $m)==1){ //test for unicode character ranged defined by user
		
			$this->found_unicode=$this->found_stuff=true;

			$this->error[]=$user->lang['NO_WORD_FOR_YOU'].' '.$this->link_filter_add_help_link();
			
			return true; //don't continue checking below
		}
	}
	
	//check the percentage of the text that is NOT unicode
	//see http://www.mawhorter.net/web-development/easily-detecting-if-a-block-of-text-is-written-in-english-non-unicode-languages
	if((!empty($this->minimum_nonunicode_text))){

		//found the length in unicode mode
		$ulen = preg_match_all("#.#u", $no_link_message, $m);
		//find length without unicode
		$len  = preg_match_all('#.#', $no_link_message, $m);
		
		//determine if % of non-unicode is enough
		if(($ulen/$len)<(float)$this->minimum_nonunicode_text){
			$this->found_unicode=$this->found_stuff=true;
			$this->error[]=$user->lang['NO_WORD_FOR_YOU'].' ('.$ulen.'/'.$len.') '.$this->link_filter_add_help_link();
		}	
	}

	return $this->found_stuff;

}

function link_filter_purge_zombies(){
	global $db, $config;
	
	if($this->load_values_from_db){ //use MOD setting from database
		$this->minimum_days=$config['links_after_num_days'];
	}
	
	if($this->minimum_days<1) return; //don't delete if there is no days setting

	// Get bot ids
	$sql = 'SELECT user_id
		FROM ' . BOTS_TABLE;
	$result = $db->sql_query($sql);

	$bot_ids = array();
	while ($row = $db->sql_fetchrow($result))
	{
		$bot_ids[] = $row['user_id'];
	}
	$db->sql_freeresult($result);

	// Select the group of users to delete
	$sql = 'SELECT user_id, username
		FROM ' . USERS_TABLE . '
		WHERE user_id <> ' . ANONYMOUS . '
			AND user_type <> ' . USER_FOUNDER .'
			AND user_regdate < ' . gmmktime(0, 0, 0, date("m"), (date("d")-$this->minimum_days), date("Y")).'
			AND user_posts = 0'; 
	$result = $db->sql_query($sql);

	$usernames = array();

	//this would be simpler with a SQL statment, but recycling the ppBB prune functon makes it more robust
	while ($row = $db->sql_fetchrow($result))
	{

		// Do not prune bots and the user currently pruning.
		if (!in_array($row['user_id'], $bot_ids))
		{
			user_delete('remove', $row['user_id']);//delete the user and all posts (there should be none though)
			//user_delete('retain', $row['user_id'],$row['username']); //delete users but not posts, safer just in case
			$usernames[]=$row['username'];//keep the list for the log
		}
	}
	$db->sql_freeresult($result);
	
	//add log message
	add_log('admin', 'LOG_PRUNE_USER_DEL_DEL', 'Zombie registration cleanup by Disable links for new users MOD:'.implode(', ', $usernames));

}

}//class

?>
Please do not PM or mail with questions. Ask in the forum where everyone can share the answer.
dangerousprototypes
Registered User
Posts: 91
Joined: Fri Feb 11, 2011 5:53 am
Contact:

Re: [RC] Disable links for new users & guests

Post by dangerousprototypes »

One final update. It looks like the zombie registration cleanup is working. Here's a picture of the log entry with all the script registrations being deleted. These accounts signed up, but did not post within 24 hours. The old script purged only 4 hours ago, so this represents about a 4 hour period of spam registrations.

Image

I'm 99.99% sure these are not legit users, and if they are they will register again. Most arn't consistent with the culture of our forum, and there are some obvious hot blonds, forex, property spam names in the bunch. All confused scripts with nowhere to dump a link :)

We used to be on SMF, and they didn't provide a prune utility at all. We had to check-all/delete on pages of 20 at a time. It was a nightmare and I usually just did it from the database directly.
Please do not PM or mail with questions. Ask in the forum where everyone can share the answer.
dangerousprototypes
Registered User
Posts: 91
Joined: Fri Feb 11, 2011 5:53 am
Contact:

Re: [RC] Disable links for new users & guests

Post by dangerousprototypes »

Some scripts are so stupid they don't know they have no signature spam and post little "ego posts" anyways (Nice post, thanks for sharing.. It informs me better..) I added a minor feature that requires the first post to be X characters or longer. In the long run this is could lead to longer first posts, but that will also make the spammer more obvious (or they have to make a real contribution to go unnoticed).
* Enter 0 to disable check
* Only applies to the first post, not every post until the filter is disabled
* Recommended stetting: 73 (light touch, less is more, it should go unnoticed by new users)
If you are testing this, you only need to replace the class file. There's no new edits to phpBB. Any testers out there?

For more than a week we've had no real spam. A few confused scripts, but that's not as offensive as the porn, gaming, and other garbage. No captcha, no questions or codes, and guest posting is enabled. Anonymous forum participation via guest seems to be a net positive for the community.

Edit: I enjoy seeing the log message with 100-200 zombie registrations purged daily :)

Code: Select all

<?php
/**
*
* functions_link_filter.php version r672
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
* Modified by Ian Lesnet (http://dangerousprototypes.com)
* Documentation and install info here: 
*  http://dangerousprototypes.com/docs/PhpBB3_MOD:_Disable_links_for_new_users
*
*/

/**
* @ignore
*/
if (!defined('IN_PHPBB'))
{
   exit;
}

class link_filter{

	//--settings variables--//
	// An array of no-nos. Add whatever you need...
   private $no_link_strings=array('http://', 'www.', '.com', '.net', '.org', '.uk', '.ly', '.me', '.ru', '.biz', '.info', 'dot com', 'dot net', 'dot org', 'dotcom', 'dotnet', 'dotorg', '_com', '_org', '_co_uk', '_ru', 'dot ru');


	//a secondary spam words filter
   private $no_word_strings=array();
	private $show_trigger_word=true; //show the user the word that triggered the error
	
	//URLS to ALLOW always. In additon to own-site urls
	private $whitelist_urls=array('http://code.google.com', 'http://sourceforge.net',);
	
	//a regex to filter a unicode character range. 
	//Will throw an error if any of these characters are included in a post. 
	//leave blank to disable this feature
	//for unicode blocks see: http://www.fileformat.info/info/unicode/block/index.htm
	//0400-04FF - Cyrillic (Russian)
	//0600-06FF - Arabic
	//3100-312F - Mandarin Bopomofo block
	//example of chaining together: '/([\x{0400}-\x{04FF}]|[\x{0600}-\x{06FF}])+/u';
	private $unicode_filter='/([\x{0400}-\x{04FF}]|[\x{0600}-\x{06FF}])+/u';
	
	//what percentage of the post must be non-unicode character (mosly useful for english language sites)
	//leave blank to disable this feature
	private $minimum_nonunicode_text=0.95; //percent of post that must be non-unicode
	
	//where can the user get more help about the filter? Could be a forum post or web page
	//if blank the link will NOT be added to the error messages
	//leave blank to no use this features
	private $help_url = 'http://dangerousprototypes.com/forum/viewtopic.php?f=2&t=1846&p=17886#p17886';
	
	private $load_values_from_db=true; //enable to use with DB, or use the values below
	private $minimum_days=1; //minimum days as member to post links
	private $minimum_posts=1; //minimum posts for member to post links
	
	private $sleeper_check=true; //users with 0 posts who try to post after minimum_days will be prohibited
	
	private $first_post_length=73;//a minimum characters for the first post. enter 0 to disable
	
	//-- Reporting variables--//
	public $filter_user=false; //we decided to filter this user (they met our criteria)
	
	public $found_stuff=false; //did we find anything? (any item below)
	public $found_sleeper=false;//did we determine this user to be a sleeper agent?
	public $found_links=false; //we found links
	public $found_words=false;//we found bad words
	public $found_unicode=false;
	
	public $error=array(); //holds text error array

	
/**
*  Test if user can have a profile yet
*  returns false if they do NOT need to be filtered
*/
function link_filter_test_profile(){
	global $user;
	
	//do we need to check this user?
	if(!$this->link_filter_check()) return false; //don't check, no error
	
	if($this->link_filter_sleeper_check()) return true; //if it is a sleeper agent just return error, don;t do the check
		
	//If there isn't a phpbb3 no_link message add one
	if (empty($user->lang['NO_LINK_FOR_YOU'])){
		$user->lang['NO_PROFILE_FOR_YOU']='Antispam: You can\'t have a profile yet. You need to post a few times first.';
	}
	
	$this->error[]=$user->lang['NO_PROFILE_FOR_YOU'].' '.$this->link_filter_add_help_link();
	
	return true;
}	

/**
*  Test a submitted signiture for links and words
*  Returns true if bad things detected
*/
function link_filter_test_signiture($signature){
	global $user;

	//do we need to check this user?
	if(!$this->link_filter_check()) return false; //don't check, no error
	
	if($this->link_filter_sleeper_check()) return true; //if it is a sleeper agent just return error, don;t do the check
	
	if($this->load_values_from_db) $this->link_filter_load_list_from_db(); //load list values from DB if configured	
	
	//If there isn't a phpbb3 no_link message add one
	if (empty($user->lang['NO_LINK_FOR_YOU'])){
		$user->lang['NO_LINK_FOR_YOU']='Antispam: You can\'t have off-site URLs in your sig until you post a few times. ';
	}
	//If there isn't a phpbb3 no_word message add one
	if (empty($user->lang['NO_WORD_FOR_YOU'])){
		$user->lang['NO_WORD_FOR_YOU']='Do you kiss your mom with that mouth? We don\'t want to read that! ';
	}
	
	//make a version of the post and subject
	//need the trailing space or it can hang forever in the while loop if only using a local URL
	return $this->link_filter_test(' '.trim($signature).' ');
	
}

/**
*  Test a submitted PM for links and words
*  Returns true if bad things detected
*/
function link_filter_test_pm($message, $subject){
	global $user;
	
	//do we need to check this user?
	if(!$this->link_filter_check()) return false; //don't check, no error
	
	//if($this->found_sleeper) return true; //if it is a sleeper agent we still allow PMs to contact an admin, but we still filter them

	if($this->load_values_from_db) $this->link_filter_load_list_from_db(); //load list values from DB if configured	

	//If there isn't a phpbb3 no_link message add one
	if (empty($user->lang['NO_LINK_FOR_YOU'])){
		$user->lang['NO_LINK_FOR_YOU']='Your message looks too spamy for a new user, please remove off-site URLs.';
	}
	//If there isn't a phpbb3 no_word message add one
	if (empty($user->lang['NO_WORD_FOR_YOU'])){
		$user->lang['NO_WORD_FOR_YOU']='Your message looks too spamy for a new user, please remove bad words or non-english text.';
	}
	
	//make a version of the post and subject
	return $this->link_filter_test(' '.trim($message.' '.$subject).' ');
}

/**
*  Test a submitted post for links and words
*  Returns true if bad things detected
*/
function link_filter_test_post($message, $subject){
	global $user;
	
	//do we need to check this user?
	if(!$this->link_filter_check()) return false; //don't check, no error
	
	if(($user->data['user_posts']==0)&& (strlen($message)<$this->first_post_length)){//first post, check length
		//If there isn't a phpbb3 message add one
		if (empty($user->lang['NO_LINK_TOO_SHORT'])){
			$user->lang['NO_LINK_TOO_SHORT']='Antispam: Sorry, your first post needs to be just a little longer.';
		}
		$this->error[]=$user->lang['NO_LINK_TOO_SHORT'].' '.$this->link_filter_add_help_link();
		return true;
	}
	
	if($this->link_filter_sleeper_check()) return true; //if it is a sleeper agent just return error, don;t do the check

	if($this->load_values_from_db) $this->link_filter_load_list_from_db(); //load list values from DB if configured	

	//If there isn't a phpbb3 no_link message add one
	if (empty($user->lang['NO_LINK_FOR_YOU'])){
		$user->lang['NO_LINK_FOR_YOU']='Your post looks too spamy for a new user, please remove off-site URLs.';
	}
	//If there isn't a phpbb3 no_word message add one
	if (empty($user->lang['NO_WORD_FOR_YOU'])){
		$user->lang['NO_WORD_FOR_YOU']='Your post looks too spamy for a new user, please remove bad words or non-english text.';
	}
	
	//make a version of the post and subject
	return $this->link_filter_test(' '.trim($message.' '.$subject).' ');
}

/**
* 	Do we need to check this user?
* 
*/
function link_filter_check()
{
	global $user, $config;

	if($this->load_values_from_db){ //use MOD setting from database
		$this->minimum_days=$config['links_after_num_days'];
		$this->minimum_posts=$config['links_after_num_posts'];
		if($config['links_disable_sleepers']=='1'){
			$this->sleeper_check=true;
		}else{
			$this->sleeper_check=false;
		}
	}

	//check if the user meets filter criteria
	$this->filter_user=((!$user->data['session_admin']) && (($user->data['user_type']==USER_IGNORE) || ($user->data['user_id']==ANONYMOUS) || ($user->data['user_posts']<=($this->minimum_posts-1)) || ($user->data['user_regdate']>((time())-(86400*$this->minimum_days)))));

	//this MIGHT be used in a bigger filter to only apply to new users.
	//$this->filter_user=((!$user->data['session_admin']) && (($user->data['user_type']==USER_IGNORE) || ($user->data['user_id']==ANONYMOUS) || ($user->data['user_new']==1 &&(($user->data['user_posts']<=($this->minimum_posts-1)) || ($user->data['user_regdate']>((time())-(86400*$this->minimum_days)))))));
	
	//If you're not special, we filter you
	return $this->filter_user;
}

/**
* 	check for spammers who come back later and  try to post (sleeper agents, or aged accounts)
* 
*/
function link_filter_sleeper_check(){
	global $user;
	
	//check for spammers who come back later and  try to post (sleeper agents, or aged accounts)
	if(($this->sleeper_check) && ($user->data['user_id']!=ANONYMOUS) && ($user->data['user_posts']==0) && ($user->data['user_regdate']<=((time())-(86400*$this->minimum_days)))){
		$this->found_sleeper=$this->found_stuff=true;
		
		//If there isn't a phpbb3 sleeper agent message add one
		if (empty($user->lang['NO_SLEEPER_SPAM_FOR_YOU'])){
			$user->lang['NO_SLEEPER_SPAM_FOR_YOU']='Antispam: account disabled, please contact an admin.';
			//could delete the user automatically here
		}
		$this->error[]=$user->lang['NO_SLEEPER_SPAM_FOR_YOU'].' '.$this->link_filter_add_help_link();
		return true;
	}
	return false;
}

/**
* 	Add a help link if it exists
*  
*/
function link_filter_add_help_link(){
	if(!empty($this->help_url)){
	
		//If there isn't a phpbb3 message add one
		if (empty($user->lang['HELP_LINK'])){
			$user->lang['HELP_LINK']='Click for help';
		}
		
		return '<a href="'.$this->help_url.'">'.$user->lang['HELP_LINK'].'</a>.';
	}
}

/**
* 	if database is enabled this will explore the filter lists into the arrays
*  We move it here so this only happen when we need to filter the user
*/
function link_filter_load_list_from_db(){
	global $config;
	//use MOD setting from database
	$this->whitelist_urls=explode(",", $config['links_allow_always']);
	$this->no_link_strings=explode(",", $config['links_link_strings']);
	$this->no_word_strings=explode(",", $config['links_word_strings']);		
	$this->whitelist_urls=explode(",", $config['links_allow_always']);		
	$this->unicode_filter=$config['links_unicode_filter'];
	$this->minimum_nonunicode_text=(float)$config['links_nonunicode_percent'];
	$this->help_url = $config['links_help_url'];
}

/**
* 	Search the text for forbidden URLs and text. 
*	Add an error to the local error array if found
*	returns true for bad stuff, false for no flags found
* 
*/
function link_filter_test($no_link_message){
	global $user, $config;

	//filter the looozers
	//remove line feeds and stuff
	$no_link_message=str_replace('\n', ' ',$no_link_message);
	$no_link_message=str_replace('\r', ' ', $no_link_message);
	//replace double spaces with single spaces (not sure why, white space?)
	while (strpos($no_link_message, '  ')){
		$no_link_message=str_replace('  ', ' ', $no_link_message);
	}
	
	//remove any own-site references, these are ok
	//first change http://mysite.com to mysite.com so we only have to look once below
	$no_link_message=str_replace($config['server_protocol'].$config['server_name'], $config['server_name'], $no_link_message);

	//whitelist other common domains too
	//we do this by relacing them with our own domain so we only have to run the search once below
	for ($x=0;$x<sizeof($this->whitelist_urls);$x++){
		if(stripos($no_link_message, $this->whitelist_urls[$x])){
			$no_link_message=str_ireplace($this->whitelist_urls[$x], $config['server_name'], $no_link_message);	
		}
	}
	
	//look at all instances of mysite.com
	while ($ok_start=stripos($no_link_message, $config['server_name'])){ //start of mysite.com
		$ok_end=strpos($no_link_message, '[', $ok_start); //find next [ (bbcode?)
		if (!$ok_end){ //if not bbcode
			$ok_end=strpos($no_link_message, ' ', $ok_start); //end is position of next space
		}
		if ($ok_end){
			$no_link_message=substr($no_link_message, 0, $ok_start).substr($no_link_message, $ok_end);//remove own URL
		}
	}
		

	//search for each link element, throw an error when found
	for ($x=0;$x<sizeof($this->no_link_strings);$x++){
		if (stripos($no_link_message, $this->no_link_strings[$x])){
			
			$this->found_links=$this->found_stuff=true;
						
			$this->error[]=$user->lang['NO_LINK_FOR_YOU'].' '.$this->link_filter_add_help_link();
			
			//$x=sizeof($no_link_strings);
			break;//no reason to go further
		}
	}
	
	//search each word, throw an error when found
	for ($x=0;$x<sizeof($this->no_word_strings);$x++){
		if (stripos($no_link_message, $this->no_word_strings[$x])){
		
			$this->found_words=$this->found_stuff=true;
		
			if($this->show_trigger_word){//show the cause so the user isn't stumped 
				$this->error[]=$user->lang['NO_WORD_FOR_YOU'].' ('.$this->no_word_strings[$x].') '.$this->link_filter_add_help_link();
			}else{//don't show the cause
				$this->error[]=$user->lang['NO_WORD_FOR_YOU'].' '.$this->link_filter_add_help_link();
			}
			
			//$x=sizeof($no_link_strings);
			break;//no reason to search further
		}
	}
	
	if($this->found_stuff==true) return true; //don't continue checking below
	
	//make a smaller subset for preg_match to save time and cycles
	if(strlen($no_link_message) > 512){
		$no_link_message = substr($no_link_message, 0, 512);
	}
	
	//Check for unicode characters that don't belong in the language of this forum	
	if(!empty($this->unicode_filter)){
		if(preg_match($this->unicode_filter, $no_link_message, $m)==1){ //test for unicode character ranged defined by user
		
			$this->found_unicode=$this->found_stuff=true;

			$this->error[]=$user->lang['NO_WORD_FOR_YOU'].' '.$this->link_filter_add_help_link();
			
			return true; //don't continue checking below
		}
	}
	
	//check the percentage of the text that is NOT unicode
	//see http://www.mawhorter.net/web-development/easily-detecting-if-a-block-of-text-is-written-in-english-non-unicode-languages
	if((!empty($this->minimum_nonunicode_text))){

		//found the length in unicode mode
		$ulen = preg_match_all("#.#u", $no_link_message, $m);
		//find length without unicode
		$len  = preg_match_all('#.#', $no_link_message, $m);
		
		//determine if % of non-unicode is enough
		if(($ulen/$len)<(float)$this->minimum_nonunicode_text){
			$this->found_unicode=$this->found_stuff=true;
			$this->error[]=$user->lang['NO_WORD_FOR_YOU'].' ('.$ulen.'/'.$len.') '.$this->link_filter_add_help_link();
		}	
	}

	return $this->found_stuff;

}

function link_filter_purge_zombies(){
	global $db, $config;
	
	if($this->load_values_from_db){ //use MOD setting from database
		$this->minimum_days=$config['links_after_num_days'];
	}
	
	if($this->minimum_days<1) return; //don't delete if there is no days setting

	// Get bot ids
	$sql = 'SELECT user_id
		FROM ' . BOTS_TABLE;
	$result = $db->sql_query($sql);

	$bot_ids = array();
	while ($row = $db->sql_fetchrow($result))
	{
		$bot_ids[] = $row['user_id'];
	}
	$db->sql_freeresult($result);

	// Select the group of users to delete
	$sql = 'SELECT user_id, username
		FROM ' . USERS_TABLE . '
		WHERE user_id <> ' . ANONYMOUS . '
			AND user_type <> ' . USER_FOUNDER .'
			AND user_regdate < ' . gmmktime(0, 0, 0, date("m"), (date("d")-$this->minimum_days), date("Y")).'
			AND user_posts = 0'; 
	$result = $db->sql_query($sql);

	$usernames = array();

	//this would be simpler with a SQL statment, but recycling the ppBB prune functon makes it more robust
	while ($row = $db->sql_fetchrow($result))
	{

		// Do not prune bots and the user currently pruning.
		if (!in_array($row['user_id'], $bot_ids))
		{
			user_delete('remove', $row['user_id']);//delete the user and all posts (there should be none though)
			//user_delete('retain', $row['user_id'],$row['username']); //delete users but not posts, safer just in case
			$usernames[]=$row['username'];//keep the list for the log
		}
	}
	$db->sql_freeresult($result);
	
	//add log message
	add_log('admin', 'LOG_PRUNE_USER_DEL_DEL', 'Zombie registration cleanup by Disable links for new users MOD:'.implode(', ', $usernames));

}

}//class

?>
Please do not PM or mail with questions. Ask in the forum where everyone can share the answer.
Philthy
Registered User
Posts: 210
Joined: Tue Dec 27, 2005 10:05 am
Location: Dawlish, Devon
Contact:

Re: [RC] Disable links for new users & guests

Post by Philthy »

Should we package this up, so it is easier for testers to install. I'm a little confused being honest?
If you want me to do it, could you list the edits, and I can make the class, and package it up.
Like you, I have guest posting enabled again, and it does seem to make a difference to the amount of posts I get. They usually sign up as members as well after making a few posts.
Go on ! it's not as steep as it looks.....
dangerousprototypes
Registered User
Posts: 91
Joined: Fri Feb 11, 2011 5:53 am
Contact:

Re: [RC] Disable links for new users & guests

Post by dangerousprototypes »

I added the list of edits to a wiki - the link is in the header of the class. It was easier than doing it in the forum and quicker to update.

I thought links were against site policy so I was hesitant to put it in my post, but here it is:
http://dangerousprototypes.com/docs/Php ... _new_users

That includes the complete list of edits (including ACP stuff), documentation of the features and settings, etc. Do with it as you please.

The absolute latest version of the class is in Google Code SVN (but is easy to download from the web too). Updates can be made by dropping the updated class in the /includes/ folder.
Please do not PM or mail with questions. Ask in the forum where everyone can share the answer.
Philthy
Registered User
Posts: 210
Joined: Tue Dec 27, 2005 10:05 am
Location: Dawlish, Devon
Contact:

Re: [RC] Disable links for new users & guests

Post by Philthy »

Typo on the wiki:

Code: Select all

function_link_filter.php
Put the class in includes/function_link_filter.php.
Latest version in SVN
Should read:

Code: Select all

functions_link_filter.php
Put the class in includes/functions_link_filter.php.
Latest version in SVN
A spelling mistake in the addition to:
/language/en/acp/board.php:

Code: Select all

'LINKS_DISABLE_SLEEPERS_EXPLAIN'=>'Disable posting, signiture,
Should read:

Code: Select all

'LINKS_DISABLE_SLEEPERS_EXPLAIN'=>'Disable posting, signature,
Still debugging/checking.
Go on ! it's not as steep as it looks.....
dangerousprototypes
Registered User
Posts: 91
Joined: Fri Feb 11, 2011 5:53 am
Contact:

Re: [RC] Disable links for new users & guests

Post by dangerousprototypes »

Thank you. I updated these items. I also fixed the spelling in the code on the wiki in one place and in the class.

Specifically in ucp_profile.php :

Code: Select all

if($f->link_filter_test_signature($signature))//run the check
And the corresponding function in the class.

Google Code is down so I cannot commit the latest class with the spelling fix, but it is attached and I'll keep trying.

EDIT: google code is up again. Commited the latest version r725 with spelling fixes in the class, wiki is already updated.

Code: Select all

<?php
/**
*
* functions_link_filter.php version r725
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
* Modified by Ian Lesnet (http://dangerousprototypes.com)
* Documentation and install info here: 
*  http://dangerousprototypes.com/docs/PhpBB3_MOD:_Disable_links_for_new_users
*
*/

/**
* @ignore
*/
if (!defined('IN_PHPBB'))
{
   exit;
}

class link_filter{

	//--settings variables--//
	// An array of no-nos. Add whatever you need...
	private $no_link_strings=array();//('http://', 'www.', '.com', '.net', '.org', '.uk', '.ly', '.me', '.ru', '.biz', '.info', 'dot com', 'dot net', 'dot org', 'dotcom', 'dotnet', 'dotorg', '_com', '_org', '_co_uk', '_ru', 'dot ru');

	//a secondary spam words filter
	private $no_word_strings=array();
   
	private $show_trigger_word=true; //show the user the word that triggered the error
	
	//URLS to ALLOW always. In additon to own-site urls
	private $whitelist_urls=array('http://code.google.com', 'http://sourceforge.net',);
	
	//a regex to filter a unicode character range. 
	//Will throw an error if any of these characters are included in a post. 
	//leave blank to disable this feature
	//for unicode blocks see: http://www.fileformat.info/info/unicode/block/index.htm
	//0400-04FF - Cyrillic (Russian)
	//0600-06FF - Arabic
	//3100-312F - Mandarin Bopomofo block
	//example of chaining together: '/([\x{0400}-\x{04FF}]|[\x{0600}-\x{06FF}])+/u';
	private $unicode_filter='/([\x{0400}-\x{04FF}]|[\x{0600}-\x{06FF}])+/u';
	
	//what percentage of the post must be non-unicode character (mosly useful for english language sites)
	//leave blank to disable this feature
	private $minimum_nonunicode_text=0.95; //percent of post that must be non-unicode
	
	//where can the user get more help about the filter? Could be a forum post or web page
	//if blank the link will NOT be added to the error messages
	//leave blank to no use this features
	private $help_url = 'http://dangerousprototypes.com/forum/viewtopic.php?f=2&t=1846&p=17886#p17886';
	
	private $load_values_from_db=true; //enable to use with DB, or use the values below
	private $minimum_days=1; //minimum days as member to post links
	private $minimum_posts=1; //minimum posts for member to post links
	
	private $sleeper_check=true; //users with 0 posts who try to post after minimum_days will be prohibited
	
	private $first_post_length=73;//a minimum characters for the first post. enter 0 to disable
	
	//-- Reporting variables--//
	public $filter_user=false; //we decided to filter this user (they met our criteria)
	
	public $found_stuff=false; //did we find anything? (any item below)
	public $found_sleeper=false;//did we determine this user to be a sleeper agent?
	public $found_links=false; //we found links
	public $found_words=false;//we found bad words
	public $found_unicode=false;
	
	public $error=array(); //holds text error array

	
/**
*  Test if user can have a profile yet
*  returns false if they do NOT need to be filtered
*/
function link_filter_test_profile(){
	global $user;
	
	//do we need to check this user?
	if(!$this->link_filter_check()) return false; //don't check, no error
	
	if($this->link_filter_sleeper_check()) return true; //if it is a sleeper agent just return error, don;t do the check
		
	//If there isn't a phpbb3 no_link message add one
	if (empty($user->lang['NO_LINK_FOR_YOU'])){
		$user->lang['NO_PROFILE_FOR_YOU']='Antispam: You can\'t have a profile yet. You need to post a few times first.';
	}
	
	$this->error[]=$user->lang['NO_PROFILE_FOR_YOU'].' '.$this->link_filter_add_help_link();
	
	return true;
}	

/**
*  Test a submitted signature for links and words
*  Returns true if bad things detected
*/
function link_filter_test_signature($signature){
	global $user;

	//do we need to check this user?
	if(!$this->link_filter_check()) return false; //don't check, no error
	
	if($this->link_filter_sleeper_check()) return true; //if it is a sleeper agent just return error, don;t do the check
	
	if($this->load_values_from_db) $this->link_filter_load_list_from_db(); //load list values from DB if configured	
	
	//If there isn't a phpbb3 no_link message add one
	if (empty($user->lang['NO_LINK_FOR_YOU'])){
		$user->lang['NO_LINK_FOR_YOU']='Antispam: You can\'t have off-site URLs in your sig until you post a few times. ';
	}
	//If there isn't a phpbb3 no_word message add one
	if (empty($user->lang['NO_WORD_FOR_YOU'])){
		$user->lang['NO_WORD_FOR_YOU']='Do you kiss your mom with that mouth? We don\'t want to read that! ';
	}
	
	//make a version of the post and subject
	//need the trailing space or it can hang forever in the while loop if only using a local URL
	return $this->link_filter_test(' '.trim($signature).' ');
	
}

/**
*  Test a submitted PM for links and words
*  Returns true if bad things detected
*/
function link_filter_test_pm($message, $subject){
	global $user;
	
	//do we need to check this user?
	if(!$this->link_filter_check()) return false; //don't check, no error
	
	//if($this->found_sleeper) return true; //if it is a sleeper agent we still allow PMs to contact an admin, but we still filter them

	if($this->load_values_from_db) $this->link_filter_load_list_from_db(); //load list values from DB if configured	

	//If there isn't a phpbb3 no_link message add one
	if (empty($user->lang['NO_LINK_FOR_YOU'])){
		$user->lang['NO_LINK_FOR_YOU']='Your message looks too spamy for a new user, please remove off-site URLs.';
	}
	//If there isn't a phpbb3 no_word message add one
	if (empty($user->lang['NO_WORD_FOR_YOU'])){
		$user->lang['NO_WORD_FOR_YOU']='Your message looks too spamy for a new user, please remove bad words or non-english text.';
	}
	
	//make a version of the post and subject
	return $this->link_filter_test(' '.trim($message.' '.$subject).' ');
}

/**
*  Test a submitted post for links and words
*  Returns true if bad things detected
*/
function link_filter_test_post($message, $subject){
	global $user;
	
	//do we need to check this user?
	if(!$this->link_filter_check()) return false; //don't check, no error
	
	if( (($user->data['user_posts']==0)||($user->data['user_type']==USER_IGNORE)||($user->data['user_id']==ANONYMOUS))&& (strlen($message)<$this->first_post_length)){//first post, check length
		//If there isn't a phpbb3 message add one
		if (empty($user->lang['NO_LINK_TOO_SHORT'])){
			$user->lang['NO_LINK_TOO_SHORT']='Antispam: Sorry, your first post needs to be just a little longer.';
		}
		$this->error[]=$user->lang['NO_LINK_TOO_SHORT'].' '.$this->link_filter_add_help_link();
		return true;
	}
	
	if($this->link_filter_sleeper_check()) return true; //if it is a sleeper agent just return error, don;t do the check

	if($this->load_values_from_db) $this->link_filter_load_list_from_db(); //load list values from DB if configured	

	//If there isn't a phpbb3 no_link message add one
	if (empty($user->lang['NO_LINK_FOR_YOU'])){
		$user->lang['NO_LINK_FOR_YOU']='Your post looks too spamy for a new user, please remove off-site URLs.';
	}
	//If there isn't a phpbb3 no_word message add one
	if (empty($user->lang['NO_WORD_FOR_YOU'])){
		$user->lang['NO_WORD_FOR_YOU']='Your post looks too spamy for a new user, please remove bad words or non-english text.';
	}
	
	//make a version of the post and subject
	return $this->link_filter_test(' '.trim($message.' '.$subject).' ');
}

/**
* 	Do we need to check this user?
* 
*/
function link_filter_check()
{
	global $user, $config;

	if($this->load_values_from_db){ //use MOD setting from database
		$this->minimum_days=$config['links_after_num_days'];
		$this->minimum_posts=$config['links_after_num_posts'];
		if($config['links_disable_sleepers']=='1'){
			$this->sleeper_check=true;
		}else{
			$this->sleeper_check=false;
		}
	}

	//check if the user meets filter criteria
	$this->filter_user=((!$user->data['session_admin']) && (($user->data['user_type']==USER_IGNORE) || ($user->data['user_id']==ANONYMOUS) || ($user->data['user_posts']<=($this->minimum_posts-1)) || ($user->data['user_regdate']>((time())-(86400*$this->minimum_days)))));

	//this MIGHT be used in a bigger filter to only apply to new users.
	//$this->filter_user=((!$user->data['session_admin']) && (($user->data['user_type']==USER_IGNORE) || ($user->data['user_id']==ANONYMOUS) || ($user->data['user_new']==1 &&(($user->data['user_posts']<=($this->minimum_posts-1)) || ($user->data['user_regdate']>((time())-(86400*$this->minimum_days)))))));
	
	//If you're not special, we filter you
	return $this->filter_user;
}

/**
* 	check for spammers who come back later and  try to post (sleeper agents, or aged accounts)
* 
*/
function link_filter_sleeper_check(){
	global $user;
	
	//check for spammers who come back later and  try to post (sleeper agents, or aged accounts)
	if(($this->sleeper_check) && ($user->data['user_id']!=ANONYMOUS) && ($user->data['user_posts']==0) && ($user->data['user_regdate']<=((time())-(86400*$this->minimum_days)))){
		$this->found_sleeper=$this->found_stuff=true;
		
		//If there isn't a phpbb3 sleeper agent message add one
		if (empty($user->lang['NO_SLEEPER_SPAM_FOR_YOU'])){
			$user->lang['NO_SLEEPER_SPAM_FOR_YOU']='Antispam: account disabled, please contact an admin.';
			//could delete the user automatically here
		}
		$this->error[]=$user->lang['NO_SLEEPER_SPAM_FOR_YOU'].' '.$this->link_filter_add_help_link();
		return true;
	}
	return false;
}

/**
* 	Add a help link if it exists
*  
*/
function link_filter_add_help_link(){
	if(!empty($this->help_url)){
	
		//If there isn't a phpbb3 message add one
		if (empty($user->lang['HELP_LINK'])){
			$user->lang['HELP_LINK']='Click for help';
		}
		
		return '<a href="'.$this->help_url.'">'.$user->lang['HELP_LINK'].'</a>.';
	}
}

/**
* 	if database is enabled this will explore the filter lists into the arrays
*  We move it here so this only happen when we need to filter the user
*/
function link_filter_load_list_from_db(){
	global $config;
	//use MOD setting from database
	$this->whitelist_urls=explode(",", $config['links_allow_always']);
	$this->no_link_strings=explode(",", $config['links_link_strings']);
	$this->no_word_strings=explode(",", $config['links_word_strings']);		
	$this->whitelist_urls=explode(",", $config['links_allow_always']);		
	$this->unicode_filter=$config['links_unicode_filter'];
	$this->minimum_nonunicode_text=(float)$config['links_nonunicode_percent'];
	$this->help_url = $config['links_help_url'];
}

/**
* 	Search the text for forbidden URLs and text. 
*	Add an error to the local error array if found
*	returns true for bad stuff, false for no flags found
* 
*/
function link_filter_test($no_link_message){
	global $user, $config;

	//filter the looozers
	//remove line feeds and stuff
	$no_link_message=str_replace('\n', ' ',$no_link_message);
	$no_link_message=str_replace('\r', ' ', $no_link_message);
	//replace double spaces with single spaces (not sure why, white space?)
	while (strpos($no_link_message, '  ')){
		$no_link_message=str_replace('  ', ' ', $no_link_message);
	}
	
	//remove any own-site references, these are ok
	//first change http://mysite.com to mysite.com so we only have to look once below
	$no_link_message=str_replace($config['server_protocol'].$config['server_name'], $config['server_name'], $no_link_message);

	//whitelist other common domains too
	//we do this by relacing them with our own domain so we only have to run the search once below
	for ($x=0;$x<sizeof($this->whitelist_urls);$x++){
		if(stripos($no_link_message, $this->whitelist_urls[$x])){
			$no_link_message=str_ireplace($this->whitelist_urls[$x], $config['server_name'], $no_link_message);	
		}
	}
	
	//look at all instances of mysite.com
	while ($ok_start=stripos($no_link_message, $config['server_name'])){ //start of mysite.com
		$ok_end=strpos($no_link_message, '[', $ok_start); //find next [ (bbcode?)
		if (!$ok_end){ //if not bbcode
			$ok_end=strpos($no_link_message, ' ', $ok_start); //end is position of next space
		}
		if ($ok_end){
			$no_link_message=substr($no_link_message, 0, $ok_start).substr($no_link_message, $ok_end);//remove own URL
		}
	}
		

	//search for each link element, throw an error when found
	for ($x=0;$x<sizeof($this->no_link_strings);$x++){
		if (stripos($no_link_message, $this->no_link_strings[$x])){
			
			$this->found_links=$this->found_stuff=true;
						
			$this->error[]=$user->lang['NO_LINK_FOR_YOU'].' '.$this->link_filter_add_help_link();
			
			//$x=sizeof($no_link_strings);
			break;//no reason to go further
		}
	}
	
	//search each word, throw an error when found
	for ($x=0;$x<sizeof($this->no_word_strings);$x++){
		if (stripos($no_link_message, $this->no_word_strings[$x])){
		
			$this->found_words=$this->found_stuff=true;
		
			if($this->show_trigger_word){//show the cause so the user isn't stumped 
				$this->error[]=$user->lang['NO_WORD_FOR_YOU'].' ('.$this->no_word_strings[$x].') '.$this->link_filter_add_help_link();
			}else{//don't show the cause
				$this->error[]=$user->lang['NO_WORD_FOR_YOU'].' '.$this->link_filter_add_help_link();
			}
			
			//$x=sizeof($no_link_strings);
			break;//no reason to search further
		}
	}
	
	if($this->found_stuff==true) return true; //don't continue checking below
	
	//make a smaller subset for preg_match to save time and cycles
	if(strlen($no_link_message) > 512){
		$no_link_message = substr($no_link_message, 0, 512);
	}
	
	//Check for unicode characters that don't belong in the language of this forum	
	if(!empty($this->unicode_filter)){
		if(preg_match($this->unicode_filter, $no_link_message, $m)==1){ //test for unicode character ranged defined by user
		
			$this->found_unicode=$this->found_stuff=true;

			$this->error[]=$user->lang['NO_WORD_FOR_YOU'].' '.$this->link_filter_add_help_link();
			
			return true; //don't continue checking below
		}
	}
	
	//check the percentage of the text that is NOT unicode
	//see http://www.mawhorter.net/web-development/easily-detecting-if-a-block-of-text-is-written-in-english-non-unicode-languages
	if((!empty($this->minimum_nonunicode_text))){

		//found the length in unicode mode
		$ulen = preg_match_all("#.#u", $no_link_message, $m);
		//find length without unicode
		$len  = preg_match_all('#.#', $no_link_message, $m);
		
		//determine if % of non-unicode is enough
		if(($ulen/$len)<(float)$this->minimum_nonunicode_text){
			$this->found_unicode=$this->found_stuff=true;
			$this->error[]=$user->lang['NO_WORD_FOR_YOU'].' ('.$ulen.'/'.$len.') '.$this->link_filter_add_help_link();
		}	
	}

	return $this->found_stuff;

}

function link_filter_purge_zombies(){
	global $db, $config;
	
	if($this->load_values_from_db){ //use MOD setting from database
		$this->minimum_days=$config['links_after_num_days'];
	}
	
	if($this->minimum_days<1) return; //don't delete if there is no days setting

	// Get bot ids
	$sql = 'SELECT user_id
		FROM ' . BOTS_TABLE;
	$result = $db->sql_query($sql);

	$bot_ids = array();
	while ($row = $db->sql_fetchrow($result))
	{
		$bot_ids[] = $row['user_id'];
	}
	$db->sql_freeresult($result);

	// Select the group of users to delete
	$sql = 'SELECT user_id, username
		FROM ' . USERS_TABLE . '
		WHERE user_id <> ' . ANONYMOUS . '
			AND user_type <> ' . USER_FOUNDER .'
			AND user_regdate < ' . gmmktime(0, 0, 0, date("m"), (date("d")-$this->minimum_days), date("Y")).'
			AND user_posts = 0'; 
	$result = $db->sql_query($sql);

	$usernames = array();

	//this would be simpler with a SQL statment, but recycling the ppBB prune functon makes it more robust
	while ($row = $db->sql_fetchrow($result))
	{

		// Do not prune bots and the user currently pruning.
		if (!in_array($row['user_id'], $bot_ids))
		{
			user_delete('remove', $row['user_id']);//delete the user and all posts (there should be none though)
			//user_delete('retain', $row['user_id'],$row['username']); //delete users but not posts, safer just in case
			$usernames[]=$row['username'];//keep the list for the log
		}
	}
	$db->sql_freeresult($result);
	
	//add log message
	add_log('admin', 'LOG_PRUNE_USER_DEL_DEL', 'Zombie registration cleanup by Disable links for new users MOD:'.implode(', ', $usernames));

}

}//class

?>
Please do not PM or mail with questions. Ask in the forum where everyone can share the answer.
John T. Folden
Registered User
Posts: 188
Joined: Tue Sep 04, 2007 12:16 am

Re: [RC] Disable links for new users & guests

Post by John T. Folden »

Philthy wrote:Updated to version 0.0.3

Minor changes in the text, to reflect that this mod only disables external links, and added some new domain references to be blocked.
Link to the updated download is in the original post.
Thanks for the update!

I just noticed, in the instructions, in the DIY section it says:
"Please remember when testing, that this mod only deletes external links."

Maybe the word should be 'prevents' or 'restricts' as it's not actually 'deleting' anything from posts?

---As to the rest of the unpackaged modifications, they look interesting.

I'm wondering if the 'account delete' portion shouldn't be a separate mod, though??? Also, in relation to that aspect of it, I read:

"*added a function to delete all users older than the age limit who have 0 posts."

One of phpbb's Prune User(s) features is that you can delete users with an account who have never posted but, also, that you can delete users who have created an account but never logged in.

I've been running this manually on my own forum, on occasion, and delete users who have never logged in after 30 days. Those that have logged in, but still have 0 posts, tend to get deleted every 6-12 months. The reason I do this is that some users start out as 'lurkers' that sign-up in order to subscribe to topics, etc, for the information but that may not frequently post. Sooo.... if I were to use a feature like this, it would be nice to have more granular control:

*delete all users who have never logged in after xx number of days.
*delete all users who have 0 posts after xx number of days.
The Blue Whale Pub - SPN/SF/F TV Discussion Forum
ZOMBIE ALERT: The Walking Dead are coming to AMC!
John T. Folden
Registered User
Posts: 188
Joined: Tue Sep 04, 2007 12:16 am

Re: [RC] Disable links for new users & guests

Post by John T. Folden »

Another question.... what does 'disable sleeper agent' do and how?

It sounds like it's tied to minimum days from registration but I don't understand why...

If I have minimum days set to '3' and a user has not made a regular post in their first 3 days, then what happens? They can't modify their sig, profile or post links until they've made enough posts to satisfy 'minimum post count'????
The Blue Whale Pub - SPN/SF/F TV Discussion Forum
ZOMBIE ALERT: The Walking Dead are coming to AMC!
dangerousprototypes
Registered User
Posts: 91
Joined: Fri Feb 11, 2011 5:53 am
Contact:

Re: [RC] Disable links for new users & guests

Post by dangerousprototypes »

Thanks for the feedback on my changes. I'll try to explain some of the features and my decisions. Please share any other thoughts you have, I'm operating from my experience alone, and it's great to understand the issues facing other forum admins.
I'm wondering if the 'account delete' portion shouldn't be a separate mod, though???
I currently have it as a separate section on the documentation wiki with separate install instructions. I figured it wasn't for everyone, and it is a bit of a hack into the fake cron used by phpBB.
One of phpbb's Prune User(s) features is that you can delete users with an account who have never posted but, also, that you can delete users who have created an account but never logged in.
Some of the features are tailored specifically for the type of forum we run, and the type of spam we get. For example, in my experience, we never have users who create an account and never log in. Spammers always click the validation link and login, so I've found that prune method unhelpful.
Those that have logged in, but still have 0 posts, tend to get deleted every 6-12 months.
Lucky :) We get hundreds of spam registrations per day. If I waited this long there would be 50-100 thousand zombie registrations in the database taking up space and compute cycles ;)

Deleting all users with 0 posts has caused us issues in the past: new users just creating an account get pruned. I had several complains when I've done aggressive 0 post pruning, thus the one day window. You can do a 1 day (or n day) prune manually from the ACP, but we have to do it daily and it gets old.

We're really lucky in that our users are 99.9% going to post immediately because it is a support forum for a business. That probably gives us a lot more latitude to be aggressive with the controls. Something more granular is a great idea.
*delete all users who have never logged in after xx number of days.
*delete all users who have 0 posts after xx number of days.
The second (n days) is super easy to add (though a pain to add to the ACP because of all the edits to store the value in the database). I assume the first one is easy to add, but I have not looked into it.

Could you please tell me a little more about your users who have not logged in? Looking at my database now, there are 400 registrations from the last 20 hours or so (about 3 real new users). Every single new account has clicked the confirmation link and logged in. I'd like to know more because I'm not (yet) facing this type of spam bot.
Another question.... what does 'disable sleeper agent' do and how?
All the functions and settings are documented in the wiki (see link a few posts back, or check the header of the class). Here's the documentation for sleeper agent functions:
Sleeper agent check

private $sleeper_check=true;

User with 0 posts after minimum account age will be prohibited from posting, signatures, and profiles.

What is this about? We ban links based on age and posts, but imagine a spammer script that:

1. Registers a bunch of accounts on one day
2. Comes back after the minimum account age
3. Posts the minimum account posts until the filter is off
4. Then blasts the forum full of spam

All that could happen before you have a chance to see and delete the initial posts.

The sleeper agent check stops this. If the new user has not posted an initial post within the minimum account age windows, then posting, signatures, and profiles are disabled and an error is displayed.

Private messages remain active (with filters enabled) so the user can contact an admin.

Most of our members join and post right away, so this is transparent to almost everyone.
Essentially, the account is disabled. This is basically a holding phase until the automated purge wipes the account. Or, if you don't use the automated prune, it can disable accounts without deleting them. On the wiki you can see a screenshot of the account disabled message.
If I have minimum days set to '3' and a user has not made a regular post in their first 3 days, then what happens? They can't modify their sig, profile or post links until they've made enough posts to satisfy 'minimum post count'????
The account is "disabled". They cannot post, have a profile, or change the signature at all. They can PM without links to get help, but honestly I don't know how an admin could get a user out of the sleeper agent state (you'd have to give them a post in the database). I just assume them to be spammers safely contained until deletion. It might be helpful to only apply this to the new user group to give admins an out, there is code to do that in the class but I don't use it on my forum so it has not been tested.

Thanks for the great feedback and suggestions. It is fairly easy to implement some of your changes in the code, the only painful part is adding the config options to the ACP. These are additional options I'd like to add at this time:
1. Enable auto-prune
2. set autoprune frequency (add proper CRON entry and database entry)

For our forum I've added some extra features to keep the spammers guessing:
*Randomness - limits end randomly, and are reinstated randomly over several definable windows :) Overall users spend less time with limits
*Sliding window - time under limits is based on first post, not signup. This removes the need for sleeper agent check, but costs a big database query

These might make it into a future version if I'm happy with them.
Please do not PM or mail with questions. Ask in the forum where everyone can share the answer.
Philthy
Registered User
Posts: 210
Joined: Tue Dec 27, 2005 10:05 am
Location: Dawlish, Devon
Contact:

Re: [RC] Disable links for new users & guests

Post by Philthy »

John T. Folden wrote: I just noticed, in the instructions, in the DIY section it says:
"Please remember when testing, that this mod only deletes external links."

Maybe the word should be 'prevents' or 'restricts' as it's not actually 'deleting' anything from posts?
Corrected.
Go on ! it's not as steep as it looks.....
John T. Folden
Registered User
Posts: 188
Joined: Tue Sep 04, 2007 12:16 am

Re: [RC] Disable links for new users & guests

Post by John T. Folden »

dangerousprototypes wrote:Lucky :) We get hundreds of spam registrations per day. If I waited this long there would be 50-100 thousand zombie registrations in the database taking up space and compute cycles ;)
I used to get a horrid volume level of spammer/zombie registrations and posts but reCaptcha and a custom profile field required at registration stopped a large number of them. This Disable Links mod stopped a majority of posts from those who are currently left and I've been able to turn guest posting back on.
We're really lucky in that our users are 99.9% going to post immediately because it is a support forum for a business. That probably gives us a lot more latitude to be aggressive with the controls. Something more granular is a great idea.
I run a number of forums but my most publicly visible one, and the one that has the greatest issue where spam is concerned, is focused around entertainment related discussion (genre TV productions in particular). Discussion tends to be seasonal and some people might register at the end of the television season but not begin to post until weeks or months later when news of the next season begins (or are cast/crew that register only to observe).
Could you please tell me a little more about your users who have not logged in? Looking at my database now, there are 400 registrations from the last 20 hours or so (about 3 real new users). Every single new account has clicked the confirmation link and logged in. I'd like to know more because I'm not (yet) facing this type of spam bot.
Sure... I just had a look at a few from today as I was deleting them.

*Usernames are completely random. They might be something obvious like 'uggbootsale' to gibberish to something slightly passable like 'amydale33'.
*Most seem to originate from China at the moment but I think I've seen other countries involved.
*They began to show up in greater and greater numbers when I converted to WordPress on a couple of other sites that have links into the forum.
*Sometimes they login but just as often, they don't (or come back a week or two later to login, then come back a week later when they might finally post spam).

The way I quickly identify them is that I have a custom profile field question required at registration which asks the registrant how they heard about the forum. These bots ALWAYS answer with the same pattern - gibberish, 6 characters in length, made up of 3 random letters followed by 3 random numbers ( ghz729, tuv342, etc...).

My personal opinion is that a bot is creating the accounts but real, live people are coming back later doing the login/posting on some of them. I can see where they've fussed around removing links in their effort to get the message to finally post.
Last edited by John T. Folden on Tue Feb 22, 2011 10:56 am, edited 1 time in total.
The Blue Whale Pub - SPN/SF/F TV Discussion Forum
ZOMBIE ALERT: The Walking Dead are coming to AMC!
User avatar
heredia21
Registered User
Posts: 942
Joined: Sun Apr 18, 2010 6:14 pm
Contact:

Re: [RC] Disable links for new users & guests

Post by heredia21 »

Are you all going to package it up? I think it will be a lot easier.
Best BlackBerry website for all users! BlackBerry News - http://blackberryempire.com
Locked

Return to “[3.0.x] Abandoned MODs”