diff options
Diffstat (limited to 'mod/vegan/start.php')
-rw-r--r-- | mod/vegan/start.php | 449 |
1 files changed, 449 insertions, 0 deletions
diff --git a/mod/vegan/start.php b/mod/vegan/start.php new file mode 100644 index 000000000..cee529e8f --- /dev/null +++ b/mod/vegan/start.php @@ -0,0 +1,449 @@ +<?php +/** + * Vegan, an anti-spam plugin for Elgg 1.8 + * + * @author hellekin + * @package lorea + * @subpackage vegan + * @copyright 2012,2013 Lorea Faeries <federation@lorea.org> + * @license GNU Affero General Public License version 3 or later + * @website https://lorea.org/plugins/vegan + * + * Friendly note to spammers: never cross my path. + */ + +elgg_register_event_handler('init', 'system', 'vegan_init'); + +/** + * vegan_init -- Initialize plugin functionality + * + * @return void + */ +function vegan_init() { + + // Register Tests + // elgg_register_plugin_hook_handler('test', 'system', 'vegan_tests'); + + // Provide Admin Interface + elgg_register_event_handler('pagesetup', 'system', 'vegan_admin_sandbox_page_setup'); + elgg_extend_view('admin/user_opt/extend', 'vegan/admin_sandboxed_users'); + elgg_extend_view('css/admin', 'vegan/css_admin'); + + // Extend CSS + elgg_extend_view('elgg', 'css', 'vegan/css'); + + // @todo Replace ReportedContent functionality + + // Hook to registration to block obvious spammers + elgg_register_plugin_hook_handler('register', 'user', 'vegan_blacklist_email'); + + // Hook to entity creation to restrict access to saved objects + elgg_register_event_handler('create', 'group', 'vegan_block_create_group'); + elgg_register_event_handler('create', 'object', 'vegan_block_create_object'); + + // Hook to actions to block spammers from saving objects + $actions = array( + 'blog/save', + 'bookmark/save', + 'thewire/add', + ); + foreach ($actions AS $action) { + elgg_register_plugin_hook_handler('action', $action, 'vegan_block_action'); + } + + // Hook to creating,river to prevent river spam + elgg_register_plugin_hook_handler('creating', 'river', 'vegan_block_river'); + + // @todo clear vegan_sandboxed flag when user joins group/make friends + // make it smart enough to make spammers' job hard to circumvent + +} + +/** + * vegan_block_action -- Prevent spammers from saving anything + * + * @hook Plugin action, *save + * + * @param String $hook the action to block + * @param String $type the entity type (usually object) + * @param Mixed $foo ignored parameter + * @param Mixed $bar ignored parameter + * @return Boolean TRUE if the current user is a recognized spammer, FALSE otherwise. + */ +function vegan_block_action($hook, $type, $foo, $bar) { + + $user_guid = elgg_get_logged_in_user_guid(); + + return vegan_is_spammer($user_guid); + +} + +/** + * vegan_block_create_group -- Force access to ACCESS_PRIVATE for sandboxed users + * + * @hook Event create, group + * + * @param String $event the event (create) + * @param String $type the entity type (group) + * @param Array $params contains 'entity' key + * + * @return Boolean TRUE to allow action, FALSE to prevent it + */ +function vegan_block_create_group($event, $type, $params) { + + $group = $params['entity']; + $guid = $group->owner_guid; + + if ($guid && vegan_is_sandboxed($guid)) { + system_message(elgg_echo('vegan:sandboxed:group')); + $group->set('access_id', ACCESS_PRIVATE); + // Return FALSE to delete group if sandboxed user is a spammer + return !vegan_is_spammer($guid); + } + + return TRUE; // Accept group creation + +} + +/** + * vegan_block_create_object -- Force access to ACCESS_PRIVATE for sandboxed users. + * + * @hook Event create, object + * + * @param String $event the event (create) + * @param String $type the entity type (object) + * @param Array $params contains 'entity' key + * + * @return Boolean TRUE to allow action, FALSE to prevent it + */ +function vegan_block_create_object($event, $type, $params) { + + $object = $params['entity']; + $guid = $object->owner_guid; + + if ($guid && vegan_is_sandboxed($guid)) { + system_message(elgg_echo('vegan:sandboxed:object')); + $object->set('access_id', ACCESS_PRIVATE); + // Return FALSE to delete object if sandboxed user is a spammer + return !vegan_is_spammer($guid); + } + + return TRUE; // Accept object creation + +} + +/** + * vegan_block_river -- Prevent river spam from sandboxed users + * + * @hook Plugin creating,river + * + * @param String $hook the plugin hook (creating) + * @param String $type the entity type (river) + * @param Mixed $foo ignored parameter + * @param Array $values arguments for view + * + * @return Mixed TRUE to prevent item from being sent to the river, void otherwise. + */ +function vegan_block_river($hook, $type, $foo, $values) { + + error_log("===== vegan_block_river $hook,$type ($foo) $values"); + + if (vegan_is_sandboxed($values['subject_guid'])) { + return TRUE; + } + +} + +/** + * vegan_is_sandboxed -- Determine whether to sandbox the user or not + * + * @param Integer $user_guid the GUID of the user entity to test + * + * @return Boolean TRUE if sandboxed, FALSE otherwise. + */ +function vegan_is_sandboxed($user_guid) { + + $sandboxed = FALSE; + + // Get user and current time + $user = get_entity($user_guid); + $now = time(); + + if (!elgg_instanceof($user, 'user')) { + error_log("===== vegan_sandboxed content owner '$user_guid' is not a user!"); + return FALSE; + } + + // Account was activated by email + if ($user->validation_method == 'email') { + + $md = elgg_get_metadata(array( + 'guid' => $user_guid, + 'metadata_name' => 'validated', + 'limit' => 1, + )); + error_log("===== vegan_sandboxed md $md->time_updated"); + + // and less than 2 minutes ago: die motherfucker + if ($user->last_login == 0 && $md && ($now - $md->time_updated) < 120) { + error_log("===== Gotcha! Killing spammer on first post"); + $user->delete(); + $sandboxed = TRUE; // return TRUE to block the river + } + // and less than an hour ago + /* + else if ($md && ($now - $md->updated_at) < 3600) { + error_log("===== vegan_sandboxed recent account"); + $sandboxed = TRUE; + } + */ + + } + + // If it's sandboxed, force ACCESS_PRIVATE + if ($user->vegan_sandboxed) { + // Keep track of retries, for future use + error_log("===== User is sandboxed ({$owner->vegan_sandboxed_tries})"); + $user->vegan_sandboxed_tries = (int)$user->vegan_sandboxed_tries + 1; + $sandboxed = TRUE; + } + + // Skip admins + if (!$user->isAdmin()) { + + // You have zero friends + if (!$user->getFriends()) { + error_log("===== Sandboxed user (no friends)"); + $sandboxed = TRUE; + } + + // You're not cooperating + if (!$user->getGroups()) { + error_log("===== Sandboxed user (no groups)"); + $sandboxed = TRUE; + } + + } + + if ($sandboxed) { + $user->set('vegan_sandboxed', TRUE); + // @todo limit access collections to private + } + + return $sandboxed; + +} + +/** + * vegan_blacklist_email -- Prevent user registration from a notorious + * spam source + * + * Blacklists suck, but enough is enough. + * + * @hook Plugin register, user + * + * @param String $hook the plugin hook (register) + * @param String $type the entity type (user) + * @param Mixed $value the return value + * @param Array $params contains 'user' key + * + * @return FALSE to prevent user registration + */ +function vegan_blacklist_email($hook, $type, $value, $params) { + + // Hardcode forbidden patterns: humans don't use them + static $noway = array( + '/\+.*@hotmail\.com$/', + ); + + // Grab the email + $email = $params['user']->email; + error_log("===== vegan_blacklist_email $hook,$type $email"); + + if (!empty($email)) { + + // Exclude forbidden patterns + foreach($noway AS $pattern) { + if (preg_match($pattern, $email)) { + error_log("===== Vegan prevented registration of $email"); + return FALSE; + } + } + + // Exclude additional site-specific blacklisted domains + $bad_domains = string_to_tag_array(elgg_get_plugin_setting('bad_domains', 'vegan')); + $domain = explode('@', $email, 2); + + error_log("===== Vegan testing $email against " . implode(', ', $bad_domains)); + + if (is_array($bad_domains) && in_array($domain[1], $bad_domains)) { + error_log("===== Vegan prevented registration of $email (bad domain: $domain[1])"); + return FALSE; + } + + } + +} + +/** + * vegan_admin_sandboxed_users -- View and manage suspicious users + * + */ + +/** + * vegan_admin_sandboxed_page_setup -- Provide admin interface + * + */ +function vegan_admin_sandboxed_page_setup() { + if (elgg_get_context() == 'admin' && elgg_is_admin_logged_in()) { + elgg_register_admin_menu_item('administer', 'vegan', 'lorea'); + } +} + +/* +function vegan_strike($hook, $type, $value, $params) { + $report = $params['report']; + $reporter = $report->getOwnerEntity(); + $current_user_guid = elgg_get_loggedin_user_guid(); + if ($reporter->guid == $current_user_guid && $reporter->spam_hunter) { + // Kill the beast + // TODO get reported object owner, or reported user + $spammer_guid = -1; + $spammer = elgg_get_user_entity($spammer_guid); + // Ban + if ($spammer->ban()) { + system_message(elgg_echo('vegan:killed_spammer')); + $report->state = 'archived'; + $report->save(); + } else { + register_error(elgg_echo('vegan:spammer_escaped')); + } + } + +} + + +function vegan_grant_hunter_role($user_guid) { + $user = elgg_get_user($user_guid); + if (elgg_instanceof($user, 'user') && !$user->banned) { + $user->spam_hunter = true; + return TRUE; + } + return FALSE; +} + +function vegan_can_hunt($user = NULL) { + if (!$user) { + $user = elgg_get_loggedin_user(); + } + if (!elgg_instanceof($user, 'user') || $user->banned) { + return FALSE; + } + + return ($user->spam_hunter); +} + + +*/ + +/** + * Vegan Reporting functions + * + * Use the `vegan_flag` function, it will sort things out. + * `vegan_*_spammer` function are for internal use. + */ +/* +function vegan_flag($stuff) { + // Build function name + $flag_func = "vegan_"; + + // Check who is reporting + $reporter = elgg_get_loggedin_user(); + + if (!$user) { + // Anonymous reporting + $vegan_func.= "watch"; + } else if ($user->spam_hunter || $user->admin) { + // Direct kill + $vegan_func = "ban"; + } else { + // Report by authenticated user + $vegan_func = "block"; + } + $vegan_func.= "_spammer"; + + // Check what is reported + if ($stuff instanceof ElggUser) { + // A user + } else if (is_numeric($stuff)) { + // A GUID + $stuff = get_entity($stuff); + } else if (preg_match($stuff, "/^https?:\/\/[^\/]+/(?:pg\/)profile\/([^\/\?\b]+)/", $stuff, $m)) { + $stuff = get_user_by_username($m[2]); + } + + if ($stuff instanceof ElggUser) { + $spammer_guid = $stuff->getGUID(); + } else if ($stuff instanceof ElggEntity) { + $spammer_guid = $stuff->getOwnerGUID(); + } else { + elgg_register_error(elgg_echo('vegan:error:invalid_reference')); + return FALSE; + } + + return $vegan_func($spammer_guid); +} + + +// Report a spammer anonymously +// will raise the vegan_watch count for that entity +function vegan_snitch_spammer($user) { + $user->vegan_watch = (int)$user->vegan_watch + 1; + // notify... + system_message(elgg_echo('vegan:action:success')); + return TRUE; +} + +// Block a spammer +// Reporter won't see it anymore +function vegan_block_spammer($user) { + $reporter_guid = get_loggedin_user_guid(); + // Watch it + $user->vegan_watch = (int)$user->vegan_watch + 1; + // Block it + elgg_create_entity_relationship($reporter_guid, $user->guid, "blocked"); + // Notify... + system_message(elgg_echo('vegan:action:success')); + return TRUE; +} + +// Ban a spammer +function vegan_ban_spammer($user) { + $reporter_guid = elgg_get_loggedin_user_guid(); + // Ban it + $user->ban("spammer"); + // Notify + system_message(elgg_echo('vegan:action:success')); + return TRUE; +} + +*/ + +/** + * vegan_spammer -- Determine whether a user is a spammer + * + * @param Integer $user_guid GUID of the user entity to test + * + * @return Boolean TRUE is user is a recognized spammer, FALSE otherwise. + */ +function vegan_is_spammer($user_guid) { + + $user = get_entity($user_guid); + + if (elgg_instanceof($user, 'user') && !$user->isBanned()) { + return (bool)$user->spammer; + } + + return FALSE; + +} |