/**
  * Creates the virtual REST service object to be used in VE's API calls. The
  * method determines whether to instantiate a ParsoidVirtualRESTService or a
  * RestbaseVirtualRESTService object based on configuration directives: if
  * $wgVirtualRestConfig['modules']['restbase'] is defined, RESTBase is chosen,
  * otherwise Parsoid is used (either by using the MW Core config, or the
  * VE-local one).
  *
  * @return VirtualRESTService the VirtualRESTService object to use
  */
 private function getVRSObject()
 {
     // the params array to create the service object with
     $params = array();
     // the VRS class to use, defaults to Parsoid
     $class = 'ParsoidVirtualRESTService';
     $config = $this->veConfig;
     // the global virtual rest service config object, if any
     $vrs = $this->getConfig()->get('VirtualRestConfig');
     if (isset($vrs['modules']) && isset($vrs['modules']['restbase'])) {
         // if restbase is available, use it
         $params = $vrs['modules']['restbase'];
         $class = 'RestbaseVirtualRESTService';
         // remove once VE generates restbase paths
         $params['parsoidCompat'] = true;
     } elseif (isset($vrs['modules']) && isset($vrs['modules']['parsoid'])) {
         // there's a global parsoid config, use it next
         $params = $vrs['modules']['parsoid'];
     } else {
         // no global modules defined, fall back to old defaults
         $params = array('URL' => $config->get('VisualEditorParsoidURL'), 'prefix' => $config->get('VisualEditorParsoidPrefix'), 'timeout' => $config->get('VisualEditorParsoidTimeout'), 'HTTPProxy' => $config->get('VisualEditorParsoidHTTPProxy'), 'forwardCookies' => $config->get('VisualEditorParsoidForwardCookies'));
     }
     // merge the global and service-specific params
     if (isset($vrs['global'])) {
         $params = array_merge($vrs['global'], $params);
     }
     // set up cookie forwarding
     if ($params['forwardCookies'] && !User::isEveryoneAllowed('read')) {
         $params['forwardCookies'] = RequestContext::getMain()->getRequest()->getHeader('Cookie');
     } else {
         $params['forwardCookies'] = false;
     }
     // create the VRS object and return it
     return new $class($params);
 }
Example #2
0
 function onView()
 {
     $this->getOutput()->disable();
     $request = $this->getRequest();
     $response = $request->response();
     $config = $this->context->getConfig();
     if (!$request->checkUrlExtension()) {
         return;
     }
     if ($this->getOutput()->checkLastModified($this->page->getTouched())) {
         return;
         // Client cache fresh and headers sent, nothing more to do.
     }
     $gen = $request->getVal('gen');
     if ($gen == 'css' || $gen == 'js') {
         $this->gen = true;
     }
     $contentType = $this->getContentType();
     $maxage = $request->getInt('maxage', $config->get('SquidMaxage'));
     $smaxage = $request->getIntOrNull('smaxage');
     if ($smaxage === null) {
         if ($contentType == 'text/css' || $contentType == 'text/javascript') {
             // CSS/JS raw content has its own CDN max age configuration.
             // Note: Title::getCdnUrls() includes action=raw for css/js pages,
             // so if using the canonical url, this will get HTCP purges.
             $smaxage = intval($config->get('ForcedRawSMaxage'));
         } else {
             // No CDN cache for anything else
             $smaxage = 0;
         }
     }
     $response->header('Content-type: ' . $contentType . '; charset=UTF-8');
     // Output may contain user-specific data;
     // vary generated content for open sessions on private wikis
     $privateCache = !User::isEveryoneAllowed('read') && ($smaxage == 0 || session_id() != '');
     // Don't accidentally cache cookies if user is logged in (T55032)
     $privateCache = $privateCache || $this->getUser()->isLoggedIn();
     $mode = $privateCache ? 'private' : 'public';
     $response->header('Cache-Control: ' . $mode . ', s-maxage=' . $smaxage . ', max-age=' . $maxage);
     $text = $this->getRawText();
     // Don't return a 404 response for CSS or JavaScript;
     // 404s aren't generally cached and it would create
     // extra hits when user CSS/JS are on and the user doesn't
     // have the pages.
     if ($text === false && $contentType == 'text/x-wiki') {
         $response->statusHeader(404);
     }
     if (!Hooks::run('RawPageViewBeforeOutput', array(&$this, &$text))) {
         wfDebug(__METHOD__ . ": RawPageViewBeforeOutput hook broke raw page output.\n");
     }
     echo $text;
 }
