(int)$mybb->user['uid'], "ipaddress" => $db->escape_binary(my_inet_pton(get_ip())), "dateline" => TIME_NOW, "module" => $db->escape_string($mybb->get_input('module')), "action" => $db->escape_string($mybb->get_input('action')), "data" => $db->escape_string(@my_serialize($data)) ); $db->insert_query("adminlog", $log_entry); } /** * Redirects the current user to a specified URL. * * @param string $url The URL to redirect to */ function admin_redirect($url) { if(!headers_sent()) { $url = str_replace("&", "&", $url); header("Location: $url"); } else { echo ""; } exit; } /** * Updates an administration session data array. * * @param string $name The name of the item in the data session to update * @param mixed $value The value */ function update_admin_session($name, $value) { global $db, $admin_session; $admin_session['data'][$name] = $value; $updated_session = array( "data" => $db->escape_string(@my_serialize($admin_session['data'])) ); $db->update_query("adminsessions", $updated_session, "sid='{$admin_session['sid']}'"); } /** * Saves a "flash message" for the current user to be shown on their next page visit. * * @param string $message The message to show * @param string $type The type of message to be shown (success|error) */ function flash_message($message, $type='') { $flash = array('message' => $message, 'type' => $type); update_admin_session('flash_message', $flash); } /** * Draw pagination for pages in the Admin CP. * * @param int $page The current page we're on * @param int $per_page The number of items per page * @param int $total_items The total number of items in this collection * @param string $url The URL for pagination of this collection * @return string The built pagination */ function draw_admin_pagination($page, $per_page, $total_items, $url) { global $mybb, $lang; if($total_items <= $per_page) { return ''; } $pages = ceil($total_items / $per_page); $pagination = "
{$lang->pages}: \n"; if($page > 1) { $prev = $page-1; $prev_page = fetch_page_url($url, $prev); $pagination .= "« {$lang->previous} \n"; } // Maximum number of "page bits" to show if(!$mybb->settings['maxmultipagelinks']) { $mybb->settings['maxmultipagelinks'] = 5; } $max_links = $mybb->settings['maxmultipagelinks']; $from = $page-floor($mybb->settings['maxmultipagelinks']/2); $to = $page+floor($mybb->settings['maxmultipagelinks']/2); if($from <= 0) { $from = 1; $to = $from+$max_links-1; } if($to > $pages) { $to = $pages; $from = $pages-$max_links+1; if($from <= 0) { $from = 1; } } if($to == 0) { $to = $pages; } if($from > 2) { $first = fetch_page_url($url, 1); $pagination .= "page} 1\" class=\"pagination_first\">1 ... "; } for($i = $from; $i <= $to; ++$i) { $page_url = fetch_page_url($url, $i); if($page == $i) { $pagination .= "{$i} \n"; } else { $pagination .= "page} {$i}\">{$i} \n"; } } if($to < $pages) { $last = fetch_page_url($url, $pages); $pagination .= "... page} {$pages}\" class=\"pagination_last\">{$pages}"; } if($page < $pages) { $next = $page+1; $next_page = fetch_page_url($url, $next); $pagination .= " {$lang->next} »\n"; } $pagination .= "
\n"; return $pagination; } /** * Builds a CSV parent list for a particular forum. * * @param int $fid The forum ID * @param string $navsep Optional separator - defaults to comma for CSV list * @return string The built parent list */ function make_parent_list($fid, $navsep=",") { global $pforumcache, $db; if(!$pforumcache) { $query = $db->simple_select("forums", "name, fid, pid", "", array("order_by" => "disporder, pid")); while($forum = $db->fetch_array($query)) { $pforumcache[$forum['fid']][$forum['pid']] = $forum; } } reset($pforumcache); reset($pforumcache[$fid]); $navigation = ''; foreach($pforumcache[$fid] as $key => $forum) { if($fid == $forum['fid']) { if(!empty($pforumcache[$forum['pid']])) { $navigation = make_parent_list($forum['pid'], $navsep).$navigation; } if($navigation) { $navigation .= $navsep; } $navigation .= $forum['fid']; } } return $navigation; } /** * @param int $fid */ function save_quick_perms($fid) { global $db, $inherit, $canview, $canpostthreads, $canpostreplies, $canpostpolls, $canpostattachments, $cache; $permission_fields = array(); $field_list = $db->show_fields_from("forumpermissions"); foreach($field_list as $field) { if(strpos($field['Field'], 'can') !== false || strpos($field['Field'], 'mod') !== false) { $permission_fields[$field['Field']] = 1; } } // "Can Only View Own Threads" and "Can Only Reply Own Threads" permissions are forum permission only options $usergroup_permission_fields = $permission_fields; unset($usergroup_permission_fields['canonlyviewownthreads']); unset($usergroup_permission_fields['canonlyreplyownthreads']); $query = $db->simple_select("usergroups", "gid"); while($usergroup = $db->fetch_array($query)) { $query2 = $db->simple_select("forumpermissions", $db->escape_string(implode(',', array_keys($permission_fields))), "fid='{$fid}' AND gid='{$usergroup['gid']}'", array('limit' => 1)); $existing_permissions = $db->fetch_array($query2); if(!$existing_permissions) { $query2 = $db->simple_select("usergroups", $db->escape_string(implode(',', array_keys($usergroup_permission_fields))), "gid='{$usergroup['gid']}'", array('limit' => 1)); $existing_permissions = $db->fetch_array($query2); } // Delete existing permissions $db->delete_query("forumpermissions", "fid='{$fid}' AND gid='{$usergroup['gid']}'"); // Only insert the new ones if we're using custom permissions if(empty($inherit[$usergroup['gid']])) { if(!empty($canview[$usergroup['gid']])) { $pview = 1; } else { $pview = 0; } if(!empty($canpostthreads[$usergroup['gid']])) { $pthreads = 1; } else { $pthreads = 0; } if(!empty($canpostreplies[$usergroup['gid']])) { $preplies = 1; } else { $preplies = 0; } if(!empty($canpostpolls[$usergroup['gid']])) { $ppolls = 1; } else { $ppolls = 0; } if(!$preplies && !$pthreads) { $ppost = 0; } else { $ppost = 1; } $insertquery = array( "fid" => (int)$fid, "gid" => (int)$usergroup['gid'], "canview" => (int)$pview, "canpostthreads" => (int)$pthreads, "canpostreplys" => (int)$preplies, "canpostpolls" => (int)$ppolls, ); foreach($permission_fields as $field => $value) { if(array_key_exists($field, $insertquery)) { continue; } $insertquery[$db->escape_string($field)] = isset($existing_permissions[$field]) ? (int)$existing_permissions[$field] : 0; } $db->insert_query("forumpermissions", $insertquery); } } $cache->update_forumpermissions(); } /** * Checks if a particular user has the necessary permissions to access a particular page. * * @param array $action Array containing module and action to check for * @param bool $error * @return bool */ function check_admin_permissions($action, $error = true) { global $mybb, $page, $lang, $modules_dir; if(is_super_admin($mybb->user['uid'])) { return true; } require_once $modules_dir."/".$action['module']."/module_meta.php"; if(function_exists($action['module']."_admin_permissions")) { $func = $action['module']."_admin_permissions"; $permissions = $func(); if( !empty($permissions['permissions'][$action['action']]) && empty($mybb->admin['permissions'][$action['module']][$action['action']]) ) { if($error) { $page->output_header($lang->access_denied); $page->add_breadcrumb_item($lang->access_denied, "index.php?module=home-index"); $page->output_error("{$lang->access_denied}"); $page->output_footer(); exit; } else { return false; } } } return true; } /** * Fetches the list of administrator permissions for a particular user or group * * @param int $get_uid The user ID to fetch permissions for * @param int $get_gid The (optional) group ID to fetch permissions for * @return array Array of permissions for specified user or group */ function get_admin_permissions($get_uid=0, $get_gid=0) { global $db, $mybb; // Set UID and GID if none $uid = $get_uid; $gid = $get_gid; $gid_array = array(); if($uid === 0) { $uid = $mybb->user['uid']; } if(!$gid) { // Prepare user's groups since the group isn't specified $gid_array[] = (-1) * (int)$mybb->user['usergroup']; if($mybb->user['additionalgroups']) { $additional_groups = explode(',', $mybb->user['additionalgroups']); if(!empty($additional_groups)) { // Make sure gids are negative foreach($additional_groups as $g) { $gid_array[] = (-1) * abs($g); } } } } else { // Group is specified // Make sure gid is negative $gid_array[] = (-1) * abs($gid); } // What are we trying to find? if($get_gid && !$get_uid) { // A group only $options = array( "order_by" => "uid", "order_dir" => "ASC", "limit" => "1" ); $query = $db->simple_select("adminoptions", "permissions", "(uid='-{$get_gid}' OR uid='0') AND permissions != ''", $options); return my_unserialize($db->fetch_field($query, "permissions")); } else { // A user and/or group $options = array( "order_by" => "uid", "order_dir" => "DESC" ); // Prepare user's groups into SQL format $group_sql = ''; foreach($gid_array as $gid) { $group_sql .= " OR uid='{$gid}'"; } $perms_group = array(); $query = $db->simple_select("adminoptions", "permissions, uid", "(uid='{$uid}'{$group_sql}) AND permissions != ''", $options); while($perm = $db->fetch_array($query)) { $perm['permissions'] = my_unserialize($perm['permissions']); // Sorting out which permission is which if($perm['uid'] > 0) { $perms_user = $perm; return $perms_user['permissions']; } elseif($perm['uid'] < 0) { $perms_group[] = $perm['permissions']; } else { $perms_def = $perm['permissions']; } } // Figure out group permissions...ugh. foreach($perms_group as $gperms) { if(!isset($final_group_perms)) { // Use this group as the base for admin group permissions $final_group_perms = $gperms; continue; } // Loop through each specific permission to find the highest permission foreach($gperms as $perm_name => $perm_value) { if($final_group_perms[$perm_name] != '1' && $perm_value == '1') { $final_group_perms[$perm_name] = '1'; } } } // Send specific user, or group permissions before default. // If user's permission are explicitly set, they've already been returned above. if(isset($final_group_perms)) { return $final_group_perms; } elseif(isset($perms_def)) { return $perms_def; } return array(); } } /** * Fetch the iconv/mb encoding for a particular MySQL encoding * * @param string $mysql_encoding The MySQL encoding * @return string The iconv/mb encoding */ function fetch_iconv_encoding($mysql_encoding) { $mysql_encoding = explode("_", $mysql_encoding); switch($mysql_encoding[0]) { case "utf8": return "utf-8"; break; case "latin1": return "iso-8859-1"; break; default: return $mysql_encoding[0]; } } /** * Adds/Updates a Page/Tab to the permissions array in the adminoptions table * * @param string $tab The name of the tab that is being affected * @param string $page The name of the page being affected (optional - if not specified, will affect everything under the specified tab) * @param integer $default Default permissions for the page (1 for allowed - 0 for disallowed - -1 to remove) */ function change_admin_permission($tab, $page="", $default=1) { global $db; $query = $db->simple_select("adminoptions", "uid, permissions", "permissions != ''"); while($adminoption = $db->fetch_array($query)) { $adminoption['permissions'] = my_unserialize($adminoption['permissions']); if($default == -1) { if(!empty($page)) { unset($adminoption['permissions'][$tab][$page]); } else { unset($adminoption['permissions'][$tab]); } } else { if(!empty($page)) { if($adminoption['uid'] == 0) { $adminoption['permissions'][$tab][$page] = 0; } else { $adminoption['permissions'][$tab][$page] = $default; } } else { if($adminoption['uid'] == 0) { $adminoption['permissions'][$tab]['tab'] = 0; } else { $adminoption['permissions'][$tab]['tab'] = $default; } } } $db->update_query("adminoptions", array('permissions' => $db->escape_string(my_serialize($adminoption['permissions']))), "uid='{$adminoption['uid']}'"); } } /** * Checks if we have had too many attempts at logging into the ACP * * @param integer $uid The uid of the admin to check * @param boolean $return_num Return an array of the number of attempts and expiry time? (default false) * @return mixed Return an array if the second parameter is true, boolean otherwise. */ function login_attempt_check_acp($uid=0, $return_num=false) { global $db, $mybb; $attempts['loginattempts'] = 0; if($uid > 0) { $query = $db->simple_select("adminoptions", "loginattempts, loginlockoutexpiry", "uid='".(int)$uid."'", 1); $attempts = $db->fetch_array($query); if(!$attempts) { return false; } } if($attempts['loginattempts'] <= 0) { return false; } if($mybb->settings['maxloginattempts'] > 0 && $attempts['loginattempts'] >= $mybb->settings['maxloginattempts']) { // Has the expiry dateline been set yet? if($attempts['loginlockoutexpiry'] == 0 && $return_num == false) { $db->update_query("adminoptions", array("loginlockoutexpiry" => TIME_NOW+((int)$mybb->settings['loginattemptstimeout']*60)), "uid='".(int)$uid."'"); } // Are we returning the # of login attempts? if($return_num == true) { return $attempts; } // Otherwise are we still locked out? else if($attempts['loginlockoutexpiry'] > TIME_NOW) { return true; } } return false; } /** * Checks whether the administrator is on a mobile device * * @param string $useragent The useragent to be checked * @return boolean A true/false depending on if the administrator is on a mobile */ function is_mobile($useragent) { return preg_match("/(android|avantgo|blackberry|bolt|boost|cricket|docomo|fone|hiptop|mini|mobi|palm|phone|pie|tablet|up\.browser|up\.link|webos|wos)/i", $useragent); } /** * Checks whether there are any 'security' issues in templates via complex syntax * * @param string $template The template to be scanned * @return boolean A true/false depending on if an issue was detected */ function check_template($template) { // Check to see if our database password is in the template if(preg_match('#\$config\[(([\'|"]database[\'|"])|([^\'"].*?))\]\[(([\'|"](database|hostname|password|table_prefix|username)[\'|"])|([^\'"].*?))\]#i', $template) !== 0) { return true; } // System calls via backtick if(preg_match('#\$\s*\{#', $template) !== 0) { return true; } // Any other malicious acts? // Courtesy of ZiNgA BuRgA $allowed = preg_replace('~\\{\\$+[a-zA-Z_][a-zA-Z_0-9]*((?:-\\>|\\:\\:)\\$*[a-zA-Z_][a-zA-Z_0-9]*|\\[\s*\\$*([\'"]?)[a-zA-Z_ 0-9 ]+\\2\\]\s*)*\\}~', '', $template); if($allowed === null || preg_match("~\\{\\$.+?\\}~s", $allowed) !== 0) { return true; } return false; } /** * Provides a function to entirely delete a user's posts, and find the threads attached to them * * @param integer $uid The uid of the user * @param int $date A UNIX timestamp to delete posts that are older * @return array An array of threads to delete, threads/forums to recount */ function delete_user_posts($uid, $date) { global $db; $uid = (int)$uid; // Build an array of posts to delete $postcache = array(); $query = $db->simple_select("posts", "pid", "uid = '".$uid."' AND dateline < '".$date."'"); while($post = $db->fetch_array($query)) { $postcache[] = $post['pid']; } if(!$db->num_rows($query)) { return false; } elseif(!empty($postcache)) { // Let's start deleting posts $user_posts = implode(",", $postcache); $query = $db->query(" SELECT p.pid, p.visible, f.usepostcounts, t.tid AS thread, t.firstpost, t.fid AS forum FROM ".TABLE_PREFIX."posts p LEFT JOIN ".TABLE_PREFIX."forums f ON (f.fid=p.fid) LEFT JOIN ".TABLE_PREFIX."threads t ON (t.tid=p.tid) WHERE p.pid IN ({$user_posts}) "); $post_count = 0; // Collect the post number to deduct from the user's postcount $thread_list = array(); $forum_list = array(); $delete_thread_list = array(); if(!$db->num_rows($query)) { return false; } else { while($post = $db->fetch_array($query)) { if($post['usepostcounts'] != 0 && $post['visible'] == 1) { ++$post_count; } if($post['pid'] == $post['firstpost']) { $delete_thread_list[] = $post['thread']; } if(!in_array($post['thread'], $thread_list) && !in_array($post['thread'], $delete_thread_list)) { $thread_list[] = $post['thread']; // Threads that have been affected by this action, that aren't marked to be deleted } if(!in_array($post['forum'], $forum_list)) { $forum_list[] = $post['forum']; // Forums that have been affected, too } // Remove the attachments to this post, then delete the post remove_attachments($post['pid']); $db->delete_query("posts", "pid = '".$post['pid']."'"); $db->delete_query("pollvotes", "pid = '".$post['pid']."'"); // Delete pollvotes attached to this post } $db->update_query("users", array("postnum" => "postnum-".$post_count.""), "uid='".$uid."'", 1, true); $to_return = array( 'to_delete' => $delete_thread_list, 'thread_update' => $thread_list, 'forum_update' => $forum_list ); return $to_return; } } } /** * Prints a selection JavaScript code for selectable groups/forums fields. */ function print_selection_javascript() { static $already_printed = false; if($already_printed) { return; } $already_printed = true; echo ""; } if(!function_exists('array_column')) { function array_column($input, $column_key) { $values = array(); if(!is_array($input)) { $input = array($input); } foreach($input as $val) { if(is_array($val) && isset($val[$column_key])) { $values[] = $val[$column_key]; } elseif(is_object($val) && isset($val->$column_key)) { $values[] = $val->$column_key; } } return $values; } } /** * Output the auto redirect block. * * @param \Form $form An existing form instance to wrap the redirect within. * @param string $prompt The prompt to show. */ function output_auto_redirect($form, $prompt) { global $lang; echo <<

{$prompt}


{$form->generate_submit_button($lang->proceed, array('class' => 'button_yes', 'id' => 'proceed_button'))}

HTML; }