/** * Searches for purchased items in the WoltLab Plugin-Store. * * @return array<string> */ public function searchForPurchasedItems() { if (!RemoteFile::supportsSSL()) { return array('noSSL' => WCF::getLanguage()->get('wcf.acp.pluginStore.api.noSSL')); } if (empty($this->parameters['username']) || empty($this->parameters['password'])) { return array('template' => $this->renderAuthorizationDialog(false)); } $request = new HTTPRequest('https://api.woltlab.com/1.0/customer/purchases/list.json', array('method' => 'POST'), array('username' => $this->parameters['username'], 'password' => $this->parameters['password'], 'wcfVersion' => WCF_VERSION)); $request->execute(); $reply = $request->getReply(); $response = JSON::decode($reply['body']); $code = isset($response['status']) ? $response['status'] : 500; switch ($code) { case 200: if (empty($response['products'])) { return array('noResults' => WCF::getLanguage()->get('wcf.acp.pluginStore.purchasedItems.noResults')); } else { WCF::getSession()->register('__pluginStoreProducts', $response['products']); WCF::getSession()->register('__pluginStoreWcfMajorReleases', $response['wcfMajorReleases']); return array('redirectURL' => LinkHandler::getInstance()->getLink('PluginStorePurchasedItems')); } break; // authentication error // authentication error case 401: return array('template' => $this->renderAuthorizationDialog(true)); break; // any other kind of errors // any other kind of errors default: throw new SystemException(WCF::getLanguage()->getDynamicVariable('wcf.acp.pluginStore.api.error', array('status' => $code))); break; } }
/** * Sends a JSON-encoded response. * * @param array $data */ protected function sendJsonResponse(array $data) { $json = JSON::encode($data); // send JSON response header('Content-type: application/json'); echo $json; exit; }
/** * @see wcf\action\AJAXInvokeAction::sendResponse() */ protected function sendResponse() { if (!isset($_POST['isFallback'])) { parent::sendResponse(); } // IE9 is mad if iframe response is application/json header('Content-type: text/plain'); echo JSON::encode($this->response); exit; }
/** * @see wcf\action\Action::execute() */ public function execute() { AbstractSecureAction::execute(); // execute clipboard action $this->executeAction(); // get editor items $returnValues = $this->getEditorItems(); // send JSON response header('Content-type: application/json'); echo JSON::encode($returnValues); exit; }
/** * @see \wcf\action\IAction::execute() */ public final function execute() { parent::execute(); $methodName = 'step' . StringUtil::firstCharToUpperCase($this->step); if (!method_exists($this, $methodName)) { throw new AJAXException("Class '" . get_class($this) . "' does not implement the required method '" . $methodName . "'"); } // execute step $this->{$methodName}(); $this->executed(); // send JSON-encoded response header('Content-type: application/json'); echo JSON::encode($this->data); exit; }
/** * Validates response. * * @param string $response */ public function validate($response) { // fail if response is empty to avoid sending api requests if (empty($response)) { throw new UserInputException('recaptchaString', 'false'); } $request = new HTTPRequest('https://www.google.com/recaptcha/api/siteverify?secret=' . rawurlencode(RECAPTCHA_PRIVATEKEY) . '&response=' . rawurlencode($response) . '&remoteip=' . rawurlencode(UserUtil::getIpAddress()), array('timeout' => 10)); try { $request->execute(); $reply = $request->getReply(); $data = JSON::decode($reply['body']); if ($data['success']) { // yeah } else { throw new UserInputException('recaptchaString', 'false'); } } catch (SystemException $e) { // log error, but accept captcha $e->getExceptionID(); } WCF::getSession()->register('recaptchaDone', true); }
/** * Displays an error message. * * @param string $name * @param array $parameters */ public function error($name, array $parameters = array()) { Log::error('package.' . $name . ':' . JSON::encode($parameters)); if ($parameters) { throw new ArgvException(CLIWCF::getLanguage()->getDynamicVariable('wcf.acp.package.error.' . $name, $parameters), $this->getUsage()); } else { throw new ArgvException(CLIWCF::getLanguage()->get('wcf.acp.package.error.' . $name), $this->argv->getUsageMessage()); } }
/** * @see \wcf\action\IAction::execute() */ public function execute() { parent::execute(); $callbackURL = LinkHandler::getInstance()->getLink('GoogleAuth', array('appendSession' => false)); // user accepted the connection if (isset($_GET['code'])) { try { // fetch access_token $request = new HTTPRequest('https://accounts.google.com/o/oauth2/token', array(), array('code' => $_GET['code'], 'client_id' => StringUtil::trim(GOOGLE_PUBLIC_KEY), 'client_secret' => StringUtil::trim(GOOGLE_PRIVATE_KEY), 'redirect_uri' => $callbackURL, 'grant_type' => 'authorization_code')); $request->execute(); $reply = $request->getReply(); $content = $reply['body']; } catch (SystemException $e) { // force logging $e->getExceptionID(); throw new IllegalLinkException(); } // validate state, validation of state is executed after fetching the access_token to invalidate 'code' if (!isset($_GET['state']) || $_GET['state'] != WCF::getSession()->getVar('__googleInit')) { throw new IllegalLinkException(); } WCF::getSession()->unregister('__googleInit'); $data = JSON::decode($content); try { // fetch userdata $request = new HTTPRequest('https://www.googleapis.com/plus/v1/people/me'); $request->addHeader('Authorization', 'Bearer ' . $data['access_token']); $request->execute(); $reply = $request->getReply(); $content = $reply['body']; } catch (SystemException $e) { // force logging $e->getExceptionID(); throw new IllegalLinkException(); } $userData = JSON::decode($content); // check whether a user is connected to this google account $user = $this->getUser($userData['id']); if ($user->userID) { // a user is already connected, but we are logged in, break if (WCF::getUser()->userID) { throw new NamedUserException(WCF::getLanguage()->get('wcf.user.3rdparty.google.connect.error.inuse')); } else { if (UserAuthenticationFactory::getInstance()->getUserAuthentication()->supportsPersistentLogins()) { $password = StringUtil::getRandomID(); $userEditor = new UserEditor($user); $userEditor->update(array('password' => $password)); // reload user to retrieve salt $user = new User($user->userID); UserAuthenticationFactory::getInstance()->getUserAuthentication()->storeAccessData($user, $user->username, $password); } WCF::getSession()->changeUser($user); WCF::getSession()->update(); HeaderUtil::redirect(LinkHandler::getInstance()->getLink()); } } else { WCF::getSession()->register('__3rdPartyProvider', 'google'); // save data for connection if (WCF::getUser()->userID) { WCF::getSession()->register('__googleUsername', $userData['displayName']); WCF::getSession()->register('__googleData', $userData); HeaderUtil::redirect(LinkHandler::getInstance()->getLink('AccountManagement') . '#3rdParty'); } else { WCF::getSession()->register('__username', $userData['displayName']); if (isset($userData['emails'][0]['value'])) { WCF::getSession()->register('__email', $userData['emails'][0]['value']); } WCF::getSession()->register('__googleData', $userData); // we assume that bots won't register on google first // thus no need for a captcha if (REGISTER_USE_CAPTCHA) { WCF::getSession()->register('noRegistrationCaptcha', true); } WCF::getSession()->update(); HeaderUtil::redirect(LinkHandler::getInstance()->getLink('Register')); } } $this->executed(); exit; } // user declined or any other error that may occur if (isset($_GET['error'])) { throw new NamedUserException(WCF::getLanguage()->get('wcf.user.3rdparty.google.login.error.' . $_GET['error'])); } // start auth by redirecting to google $token = StringUtil::getRandomID(); WCF::getSession()->register('__googleInit', $token); HeaderUtil::redirect("https://accounts.google.com/o/oauth2/auth?client_id=" . rawurlencode(StringUtil::trim(GOOGLE_PUBLIC_KEY)) . "&redirect_uri=" . rawurlencode($callbackURL) . "&state=" . $token . "&scope=profile+email&response_type=code"); $this->executed(); exit; }
/** * @see wcf\action\IAction::execute() */ public function execute() { parent::execute(); // validate class name if (!class_exists($this->className)) { throw new SystemException("unknown class '" . $this->className . "'"); } if (!ClassUtil::isInstanceOf($this->className, 'wcf\\data\\IDatabaseObjectAction')) { throw new SystemException("'" . $this->className . "' should implement wcf\\system\\IDatabaseObjectAction"); } // create object action instance $this->objectAction = new $this->className($this->objectIDs, $this->actionName, $this->parameters); // validate action try { $this->objectAction->validateAction(); } catch (UserInputException $e) { $this->throwException($e); } catch (ValidateActionException $e) { $this->throwException($e); } // execute action try { $this->response = $this->objectAction->executeAction(); } catch (\Exception $e) { $this->throwException($e); } $this->executed(); // send JSON-encoded response header('Content-type: application/json'); echo JSON::encode($this->response); exit; }
/** * Writes an error to log file. */ protected function logError() { if (!empty($this->exceptionID)) { return; } $logFile = WCF_DIR . 'log/' . gmdate('Y-m-d', TIME_NOW) . '.txt'; // try to create file @touch($logFile); // validate if file exists and is accessible for us if (!file_exists($logFile) || !is_writable($logFile)) { /* We cannot recover if we reached this point, the server admin is urged to fix his pretty much broken configuration. GLaDOS: Look at you, sailing through the air majestically, like an eagle... piloting a blimp. */ return; } $e = $this->getPrevious() ?: $this; // don't forget to update ExceptionLogViewPage, when changing the log file format $message = gmdate('r', TIME_NOW) . "\n" . 'Message: ' . $e->getMessage() . "\n" . 'File: ' . $e->getFile() . ' (' . $e->getLine() . ")\n" . 'PHP version: ' . phpversion() . "\n" . 'WCF version: ' . WCF_VERSION . "\n" . 'Request URI: ' . (isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '') . "\n" . 'Referrer: ' . (isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '') . "\n" . 'User-Agent: ' . (isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '') . "\n" . 'Information: ' . JSON::encode($this->information) . "\n" . "Stacktrace: \n " . implode("\n ", explode("\n", $this->__getTraceAsString())) . "\n"; // calculate Exception-ID $this->exceptionID = StringUtil::getHash($message); $message = "<<<<<<<<" . $this->exceptionID . "<<<<\n" . $message . "<<<<\n\n"; // append @file_put_contents($logFile, $message, FILE_APPEND); }
/** * @see wcf\action\IAction::execute() */ public function execute() { parent::execute(); // get object ids $objectIDs = $this->getObjectIDs(); // create object action instance $this->objectAction = new $this->parameters['className']($objectIDs, $this->parameters['actionName']); // validate action try { $this->objectAction->validateAction(); } catch (ValidateActionException $e) { throw new AJAXException("validation failed: " . $e->getMessage()); } // execute action try { $this->response = $this->objectAction->executeAction(); } catch (\Exception $e) { throw new AJAXException('unknown exception caught: ' . $e->getMessage()); } $this->executed(); // send JSON-encoded response header('Content-type: application/json'); echo JSON::encode($this->response); exit; }
/** * Initializes command handling. */ protected function initCommands() { // add command name completer self::getReader()->addCompleter(new CLICommandNameCompleter()); while (true) { // roll back open transactions of the previous command, as they are dangerous in a long living script if (WCF::getDB()->rollBackTransaction()) { Log::warn('Previous command had an open transaction.'); } self::getReader()->setHistoryEnabled(true); $line = self::getReader()->readLine('>'); if ($line === null) { exit; } $line = StringUtil::trim($line); try { $command = CLICommandHandler::getCommand($line); $command->execute(CLICommandHandler::getParameters($line)); } catch (IllegalLinkException $e) { Log::error('notFound:' . JSON::encode(array('command' => $line))); self::getReader()->println(WCF::getLanguage()->getDynamicVariable('wcf.cli.error.command.notFound', array('command' => $line))); if (self::getArgvParser()->exitOnFail) { exit(1); } continue; } catch (PermissionDeniedException $e) { Log::error('permissionDenied'); self::getReader()->println(WCF::getLanguage()->getDynamicVariable('wcf.global.error.permissionDenied')); if (self::getArgvParser()->exitOnFail) { exit(1); } continue; } catch (ArgvException $e) { // show error message and usage if ($e->getMessage()) { echo $e->getMessage() . PHP_EOL; } echo $e->getUsageMessage(); if (self::getArgvParser()->exitOnFail) { exit(1); } continue; } catch (\Exception $e) { Log::error($e); if (self::getArgvParser()->exitOnFail) { exit(1); } continue; } } }
/** * Throws a JSON-encoded error message * * @param string $message * @param boolean $isDoomsday * @param string $stacktrace * @param array<mixed> $returnValues */ public function __construct($message, $errorType = self::INTERNAL_ERROR, $stacktrace = null, $returnValues = array()) { if ($stacktrace === null) $stacktrace = $this->getTraceAsString(); if (WCF::debugModeIsEnabled()) { $responseData = array( 'message' => $message, 'stacktrace' => nl2br($stacktrace) ); } else { $responseData = array( 'message' => $this->_getMessage() ); } $responseData['code'] = $errorType; $responseData['returnValues'] = $returnValues; $statusHeader = ''; switch ($errorType) { case self::MISSING_PARAMETERS: $statusHeader = 'HTTP/1.0 400 Bad Request'; $responseData['message'] = WCF::getLanguage()->get('wcf.ajax.error.badRequest'); $this->logError(); break; case self::SESSION_EXPIRED: $statusHeader = 'HTTP/1.0 401 Unauthorized'; break; case self::INSUFFICIENT_PERMISSIONS: $statusHeader = 'HTTP/1.0 403 Forbidden'; break; case self::BAD_PARAMETERS: $statusHeader = 'HTTP/1.0 412 Precondition Failed'; break; default: case self::INTERNAL_ERROR: //header('HTTP/1.0 418 I\'m a Teapot'); header('HTTP/1.0 503 Service Unavailable'); $responseData['code'] = self::INTERNAL_ERROR; if (!WCF::debugModeIsEnabled()) { $responseData['message'] = WCF::getLanguage()->get('wcf.ajax.error.internalError'); } $this->logError(); break; } header($statusHeader); header('Content-type: application/json'); echo JSON::encode($responseData); exit; }
/** * @see \wcf\page\IPage::readData() */ public function readData() { AbstractPage::readData(); $fileNameRegex = new Regex('(?:^|/)\\d{4}-\\d{2}-\\d{2}\\.txt$'); $this->logFiles = DirectoryUtil::getInstance(WCF_DIR . 'log/')->getFiles(SORT_DESC, $fileNameRegex); if ($this->exceptionID) { // search the appropriate file foreach ($this->logFiles as $logFile) { $contents = file_get_contents($logFile); if (mb_strpos($contents, '<<<<<<<<' . $this->exceptionID . '<<<<') !== false) { $fileNameRegex->match($logFile); $matches = $fileNameRegex->getMatches(); $this->logFile = $matches[0]; break; } unset($contents); } if (!isset($contents)) { $this->logFile = ''; return; } } else { if ($this->logFile) { if (!$fileNameRegex->match(basename($this->logFile))) { throw new IllegalLinkException(); } if (!file_exists(WCF_DIR . 'log/' . $this->logFile)) { throw new IllegalLinkException(); } $contents = file_get_contents(WCF_DIR . 'log/' . $this->logFile); } else { return; } } // unify newlines $contents = StringUtil::unifyNewlines($contents); // split contents $split = new Regex('(?:^|\\n<<<<\\n\\n)(?:<<<<<<<<([a-f0-9]{40})<<<<\\n|$)'); $contents = $split->split($contents, Regex::SPLIT_NON_EMPTY_ONLY | Regex::CAPTURE_SPLIT_DELIMITER); // even items become keys, odd items become values try { $this->exceptions = call_user_func_array('array_merge', array_map(function ($v) { return array($v[0] => $v[1]); }, array_chunk($contents, 2))); } catch (\Exception $e) { // logfile contents are pretty malformed, abort return; } if ($this->exceptionID) { $this->searchPage($this->exceptionID); } $this->calculateNumberOfPages(); $i = 0; $exceptionRegex = new Regex('(?P<date>[MTWFS][a-z]{2}, \\d{1,2} [JFMASOND][a-z]{2} \\d{4} \\d{2}:\\d{2}:\\d{2} [+-]\\d{4}) Message: (?P<message>.*?) File: (?P<file>.*?) \\((?P<line>\\d+)\\) PHP version: (?P<phpVersion>.*?) WCF version: (?P<wcfVersion>.*?) Request URI: (?P<requestURI>.*?) Referrer: (?P<referrer>.*?) User-Agent: (?P<userAgent>.*?) Information: (?P<information>.*?) Stacktrace: (?P<stacktrace>.*)', Regex::DOT_ALL); $stackTraceFormatter = new Regex('^\\s+(#\\d+)', Regex::MULTILINE); foreach ($this->exceptions as $key => $val) { $i++; if ($i < $this->startIndex || $i > $this->endIndex) { unset($this->exceptions[$key]); continue; } if (!$exceptionRegex->match($val)) { unset($this->exceptions[$key]); continue; } $this->exceptions[$key] = $exceptionRegex->getMatches(); $this->exceptions[$key]['stacktrace'] = explode("\n", $stackTraceFormatter->replace(StringUtil::encodeHTML($this->exceptions[$key]['stacktrace']), '<strong>\\1</strong>')); $this->exceptions[$key]['information'] = JSON::decode($this->exceptions[$key]['information']); } }
/** * Throws a JSON-encoded error message * * @param string $message * @param boolean $isDoomsday * @param string $stacktrace * @param array $returnValues * @param string $exceptionID * @param array<mixed> $returnValues */ public function __construct($message, $errorType = self::INTERNAL_ERROR, $stacktrace = null, $returnValues = array(), $exceptionID = '') { if ($stacktrace === null) { $stacktrace = $this->getTraceAsString(); } $responseData = array('code' => $errorType, 'message' => $message, 'returnValues' => $returnValues); // include a stacktrace if: // - debug mode is enabled // - within ACP and a SystemException was thrown if (WCF::debugModeIsEnabled(false) || WCF::debugModeIsEnabled() && self::INTERNAL_ERROR) { $responseData['stacktrace'] = nl2br($stacktrace); } $statusHeader = ''; switch ($errorType) { case self::MISSING_PARAMETERS: $statusHeader = 'HTTP/1.0 400 Bad Request'; $responseData['exceptionID'] = $exceptionID; $responseData['message'] = WCF::getLanguage()->get('wcf.ajax.error.badRequest'); break; case self::SESSION_EXPIRED: $statusHeader = 'HTTP/1.0 409 Conflict'; break; case self::INSUFFICIENT_PERMISSIONS: $statusHeader = 'HTTP/1.0 403 Forbidden'; break; case self::BAD_PARAMETERS: $statusHeader = 'HTTP/1.0 431 Bad Parameters'; $responseData['exceptionID'] = $exceptionID; break; default: case self::INTERNAL_ERROR: //header('HTTP/1.0 418 I\'m a Teapot'); header('HTTP/1.0 503 Service Unavailable'); $responseData['code'] = self::INTERNAL_ERROR; $responseData['exceptionID'] = $exceptionID; if (!WCF::debugModeIsEnabled()) { $responseData['message'] = WCF::getLanguage()->get('wcf.ajax.error.internalError'); } break; } header($statusHeader); header('Content-type: application/json'); echo JSON::encode($responseData); exit; }
/** * @see \wcf\action\IAction::execute() */ public function execute() { AbstractAction::execute(); $returnValues = null; switch ($this->actionName) { case 'count': $returnValues = array('count' => $this->count()); break; case 'getQuotes': $returnValues = array('template' => $this->getQuotes()); break; case 'markForRemoval': $this->markForRemoval(); break; case 'remove': $returnValues = array('count' => $this->remove()); break; case 'removeMarkedQuotes': $returnValues = array('count' => $this->removeMarkedQuotes()); break; default: throw new SystemException("Unknown action '" . $this->actionName . "'"); break; } if (is_array($returnValues) && $this->_getFullQuoteObjectIDs) { $returnValues['fullQuoteObjectIDs'] = $this->getFullQuoteObjectIDs(); } $this->executed(); // force session update WCF::getSession()->update(); WCF::getSession()->disableUpdate(); if ($returnValues !== null) { // send JSON-encoded response header('Content-type: application/json'); echo JSON::encode($returnValues); } exit; }
/** * @see \wcf\action\IAction::execute() */ public function execute() { parent::execute(); // user accepted the connection if (isset($_GET['code'])) { try { // fetch access_token $request = new HTTPRequest('https://github.com/login/oauth/access_token', array(), array('client_id' => StringUtil::trim(GITHUB_PUBLIC_KEY), 'client_secret' => StringUtil::trim(GITHUB_PRIVATE_KEY), 'code' => $_GET['code'])); $request->execute(); $reply = $request->getReply(); $content = $reply['body']; } catch (SystemException $e) { // force logging $e->getExceptionID(); throw new IllegalLinkException(); } // validate state, validation of state is executed after fetching the access_token to invalidate 'code' if (!isset($_GET['state']) || $_GET['state'] != WCF::getSession()->getVar('__githubInit')) { throw new IllegalLinkException(); } WCF::getSession()->unregister('__githubInit'); parse_str($content, $data); // check whether the token is okay if (isset($data['error'])) { throw new IllegalLinkException(); } // check whether a user is connected to this github account $user = $this->getUser($data['access_token']); if ($user->userID) { // a user is already connected, but we are logged in, break if (WCF::getUser()->userID) { throw new NamedUserException(WCF::getLanguage()->get('wcf.user.3rdparty.github.connect.error.inuse')); } else { if (UserAuthenticationFactory::getInstance()->getUserAuthentication()->supportsPersistentLogins()) { $password = StringUtil::getRandomID(); $userEditor = new UserEditor($user); $userEditor->update(array('password' => $password)); // reload user to retrieve salt $user = new User($user->userID); UserAuthenticationFactory::getInstance()->getUserAuthentication()->storeAccessData($user, $user->username, $password); } WCF::getSession()->changeUser($user); WCF::getSession()->update(); HeaderUtil::redirect(LinkHandler::getInstance()->getLink()); } } else { try { // fetch userdata $request = new HTTPRequest('https://api.github.com/user?access_token=' . $data['access_token']); $request->execute(); $reply = $request->getReply(); $userData = JSON::decode(StringUtil::trim($reply['body'])); } catch (SystemException $e) { // force logging $e->getExceptionID(); throw new IllegalLinkException(); } WCF::getSession()->register('__3rdPartyProvider', 'github'); // save data for connection if (WCF::getUser()->userID) { WCF::getSession()->register('__githubUsername', $userData['login']); WCF::getSession()->register('__githubToken', $data['access_token']); HeaderUtil::redirect(LinkHandler::getInstance()->getLink('AccountManagement') . '#3rdParty'); } else { WCF::getSession()->register('__githubData', $userData); WCF::getSession()->register('__username', $userData['login']); // check whether user has entered a public email if (isset($userData) && isset($userData['email']) && $userData['email'] !== null) { WCF::getSession()->register('__email', $userData['email']); } else { try { $request = new HTTPRequest('https://api.github.com/user/emails?access_token=' . $data['access_token']); $request->execute(); $reply = $request->getReply(); $emails = JSON::decode(StringUtil::trim($reply['body'])); // handle future response as well a current response (see. http://developer.github.com/v3/users/emails/) if (is_string($emails[0])) { $email = $emails[0]; } else { $email = $emails[0]['email']; foreach ($emails as $tmp) { if ($tmp['primary']) { $email = $tmp['email']; } break; } } WCF::getSession()->register('__email', $email); } catch (SystemException $e) { } } WCF::getSession()->register('__githubToken', $data['access_token']); // we assume that bots won't register on github first // thus no need for a captcha if (REGISTER_USE_CAPTCHA) { WCF::getSession()->register('noRegistrationCaptcha', true); } WCF::getSession()->update(); HeaderUtil::redirect(LinkHandler::getInstance()->getLink('Register')); } } $this->executed(); exit; } // user declined or any other error that may occur if (isset($_GET['error'])) { throw new NamedUserException(WCF::getLanguage()->get('wcf.user.3rdparty.github.login.error.' . $_GET['error'])); } // start auth by redirecting to github $token = StringUtil::getRandomID(); WCF::getSession()->register('__githubInit', $token); HeaderUtil::redirect("https://github.com/login/oauth/authorize?client_id=" . rawurlencode(StringUtil::trim(GITHUB_PUBLIC_KEY)) . "&scope=" . rawurlencode('user:email') . "&state=" . $token); $this->executed(); exit; }
/** * @see \wcf\action\IAction::execute() */ public function execute() { parent::execute(); $callbackURL = LinkHandler::getInstance()->getLink('FacebookAuth'); // Work around Facebook performing an illegal substitution of the Slash // by '%2F' when entering redirect URI (RFC 3986 sect. 2.2, sect. 3.4) $callbackURL = preg_replace_callback('/(?<=\\?).*/', function ($matches) { return rawurlencode($matches[0]); }, $callbackURL); // user accepted the connection if (isset($_GET['code'])) { try { // fetch access_token $request = new HTTPRequest('https://graph.facebook.com/oauth/access_token?client_id=' . StringUtil::trim(FACEBOOK_PUBLIC_KEY) . '&redirect_uri=' . rawurlencode($callbackURL) . '&client_secret=' . StringUtil::trim(FACEBOOK_PRIVATE_KEY) . '&code=' . rawurlencode($_GET['code'])); $request->execute(); $reply = $request->getReply(); $content = $reply['body']; } catch (SystemException $e) { // force logging $e->getExceptionID(); throw new IllegalLinkException(); } // validate state, validation of state is executed after fetching the access_token to invalidate 'code' if (!isset($_GET['state']) || $_GET['state'] != WCF::getSession()->getVar('__facebookInit')) { throw new IllegalLinkException(); } WCF::getSession()->unregister('__facebookInit'); parse_str($content, $data); try { // fetch userdata $request = new HTTPRequest('https://graph.facebook.com/me?access_token=' . rawurlencode($data['access_token']) . '&fields=birthday,bio,email,gender,id,location,name,picture.type(large),website'); $request->execute(); $reply = $request->getReply(); $content = $reply['body']; } catch (SystemException $e) { // force logging $e->getExceptionID(); throw new IllegalLinkException(); } $userData = JSON::decode($content); // check whether a user is connected to this facebook account $user = $this->getUser($userData['id']); if ($user->userID) { // a user is already connected, but we are logged in, break if (WCF::getUser()->userID) { throw new NamedUserException(WCF::getLanguage()->get('wcf.user.3rdparty.facebook.connect.error.inuse')); } else { if (UserAuthenticationFactory::getInstance()->getUserAuthentication()->supportsPersistentLogins()) { $password = StringUtil::getRandomID(); $userEditor = new UserEditor($user); $userEditor->update(array('password' => $password)); // reload user to retrieve salt $user = new User($user->userID); UserAuthenticationFactory::getInstance()->getUserAuthentication()->storeAccessData($user, $user->username, $password); } WCF::getSession()->changeUser($user); WCF::getSession()->update(); HeaderUtil::redirect(LinkHandler::getInstance()->getLink()); } } else { WCF::getSession()->register('__3rdPartyProvider', 'facebook'); // save data for connection if (WCF::getUser()->userID) { WCF::getSession()->register('__facebookUsername', $userData['name']); WCF::getSession()->register('__facebookData', $userData); HeaderUtil::redirect(LinkHandler::getInstance()->getLink('AccountManagement') . '#3rdParty'); } else { WCF::getSession()->register('__username', $userData['name']); if (isset($userData['email'])) { WCF::getSession()->register('__email', $userData['email']); } WCF::getSession()->register('__facebookData', $userData); // we assume that bots won't register on facebook first // thus no need for a captcha if (REGISTER_USE_CAPTCHA) { WCF::getSession()->register('noRegistrationCaptcha', true); } WCF::getSession()->update(); HeaderUtil::redirect(LinkHandler::getInstance()->getLink('Register')); } } $this->executed(); exit; } // user declined or any other error that may occur if (isset($_GET['error'])) { throw new NamedUserException(WCF::getLanguage()->get('wcf.user.3rdparty.facebook.login.error.' . $_GET['error'])); } // start auth by redirecting to facebook $token = StringUtil::getRandomID(); WCF::getSession()->register('__facebookInit', $token); HeaderUtil::redirect("https://www.facebook.com/dialog/oauth?client_id=" . StringUtil::trim(FACEBOOK_PUBLIC_KEY) . "&redirect_uri=" . rawurlencode($callbackURL) . "&state=" . $token . "&scope=email,user_about_me,user_birthday,user_location,user_website"); $this->executed(); exit; }
/** * Sends JSON-Encoded response. */ protected function sendResponse() { header('Content-type: application/json'); echo JSON::encode($this->response); exit; }
/** * @see \wcf\action\IAction::execute() */ public function execute() { AbstractAction::execute(); $returnValues = array('actionName' => $this->actionName, 'pollID' => $this->pollID); switch ($this->actionName) { case 'getResult': $this->getResult($returnValues); break; case 'getVote': $this->getVote($returnValues); break; case 'vote': $this->vote($returnValues); break; } $this->executed(); // send JSON-encoded response header('Content-type: application/json'); echo JSON::encode($returnValues); exit; }
/** * Sends a JSON-encoded response. * * @param integer $progress * @param array $parameters * @param string $proceedURL */ protected function sendResponse($progress = 0, array $parameters = null, $proceedURL = '') { if ($parameters === null) { $parameters = $this->parameters; } // build return values $returnValues = array('className' => $this->className, 'loopCount' => $this->loopCount + 1, 'parameters' => $parameters, 'proceedURL' => $proceedURL, 'progress' => $progress); // include template on startup if ($this->loopCount == -1) { $returnValues['template'] = WCF::getTPL()->fetch('worker'); } // send JSON-encoded response header('Content-type: application/json'); echo JSON::encode($returnValues); exit; }
/** * Reads a value and validates it. If you set $allowEmpty to true, no exception will * be thrown if the variable evaluates to 0 (integer) or '' (string). Furthermore the * variable will be always created with a sane value if it does not exist. * * @param string $variableName * @param boolean $allowEmpty * @param string $arrayIndex * @param integer $type */ protected function readValue($variableName, $allowEmpty, $arrayIndex, $type) { if ($arrayIndex) { if (!isset($this->parameters[$arrayIndex])) { throw new SystemException("Corrupt parameters, index '" . $arrayIndex . "' is missing"); } $target =& $this->parameters[$arrayIndex]; } else { $target =& $this->parameters; } switch ($type) { case self::TYPE_INTEGER: if (!isset($target[$variableName])) { if ($allowEmpty) { $target[$variableName] = 0; } else { throw new UserInputException($variableName); } } else { $target[$variableName] = intval($target[$variableName]); if (!$allowEmpty && !$target[$variableName]) { throw new UserInputException($variableName); } } break; case self::TYPE_STRING: if (!isset($target[$variableName])) { if ($allowEmpty) { $target[$variableName] = ''; } else { throw new UserInputException($variableName); } } else { $target[$variableName] = StringUtil::trim($target[$variableName]); if (!$allowEmpty && empty($target[$variableName])) { throw new UserInputException($variableName); } } break; case self::TYPE_BOOLEAN: if (!isset($target[$variableName])) { if ($allowEmpty) { $target[$variableName] = false; } else { throw new UserInputException($variableName); } } else { if (is_numeric($target[$variableName])) { $target[$variableName] = (bool) $target[$variableName]; } else { $target[$variableName] = $target[$variableName] != 'false'; } } break; case self::TYPE_JSON: if (!isset($target[$variableName])) { if ($allowEmpty) { $target[$variableName] = array(); } else { throw new UserInputException($variableName); } } else { try { $target[$variableName] = JSON::decode($target[$variableName]); } catch (SystemException $e) { throw new UserInputException($variableName); } if (!$allowEmpty && empty($target[$variableName])) { throw new UserInputException($variableName); } } break; } }