Example #3
0
 protected function requestParsoid($method, $title, $params)
 {
     global $wgVisualEditorParsoidURL, $wgVisualEditorParsoidTimeout, $wgVisualEditorParsoidForwardCookies;
     $url = $wgVisualEditorParsoidURL . '/' . urlencode($this->getApiSource()) . '/' . urlencode($title->getPrefixedDBkey());
     $data = array_merge($this->getProxyConf(), array('method' => $method, 'timeout' => $wgVisualEditorParsoidTimeout));
     if ($method === 'POST') {
         $data['postData'] = $params;
     } else {
         $url = wfAppendQuery($url, $params);
     }
     $req = MWHttpRequest::factory($url, $data);
     // Forward cookies, but only if configured to do so and if there are read restrictions
     if ($wgVisualEditorParsoidForwardCookies && !User::isEveryoneAllowed('read')) {
         $req->setHeader('Cookie', $this->getRequest()->getHeader('Cookie'));
     }
     $status = $req->execute();
     if ($status->isOK()) {
         // Pass thru performance data from Parsoid to the client, unless the response was
         // served directly from Varnish, in  which case discard the value of the XPP header
         // and use it to declare the cache hit instead.
         $xCache = $req->getResponseHeader('X-Cache');
         if (is_string($xCache) && strpos(strtolower($xCache), 'hit') !== false) {
             $xpp = 'cached-response=true';
             $hit = true;
         } else {
             $xpp = $req->getResponseHeader('X-Parsoid-Performance');
             $hit = false;
         }
         WikiaLogger::instance()->debug('ApiVisualEditor', array('hit' => $hit, 'method' => $method, 'url' => $url));
         if ($xpp !== null) {
             $resp = $this->getRequest()->response();
             $resp->header('X-Parsoid-Performance: ' . $xpp);
         }
     } elseif ($status->isGood()) {
         $this->dieUsage($req->getContent(), 'parsoidserver-http-' . $req->getStatus());
     } elseif ($errors = $status->getErrorsByType('error')) {
         $error = $errors[0];
         $code = $error['message'];
         if (count($error['params'])) {
             $message = $error['params'][0];
         } else {
             $message = 'MWHttpRequest error';
         }
         $this->dieUsage($message, 'parsoidserver-' . $code);
     }
     // TODO pass through X-Parsoid-Performance header, merge with getHTML above
     return $req->getContent();
 }
Example #4
0
 /**
  * Check that the user is allowed to read this page.
  *
  * @param string $action The action to check
  * @param User $user User to check
  * @param array $errors List of current errors
  * @param string $rigor Same format as Title::getUserPermissionsErrors()
  * @param bool $short Short circuit on first error
  *
  * @return array List of errors
  */
 private function checkReadPermissions($action, $user, $errors, $rigor, $short)
 {
     global $wgWhitelistRead, $wgWhitelistReadRegexp;
     $whitelisted = false;
     if (User::isEveryoneAllowed('read')) {
         # Shortcut for public wikis, allows skipping quite a bit of code
         $whitelisted = true;
     } elseif ($user->isAllowed('read')) {
         # If the user is allowed to read pages, he is allowed to read all pages
         $whitelisted = true;
     } elseif ($this->isSpecial('Userlogin') || $this->isSpecial('ChangePassword') || $this->isSpecial('PasswordReset')) {
         # Always grant access to the login page.
         # Even anons need to be able to log in.
         $whitelisted = true;
     } elseif (is_array($wgWhitelistRead) && count($wgWhitelistRead)) {
         # Time to check the whitelist
         # Only do these checks is there's something to check against
         $name = $this->getPrefixedText();
         $dbName = $this->getPrefixedDBkey();
         // Check for explicit whitelisting with and without underscores
         if (in_array($name, $wgWhitelistRead, true) || in_array($dbName, $wgWhitelistRead, true)) {
             $whitelisted = true;
         } elseif ($this->getNamespace() == NS_MAIN) {
             # Old settings might have the title prefixed with
             # a colon for main-namespace pages
             if (in_array(':' . $name, $wgWhitelistRead)) {
                 $whitelisted = true;
             }
         } elseif ($this->isSpecialPage()) {
             # If it's a special page, ditch the subpage bit and check again
             $name = $this->getDBkey();
             list($name, ) = SpecialPageFactory::resolveAlias($name);
             if ($name) {
                 $pure = SpecialPage::getTitleFor($name)->getPrefixedText();
                 if (in_array($pure, $wgWhitelistRead, true)) {
                     $whitelisted = true;
                 }
             }
         }
     }
     if (!$whitelisted && is_array($wgWhitelistReadRegexp) && !empty($wgWhitelistReadRegexp)) {
         $name = $this->getPrefixedText();
         // Check for regex whitelisting
         foreach ($wgWhitelistReadRegexp as $listItem) {
             if (preg_match($listItem, $name)) {
                 $whitelisted = true;
                 break;
             }
         }
     }
     if (!$whitelisted) {
         # If the title is not whitelisted, give extensions a chance to do so...
         Hooks::run('TitleReadWhitelist', array($this, $user, &$whitelisted));
         if (!$whitelisted) {
             $errors[] = $this->missingPermissionError($action, $short);
         }
     }
     return $errors;
 }
