/** * @covers User::getGroupPermissions */ public function testRevokePermissions() { $rights = User::getGroupPermissions(array('unittesters', 'formertesters')); $this->assertNotContains('runtest', $rights); $this->assertNotContains('writetest', $rights); $this->assertNotContains('modifytest', $rights); $this->assertNotContains('nukeworld', $rights); }
/** Pass the request to our internal function. * BEWARE! Data are passed as they have been supplied by the user, * they should be carefully handled in the function processing the * request. */ function performAction() { global $wgAjaxExportList, $wgOut, $wgUser; if (empty($this->mode)) { return; } /* * Wikia Change - begin */ Transaction::setEntryPoint(Transaction::ENTRY_POINT_AJAX); Transaction::setAttribute(Transaction::PARAM_FUNCTION, $this->func_name); if (function_exists('newrelic_disable_autorum')) { newrelic_disable_autorum(); } /* * Wikia Change - end */ wfProfileIn(__METHOD__); if (!in_array($this->func_name, $wgAjaxExportList)) { wfDebug(__METHOD__ . ' Bad Request for unknown function ' . $this->func_name . "\n"); wfHttpError(400, 'Bad Request', "unknown function " . (string) $this->func_name); } elseif (!in_array('read', User::getGroupPermissions(array('*')), true) && !$wgUser->isAllowed('read')) { wfHttpError(403, 'Forbidden', 'You must log in to view pages.'); } else { wfDebug(__METHOD__ . ' dispatching ' . $this->func_name . "\n"); if (strpos($this->func_name, '::') !== false) { $func = explode('::', $this->func_name, 2); } else { $func = $this->func_name; } try { $result = call_user_func_array($func, $this->args); if ($result === false || $result === null) { wfDebug(__METHOD__ . ' ERROR while dispatching ' . $this->func_name . "(" . var_export($this->args, true) . "): " . "no data returned\n"); /* Wikia changes start */ //let's avoid falling back to Iowa (500, 503) in this case, //probably someone is asking for a non-existing dynamic method name wfHttpError(501, 'Not Implemented', "{$this->func_name} returned no data"); } else { if (is_string($result)) { $result = new AjaxResponse($result); } $result->sendHeaders(); $result->printText(); wfDebug(__METHOD__ . ' dispatch complete for ' . $this->func_name . "\n"); } } catch (Exception $e) { wfDebug(__METHOD__ . ' ERROR while dispatching ' . $this->func_name . "(" . var_export($this->args, true) . "): " . get_class($e) . ": " . $e->getMessage() . "\n"); if (!headers_sent()) { wfHttpError(500, 'Internal Error', $e->getMessage()); } else { print $e->getMessage(); } } } $wgOut = null; wfProfileOut(__METHOD__); }
/** Pass the request to our internal function. * BEWARE! Data are passed as they have been supplied by the user, * they should be carefully handled in the function processing the * request. */ function performAction() { global $wgAjaxExportList, $wgOut, $wgUser; if (empty($this->mode)) { return; } wfProfileIn(__METHOD__); if (!in_array($this->func_name, $wgAjaxExportList)) { wfDebug(__METHOD__ . ' Bad Request for unknown function ' . $this->func_name . "\n"); wfHttpError(400, 'Bad Request', "unknown function " . (string) $this->func_name); } elseif (!in_array('read', User::getGroupPermissions(array('*')), true) && !$wgUser->isAllowed('read')) { wfHttpError(403, 'Forbidden', 'You must log in to view pages.'); } else { wfDebug(__METHOD__ . ' dispatching ' . $this->func_name . "\n"); if (strpos($this->func_name, '::') !== false) { $func = explode('::', $this->func_name, 2); } else { $func = $this->func_name; } try { $result = call_user_func_array($func, $this->args); if ($result === false || $result === null) { wfDebug(__METHOD__ . ' ERROR while dispatching ' . $this->func_name . "(" . var_export($this->args, true) . "): " . "no data returned\n"); wfHttpError(500, 'Internal Error', "{$this->func_name} returned no data"); } else { if (is_string($result)) { $result = new AjaxResponse($result); } $result->sendHeaders(); $result->printText(); wfDebug(__METHOD__ . ' dispatch complete for ' . $this->func_name . "\n"); } } catch (Exception $e) { wfDebug(__METHOD__ . ' ERROR while dispatching ' . $this->func_name . "(" . var_export($this->args, true) . "): " . get_class($e) . ": " . $e->getMessage() . "\n"); if (!headers_sent()) { wfHttpError(500, 'Internal Error', $e->getMessage()); } else { print $e->getMessage(); } } } $wgOut = null; wfProfileOut(__METHOD__); }
/** * Image authorisation script * * To use this: * * Set $wgUploadDirectory to a non-public directory (not web accessible) * Set $wgUploadPath to point to this file * * Your server needs to support PATH_INFO; CGI-based configurations * usually don't. */ define('MW_NO_OUTPUT_COMPRESSION', 1); require_once dirname(__FILE__) . '/includes/WebStart.php'; wfProfileIn('img_auth.php'); require_once dirname(__FILE__) . '/includes/StreamFile.php'; $perms = User::getGroupPermissions(array('*')); if (in_array('read', $perms, true)) { wfDebugLog('img_auth', 'Public wiki'); wfPublicError(); } // Extract path and image information if (!isset($_SERVER['PATH_INFO'])) { wfDebugLog('img_auth', 'Missing PATH_INFO'); wfForbidden(); } $path = $_SERVER['PATH_INFO']; $filename = realpath($wgUploadDirectory . $_SERVER['PATH_INFO']); $realUpload = realpath($wgUploadDirectory); wfDebugLog('img_auth', "\$path is {$path}"); wfDebugLog('img_auth', "\$filename is {$filename}"); // Basic directory traversal check
/** * Stream a thumbnail specified by parameters * * @param $params Array * @return void */ function wfStreamThumb(array $params) { wfProfileIn(__METHOD__); $headers = array(); // HTTP headers to send $fileName = isset($params['f']) ? $params['f'] : ''; unset($params['f']); // Backwards compatibility parameters if (isset($params['w'])) { $params['width'] = $params['w']; unset($params['w']); } if (isset($params['p'])) { $params['page'] = $params['p']; } unset($params['r']); // ignore 'r' because we unconditionally pass File::RENDER // Is this a thumb of an archived file? $isOld = isset($params['archived']) && $params['archived']; unset($params['archived']); // Some basic input validation $fileName = strtr($fileName, '\\/', '__'); // Actually fetch the image. Method depends on whether it is archived or not. if ($isOld) { // Format is <timestamp>!<name> $bits = explode('!', $fileName, 2); if (count($bits) != 2) { wfThumbError(404, wfMessage('badtitletext')->parse()); wfProfileOut(__METHOD__); return; } $title = Title::makeTitleSafe(NS_FILE, $bits[1]); if (!$title) { wfThumbError(404, wfMessage('badtitletext')->parse()); wfProfileOut(__METHOD__); return; } $img = RepoGroup::singleton()->getLocalRepo()->newFromArchiveName($title, $fileName); } else { $img = wfLocalFile($fileName); } // Check permissions if there are read restrictions if (!in_array('read', User::getGroupPermissions(array('*')), true)) { if (!$img->getTitle()->userCan('read')) { wfThumbError(403, 'Access denied. You do not have permission to access ' . 'the source file.'); wfProfileOut(__METHOD__); return; } $headers[] = 'Cache-Control: private'; $headers[] = 'Vary: Cookie'; } // Check the source file storage path if (!$img) { wfThumbError(404, wfMessage('badtitletext')->parse()); wfProfileOut(__METHOD__); return; } if (!$img->exists()) { wfThumbError(404, 'The source file for the specified thumbnail does not exist.'); wfProfileOut(__METHOD__); return; } $sourcePath = $img->getPath(); if ($sourcePath === false) { wfThumbError(500, 'The source file is not locally accessible.'); wfProfileOut(__METHOD__); return; } // Check IMS against the source file // This means that clients can keep a cached copy even after it has been deleted on the server if (!empty($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { // Fix IE brokenness $imsString = preg_replace('/;.*$/', '', $_SERVER["HTTP_IF_MODIFIED_SINCE"]); // Calculate time wfSuppressWarnings(); $imsUnix = strtotime($imsString); wfRestoreWarnings(); $sourceTsUnix = wfTimestamp(TS_UNIX, $img->getTimestamp()); if ($sourceTsUnix <= $imsUnix) { header('HTTP/1.1 304 Not Modified'); wfProfileOut(__METHOD__); return; } } // Stream the file if it exists already... try { $thumbName = $img->thumbName($params); if (strlen($thumbName)) { // valid params? // For 404 handled thumbnails, we only use the the base name of the URI // for the thumb params and the parent directory for the source file name. // Check that the zone relative path matches up so squid caches won't pick // up thumbs that would not be purged on source file deletion (bug 34231). if (isset($params['rel404']) && urldecode($params['rel404']) !== $img->getThumbRel($thumbName)) { wfThumbError(404, 'The source file for the specified thumbnail does not exist.'); wfProfileOut(__METHOD__); return; } $thumbPath = $img->getThumbPath($thumbName); if ($img->getRepo()->fileExists($thumbPath)) { $img->getRepo()->streamFile($thumbPath, $headers); wfProfileOut(__METHOD__); return; } } } catch (MWException $e) { wfThumbError(500, $e->getHTML()); wfProfileOut(__METHOD__); return; } // Thumbnail isn't already there, so create the new thumbnail... try { $thumb = $img->transform($params, File::RENDER_NOW); } catch (Exception $ex) { // Tried to select a page on a non-paged file? $thumb = false; } // Check for thumbnail generation errors... $errorMsg = false; if (!$thumb) { $errorMsg = wfMsgHtml('thumbnail_error', 'File::transform() returned false'); } elseif ($thumb->isError()) { $errorMsg = $thumb->getHtmlMsg(); } elseif (!$thumb->hasFile()) { $errorMsg = wfMsgHtml('thumbnail_error', 'No path supplied in thumbnail object'); } elseif ($thumb->fileIsSource()) { $errorMsg = wfMsgHtml('thumbnail_error', 'Image was not scaled, is the requested width bigger than the source?'); } if ($errorMsg !== false) { wfThumbError(500, $errorMsg); } else { // Stream the file if there were no errors $thumb->streamFile($headers); } wfProfileOut(__METHOD__); }
/** * Remove the user from the given group. * This takes immediate effect. * @string $group */ function removeGroup($group) { $dbw =& wfGetDB(DB_MASTER); $dbw->delete('user_groups', array('ug_user' => $this->getID(), 'ug_group' => $group), 'User::removeGroup'); $this->mGroups = array_diff($this->mGroups, array($group)); $this->mRights = User::getGroupPermissions($this->getEffectiveGroups()); $this->invalidateCache(); $this->saveSettings(); }
/** * Check for sufficient permissions to execute * @param $module ApiBase An Api module */ protected function checkExecutePermissions($module) { global $wgUser; if ($module->isReadMode() && !in_array('read', User::getGroupPermissions(array('*')), true) && !$wgUser->isAllowed('read')) { $this->dieUsageMsg('readrequired'); } if ($module->isWriteMode()) { if (!$this->mEnableWrite) { $this->dieUsageMsg('writedisabled'); } if (!$wgUser->isAllowed('writeapi')) { $this->dieUsageMsg('writerequired'); } if (wfReadOnly()) { $this->dieReadOnly(); } } }
/** * Stream a thumbnail specified by parameters * * @param $params Array * @return void */ function wfStreamThumb(array $params) { global $wgVaryOnXFP; wfProfileIn(__METHOD__); $headers = array(); // HTTP headers to send $fileName = isset($params['f']) ? $params['f'] : ''; unset($params['f']); // Backwards compatibility parameters if (isset($params['w'])) { $params['width'] = $params['w']; unset($params['w']); } if (isset($params['p'])) { $params['page'] = $params['p']; } unset($params['r']); // ignore 'r' because we unconditionally pass File::RENDER // Is this a thumb of an archived file? $isOld = isset($params['archived']) && $params['archived']; unset($params['archived']); // handlers don't care // Is this a thumb of a temp file? $isTemp = isset($params['temp']) && $params['temp']; unset($params['temp']); // handlers don't care // Some basic input validation $fileName = strtr($fileName, '\\/', '__'); // Actually fetch the image. Method depends on whether it is archived or not. if ($isTemp) { $repo = RepoGroup::singleton()->getLocalRepo()->getTempRepo(); $img = new UnregisteredLocalFile(null, $repo, $repo->getZonePath('public') . '/' . $repo->getTempHashPath($fileName) . $fileName); } elseif ($isOld) { // Format is <timestamp>!<name> $bits = explode('!', $fileName, 2); if (count($bits) != 2) { wfThumbError(404, wfMessage('badtitletext')->text()); wfProfileOut(__METHOD__); return; } $title = Title::makeTitleSafe(NS_FILE, $bits[1]); if (!$title) { wfThumbError(404, wfMessage('badtitletext')->text()); wfProfileOut(__METHOD__); return; } $img = RepoGroup::singleton()->getLocalRepo()->newFromArchiveName($title, $fileName); } else { $img = wfLocalFile($fileName); } // Check the source file title if (!$img) { wfThumbError(404, wfMessage('badtitletext')->text()); wfProfileOut(__METHOD__); return; } // Check permissions if there are read restrictions $varyHeader = array(); if (!in_array('read', User::getGroupPermissions(array('*')), true)) { if (!$img->getTitle() || !$img->getTitle()->userCan('read')) { wfThumbError(403, 'Access denied. You do not have permission to access ' . 'the source file.'); wfProfileOut(__METHOD__); return; } $headers[] = 'Cache-Control: private'; $varyHeader[] = 'Cookie'; } // Check the source file storage path if (!$img->exists()) { wfThumbError(404, "The source file '{$fileName}' does not exist."); wfProfileOut(__METHOD__); return; } elseif ($img->getPath() === false) { wfThumbError(500, "The source file '{$fileName}' is not locally accessible."); wfProfileOut(__METHOD__); return; } // Check IMS against the source file // This means that clients can keep a cached copy even after it has been deleted on the server if (!empty($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { // Fix IE brokenness $imsString = preg_replace('/;.*$/', '', $_SERVER["HTTP_IF_MODIFIED_SINCE"]); // Calculate time wfSuppressWarnings(); $imsUnix = strtotime($imsString); wfRestoreWarnings(); if (wfTimestamp(TS_UNIX, $img->getTimestamp()) <= $imsUnix) { header('HTTP/1.1 304 Not Modified'); wfProfileOut(__METHOD__); return; } } // Get the normalized thumbnail name from the parameters... try { $thumbName = $img->thumbName($params); if (!strlen($thumbName)) { // invalid params? wfThumbError(400, 'The specified thumbnail parameters are not valid.'); wfProfileOut(__METHOD__); return; } $thumbName2 = $img->thumbName($params, File::THUMB_FULL_NAME); // b/c; "long" style } catch (MWException $e) { wfThumbError(500, $e->getHTML()); wfProfileOut(__METHOD__); return; } // For 404 handled thumbnails, we only use the the base name of the URI // for the thumb params and the parent directory for the source file name. // Check that the zone relative path matches up so squid caches won't pick // up thumbs that would not be purged on source file deletion (bug 34231). if (isset($params['rel404'])) { // thumbnail was handled via 404 if (rawurldecode($params['rel404']) === $img->getThumbRel($thumbName)) { // Request for the canonical thumbnail name } elseif (rawurldecode($params['rel404']) === $img->getThumbRel($thumbName2)) { // Request for the "long" thumbnail name; redirect to canonical name $response = RequestContext::getMain()->getRequest()->response(); $response->header("HTTP/1.1 301 " . HttpStatus::getMessage(301)); $response->header('Location: ' . wfExpandUrl($img->getThumbUrl($thumbName), PROTO_CURRENT)); $response->header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 7 * 86400) . ' GMT'); if ($wgVaryOnXFP) { $varyHeader[] = 'X-Forwarded-Proto'; } if (count($varyHeader)) { $response->header('Vary: ' . implode(', ', $varyHeader)); } wfProfileOut(__METHOD__); return; } else { wfThumbError(404, "The given path of the specified thumbnail is incorrect;\n\t\t\t\texpected '" . $img->getThumbRel($thumbName) . "' but got '" . rawurldecode($params['rel404']) . "'."); wfProfileOut(__METHOD__); return; } } // Suggest a good name for users downloading this thumbnail $headers[] = "Content-Disposition: {$img->getThumbDisposition($thumbName)}"; if (count($varyHeader)) { $headers[] = 'Vary: ' . implode(', ', $varyHeader); } // Stream the file if it exists already... $thumbPath = $img->getThumbPath($thumbName); if ($img->getRepo()->fileExists($thumbPath)) { $img->getRepo()->streamFile($thumbPath, $headers); wfProfileOut(__METHOD__); return; } // Thumbnail isn't already there, so create the new thumbnail... try { $thumb = $img->transform($params, File::RENDER_NOW); } catch (Exception $ex) { // Tried to select a page on a non-paged file? $thumb = false; } // Check for thumbnail generation errors... $errorMsg = false; $msg = wfMessage('thumbnail_error'); if (!$thumb) { $errorMsg = $msg->rawParams('File::transform() returned false')->escaped(); } elseif ($thumb->isError()) { $errorMsg = $thumb->getHtmlMsg(); } elseif (!$thumb->hasFile()) { $errorMsg = $msg->rawParams('No path supplied in thumbnail object')->escaped(); } elseif ($thumb->fileIsSource()) { $errorMsg = $msg->rawParams('Image was not scaled, is the requested width bigger than the source?')->escaped(); } if ($errorMsg !== false) { wfThumbError(500, $errorMsg); } else { // Stream the file if there were no errors $thumb->streamFile($headers); } wfProfileOut(__METHOD__); }
public function execute() { $db = $this->getDB(); $params = $this->extractRequestParams(); $prop = $params['prop']; if (!is_null($prop)) { $prop = array_flip($prop); $fld_blockinfo = isset($prop['blockinfo']); $fld_editcount = isset($prop['editcount']); $fld_groups = isset($prop['groups']); $fld_rights = isset($prop['rights']); $fld_registration = isset($prop['registration']); $fld_implicitgroups = isset($prop['implicitgroups']); } else { $fld_blockinfo = $fld_editcount = $fld_groups = $fld_registration = $fld_rights = $fld_implicitgroups = false; } $limit = $params['limit']; $this->addTables('user'); $useIndex = true; $dir = $params['dir'] == 'descending' ? 'older' : 'newer'; $from = is_null($params['from']) ? null : $this->keyToTitle($params['from']); $to = is_null($params['to']) ? null : $this->keyToTitle($params['to']); $this->addWhereRange('user_name', $dir, $from, $to); if (!is_null($params['prefix'])) { $this->addWhere('user_name' . $db->buildLike($this->keyToTitle($params['prefix']), $db->anyString())); } if (!is_null($params['rights'])) { $groups = array(); foreach ($params['rights'] as $r) { $groups = array_merge($groups, User::getGroupsWithPermission($r)); } $groups = array_unique($groups); if (is_null($params['group'])) { $params['group'] = $groups; } else { $params['group'] = array_unique(array_merge($params['group'], $groups)); } } if (!is_null($params['group']) && !is_null($params['excludegroup'])) { $this->dieUsage('group and excludegroup cannot be used together', 'group-excludegroup'); } if (!is_null($params['group']) && count($params['group'])) { $useIndex = false; // Filter only users that belong to a given group $this->addTables('user_groups', 'ug1'); $this->addJoinConds(array('ug1' => array('INNER JOIN', array('ug1.ug_user=user_id', 'ug1.ug_group' => $params['group'])))); } if (!is_null($params['excludegroup']) && count($params['excludegroup'])) { $useIndex = false; // Filter only users don't belong to a given group $this->addTables('user_groups', 'ug1'); if (count($params['excludegroup']) == 1) { $exclude = array('ug1.ug_group' => $params['excludegroup'][0]); } else { $exclude = array($db->makeList(array('ug1.ug_group' => $params['excludegroup']), LIST_OR)); } $this->addJoinConds(array('ug1' => array('LEFT OUTER JOIN', array_merge(array('ug1.ug_user=user_id'), $exclude)))); $this->addWhere('ug1.ug_user IS NULL'); } if ($params['witheditsonly']) { $this->addWhere('user_editcount > 0'); } $this->showHiddenUsersAddBlockInfo($fld_blockinfo); if ($fld_groups || $fld_rights) { // Show the groups the given users belong to // request more than needed to avoid not getting all rows that belong to one user $groupCount = count(User::getAllGroups()); $sqlLimit = $limit + $groupCount + 1; $this->addTables('user_groups', 'ug2'); $this->addJoinConds(array('ug2' => array('LEFT JOIN', 'ug2.ug_user=user_id'))); $this->addFields('ug2.ug_group ug_group2'); } else { $sqlLimit = $limit + 1; } if ($params['activeusers']) { global $wgActiveUserDays; $this->addTables('recentchanges'); $this->addJoinConds(array('recentchanges' => array('INNER JOIN', 'rc_user_text=user_name'))); $this->addFields('COUNT(*) AS recentedits'); $this->addWhere("rc_log_type IS NULL OR rc_log_type != 'newusers'"); $timestamp = $db->timestamp(wfTimestamp(TS_UNIX) - $wgActiveUserDays * 24 * 3600); $this->addWhere("rc_timestamp >= {$db->addQuotes($timestamp)}"); $this->addOption('GROUP BY', 'user_name'); } $this->addOption('LIMIT', $sqlLimit); $this->addFields(array('user_name', 'user_id')); $this->addFieldsIf('user_editcount', $fld_editcount); $this->addFieldsIf('user_registration', $fld_registration); if ($useIndex) { $this->addOption('USE INDEX', array('user' => 'user_name')); } $res = $this->select(__METHOD__); $count = 0; $lastUserData = false; $lastUser = false; $result = $this->getResult(); // // This loop keeps track of the last entry. // For each new row, if the new row is for different user then the last, the last entry is added to results. // Otherwise, the group of the new row is appended to the last entry. // The setContinue... is more complex because of this, and takes into account the higher sql limit // to make sure all rows that belong to the same user are received. foreach ($res as $row) { $count++; if ($lastUser !== $row->user_name) { // Save the last pass's user data if (is_array($lastUserData)) { $fit = $result->addValue(array('query', $this->getModuleName()), null, $lastUserData); $lastUserData = null; if (!$fit) { $this->setContinueEnumParameter('from', $this->keyToTitle($lastUserData['name'])); break; } } if ($count > $limit) { // We've reached the one extra which shows that there are additional pages to be had. Stop here... $this->setContinueEnumParameter('from', $this->keyToTitle($row->user_name)); break; } // Record new user's data $lastUser = $row->user_name; $lastUserData = array('userid' => $row->user_id, 'name' => $lastUser); if ($fld_blockinfo && !is_null($row->ipb_by_text)) { $lastUserData['blockedby'] = $row->ipb_by_text; $lastUserData['blockreason'] = $row->ipb_reason; $lastUserData['blockexpiry'] = $row->ipb_expiry; } if ($row->ipb_deleted) { $lastUserData['hidden'] = ''; } if ($fld_editcount) { $lastUserData['editcount'] = intval($row->user_editcount); } if ($params['activeusers']) { $lastUserData['recenteditcount'] = intval($row->recentedits); } if ($fld_registration) { $lastUserData['registration'] = $row->user_registration ? wfTimestamp(TS_ISO_8601, $row->user_registration) : ''; } } if ($sqlLimit == $count) { // BUG! database contains group name that User::getAllGroups() does not return // TODO: should handle this more gracefully ApiBase::dieDebug(__METHOD__, 'MediaWiki configuration error: the database contains more user groups than known to User::getAllGroups() function'); } // Add user's group info if ($fld_groups) { if (!isset($lastUserData['groups'])) { $lastUserData['groups'] = ApiQueryUsers::getAutoGroups(User::newFromName($lastUser)); } if (!is_null($row->ug_group2)) { $lastUserData['groups'][] = $row->ug_group2; } $result->setIndexedTagName($lastUserData['groups'], 'g'); } if ($fld_implicitgroups && !isset($lastUserData['implicitgroups'])) { $lastUserData['implicitgroups'] = ApiQueryUsers::getAutoGroups(User::newFromName($lastUser)); $result->setIndexedTagName($lastUserData['implicitgroups'], 'g'); } if ($fld_rights) { if (!isset($lastUserData['rights'])) { $lastUserData['rights'] = User::getGroupPermissions(User::newFromName($lastUser)->getAutomaticGroups()); } if (!is_null($row->ug_group2)) { $lastUserData['rights'] = array_unique(array_merge($lastUserData['rights'], User::getGroupPermissions(array($row->ug_group2)))); } $result->setIndexedTagName($lastUserData['rights'], 'r'); } } if (is_array($lastUserData)) { $fit = $result->addValue(array('query', $this->getModuleName()), null, $lastUserData); if (!$fit) { $this->setContinueEnumParameter('from', $this->keyToTitle($lastUserData['name'])); } } $result->setIndexedTagName_internal(array('query', $this->getModuleName()), 'u'); }
public function execute() { $params = $this->extractRequestParams(); $activeUserDays = $this->getConfig()->get('ActiveUserDays'); $db = $this->getDB(); $prop = $params['prop']; if (!is_null($prop)) { $prop = array_flip($prop); $fld_blockinfo = isset($prop['blockinfo']); $fld_editcount = isset($prop['editcount']); $fld_groups = isset($prop['groups']); $fld_rights = isset($prop['rights']); $fld_registration = isset($prop['registration']); $fld_implicitgroups = isset($prop['implicitgroups']); $fld_centralids = isset($prop['centralids']); } else { $fld_blockinfo = $fld_editcount = $fld_groups = $fld_registration = $fld_rights = $fld_implicitgroups = $fld_centralids = false; } $limit = $params['limit']; $this->addTables('user'); $dir = $params['dir'] == 'descending' ? 'older' : 'newer'; $from = is_null($params['from']) ? null : $this->getCanonicalUserName($params['from']); $to = is_null($params['to']) ? null : $this->getCanonicalUserName($params['to']); # MySQL can't figure out that 'user_name' and 'qcc_title' are the same # despite the JOIN condition, so manually sort on the correct one. $userFieldToSort = $params['activeusers'] ? 'qcc_title' : 'user_name'; # Some of these subtable joins are going to give us duplicate rows, so # calculate the maximum number of duplicates we might see. $maxDuplicateRows = 1; $this->addWhereRange($userFieldToSort, $dir, $from, $to); if (!is_null($params['prefix'])) { $this->addWhere($userFieldToSort . $db->buildLike($this->getCanonicalUserName($params['prefix']), $db->anyString())); } if (!is_null($params['rights']) && count($params['rights'])) { $groups = array(); foreach ($params['rights'] as $r) { $groups = array_merge($groups, User::getGroupsWithPermission($r)); } // no group with the given right(s) exists, no need for a query if (!count($groups)) { $this->getResult()->addIndexedTagName(array('query', $this->getModuleName()), ''); return; } $groups = array_unique($groups); if (is_null($params['group'])) { $params['group'] = $groups; } else { $params['group'] = array_unique(array_merge($params['group'], $groups)); } } if (!is_null($params['group']) && !is_null($params['excludegroup'])) { $this->dieUsage('group and excludegroup cannot be used together', 'group-excludegroup'); } if (!is_null($params['group']) && count($params['group'])) { // Filter only users that belong to a given group. This might // produce as many rows-per-user as there are groups being checked. $this->addTables('user_groups', 'ug1'); $this->addJoinConds(array('ug1' => array('INNER JOIN', array('ug1.ug_user=user_id', 'ug1.ug_group' => $params['group'])))); $maxDuplicateRows *= count($params['group']); } if (!is_null($params['excludegroup']) && count($params['excludegroup'])) { // Filter only users don't belong to a given group. This can only // produce one row-per-user, because we only keep on "no match". $this->addTables('user_groups', 'ug1'); if (count($params['excludegroup']) == 1) { $exclude = array('ug1.ug_group' => $params['excludegroup'][0]); } else { $exclude = array($db->makeList(array('ug1.ug_group' => $params['excludegroup']), LIST_OR)); } $this->addJoinConds(array('ug1' => array('LEFT OUTER JOIN', array_merge(array('ug1.ug_user=user_id'), $exclude)))); $this->addWhere('ug1.ug_user IS NULL'); } if ($params['witheditsonly']) { $this->addWhere('user_editcount > 0'); } $this->showHiddenUsersAddBlockInfo($fld_blockinfo); if ($fld_groups || $fld_rights) { $this->addFields(array('groups' => $db->buildGroupConcatField('|', 'user_groups', 'ug_group', 'ug_user=user_id'))); } if ($params['activeusers']) { $activeUserSeconds = $activeUserDays * 86400; // Filter query to only include users in the active users cache. // There shouldn't be any duplicate rows in querycachetwo here. $this->addTables('querycachetwo'); $this->addJoinConds(array('querycachetwo' => array('INNER JOIN', array('qcc_type' => 'activeusers', 'qcc_namespace' => NS_USER, 'qcc_title=user_name')))); // Actually count the actions using a subquery (bug 64505 and bug 64507) $timestamp = $db->timestamp(wfTimestamp(TS_UNIX) - $activeUserSeconds); $this->addFields(array('recentactions' => '(' . $db->selectSQLText('recentchanges', 'COUNT(*)', array('rc_user_text = user_name', 'rc_type != ' . $db->addQuotes(RC_EXTERNAL), 'rc_log_type IS NULL OR rc_log_type != ' . $db->addQuotes('newusers'), 'rc_timestamp >= ' . $db->addQuotes($timestamp))) . ')')); } $sqlLimit = $limit + $maxDuplicateRows; $this->addOption('LIMIT', $sqlLimit); $this->addFields(array('user_name', 'user_id')); $this->addFieldsIf('user_editcount', $fld_editcount); $this->addFieldsIf('user_registration', $fld_registration); $res = $this->select(__METHOD__); $count = 0; $countDuplicates = 0; $lastUser = false; $result = $this->getResult(); foreach ($res as $row) { $count++; if ($lastUser === $row->user_name) { // Duplicate row due to one of the needed subtable joins. // Ignore it, but count the number of them to sanely handle // miscalculation of $maxDuplicateRows. $countDuplicates++; if ($countDuplicates == $maxDuplicateRows) { ApiBase::dieDebug(__METHOD__, 'Saw more duplicate rows than expected'); } continue; } $countDuplicates = 0; $lastUser = $row->user_name; if ($count > $limit) { // We've reached the one extra which shows that there are // additional pages to be had. Stop here... $this->setContinueEnumParameter('from', $row->user_name); break; } if ($count == $sqlLimit) { // Should never hit this (either the $countDuplicates check or // the $count > $limit check should hit first), but check it // anyway just in case. ApiBase::dieDebug(__METHOD__, 'Saw more duplicate rows than expected'); } if ($params['activeusers'] && $row->recentactions === 0) { // activeusers cache was out of date continue; } $data = array('userid' => (int) $row->user_id, 'name' => $row->user_name); if ($fld_centralids) { $data += ApiQueryUserInfo::getCentralUserInfo($this->getConfig(), User::newFromId($row->user_id), $params['attachedwiki']); } if ($fld_blockinfo && !is_null($row->ipb_by_text)) { $data['blockid'] = (int) $row->ipb_id; $data['blockedby'] = $row->ipb_by_text; $data['blockedbyid'] = (int) $row->ipb_by; $data['blockedtimestamp'] = wfTimestamp(TS_ISO_8601, $row->ipb_timestamp); $data['blockreason'] = $row->ipb_reason; $data['blockexpiry'] = $row->ipb_expiry; } if ($row->ipb_deleted) { $data['hidden'] = true; } if ($fld_editcount) { $data['editcount'] = intval($row->user_editcount); } if ($params['activeusers']) { $data['recentactions'] = intval($row->recentactions); // @todo 'recenteditcount' is set for BC, remove in 1.25 $data['recenteditcount'] = $data['recentactions']; } if ($fld_registration) { $data['registration'] = $row->user_registration ? wfTimestamp(TS_ISO_8601, $row->user_registration) : ''; } if ($fld_implicitgroups || $fld_groups || $fld_rights) { $implicitGroups = User::newFromId($row->user_id)->getAutomaticGroups(); if (isset($row->groups) && $row->groups !== '') { $groups = array_merge($implicitGroups, explode('|', $row->groups)); } else { $groups = $implicitGroups; } if ($fld_groups) { $data['groups'] = $groups; ApiResult::setIndexedTagName($data['groups'], 'g'); ApiResult::setArrayType($data['groups'], 'array'); } if ($fld_implicitgroups) { $data['implicitgroups'] = $implicitGroups; ApiResult::setIndexedTagName($data['implicitgroups'], 'g'); ApiResult::setArrayType($data['implicitgroups'], 'array'); } if ($fld_rights) { $data['rights'] = User::getGroupPermissions($groups); ApiResult::setIndexedTagName($data['rights'], 'r'); ApiResult::setArrayType($data['rights'], 'array'); } } $fit = $result->addValue(array('query', $this->getModuleName()), null, $data); if (!$fit) { $this->setContinueEnumParameter('from', $data['name']); break; } } $result->addIndexedTagName(array('query', $this->getModuleName()), 'u'); }
* * For security reasons, you usually don't want your user to know *why* access was denied, just that it was. * If you want to change this, you can set $wgImgAuthDetails to 'true' in localsettings.php and it will give the user the reason * why access was denied. * * Your server needs to support PATH_INFO; CGI-based configurations usually don't. * * @file * **/ define('MW_NO_OUTPUT_COMPRESSION', 1); require_once dirname(__FILE__) . '/includes/WebStart.php'; wfProfileIn('img_auth.php'); require_once dirname(__FILE__) . '/includes/StreamFile.php'; // See if this is a public Wiki (no protections) if ($wgImgAuthPublicTest && in_array('read', User::getGroupPermissions(array('*')), true)) { wfForbidden('img-auth-accessdenied', 'img-auth-public'); } // Check for bug 28235: QUERY_STRING overriding the correct extension if (isset($_SERVER['QUERY_STRING']) && preg_match('/\\.[^\\/:*?"<>|%]+(#|\\?|$)/i', $_SERVER['QUERY_STRING'])) { wfForbidden('img-auth-accessdenied', 'img-auth-bad-query-string'); } // Extract path and image information if (!isset($_SERVER['PATH_INFO'])) { wfForbidden('img-auth-accessdenied', 'img-auth-nopathinfo'); } $path = $_SERVER['PATH_INFO']; $filename = realpath($wgUploadDirectory . $_SERVER['PATH_INFO']); $realUpload = realpath($wgUploadDirectory); // Basic directory traversal check if (substr($filename, 0, strlen($realUpload)) != $realUpload) {
function wfImageAuthMain() { global $wgImgAuthUrlPathMap; $request = RequestContext::getMain()->getRequest(); $publicWiki = in_array('read', User::getGroupPermissions(array('*')), true); // Get the requested file path (source file or thumbnail) $matches = WebRequest::getPathInfo(); if (!isset($matches['title'])) { wfForbidden('img-auth-accessdenied', 'img-auth-nopathinfo'); return; } $path = $matches['title']; if ($path && $path[0] !== '/') { // Make sure $path has a leading / $path = "/" . $path; } // Check for bug 28235: QUERY_STRING overriding the correct extension $whitelist = array(); $extension = FileBackend::extensionFromPath($path, 'rawcase'); if ($extension != '') { $whitelist[] = $extension; } if (!$request->checkUrlExtension($whitelist)) { return; } // Various extensions may have their own backends that need access. // Check if there is a special backend and storage base path for this file. foreach ($wgImgAuthUrlPathMap as $prefix => $storageDir) { $prefix = rtrim($prefix, '/') . '/'; // implicit trailing slash if (strpos($path, $prefix) === 0) { $be = FileBackendGroup::singleton()->backendFromPath($storageDir); $filename = $storageDir . substr($path, strlen($prefix)); // strip prefix // Check basic user authorization if (!RequestContext::getMain()->getUser()->isAllowed('read')) { wfForbidden('img-auth-accessdenied', 'img-auth-noread', $path); return; } if ($be->fileExists(array('src' => $filename))) { wfDebugLog('img_auth', "Streaming `" . $filename . "`."); $be->streamFile(array('src' => $filename), array('Cache-Control: private', 'Vary: Cookie')); } else { wfForbidden('img-auth-accessdenied', 'img-auth-nofile', $path); } return; } } // Get the local file repository $repo = RepoGroup::singleton()->getRepo('local'); $zone = strstr(ltrim($path, '/'), '/', true); // Get the full file storage path and extract the source file name. // (e.g. 120px-Foo.png => Foo.png or page2-120px-Foo.png => Foo.png). // This only applies to thumbnails/transcoded, and each of them should // be under a folder that has the source file name. if ($zone === 'thumb' || $zone === 'transcoded') { $name = wfBaseName(dirname($path)); $filename = $repo->getZonePath($zone) . substr($path, strlen("/" . $zone)); // Check to see if the file exists if (!$repo->fileExists($filename)) { wfForbidden('img-auth-accessdenied', 'img-auth-nofile', $filename); return; } } else { $name = wfBaseName($path); // file is a source file $filename = $repo->getZonePath('public') . $path; // Check to see if the file exists and is not deleted $bits = explode('!', $name, 2); if (substr($path, 0, 9) === '/archive/' && count($bits) == 2) { $file = $repo->newFromArchiveName($bits[1], $name); } else { $file = $repo->newFile($name); } if (!$file->exists() || $file->isDeleted(File::DELETED_FILE)) { wfForbidden('img-auth-accessdenied', 'img-auth-nofile', $filename); return; } } $headers = array(); // extra HTTP headers to send if (!$publicWiki) { // For private wikis, run extra auth checks and set cache control headers $headers[] = 'Cache-Control: private'; $headers[] = 'Vary: Cookie'; $title = Title::makeTitleSafe(NS_FILE, $name); if (!$title instanceof Title) { // files have valid titles wfForbidden('img-auth-accessdenied', 'img-auth-badtitle', $name); return; } // Run hook for extension authorization plugins /** @var $result array */ $result = null; if (!wfRunHooks('ImgAuthBeforeStream', array(&$title, &$path, &$name, &$result))) { wfForbidden($result[0], $result[1], array_slice($result, 2)); return; } // Check user authorization for this title // Checks Whitelist too if (!$title->userCan('read')) { wfForbidden('img-auth-accessdenied', 'img-auth-noread', $name); return; } } if ($request->getCheck('download')) { $headers[] = 'Content-Disposition: attachment'; } // Stream the requested file wfDebugLog('img_auth', "Streaming `" . $filename . "`."); $repo->streamFile($filename, $headers); }
public function execute() { $params = $this->extractRequestParams(); $activeUserDays = $this->getConfig()->get('ActiveUserDays'); if ($params['activeusers']) { // Update active user cache SpecialActiveUsers::mergeActiveUsers(600, $activeUserDays); } $db = $this->getDB(); $prop = $params['prop']; if (!is_null($prop)) { $prop = array_flip($prop); $fld_blockinfo = isset($prop['blockinfo']); $fld_editcount = isset($prop['editcount']); $fld_groups = isset($prop['groups']); $fld_rights = isset($prop['rights']); $fld_registration = isset($prop['registration']); $fld_implicitgroups = isset($prop['implicitgroups']); } else { $fld_blockinfo = $fld_editcount = $fld_groups = $fld_registration = $fld_rights = $fld_implicitgroups = false; } $limit = $params['limit']; $this->addTables('user'); $useIndex = true; $dir = $params['dir'] == 'descending' ? 'older' : 'newer'; $from = is_null($params['from']) ? null : $this->getCanonicalUserName($params['from']); $to = is_null($params['to']) ? null : $this->getCanonicalUserName($params['to']); # MySQL can't figure out that 'user_name' and 'qcc_title' are the same # despite the JOIN condition, so manually sort on the correct one. $userFieldToSort = $params['activeusers'] ? 'qcc_title' : 'user_name'; $this->addWhereRange($userFieldToSort, $dir, $from, $to); if (!is_null($params['prefix'])) { $this->addWhere($userFieldToSort . $db->buildLike($this->getCanonicalUserName($params['prefix']), $db->anyString())); } if (!is_null($params['rights']) && count($params['rights'])) { $groups = array(); foreach ($params['rights'] as $r) { $groups = array_merge($groups, User::getGroupsWithPermission($r)); } // no group with the given right(s) exists, no need for a query if (!count($groups)) { $this->getResult()->setIndexedTagName_internal(array('query', $this->getModuleName()), ''); return; } $groups = array_unique($groups); if (is_null($params['group'])) { $params['group'] = $groups; } else { $params['group'] = array_unique(array_merge($params['group'], $groups)); } } if (!is_null($params['group']) && count($params['group'])) { // Filter only users that belong to a given group $this->addWhere('EXISTS (' . $db->selectSQLText('user_groups', '1', array('ug_user=user_id', 'ug_group' => $params['group'])) . ')'); } if (!is_null($params['excludegroup']) && count($params['excludegroup'])) { // Filter only users don't belong to a given group $this->addWhere('NOT EXISTS (' . $db->selectSQLText('user_groups', '1', array('ug_user=user_id', 'ug_group' => $params['excludegroup'])) . ')'); } if ($params['witheditsonly']) { $this->addWhere('user_editcount > 0'); } $this->showHiddenUsersAddBlockInfo($fld_blockinfo); if ($fld_groups || $fld_rights) { // Show the groups the given users belong to // request more than needed to avoid not getting all rows that belong to one user $groupCount = count(User::getAllGroups()); $sqlLimit = $limit + $groupCount + 1; $this->addTables('user_groups', 'ug2'); $this->addJoinConds(array('ug2' => array('LEFT JOIN', 'ug2.ug_user=user_id'))); $this->addFields(array('ug_group2' => 'ug2.ug_group')); } else { $sqlLimit = $limit + 1; } if ($params['activeusers']) { $activeUserSeconds = $activeUserDays * 86400; // Filter query to only include users in the active users cache $this->addTables('querycachetwo'); $this->addJoinConds(array('querycachetwo' => array('INNER JOIN', array('qcc_type' => 'activeusers', 'qcc_namespace' => NS_USER, 'qcc_title=user_name')))); // Actually count the actions using a subquery (bug 64505 and bug 64507) $timestamp = $db->timestamp(wfTimestamp(TS_UNIX) - $activeUserSeconds); $this->addFields(array('recentactions' => '(' . $db->selectSQLText('recentchanges', 'COUNT(*)', array('rc_user_text = user_name', 'rc_type != ' . $db->addQuotes(RC_EXTERNAL), 'rc_log_type IS NULL OR rc_log_type != ' . $db->addQuotes('newusers'), 'rc_timestamp >= ' . $db->addQuotes($timestamp))) . ')')); } $this->addOption('LIMIT', $sqlLimit); $this->addFields(array('user_name', 'user_id')); $this->addFieldsIf('user_editcount', $fld_editcount); $this->addFieldsIf('user_registration', $fld_registration); if ($useIndex) { $this->addOption('USE INDEX', array('user' => 'user_name')); } $res = $this->select(__METHOD__); $count = 0; $lastUserData = false; $lastUser = false; $result = $this->getResult(); // This loop keeps track of the last entry. For each new row, if the // new row is for different user then the last, the last entry is added // to results. Otherwise, the group of the new row is appended to the // last entry. The setContinue... is more complex because of this, and // takes into account the higher sql limit to make sure all rows that // belong to the same user are received. foreach ($res as $row) { $count++; if ($lastUser !== $row->user_name) { // Save the last pass's user data if (is_array($lastUserData)) { if ($params['activeusers'] && $lastUserData['recentactions'] === 0) { // activeusers cache was out of date $fit = true; } else { $fit = $result->addValue(array('query', $this->getModuleName()), null, $lastUserData); } $lastUserData = null; if (!$fit) { $this->setContinueEnumParameter('from', $lastUserData['name']); break; } } if ($count > $limit) { // We've reached the one extra which shows that there are // additional pages to be had. Stop here... $this->setContinueEnumParameter('from', $row->user_name); break; } // Record new user's data $lastUser = $row->user_name; $lastUserData = array('userid' => $row->user_id, 'name' => $lastUser); if ($fld_blockinfo && !is_null($row->ipb_by_text)) { $lastUserData['blockid'] = $row->ipb_id; $lastUserData['blockedby'] = $row->ipb_by_text; $lastUserData['blockedbyid'] = $row->ipb_by; $lastUserData['blockreason'] = $row->ipb_reason; $lastUserData['blockexpiry'] = $row->ipb_expiry; } if ($row->ipb_deleted) { $lastUserData['hidden'] = ''; } if ($fld_editcount) { $lastUserData['editcount'] = intval($row->user_editcount); } if ($params['activeusers']) { $lastUserData['recentactions'] = intval($row->recentactions); // @todo 'recenteditcount' is set for BC, remove in 1.25 $lastUserData['recenteditcount'] = $lastUserData['recentactions']; } if ($fld_registration) { $lastUserData['registration'] = $row->user_registration ? wfTimestamp(TS_ISO_8601, $row->user_registration) : ''; } } if ($sqlLimit == $count) { // @todo BUG! database contains group name that User::getAllGroups() does not return // Should handle this more gracefully ApiBase::dieDebug(__METHOD__, 'MediaWiki configuration error: The database contains more ' . 'user groups than known to User::getAllGroups() function'); } $lastUserObj = User::newFromId($row->user_id); // Add user's group info if ($fld_groups) { if (!isset($lastUserData['groups'])) { if ($lastUserObj) { $lastUserData['groups'] = $lastUserObj->getAutomaticGroups(); } else { // This should not normally happen $lastUserData['groups'] = array(); } } if (!is_null($row->ug_group2)) { $lastUserData['groups'][] = $row->ug_group2; } $result->setIndexedTagName($lastUserData['groups'], 'g'); } if ($fld_implicitgroups && !isset($lastUserData['implicitgroups']) && $lastUserObj) { $lastUserData['implicitgroups'] = $lastUserObj->getAutomaticGroups(); $result->setIndexedTagName($lastUserData['implicitgroups'], 'g'); } if ($fld_rights) { if (!isset($lastUserData['rights'])) { if ($lastUserObj) { $lastUserData['rights'] = User::getGroupPermissions($lastUserObj->getAutomaticGroups()); } else { // This should not normally happen $lastUserData['rights'] = array(); } } if (!is_null($row->ug_group2)) { $lastUserData['rights'] = array_unique(array_merge($lastUserData['rights'], User::getGroupPermissions(array($row->ug_group2)))); } $result->setIndexedTagName($lastUserData['rights'], 'r'); } } if (is_array($lastUserData) && !($params['activeusers'] && $lastUserData['recentactions'] === 0)) { $fit = $result->addValue(array('query', $this->getModuleName()), null, $lastUserData); if (!$fit) { $this->setContinueEnumParameter('from', $lastUserData['name']); } } $result->setIndexedTagName_internal(array('query', $this->getModuleName()), 'u'); }
/** * Check for sufficient permissions to execute * @param $module ApiBase An Api module */ protected function checkExecutePermissions($module) { $user = $this->getUser(); if ($module->isReadMode() && !in_array('read', User::getGroupPermissions(array('*')), true) && !$user->isAllowed('read')) { $this->dieUsageMsg('readrequired'); } if ($module->isWriteMode()) { if (!$this->mEnableWrite) { $this->dieUsageMsg('writedisabled'); } if (!$user->isAllowed('writeapi')) { $this->dieUsageMsg('writerequired'); } if (wfReadOnly()) { $this->dieReadOnly(); } } // Allow extensions to stop execution for arbitrary reasons. $message = false; if (!wfRunHooks('ApiCheckCanExecute', array($module, $user, &$message))) { $this->dieUsageMsg($message); } }
function wfImageAuthMain() { global $wgImgAuthPublicTest, $wgImgAuthUrlPathMap, $wgRequest; // See if this is a public Wiki (no protections). if ($wgImgAuthPublicTest && in_array('read', User::getGroupPermissions(array('*')), true)) { // This is a public wiki, so disable this script (for private wikis only) wfForbidden('img-auth-accessdenied', 'img-auth-public'); return; } // Get the requested file path (source file or thumbnail) $matches = WebRequest::getPathInfo(); if (!isset($matches['title'])) { wfForbidden('img-auth-accessdenied', 'img-auth-nopathinfo'); return; } $path = $matches['title']; if ($path && $path[0] !== '/') { // Make sure $path has a leading / $path = "/" . $path; } // Check for bug 28235: QUERY_STRING overriding the correct extension $whitelist = array(); $extension = FileBackend::extensionFromPath($path, 'rawcase'); if ($extension != '') { $whitelist[] = $extension; } if (!$wgRequest->checkUrlExtension($whitelist)) { return; } // Various extensions may have their own backends that need access. // Check if there is a special backend and storage base path for this file. foreach ($wgImgAuthUrlPathMap as $prefix => $storageDir) { $prefix = rtrim($prefix, '/') . '/'; // implicit trailing slash if (strpos($path, $prefix) === 0) { $be = FileBackendGroup::singleton()->backendFromPath($storageDir); $filename = $storageDir . substr($path, strlen($prefix)); // strip prefix // Check basic user authorization if (!RequestContext::getMain()->getUser()->isAllowed('read')) { wfForbidden('img-auth-accessdenied', 'img-auth-noread', $path); return; } if ($be->fileExists(array('src' => $filename))) { wfDebugLog('img_auth', "Streaming `" . $filename . "`."); $be->streamFile(array('src' => $filename), array('Cache-Control: private', 'Vary: Cookie')); } else { wfForbidden('img-auth-accessdenied', 'img-auth-nofile', $path); } return; } } // Get the local file repository $repo = RepoGroup::singleton()->getRepo('local'); // Get the full file storage path and extract the source file name. // (e.g. 120px-Foo.png => Foo.png or page2-120px-Foo.png => Foo.png). // This only applies to thumbnails, and all thumbnails should // be under a folder that has the source file name. if (strpos($path, '/thumb/') === 0) { $name = wfBaseName(dirname($path)); // file is a thumbnail $filename = $repo->getZonePath('thumb') . substr($path, 6); // strip "/thumb" } else { $name = wfBaseName($path); // file is a source file $filename = $repo->getZonePath('public') . $path; } // Check to see if the file exists if (!$repo->fileExists($filename)) { wfForbidden('img-auth-accessdenied', 'img-auth-nofile', $filename); return; } $title = Title::makeTitleSafe(NS_FILE, $name); if (!$title instanceof Title) { // files have valid titles wfForbidden('img-auth-accessdenied', 'img-auth-badtitle', $name); return; } // Run hook for extension authorization plugins /** @var $result array */ $result = null; if (!wfRunHooks('ImgAuthBeforeStream', array(&$title, &$path, &$name, &$result))) { wfForbidden($result[0], $result[1], array_slice($result, 2)); return; } // Check user authorization for this title // Checks Whitelist too if (!$title->userCan('read')) { wfForbidden('img-auth-accessdenied', 'img-auth-noread', $name); return; } if ($wgRequest->getCheck('download')) { header('Content-Disposition: attachment'); } // Stream the requested file wfDebugLog('img_auth', "Streaming `" . $filename . "`."); $repo->streamFile($filename, array('Cache-Control: private', 'Vary: Cookie')); }
/** * Synchronize the local group database with the remote database. */ public function refreshGroups(&$user) { wfDebug("- START: " . __FUNCTION__ . "\n"); $user->load(); $local_groups = $user->getGroups(); $rest_groups = RestAuthGroup::getAll($this->conn, $user->getName()); $remote_groups = array(); foreach ($rest_groups as $rest_group) { $remote_groups[] = $rest_group->name; } # get database slave: $dbw = wfGetDB(DB_MASTER); # remove groups no longer found in the remote database: # NOTE: We do not call User::removeGroup here, because that would call the hook. # If this whould be done, this would remove the group from the RestAuth server # when loading groups from the RestAuth server, which doesn't make sense. $rem_groups = array_diff($local_groups, $remote_groups); foreach ($rem_groups as $group) { $dbw->delete('user_groups', array('ug_user' => $user->getID(), 'ug_group' => $group), __METHOD__); // Remember that the user was in this group $dbw->insert('user_former_groups', array('ufg_user' => $user->getID(), 'ufg_group' => $group), __METHOD__, array('IGNORE')); $user->mGroups = array_diff($user->mGroups, array($group)); } # add new groups found in the remote database: # NOTE: We do not call User::addGroup here, because that would call the hook. # If this whould be done, this would add the group at the RestAuth server # when loading groups from the RestAuth server, which doesn't make sense. $add_groups = array_diff($remote_groups, $local_groups); foreach ($add_groups as $group) { if ($user->getId()) { $dbw->insert('user_groups', array('ug_user' => $user->getID(), 'ug_group' => $group), __METHOD__, array('IGNORE')); } $user->mGroups[] = $group; } # reload cache $user->getGroups(); $user->mRights = User::getGroupPermissions($user->getEffectiveGroups(true)); wfDebug("- END: " . __FUNCTION__ . "\n"); }
function wfImageAuthMain() { global $wgImgAuthPublicTest, $wgRequest; // See if this is a public Wiki (no protections). if ($wgImgAuthPublicTest && in_array('read', User::getGroupPermissions(array('*')), true)) { // This is a public wiki, so disable this script (for private wikis only) wfForbidden('img-auth-accessdenied', 'img-auth-public'); return; } // Get the requested file path (source file or thumbnail) $matches = WebRequest::getPathInfo(); if (!isset($matches['title'])) { wfForbidden('img-auth-accessdenied', 'img-auth-nopathinfo'); return; } $path = $matches['title']; if ($path && $path[0] !== '/') { // Make sure $path has a leading / $path = "/" . $path; } // Check for bug 28235: QUERY_STRING overriding the correct extension $whitelist = array(); $dotPos = strrpos($path, '.'); if ($dotPos !== false) { $whitelist[] = substr($path, $dotPos + 1); } if (!$wgRequest->checkUrlExtension($whitelist)) { return; } // Get the local file repository $repo = RepoGroup::singleton()->getRepo('local'); // Get the full file storage path and extract the source file name. // (e.g. 120px-Foo.png => Foo.png or page2-120px-Foo.png => Foo.png). // This only applies to thumbnails, and all thumbnails should // be under a folder that has the source file name. if (strpos($path, '/thumb/') === 0) { $name = wfBaseName(dirname($path)); // file is a thumbnail $filename = $repo->getZonePath('thumb') . substr($path, 6); // strip "/thumb" } else { $name = wfBaseName($path); // file is a source file $filename = $repo->getZonePath('public') . $path; } // Check to see if the file exists if (!$repo->fileExists($filename)) { wfForbidden('img-auth-accessdenied', 'img-auth-nofile', $filename); return; } $title = Title::makeTitleSafe(NS_FILE, $name); if (!$title instanceof Title) { // files have valid titles wfForbidden('img-auth-accessdenied', 'img-auth-badtitle', $name); return; } // Run hook for extension authorization plugins /** @var $result array */ $result = null; if (!wfRunHooks('ImgAuthBeforeStream', array(&$title, &$path, &$name, &$result))) { wfForbidden($result[0], $result[1], array_slice($result, 2)); return; } // Check user authorization for this title // Checks Whitelist too if (!$title->userCan('read')) { wfForbidden('img-auth-accessdenied', 'img-auth-noread', $name); return; } // Stream the requested file wfDebugLog('img_auth', "Streaming `" . $filename . "`."); $repo->streamFile($filename, array('Cache-Control: private', 'Vary: Cookie')); }
public function execute() { $params = $this->extractRequestParams(); if (!is_null($params['prop'])) { $this->prop = array_flip($params['prop']); } else { $this->prop = array(); } $users = (array) $params['users']; $goodNames = $done = array(); $result = $this->getResult(); // Canonicalize user names foreach ($users as $u) { $n = User::getCanonicalName($u); if ($n === false || $n === '') { $vals = array('name' => $u, 'invalid' => ''); $fit = $result->addValue(array('query', $this->getModuleName()), null, $vals); if (!$fit) { $this->setContinueEnumParameter('users', implode('|', array_diff($users, $done))); $goodNames = array(); break; } $done[] = $u; } else { $goodNames[] = $n; } } if (count($goodNames)) { $this->addTables('user', 'u1'); $this->addFields('u1.*'); $this->addWhereFld('u1.user_name', $goodNames); if (isset($this->prop['groups'])) { $this->addTables('user_groups'); $this->addJoinConds(array('user_groups' => array('LEFT JOIN', 'ug_user=u1.user_id'))); $this->addFields('ug_group'); } $this->showHiddenUsersAddBlockInfo(isset($this->prop['blockinfo'])); $data = array(); $res = $this->select(__METHOD__); foreach ($res as $row) { $user = User::newFromRow($row); $name = $user->getName(); $data[$name]['name'] = $name; if (isset($this->prop['editcount'])) { $data[$name]['editcount'] = intval($user->getEditCount()); } if (isset($this->prop['registration'])) { $data[$name]['registration'] = wfTimestampOrNull(TS_ISO_8601, $user->getRegistration()); } if (isset($this->prop['groups']) && !is_null($row->ug_group)) { // This row contains only one group, others will be added from other rows $data[$name]['groups'][] = $row->ug_group; } if (isset($this->prop['rights']) && !is_null($row->ug_group)) { if (!isset($data[$name]['rights'])) { $data[$name]['rights'] = User::getGroupPermissions(User::getImplicitGroups()); } $data[$name]['rights'] = array_unique(array_merge($data[$name]['rights'], User::getGroupPermissions(array($row->ug_group)))); $result->setIndexedTagName($data[$name]['rights'], 'r'); } if ($row->ipb_deleted) { $data[$name]['hidden'] = ''; } if (isset($this->prop['blockinfo']) && !is_null($row->ipb_by_text)) { $data[$name]['blockedby'] = $row->ipb_by_text; $data[$name]['blockreason'] = $row->ipb_reason; $data[$name]['blockexpiry'] = $row->ipb_expiry; } if (isset($this->prop['emailable']) && $user->canReceiveEmail()) { $data[$name]['emailable'] = ''; } if (isset($this->prop['gender'])) { $gender = $user->getOption('gender'); if (strval($gender) === '') { $gender = 'unknown'; } $data[$name]['gender'] = $gender; } if (!is_null($params['token'])) { $tokenFunctions = $this->getTokenFunctions(); foreach ($params['token'] as $t) { $val = call_user_func($tokenFunctions[$t], $user); if ($val === false) { $this->setWarning("Action '{$t}' is not allowed for the current user"); } else { $data[$name][$t . 'token'] = $val; } } } } } // Second pass: add result data to $retval foreach ($goodNames as $u) { if (!isset($data[$u])) { $data[$u] = array('name' => $u); $urPage = new UserrightsPage(); $iwUser = $urPage->fetchUser($u); if ($iwUser instanceof UserRightsProxy) { $data[$u]['interwiki'] = ''; if (!is_null($params['token'])) { $tokenFunctions = $this->getTokenFunctions(); foreach ($params['token'] as $t) { $val = call_user_func($tokenFunctions[$t], $iwUser); if ($val === false) { $this->setWarning("Action '{$t}' is not allowed for the current user"); } else { $data[$u][$t . 'token'] = $val; } } } } else { $data[$u]['missing'] = ''; } } else { if (isset($this->prop['groups']) && isset($data[$u]['groups'])) { $autolist = ApiQueryUsers::getAutoGroups(User::newFromName($u)); $data[$u]['groups'] = array_merge($autolist, $data[$u]['groups']); $this->getResult()->setIndexedTagName($data[$u]['groups'], 'g'); } } $fit = $result->addValue(array('query', $this->getModuleName()), null, $data[$u]); if (!$fit) { $this->setContinueEnumParameter('users', implode('|', array_diff($users, $done))); break; } $done[] = $u; } return $this->getResult()->setIndexedTagName_internal(array('query', $this->getModuleName()), 'user'); }
/** * Execute the actual module, without any error handling */ protected function executeAction() { // First add the id to the top element $requestid = $this->getParameter('requestid'); if (!is_null($requestid)) { $this->getResult()->addValue(null, 'requestid', $requestid); } $params = $this->extractRequestParams(); $this->mShowVersions = $params['version']; $this->mAction = $params['action']; if (!is_string($this->mAction)) { $this->dieUsage("The API requires a valid action parameter", 'unknown_action'); } // Instantiate the module requested by the user $module = new $this->mModules[$this->mAction]($this, $this->mAction); $this->mModule = $module; $moduleParams = $module->extractRequestParams(); // Die if token required, but not provided (unless there is a gettoken parameter) $salt = $module->getTokenSalt(); if ($salt !== false && !isset($moduleParams['gettoken'])) { if (!isset($moduleParams['token'])) { $this->dieUsageMsg(array('missingparam', 'token')); } else { global $wgUser; if (!$wgUser->matchEditToken($moduleParams['token'], $salt)) { $this->dieUsageMsg(array('sessionfailure')); } } } if ($module->shouldCheckMaxlag() && isset($params['maxlag'])) { // Check for maxlag global $wgShowHostnames; $maxLag = $params['maxlag']; list($host, $lag) = wfGetLB()->getMaxLag(); if ($lag > $maxLag) { header('Retry-After: ' . max(intval($maxLag), 5)); header('X-Database-Lag: ' . intval($lag)); if ($wgShowHostnames) { $this->dieUsage("Waiting for {$host}: {$lag} seconds lagged", 'maxlag'); } else { $this->dieUsage("Waiting for a database server: {$lag} seconds lagged", 'maxlag'); } return; } } global $wgUser, $wgGroupPermissions; if ($module->isReadMode() && !in_array('read', User::getGroupPermissions(array('*')), true) && !$wgUser->isAllowed('read')) { $this->dieUsageMsg(array('readrequired')); } if ($module->isWriteMode()) { if (!$this->mEnableWrite) { $this->dieUsageMsg(array('writedisabled')); } if (!$wgUser->isAllowed('writeapi')) { $this->dieUsageMsg(array('writerequired')); } if (wfReadOnly()) { $this->dieReadOnly(); } } if (!$this->mInternalMode) { // Ignore mustBePosted() for internal calls if ($module->mustBePosted() && !$this->mRequest->wasPosted()) { $this->dieUsageMsg(array('mustbeposted', $this->mAction)); } // See if custom printer is used $this->mPrinter = $module->getCustomPrinter(); if (is_null($this->mPrinter)) { // Create an appropriate printer $this->mPrinter = $this->createPrinterByName($params['format']); } if ($this->mPrinter->getNeedsRawData()) { $this->getResult()->setRawMode(); } } // Execute $module->profileIn(); $module->execute(); wfRunHooks('APIAfterExecute', array(&$module)); $module->profileOut(); if (!$this->mInternalMode) { // Print result data $this->printResult(false); } }
function wfThumbMain() { wfProfileIn(__METHOD__); $headers = array(); // Get input parameters if (get_magic_quotes_gpc()) { $params = array_map('stripslashes', $_REQUEST); } else { $params = $_REQUEST; } $fileName = isset($params['f']) ? $params['f'] : ''; unset($params['f']); // Backwards compatibility parameters if (isset($params['w'])) { $params['width'] = $params['w']; unset($params['w']); } if (isset($params['p'])) { $params['page'] = $params['p']; } unset($params['r']); // ignore 'r' because we unconditionally pass File::RENDER // Is this a thumb of an archived file? $isOld = isset($params['archived']) && $params['archived']; unset($params['archived']); // Some basic input validation $fileName = strtr($fileName, '\\/', '__'); // Actually fetch the image. Method depends on whether it is archived or not. if ($isOld) { // Format is <timestamp>!<name> $bits = explode('!', $fileName, 2); if (!isset($bits[1])) { wfThumbError(404, wfMsg('badtitletext')); wfProfileOut(__METHOD__); return; } $title = Title::makeTitleSafe(NS_FILE, $bits[1]); if (is_null($title)) { wfThumbError(404, wfMsg('badtitletext')); wfProfileOut(__METHOD__); return; } $img = RepoGroup::singleton()->getLocalRepo()->newFromArchiveName($title, $fileName); } else { $img = wfLocalFile($fileName); } // Check permissions if there are read restrictions if (!in_array('read', User::getGroupPermissions(array('*')), true)) { if (!$img->getTitle()->userCanRead()) { wfThumbError(403, 'Access denied. You do not have permission to access ' . 'the source file.'); wfProfileOut(__METHOD__); return; } $headers[] = 'Cache-Control: private'; $headers[] = 'Vary: Cookie'; } if (!$img) { wfThumbError(404, wfMsg('badtitletext')); wfProfileOut(__METHOD__); return; } if (!$img->exists()) { wfThumbError(404, 'The source file for the specified thumbnail does not exist.'); wfProfileOut(__METHOD__); return; } $sourcePath = $img->getPath(); if ($sourcePath === false) { wfThumbError(500, 'The source file is not locally accessible.'); wfProfileOut(__METHOD__); return; } // Check IMS against the source file // This means that clients can keep a cached copy even after it has been deleted on the server if (!empty($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { // Fix IE brokenness $imsString = preg_replace('/;.*$/', '', $_SERVER["HTTP_IF_MODIFIED_SINCE"]); // Calculate time wfSuppressWarnings(); $imsUnix = strtotime($imsString); $stat = stat($sourcePath); wfRestoreWarnings(); if ($stat['mtime'] <= $imsUnix) { header('HTTP/1.1 304 Not Modified'); wfProfileOut(__METHOD__); return; } } // Stream the file if it exists already try { if (false != ($thumbName = $img->thumbName($params))) { $thumbPath = $img->getThumbPath($thumbName); if (is_file($thumbPath)) { wfStreamFile($thumbPath, $headers); wfProfileOut(__METHOD__); return; } } } catch (MWException $e) { wfThumbError(500, $e->getHTML()); wfProfileOut(__METHOD__); return; } try { $thumb = $img->transform($params, File::RENDER_NOW); } catch (Exception $ex) { // Tried to select a page on a non-paged file? $thumb = false; } $errorMsg = false; if (!$thumb) { $errorMsg = wfMsgHtml('thumbnail_error', 'File::transform() returned false'); } elseif ($thumb->isError()) { $errorMsg = $thumb->getHtmlMsg(); } elseif (!$thumb->getPath()) { $errorMsg = wfMsgHtml('thumbnail_error', 'No path supplied in thumbnail object'); } elseif ($thumb->getPath() == $img->getPath()) { $errorMsg = wfMsgHtml('thumbnail_error', 'Image was not scaled, ' . 'is the requested width bigger than the source?'); } else { wfStreamFile($thumb->getPath(), $headers); } if ($errorMsg !== false) { wfThumbError(500, $errorMsg); } wfProfileOut(__METHOD__); }
/** * As recCheckCondition, but *not* recursive. The only valid conditions * are those whose first element is APCOND_EMAILCONFIRMED/APCOND_EDITCOUNT/ * APCOND_AGE. Other types will throw an exception if no extension evalu- * ates them. * * @param $cond Array: A condition, which must not contain other conditions * @param $user User The user to check the condition against * @return bool Whether the condition is true for the user */ private static function checkCondition($cond, User $user) { global $wgEmailAuthentication, $wgEnableEditCountLocal; if (count($cond) < 1) { return false; } switch ($cond[0]) { case APCOND_EMAILCONFIRMED: if (Sanitizer::validateEmail($user->getEmail())) { if ($wgEmailAuthentication) { return (bool) $user->getEmailAuthenticationTimestamp(); } else { return true; } } return false; case APCOND_EDITCOUNT: if (!empty($wgEnableEditCountLocal)) { return $user->getEditCountLocal() >= $cond[1]; } else { return $user->getEditCount() >= $cond[1]; } case APCOND_AGE: $age = time() - wfTimestampOrNull(TS_UNIX, $user->getRegistration()); return $age >= $cond[1]; case APCOND_AGE_FROM_EDIT: $age = time() - wfTimestampOrNull(TS_UNIX, $user->getFirstEditTimestamp()); return $age >= $cond[1]; case APCOND_INGROUPS: $groups = array_slice($cond, 1); return count(array_intersect($groups, $user->getGroups())) == count($groups); case APCOND_ISIP: return $cond[1] == $user->getRequest()->getIP(); case APCOND_IPINRANGE: return IP::isInRange($user->getRequest()->getIP(), $cond[1]); case APCOND_BLOCKED: return $user->isBlocked(); case APCOND_ISBOT: return in_array('bot', User::getGroupPermissions($user->getGroups())); default: $result = null; wfRunHooks('AutopromoteCondition', array($cond[0], array_slice($cond, 1), $user, &$result)); if ($result === null) { throw new MWException("Unrecognized condition {$cond[0]} for autopromotion!"); } return (bool) $result; } }
/** * Set the type of caching headers which will be sent. * * @param $mode One of: * - 'public': Cache this object in public caches, if the maxage or smaxage * parameter is set, or if setCacheMaxAge() was called. If a maximum age is * not provided by any of these means, the object will be private. * - 'private': Cache this object only in private client-side caches. * - 'anon-public-user-private': Make this object cacheable for logged-out * users, but private for logged-in users. IMPORTANT: If this is set, it must be * set consistently for a given URL, it cannot be set differently depending on * things like the contents of the database, or whether the user is logged in. * * If the wiki does not allow anonymous users to read it, the mode set here * will be ignored, and private caching headers will always be sent. In other words, * the "public" mode is equivalent to saying that the data sent is as public as a page * view. * * For user-dependent data, the private mode should generally be used. The * anon-public-user-private mode should only be used where there is a particularly * good performance reason for caching the anonymous response, but where the * response to logged-in users may differ, or may contain private data. * * If this function is never called, then the default will be the private mode. */ public function setCacheMode($mode) { if (!in_array($mode, array('private', 'public', 'anon-public-user-private'))) { wfDebug(__METHOD__ . ": unrecognised cache mode \"{$mode}\"\n"); // Ignore for forwards-compatibility return; } if (!in_array('read', User::getGroupPermissions(array('*')), true)) { // Private wiki, only private headers if ($mode !== 'private') { wfDebug(__METHOD__ . ": ignoring request for {$mode} cache mode, private wiki\n"); return; } } wfDebug(__METHOD__ . ": setting cache mode {$mode}\n"); $this->mCacheMode = $mode; }
/** * Remove the user from the given group. * This takes immediate effect. * @param $group String Name of the group to remove */ public function removeGroup($group) { $this->load(); if (wfRunHooks('UserRemoveGroup', array($this, &$group))) { $dbw = wfGetDB(DB_MASTER); $dbw->delete('user_groups', array('ug_user' => $this->getID(), 'ug_group' => $group), __METHOD__); // Remember that the user was in this group $dbw->insert('user_former_groups', array('ufg_user' => $this->getID(), 'ufg_group' => $group), __METHOD__, array('IGNORE')); } $this->loadGroups(); $this->mGroups = array_diff($this->mGroups, array($group)); $this->mRights = User::getGroupPermissions($this->getEffectiveGroups(true)); $this->invalidateCache(); }
/** * Check for sufficient permissions to execute * @param $module ApiBase An Api module */ protected function checkExecutePermissions($module) { $accessAllowedByController = $this->accessAllowedByController(); $user = $this->getUser(); if ($module->isReadMode() && !in_array('read', User::getGroupPermissions(array('*')), true) && !$user->isAllowed('read') && !$accessAllowedByController) { $this->dieUsageMsg('readrequired'); } // TODO: do we have to take into account $accessAllowedByController for write mode? if ($module->isWriteMode()) { if (!$this->mEnableWrite) { $this->dieUsageMsg('writedisabled'); } if (!$user->isAllowed('writeapi')) { $this->dieUsageMsg('writerequired'); } if (wfReadOnly()) { $this->dieReadOnly(); } } }
/** * Stream a thumbnail specified by parameters * * @param array $params List of thumbnailing parameters. In addition to parameters * passed to the MediaHandler, this may also includes the keys: * f (for filename), archived (if archived file), temp (if temp file), * w (alias for width), p (alias for page), r (ignored; historical), * rel404 (path for render on 404 to verify hash path correct), * thumbName (thumbnail name to potentially extract more parameters from * e.g. 'lossy-page1-120px-Foo.tiff' would add page, lossy and width * to the parameters) * @return void */ function wfStreamThumb(array $params) { global $wgVaryOnXFP; $headers = array(); // HTTP headers to send $fileName = isset($params['f']) ? $params['f'] : ''; // Backwards compatibility parameters if (isset($params['w'])) { $params['width'] = $params['w']; unset($params['w']); } if (isset($params['width']) && substr($params['width'], -2) == 'px') { // strip the px (pixel) suffix, if found $params['width'] = substr($params['width'], 0, -2); } if (isset($params['p'])) { $params['page'] = $params['p']; } // Is this a thumb of an archived file? $isOld = isset($params['archived']) && $params['archived']; unset($params['archived']); // handlers don't care // Is this a thumb of a temp file? $isTemp = isset($params['temp']) && $params['temp']; unset($params['temp']); // handlers don't care // Some basic input validation $fileName = strtr($fileName, '\\/', '__'); // Actually fetch the image. Method depends on whether it is archived or not. if ($isTemp) { $repo = RepoGroup::singleton()->getLocalRepo()->getTempRepo(); $img = new UnregisteredLocalFile(null, $repo, $repo->getZonePath('public') . '/' . $repo->getTempHashPath($fileName) . $fileName); } elseif ($isOld) { // Format is <timestamp>!<name> $bits = explode('!', $fileName, 2); if (count($bits) != 2) { wfThumbError(404, wfMessage('badtitletext')->parse()); return; } $title = Title::makeTitleSafe(NS_FILE, $bits[1]); if (!$title) { wfThumbError(404, wfMessage('badtitletext')->parse()); return; } $img = RepoGroup::singleton()->getLocalRepo()->newFromArchiveName($title, $fileName); } else { $img = wfLocalFile($fileName); } // Check the source file title if (!$img) { wfThumbError(404, wfMessage('badtitletext')->parse()); return; } // Check permissions if there are read restrictions $varyHeader = array(); if (!in_array('read', User::getGroupPermissions(array('*')), true)) { if (!$img->getTitle() || !$img->getTitle()->userCan('read')) { wfThumbError(403, 'Access denied. You do not have permission to access ' . 'the source file.'); return; } $headers[] = 'Cache-Control: private'; $varyHeader[] = 'Cookie'; } // Check if the file is hidden if ($img->isDeleted(File::DELETED_FILE)) { wfThumbErrorText(404, "The source file '{$fileName}' does not exist."); return; } // Do rendering parameters extraction from thumbnail name. if (isset($params['thumbName'])) { $params = wfExtractThumbParams($img, $params); } if ($params == null) { wfThumbError(400, 'The specified thumbnail parameters are not recognized.'); return; } // Check the source file storage path if (!$img->exists()) { $redirectedLocation = false; if (!$isTemp) { // Check for file redirect // Since redirects are associated with pages, not versions of files, // we look for the most current version to see if its a redirect. $possRedirFile = RepoGroup::singleton()->getLocalRepo()->findFile($img->getName()); if ($possRedirFile && !is_null($possRedirFile->getRedirected())) { $redirTarget = $possRedirFile->getName(); $targetFile = wfLocalFile(Title::makeTitleSafe(NS_FILE, $redirTarget)); if ($targetFile->exists()) { $newThumbName = $targetFile->thumbName($params); if ($isOld) { /** @var array $bits */ $newThumbUrl = $targetFile->getArchiveThumbUrl($bits[0] . '!' . $targetFile->getName(), $newThumbName); } else { $newThumbUrl = $targetFile->getThumbUrl($newThumbName); } $redirectedLocation = wfExpandUrl($newThumbUrl, PROTO_CURRENT); } } } if ($redirectedLocation) { // File has been moved. Give redirect. $response = RequestContext::getMain()->getRequest()->response(); $response->statusHeader(302); $response->header('Location: ' . $redirectedLocation); $response->header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 12 * 3600) . ' GMT'); if ($wgVaryOnXFP) { $varyHeader[] = 'X-Forwarded-Proto'; } if (count($varyHeader)) { $response->header('Vary: ' . implode(', ', $varyHeader)); } $response->header('Content-Length: 0'); return; } // If its not a redirect that has a target as a local file, give 404. wfThumbErrorText(404, "The source file '{$fileName}' does not exist."); return; } elseif ($img->getPath() === false) { wfThumbErrorText(400, "The source file '{$fileName}' is not locally accessible."); return; } // Check IMS against the source file // This means that clients can keep a cached copy even after it has been deleted on the server if (!empty($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { // Fix IE brokenness $imsString = preg_replace('/;.*$/', '', $_SERVER["HTTP_IF_MODIFIED_SINCE"]); // Calculate time MediaWiki\suppressWarnings(); $imsUnix = strtotime($imsString); MediaWiki\restoreWarnings(); if (wfTimestamp(TS_UNIX, $img->getTimestamp()) <= $imsUnix) { HttpStatus::header(304); return; } } $rel404 = isset($params['rel404']) ? $params['rel404'] : null; unset($params['r']); // ignore 'r' because we unconditionally pass File::RENDER unset($params['f']); // We're done with 'f' parameter. unset($params['rel404']); // moved to $rel404 // Get the normalized thumbnail name from the parameters... try { $thumbName = $img->thumbName($params); if (!strlen($thumbName)) { // invalid params? throw new MediaTransformInvalidParametersException('Empty return from File::thumbName'); } $thumbName2 = $img->thumbName($params, File::THUMB_FULL_NAME); // b/c; "long" style } catch (MediaTransformInvalidParametersException $e) { wfThumbError(400, 'The specified thumbnail parameters are not valid: ' . $e->getMessage()); return; } catch (MWException $e) { wfThumbError(500, $e->getHTML()); return; } // For 404 handled thumbnails, we only use the base name of the URI // for the thumb params and the parent directory for the source file name. // Check that the zone relative path matches up so squid caches won't pick // up thumbs that would not be purged on source file deletion (bug 34231). if ($rel404 !== null) { // thumbnail was handled via 404 if (rawurldecode($rel404) === $img->getThumbRel($thumbName)) { // Request for the canonical thumbnail name } elseif (rawurldecode($rel404) === $img->getThumbRel($thumbName2)) { // Request for the "long" thumbnail name; redirect to canonical name $response = RequestContext::getMain()->getRequest()->response(); $response->statusHeader(301); $response->header('Location: ' . wfExpandUrl($img->getThumbUrl($thumbName), PROTO_CURRENT)); $response->header('Expires: ' . gmdate('D, d M Y H:i:s', time() + 7 * 86400) . ' GMT'); if ($wgVaryOnXFP) { $varyHeader[] = 'X-Forwarded-Proto'; } if (count($varyHeader)) { $response->header('Vary: ' . implode(', ', $varyHeader)); } return; } else { wfThumbErrorText(404, "The given path of the specified thumbnail is incorrect;\n\t\t\t\texpected '" . $img->getThumbRel($thumbName) . "' but got '" . rawurldecode($rel404) . "'."); return; } } $dispositionType = isset($params['download']) ? 'attachment' : 'inline'; // Suggest a good name for users downloading this thumbnail $headers[] = "Content-Disposition: {$img->getThumbDisposition($thumbName, $dispositionType)}"; if (count($varyHeader)) { $headers[] = 'Vary: ' . implode(', ', $varyHeader); } // Stream the file if it exists already... $thumbPath = $img->getThumbPath($thumbName); if ($img->getRepo()->fileExists($thumbPath)) { $starttime = microtime(true); $success = $img->getRepo()->streamFile($thumbPath, $headers); $streamtime = microtime(true) - $starttime; if (!$success) { wfThumbError(500, 'Could not stream the file'); } else { RequestContext::getMain()->getStats()->timing('media.thumbnail.stream', $streamtime); } return; } $user = RequestContext::getMain()->getUser(); if (!wfThumbIsStandard($img, $params) && $user->pingLimiter('renderfile-nonstandard')) { wfThumbError(429, wfMessage('actionthrottledtext')->parse()); return; } elseif ($user->pingLimiter('renderfile')) { wfThumbError(429, wfMessage('actionthrottledtext')->parse()); return; } list($thumb, $errorMsg) = wfGenerateThumbnail($img, $params, $thumbName, $thumbPath); /** @var MediaTransformOutput|MediaTransformError|bool $thumb */ // Check for thumbnail generation errors... $msg = wfMessage('thumbnail_error'); $errorCode = 500; if (!$thumb) { $errorMsg = $errorMsg ?: $msg->rawParams('File::transform() returned false')->escaped(); if ($errorMsg instanceof MessageSpecifier && $errorMsg->getKey() === 'thumbnail_image-failure-limit') { $errorCode = 429; } } elseif ($thumb->isError()) { $errorMsg = $thumb->getHtmlMsg(); } elseif (!$thumb->hasFile()) { $errorMsg = $msg->rawParams('No path supplied in thumbnail object')->escaped(); } elseif ($thumb->fileIsSource()) { $errorMsg = $msg->rawParams('Image was not scaled, is the requested width bigger than the source?')->escaped(); $errorCode = 400; } if ($errorMsg !== false) { wfThumbError($errorCode, $errorMsg); } else { // Stream the file if there were no errors $success = $thumb->streamFile($headers); if (!$success) { wfThumbError(500, 'Could not stream the file'); } } }
/** * Remove the user from the given group. * This takes immediate effect. * @param $group \string Name of the group to remove */ function removeGroup($group) { $this->load(); $dbw = wfGetDB(DB_MASTER); $dbw->delete('user_groups', array('ug_user' => $this->getID(), 'ug_group' => $group), __METHOD__); $this->loadGroups(); $this->mGroups = array_diff($this->mGroups, array($group)); $this->mRights = User::getGroupPermissions($this->getEffectiveGroups(true)); $this->invalidateCache(); }