function Streams_related_validate() { switch (Q_Request::method()) { case 'POST': $required = array('toPublisherId', 'toStreamName', 'type', 'fromPublisherId', 'fromStreamName'); break; case 'DELETE': $required = array('toPublisherId', 'toStreamName', 'type', 'fromPublisherId', 'fromStreamName'); break; case 'PUT': $required = array('toPublisherId', 'toStreamName', 'type', 'fromPublisherId', 'fromStreamName', 'weight'); if (isset($_REQUEST['adjustWeights'])) { if (!is_numeric($_REQUEST['adjustWeights'])) { Q_Response::addError(new Q_Exception_WrongValue(array('field' => 'adjustWeights', 'range' => 'a numeric value'), 'adjustWeights')); } } break; case 'GET': $required = array(); break; } foreach ($required as $r) { if (!isset($_REQUEST[$r])) { Q_Response::addError(new Q_Exception_RequiredField(array('field' => $r))); } } }
function Streams_interest_validate($params) { // Protect against CSRF attacks: if (Q_Request::method() !== 'GET') { Q_Valid::nonce(true); } }
function Streams_invite_validate() { if (Q_Request::method() === 'PUT') { return; } if (Q_Request::method() !== 'GET') { Q_Valid::nonce(true); } $fields = array('publisherId', 'streamName'); if (Q_Request::method() === 'POST') { if (Q_Valid::requireFields($fields)) { return; } foreach ($fields as $f) { if (strlen(trim($_REQUEST[$f])) === 0) { Q_Response::addError(new Q_Exception("{$f} can't be empty", $f)); } } } if (isset($_REQUEST['fullName'])) { $length_min = Q_Config::get('Streams', 'inputs', 'fullName', 'lengthMin', 5); $length_max = Q_Config::get('Streams', 'inputs', 'fullName', 'lengthMax', 30); if (strlen($_REQUEST['fullName']) < $length_min) { throw new Q_Exception("A user's full name can't be that short.", 'fullName'); } if (strlen($_REQUEST['fullName']) > $length_max) { throw new Q_Exception("A user's full name can't be that long.", 'fullName'); } } }
function Q_notice_response_data() { $method = Q_Request::method(); if ($method !== 'DELETE') { throw new Q_Exception_MethodNotSupported($method); } return Q::$cache['notice_deleted']; }
function Streams_stream_validate($params) { // Protect against CSRF attacks: if (Q_Request::method() !== 'GET') { Q_Valid::nonce(true); } $type = Streams::requestedType(); if ($type && Q::canHandle("Streams/validate/{$type}")) { return Q::event("Streams/validate/{$type}", $params); } }
function Users_login_validate() { if (Q_Request::method() === 'GET') { return; } Q_Valid::nonce(true); foreach (array('identifier', 'passphrase') as $field) { if (!isset($_REQUEST[$field])) { throw new Q_Exception("{$field} is missing", array($field)); } } }
function Q_nonce_response_data() { $method = Q_Request::method(); if ($method !== 'POST') { throw new Q_Exception_MethodNotSupported($method); } // we could technically return the nonce in the response, // because other sites can't read the response from a cross-domain post // but we aren't going to do that because we already set the cookie // so just return true return true; }
function Users_activate_response_content() { $email = $mobile = $type = $user = $emailAddress = $mobileNumber = null; extract(Users::$cache, EXTR_IF_EXISTS); $complete = false; if ($user and !empty($user->passphraseHash)) { if ($emailAddress and $user->emailAddress == $emailAddress) { $complete = true; } else { if ($mobileNumber and $user->mobileNumber = $mobileNumber) { $complete = true; } } } if (!empty(Users::$cache['success'])) { $app = Q_Config::expect('Q', 'app'); $successUrl = Q_Config::get('Users', 'uris', "{$app}/successUrl", "{$app}/home"); if (Q_Request::method() === 'POST') { if ($qs = $_SERVER['QUERY_STRING']) { $qs = "&{$qs}"; } Q_Response::redirect(Q_Config::get('Users', 'uris', "{$app}/afterActivate", $successUrl) . '?Q.fromSuccess=Users/activate' . $qs); return true; } } $view = Q_Config::get('Users', 'activateView', 'Users/content/activate.php'); $t = $email ? 'e' : 'm'; $identifier = $email ? $emailAddress : $mobileNumber; // Generate 10 passphrase suggestions $suggestions = array(); $arr = (include USERS_PLUGIN_FILES_DIR . DS . 'Users' . DS . 'passphrases.php'); for ($i = 0; $i < 10; ++$i) { $pre1 = $arr['pre'][mt_rand(0, count($arr['pre']) - 1)]; $noun1 = $arr['nouns'][mt_rand(0, count($arr['nouns']) - 1)]; $verb = $arr['verbs'][mt_rand(0, count($arr['verbs']) - 1)]; $pre2 = $arr['pre'][mt_rand(0, count($arr['pre']) - 1)]; $adj = $arr['adjectives'][mt_rand(0, count($arr['adjectives']) - 1)]; $noun2 = $arr['nouns'][mt_rand(0, count($arr['nouns']) - 1)]; //$suggestions[] = strtolower("$pre1 $noun1 $verb $pre2 $adj $noun2"); $suggestions[] = strtolower("{$pre1} {$noun1} {$verb} {$pre2} {$noun2}"); } $verb_ue = urlencode($arr['verbs'][mt_rand() % count($arr['verbs'])]); $noun_ue = urlencode($arr['nouns'][mt_rand() % count($arr['nouns'])]); $code = Q::ifset($_REQUEST['code']); Q_Response::addScriptLine("Q.onReady.set(function () {\n\t\tif (Q.Notice) {\n\t\t\tQ.Notice.hide('Users/email');\n\t\t\tQ.Notice.hide('Users/mobile');\n\t\t}\n\t});"); // shh! not while I'm activating! lol return Q::view($view, compact('identifier', 'type', 'user', 'code', 'suggestions', 'verb_ue', 'noun_ue', 't', 'app', 'home', 'complete')); }
/** * Excecute web request * @method execute * @static */ static function execute() { // Fixes for different platforms: if (isset($_SERVER['HTTP_X_REWRITE_URL'])) { // ISAPI 3.0 $_SERVER['REQUEST_URI'] = $_SERVER['HTTP_X_REWRITE_URL']; } // Get the base URL $base_url = Q_Request::baseUrl(); if (Q::$controller === 'Q_ActionController') { // we detected action.php in the URL, but // a misconfigured web server executed index.php instead return Q_ActionController::execute(); } // Set the controller that is being used if (!isset(Q::$controller)) { Q::$controller = 'Q_WebController'; } try { $slots = Q_Request::slotNames(false); $slots = $slots ? ' slots: (' . implode(',', $slots) . ') from' : ''; $method = Q_Request::method(); Q::log("{$method}{$slots} url: " . Q_Request::url(true), null, null, array('maxLength' => 10000)); Q_Dispatcher::dispatch(); $dispatchResult = Q_Dispatcher::result(); if (!isset($dispatchResult)) { $dispatchResult = 'Ran dispatcher'; } $uri = Q_Request::uri(); $module = $uri->module; $action = $uri->action; if ($module and $action) { $slotNames = Q_Request::slotNames(); $returned_slots = empty($slotNames) ? '' : implode(',', $slotNames); Q::log("~" . ceil(Q::milliseconds()) . 'ms+' . ceil(memory_get_peak_usage() / 1000) . 'kb.' . " {$dispatchResult} for {$module}/{$action}" . " ({$returned_slots})", null, null, array('maxLength' => 10000)); } else { Q::log("~" . ceil(Q::milliseconds()) . 'ms+' . ceil(memory_get_peak_usage() / 1000) . 'kb.' . " {$dispatchResult} No route for " . $_SERVER['REQUEST_URI'], null, null, array('maxLength' => 10000)); } } catch (Exception $exception) { /** * @event Q/exception * @param {Exception} exception */ Q::event('Q/exception', compact('exception')); } }
function Streams_basic_validate() { Q_Valid::nonce(true); if (Q_Request::method() !== 'POST') { return; } $fields = array('firstName' => 'First name', 'lastName' => 'Last name', 'gender' => 'Gender', 'birthday_month' => 'Month', 'birthday_day' => 'Day', 'birthday_year' => 'Year'); if (isset($_REQUEST['fullName'])) { $length_min = Q_Config::get('Streams', 'inputs', 'fullName', 'lengthMin', 5); $length_max = Q_Config::get('Streams', 'inputs', 'fullName', 'lengthMax', 30); if (strlen($_REQUEST['fullName']) < $length_min) { Q_Response::addError(new Q_Exception("Your full name can't be that short.", 'fullName')); } if (strlen($_REQUEST['fullName']) > $length_max) { Q_Response::addError(new Q_Exception("Your full name can't be that long.", 'fullName')); } } if (Q_Response::getErrors()) { return; } if (!empty($_REQUEST['birthday_month']) or !empty($_REQUEST['birthday_day']) or !empty($_REQUEST['birthday_year'])) { foreach (array('birthday_month', 'birthday_day', 'birthday_year') as $field) { if (empty($_REQUEST[$field]) or !trim($_REQUEST[$field])) { throw new Q_Exception_RequiredField(compact('field'), $field); } } if (!checkdate($_REQUEST['birthday_month'], $_REQUEST['birthday_day'], $_REQUEST['birthday_year'])) { Q_Response::addError(new Q_Exception("Not a valid date", "birthday_day")); } if ($_REQUEST['birthday_year'] > date('Y') - 13) { // compliance with COPPA Q_Response::addError(new Q_Exception("You're still a kid.", "birthday_year")); } if ($_REQUEST['birthday_year'] < date('Y') - 100) { Q_Response::addError(new Q_Exception("A world record? Really?", "birthday_year")); } } if (!empty($_REQUEST['gender'])) { if (!in_array($_REQUEST['gender'], array('male', 'female'))) { Q_Response::addError(new Q_Exception("Please enter male or female", "gender")); } } }
function Places_zipcode_response() { if (Q_Request::method() !== 'GET') { return null; } $zip = array(); if (isset($_REQUEST['zipcodes'])) { $zip = $_REQUEST['zipcodes']; } else { if (isset($_REQUEST['zipcode'])) { $zip = $_REQUEST['zipcode']; } } if (is_string($zip)) { $zip = explode(',', $zip); } $zipcodes = Places_Zipcode::select('*')->where(array('zipcode' => $zip))->fetchDbRows(); Q_Response::setSlot('zipcodes', $zipcodes); }
/** * The standard action front controller * @method execute * @static * @throws {Q_Exception_BadUrl} * @throws {Q_Exception} * @throws {Q_Exception_MissingConfig} */ static function execute($url = null) { // Fixes for different platforms: if (isset($_SERVER['HTTP_X_REWRITE_URL'])) { // ISAPI 3.0 $_SERVER['REQUEST_URI'] = $_SERVER['HTTP_X_REWRITE_URL']; } // Set the controller that is being used if (!isset(Q::$controller)) { Q::$controller = 'Q_ActionController'; } try { $slots = Q_Request::slotNames(false); $slots = $slots ? ' slots: (' . implode(',', $slots) . ') from' : ''; $method = Q_Request::method(); Q::log("{$method}{$slots} url: " . Q_Request::url(true)); $tail = Q_Request::tail($url); if (!isset($tail)) { // Bad url was requested somehow $url = Q_Request::url(true); $base_url = Q_Request::baseUrl(true); throw new Q_Exception_BadUrl(compact('base_url', 'url')); } $parts = explode('/', $tail); $parts_len = count($parts); if ($parts_len >= 1) { $module = $parts[0]; } if ($parts_len >= 2) { $action = $parts[1]; } if (empty($module) or empty($action)) { throw new Q_Exception("Not implemented"); } // Make sure the 'Q'/'web' config fields are set, // otherwise URLs will be formed pointing to the wrong // controller script. $ar = Q_Config::get('Q', 'web', 'appRootUrl', null); if (!isset($ar)) { throw new Q_Exception_MissingConfig(array('fieldpath' => 'Q/web/appRootUrl')); } // Dispatch the request $uri = Q_Uri::from(compact('module', 'action')); Q_Dispatcher::dispatch($uri); $dispatchResult = Q_Dispatcher::result(); if (!isset($dispatchResult)) { $dispatchResult = 'Ran dispatcher'; } if ($module and $action) { $slotNames = Q_Request::slotNames(); $requestedSlots = empty($slotNames) ? '' : implode(',', $slotNames); Q::log("~" . ceil(Q::milliseconds()) . 'ms+' . ceil(memory_get_peak_usage() / 1000) . 'kb.' . " {$dispatchResult} for {$module}/{$action}" . " ({$requestedSlots})"); } else { Q::log("~" . ceil(Q::milliseconds()) . 'ms+' . ceil(memory_get_peak_usage() / 1000) . 'kb.' . " No route for " . $_SERVER['REQUEST_URI']); } } catch (Exception $exception) { /** * @event Q/exception * @param {Exception} exception */ Q::event('Q/exception', compact('exception')); } }
function Users_activate_objects_mobile($mobileNumber, &$mobile) { Q_Response::removeNotice('Users/activate/objects'); $mobile = new Users_Mobile(); if (!Q_Valid::phone($mobileNumber, $normalized)) { return; } $mobile->number = $normalized; if (!$mobile->retrieve()) { throw new Q_Exception_MissingRow(array('table' => 'mobile phone', 'criteria' => "number {$normalized}")); } $user = Users::loggedInUser(); if ($user) { if ($user->id != $mobile->userId) { throw new Q_Exception("You are logged in as a different user. Please log out and click the link again."); } } else { $user = new Users_User(); $user->id = $mobile->userId; if (!$user->retrieve()) { throw new Q_Exception_MissingRow(array('table' => 'user', 'criteria' => 'id = ' . $user->id)); } } if ($mobile->activationCode != $_REQUEST['code']) { throw new Q_Exception("The activation code does not match. Did you get a newer message?", 'code'); } $timestamp = Users_Mobile::db()->getCurrentTimestamp(); if ($timestamp > Users_Mobile::db()->fromDateTime($mobile->activationCodeExpires)) { throw new Q_Exception("Activation code expired"); } if (Q_Request::method() !== 'POST' and empty($_REQUEST['p']) and isset($user->mobileNumber) and $user->mobileNumber == $mobile->number) { Q_Response::setNotice('Users/activate/objects', "{$normalized} has already been activated for {$user->username}", true); return $user; } return $user; }
/** * Dispatches a URI for internal processing. * Usually called by a front controller. * @method dispatch * @static * @param {mixed} [$uri=null] You can pass a custom URI to dispatch. Otherwise, Qbix will attempt * to route the requested URL, if any. * @param {array} [$check=array('accessible')] Pass array() to skip checking whether the URI can be obtained * as a result of routing some URL. * @return {boolean} * @throws {Q_Exception_MethodNotSupported} * @throws {Q_Exception_Recursion} * @throws {Q_Exception_DispatcherErrors} * @throws {Q_Exception_DispatcherForward} */ static function dispatch($uri = null, $check = array('accessible')) { if (!is_array($check)) { $check = array('accessible'); } if (isset($uri)) { if (in_array('accessible', $check)) { if (!Q_Uri::url($uri)) { // We shouldn't dispatch to this URI $uri = Q_Uri::from(array()); } } self::$uri = Q_Uri::from($uri); } else { $request_uri = Q_Request::uri(); self::$uri = clone $request_uri; } // if file or dir is requested, try to serve it $served = false; $skip = Q_Config::get('Q', 'dispatcherSkipFilename', false); $filename = $skip ? false : Q_Request::filename(); if ($filename) { if (is_dir($filename)) { /** * @event Q/dir * @param {string} filename * @param {string} routed_uri * @return {boolean} */ $served = Q::event("Q/dir", compact('filename', 'routed_uri')); $dir_was_served = true; } else { /** * @event Q/file * @param {string} filename * @param {string} routed_uri * @return {boolean} */ $served = Q::event("Q/file", compact('filename', 'routed_uri')); $dir_was_served = false; } } // if response was served, then return if ($served) { self::result($dir_was_served ? "Dir served" : "File served"); return true; } // This loop is for forwarding $max_forwards = Q_Config::get('Q', 'maxForwards', 10); for ($try = 0; $try < $max_forwards; ++$try) { // Make an array from the routed URI $routed_uri_array = array(); if (self::$uri instanceof Q_Uri) { $routed_uri_array = self::$uri->toArray(); } // If no module was found, then respond with noModule and return if (!isset(self::$uri->module)) { /** * @event Q/noModule * @param {array} $routed_uri_array */ Q::event("Q/noModule", $routed_uri_array); // should echo things self::result("No module"); return false; } $module = self::$uri->module; try { // Implement restricting of modules we are allowed to access $routed_modules = Q_Config::get('Q', 'routedModules', null); if (isset($routed_modules)) { if (!in_array($module, $routed_modules)) { /** * @event Q/notFound * @param {array} $routed_uri_array */ Q::event('Q/notFound', $routed_uri_array); // should echo things self::result("Unknown module"); return false; } } else { if (!Q::realPath("handlers/{$module}")) { Q::event('Q/notFound', $routed_uri_array); // should echo things self::result("Unknown module"); return false; } } // Implement notFound if action was not found if (empty(self::$uri->action)) { Q::event('Q/notFound', $routed_uri_array); // should echo things self::result("Unknown action"); return false; } // Fire a pure event, for aggregation etc if (!isset(self::$skip['Q/prepare'])) { /** * @event Q/prepare * @param {array} $routed_uri_array */ Q::event('Q/prepare', $routed_uri_array, true); } // Perform validation if (!isset(self::$skip['Q/validate'])) { /** * @event Q/validate * @param {array} $routed_uri_array */ Q::event('Q/validate', $routed_uri_array); if (!isset(self::$skip['Q/errors'])) { // Check if any errors accumulated if (Q_Response::getErrors()) { // There were validation errors -- render a response self::result('Validation errors'); self::errors(null, $module, null); return false; } } } // Time to instantiate some app objects from the request if (!isset(self::$skip['Q/objects'])) { /** * @event Q/objects * @param {array} $routed_uri_array */ Q::event('Q/objects', $routed_uri_array, true); } // We might want to reroute the request if (!isset(self::$skip['Q/reroute'])) { /** * @event Q/reroute * @param {array} $routed_uri_array * @return {boolean} whether to stop the dispatch */ $stop_dispatch = Q::event('Q/reroute', $routed_uri_array, true); if ($stop_dispatch) { self::result("Stopped dispatch"); return false; } } // Make some changes to server state, possibly $method = Q_Request::method(); if ($method != 'GET') { $methods = Q_Config::get('Q', 'methods', array('POST', 'PUT', 'DELETE', 'OPTIONS', 'HEAD')); if (!in_array($method, $methods)) { throw new Q_Exception_MethodNotSupported(compact('method')); } $method_event = 'Q/' . strtolower($method); if (!isset(self::$skip['Q/method']) and !isset(self::$skip[$method_event])) { if (!Q::canHandle($method_event)) { throw new Q_Exception_MethodNotSupported(compact('method')); } Q::event($method_event); } } // You can calculate some analytics here, and store them somewhere if (!isset(self::$skip['Q/analytics'])) { /** * @event Q/analytics * @param {array} $routed_uri_array */ Q::event('Q/analytics', $routed_uri_array, true); } if (!isset(self::$skip['Q/errors'])) { // Check if any errors accumulated if (Q_Response::getErrors()) { // There were processing errors -- render a response self::result('Processing errors'); self::errors(null, $module, null); return false; } } // When handling all further events, you should probably // refrain from changing server state, and only do reading. // That is because GET in HTTP is not supposed to have side effects // for which the client is responsible. // Start buffering the response, unless otherwise requested $handler = Q_Response::isBuffered(); if ($handler !== false) { $ob = new Q_OutputBuffer($handler); } // Generate and render a response /** * @event Q/response * @param {array} $routed_uri_array */ self::$response_started = true; Q::event("Q/response", $routed_uri_array); if (!empty($ob)) { $ob->endFlush(); } self::result("Served response"); return true; } catch (Q_Exception_DispatcherForward $e) { if (!empty($ob)) { $ob->getClean(); } self::handleForwardException($e); } catch (Q_Exception_DispatcherErrors $e) { if (!empty($ob)) { $partial_response = $ob->getClean(); } else { $partial_response = null; } self::errors(null, $module, $partial_response); self::result("Rendered errors"); return true; } catch (Exception $exception) { if (!empty($ob)) { $partial_response = $ob->getClean(); } else { $partial_response = null; } $message = $exception->getMessage(); $file = $exception->getFile(); $line = $exception->getLine(); if (is_callable(array($exception, 'getTraceAsStringEx'))) { $trace_string = $exception->getTraceAsStringEx(); } else { $trace_string = $exception->getTraceAsString(); } $colored = Q_Exception::coloredString($message, $file, $line, $trace_string); self::result("Exception occurred:\n\n{$colored}"); try { self::errors($exception, $module, $partial_response); } catch (Exception $e) { if (!empty($forwarding_to_error_action)) { // Looks like there were errors in the error action // So show the default one with the original exception throw $exception; } if (get_class($e) === 'Q_Exception_DispatcherForward') { $forwarding_to_error_action = true; self::handleForwardException($e); continue; } else { throw $e; } } return false; } } // If we are here, we have done forwarding too much throw new Q_Exception_Recursion(array('function_name' => 'Dispatcher::forward()')); }
/** * Used to get a stream * * @param {array} $_REQUEST * @param {string} $_REQUEST.publisherId Required * @param {string} $_REQUEST.streamName Required streamName or name * @param {integer} [$_REQUEST.messages] optionally pass a number here to fetch latest messages * @param {integer} [$_REQUEST.participants] optionally pass a number here to fetch participants * @return {void} */ function Streams_stream_response() { // this handler is only for GET requests if (Q_Request::method() !== 'GET') { return null; } $publisherId = Streams::requestedPublisherId(true); $name = Streams::requestedName(true); $fields = Streams::requestedFields(); $user = Users::loggedInUser(); $userId = $user ? $user->id : ""; if (isset(Streams::$cache['stream'])) { $stream = Streams::$cache['stream']; } else { $streams = Streams::fetch($userId, $publisherId, $name, $fields ? $fields : '*', array('withParticipant' => true)); if (Q_Request::slotName('streams')) { Q_Response::setSlot('streams', Db::exportArray($streams)); } if (empty($streams)) { if (Q_Request::slotName('stream')) { Q_Response::setSlot('stream', null); Q_Response::setSlot('messages', null); Q_Response::setSlot('participants', null); Q_Response::setSlot('related', null); Q_Response::setSlot('relatedTo', null); } else { if (!Q_Request::slotName('streams')) { $app = Q_Config::expect('Q', 'app'); Q_Dispatcher::forward("{$app}/notFound"); } } return null; } // The rest of the data is joined only on the first stream Streams::$cache['stream'] = $stream = reset($streams); } if (empty($stream)) { if (Q_Request::slotName('stream')) { Q_Response::setSlot('stream', null); } return null; } if ($userId && !empty($_REQUEST['join'])) { $stream->join(); // NOTE: one of the rare times we may change state in a response handler } if (Q_Request::slotName('stream')) { Q_Response::setSlot('stream', $stream->exportArray()); } if (!empty($_REQUEST['messages'])) { $max = -1; $limit = $_REQUEST['messages']; $messages = false; $type = isset($_REQUEST['messageType']) ? $_REQUEST['messageType'] : null; if ($stream->testReadLevel('messages')) { $messages = Db::exportArray($stream->getMessages(compact('type', 'max', 'limit'))); } Q_Response::setSlot('messages', $messages); } if (!empty($_REQUEST['participants'])) { $limit = $_REQUEST['participants']; $participants = false; if ($stream->testReadLevel('participants')) { $participants = Db::exportArray($stream->getParticipants(compact('limit', 'offset'))); } Q_Response::setSlot('participants', $participants); } }