/**
  * @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);
 }
Example #2
0
 /** 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__);
 }
Example #3
0
 /** 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__);
 }
Example #4
0
/**
 * 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
Example #5
0
/**
 * 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__);
}
Example #6
0
 /**
  * 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();
 }
Example #7
0
 /**
  * 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();
         }
     }
 }
Example #8
0
/**
 * 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__);
}
Example #9
0
 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');
 }
Example #11
0
 *
 *  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) {
Example #12
0
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');
 }
Example #14
0
 /**
  * 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);
     }
 }
Example #15
0
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'));
}
Example #16
0
 /**
  * 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");
 }
Example #17
0
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'));
}
Example #18
0
 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');
 }
Example #19
0
 /**
  * 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);
     }
 }
Example #20
0
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__);
}
Example #21
0
 /**
  * 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;
     }
 }
Example #22
0
 /**
  * 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;
 }
Example #23
0
 /**
  * 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();
 }
Example #24
0
 /**
  * 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();
         }
     }
 }
Example #25
0
/**
 * 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');
        }
    }
}
Example #26
0
 /**
  * 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();
 }