Example #5
0
 /**
  * Check for sufficient permissions to execute
  * @param ApiBase $module An Api module
  */
 protected function checkExecutePermissions($module)
 {
     $user = $this->getUser();
     if ($module->isReadMode() && !User::isEveryoneAllowed('read') && !$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 (!Hooks::run('ApiCheckCanExecute', array($module, $user, &$message))) {
         $this->dieUsageMsg($message);
     }
 }
Example #6
0
 /**
  * Check for sufficient permissions to execute
  * @param ApiBase $module An Api module
  */
 protected function checkExecutePermissions($module)
 {
     $user = $this->getUser();
     if ($module->isReadMode() && !User::isEveryoneAllowed('read') && !$user->isAllowed('read')) {
         $this->dieUsageMsg('readrequired');
     }
     if ($module->isWriteMode()) {
         if (!$this->mEnableWrite) {
             $this->dieUsageMsg('writedisabled');
         } elseif (!$user->isAllowed('writeapi')) {
             $this->dieUsageMsg('writerequired');
         } elseif ($this->getRequest()->getHeader('Promise-Non-Write-API-Action')) {
             $this->dieUsage("Promise-Non-Write-API-Action HTTP header cannot be sent to write API modules", 'promised-nonwrite-api');
         }
         $this->checkReadOnly($module);
     }
     // Allow extensions to stop execution for arbitrary reasons.
     $message = false;
     if (!Hooks::run('ApiCheckCanExecute', array($module, $user, &$message))) {
         $this->dieUsageMsg($message);
     }
 }
 /**
  * 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.
  *
  * @param User $user
  */
 function performAction(User $user)
 {
     if (empty($this->mode)) {
         return;
     }
     if (!in_array($this->func_name, $this->config->get('AjaxExportList'))) {
         wfDebug(__METHOD__ . ' Bad Request for unknown function ' . $this->func_name . "\n");
         wfHttpError(400, 'Bad Request', "unknown function " . $this->func_name);
     } elseif (!User::isEveryoneAllowed('read') && !$user->isAllowed('read')) {
         wfHttpError(403, 'Forbidden', 'You are not allowed to view pages.');
     } else {
         wfDebug(__METHOD__ . ' dispatching ' . $this->func_name . "\n");
         try {
             $result = call_user_func_array($this->func_name, $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);
                 }
                 // Make sure DB commit succeeds before sending a response
                 wfGetLBFactory()->commitMasterChanges(__METHOD__);
                 $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();
             }
         }
     }
 }
Example #8
0
 function onView()
 {
     global $wgSquidMaxage, $wgForcedRawSMaxage;
     $this->getOutput()->disable();
     $request = $this->getRequest();
     if (!$request->checkUrlExtension()) {
         return;
     }
     if ($this->getOutput()->checkLastModified($this->page->getTouched())) {
         return;
         // Client cache fresh and headers sent, nothing more to do.
     }
     # special case for 'generated' raw things: user css/js
     # This is deprecated and will only return empty content
     $gen = $request->getVal('gen');
     $smaxage = $request->getIntOrNull('smaxage');
     if ($gen == 'css' || $gen == 'js') {
         $this->mGen = $gen;
         if ($smaxage === null) {
             $smaxage = $wgSquidMaxage;
         }
     } else {
         $this->mGen = false;
     }
     $contentType = $this->getContentType();
     # Force caching for CSS and JS raw content, default: 5 minutes.
     # Note: If using a canonical url for userpage css/js, we send an HTCP purge.
     if ($smaxage === null) {
         if ($contentType == 'text/css' || $contentType == 'text/javascript') {
             $smaxage = intval($wgForcedRawSMaxage);
         } else {
             $smaxage = 0;
         }
     }
     $maxage = $request->getInt('maxage', $wgSquidMaxage);
     $response = $request->response();
     $response->header('Content-type: ' . $contentType . '; charset=UTF-8');
     # Output may contain user-specific data;
     # vary generated content for open sessions on private wikis
     $privateCache = !User::isEveryoneAllowed('read') && ($smaxage == 0 || session_id() != '');
     // Bug 53032 - make this private if user is logged in,
     // so we don't accidentally cache cookies
     $privateCache = $privateCache ?: $this->getUser()->isLoggedIn();
     # allow the client to cache this for 24 hours
     $mode = $privateCache ? 'private' : 'public';
     $response->header('Cache-Control: ' . $mode . ', s-maxage=' . $smaxage . ', max-age=' . $maxage);
     $text = $this->getRawText();
     if ($text === false && $contentType == 'text/x-wiki') {
         # Don't return a 404 response for CSS or JavaScript;
         # 404s aren't generally cached and it would create
         # extra hits when user CSS/JS are on and the user doesn't
         # have the pages.
         $response->header('HTTP/1.x 404 Not Found');
     }
     if (!wfRunHooks('RawPageViewBeforeOutput', array(&$this, &$text))) {
         wfDebug(__METHOD__ . ": RawPageViewBeforeOutput hook broke raw page output.\n");
     }
     echo $text;
 }
 /**
  * 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, $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 " . $this->func_name);
     } elseif (!User::isEveryoneAllowed('read') && !$wgUser->isAllowed('read')) {
         wfHttpError(403, 'Forbidden', 'You are not allowed to view pages.');
     } else {
         wfDebug(__METHOD__ . ' dispatching ' . $this->func_name . "\n");
         try {
             $result = call_user_func_array($this->func_name, $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();
             }
         }
     }
     wfProfileOut(__METHOD__);
 }