* @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; }