/**
  * Produces the proxied version of a URL if it falls within the content-rewrite params and
  * will append a refresh param to the proxied url based on the expires param, and the gadget
  * url so that the proxy server knows to rewrite it's content or not
  *
  * @param string $url
  * @return string
  */
 private function getProxyUrl($url)
 {
     if (strpos(strtolower($url), 'http://') === false && strpos(strtolower($url), 'https://') === false) {
         $url = $this->baseUrl . $url;
     }
     $url = Config::get('web_prefix') . '/gadgets/proxy?url=' . urlencode($url);
     $url .= '&refresh=' . (isset($this->rewrite['expires']) && is_numeric($this->rewrite['expires']) ? $this->rewrite['expires'] : '3600');
     $url .= '&gadget=' . urlencode($this->context->getUrl());
     $url .= '&st=' . urlencode(BasicSecurityToken::getTokenStringFromRequest());
     return $url;
 }
 /**
  * Fetches the content and returns it as-is using the headers as returned
  * by the remote host.
  *
  * @param string $url the url to retrieve
  */
 public function fetch($url)
 {
     // TODO: Check to see if we can just use MakeRequestOptions::fromCurrentRequest
     $st = BasicSecurityToken::getTokenStringFromRequest();
     $body = isset($_GET['postData']) ? $_GET['postData'] : (isset($_POST['postData']) ? $_POST['postData'] : false);
     $authz = isset($_GET['authz']) ? $_GET['authz'] : (isset($_POST['authz']) ? $_POST['authz'] : null);
     $headers = isset($_GET['headers']) ? $_GET['headers'] : (isset($_POST['headers']) ? $_POST['headers'] : null);
     $params = new MakeRequestOptions($url);
     $params->setSecurityTokenString($st)->setAuthz($authz)->setRequestBody($body)->setHttpMethod('GET')->setFormEncodedRequestHeaders($headers)->setNoCache($this->context->getIgnoreCache());
     $result = $this->makeRequest->fetch($this->context, $params);
     $httpCode = (int) $result->getHttpCode();
     $cleanedResponseHeaders = $this->makeRequest->cleanResponseHeaders($result->getResponseHeaders());
     $isShockwaveFlash = false;
     foreach ($cleanedResponseHeaders as $key => $val) {
         header("{$key}: {$val}", true);
         if (strtoupper($key) == 'CONTENT-TYPE' && strtolower($val) == 'application/x-shockwave-flash') {
             // We're skipping the content disposition header for flash due to an issue with Flash player 10
             // This does make some sites a higher value phishing target, but this can be mitigated by
             // additional referer checks.
             $isShockwaveFlash = true;
         }
     }
     if (!$isShockwaveFlash && !Config::get('debug')) {
         header('Content-Disposition: attachment;filename=p.txt');
     }
     $lastModified = $result->getResponseHeader('Last-Modified') != null ? $result->getResponseHeader('Last-Modified') : gmdate('D, d M Y H:i:s', $result->getCreated()) . ' GMT';
     $notModified = false;
     if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && $lastModified && !isset($_SERVER['HTTP_IF_NONE_MATCH'])) {
         $if_modified_since = strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']);
         // Use the request's Last-Modified, otherwise fall back on our internal time keeping (the time the request was created)
         $lastModified = strtotime($lastModified);
         if ($lastModified <= $if_modified_since) {
             $notModified = true;
         }
     }
     if ($httpCode == 200) {
         // only set caching headers if the result was 'OK'
         $this->setCachingHeaders($lastModified);
         // was the &gadget=<gadget url> specified in the request? if so parse it and check the rewrite settings
         if (isset($_GET['gadget'])) {
             $this->rewriteContent($_GET['gadget'], $result);
         }
     }
     // If the cached file time is within the refreshInterval params value, return not-modified
     if ($notModified) {
         header('HTTP/1.0 304 Not Modified', true);
         header('Content-Length: 0', true);
     } else {
         header("HTTP/1.1 {$httpCode} " . $result->getHttpCodeMsg());
         // then echo the content
         echo $result->getResponseContent();
     }
 }
 /**
  *
  * @return SecurityToken
  */
 private function getSecurityToken()
 {
     $token = BasicSecurityToken::getTokenStringFromRequest();
     if (empty($token)) {
         if (Config::get('allow_anonymous_token')) {
             // no security token, continue anonymously, remeber to check
             // for private profiles etc in your code so their not publicly
             // accessable to anoymous users! Anonymous == owner = viewer = appId = modId = 0
             // create token with 0 values, no gadget url, no domain and 0 duration
             $gadgetSigner = Config::get('security_token');
             return new $gadgetSigner(null, 0, SecurityToken::$ANONYMOUS, SecurityToken::$ANONYMOUS, 0, '', '', 0, Config::get('container_id'));
         } else {
             return null;
         }
     }
     $gadgetSigner = Config::get('security_token_signer');
     $gadgetSigner = new $gadgetSigner();
     return $gadgetSigner->createToken($token);
 }
 /**
  * Builds a MakeRequestOptions object from the current $_GET and $_POST
  * superglobals.
  *
  * @return MakeRequestOptions An object initialized from the current request.
  * @throws MakeRequestParameterException If any of the parameters were
  *     invalid.
  */
 public static function fromCurrentRequest()
 {
     $href = MakeRequestOptions::getRequestParam('href');
     if (!isset($href)) {
         $href = MakeRequestOptions::getRequestParam('url');
     }
     $options = new MakeRequestOptions($href);
     $options->setHttpMethod(MakeRequestOptions::getRequestParam('httpMethod'))->setRequestBody(MakeRequestOptions::getRequestParam('postData'))->setFormEncodedRequestHeaders(MakeRequestOptions::getRequestParam('headers'))->setResponseFormat(MakeRequestOptions::getRequestParam('contentType'))->setAuthz(MakeRequestOptions::getRequestParam('authz'))->setSignViewer(MakeRequestOptions::getRequestParam('signViewer', 'boolean'))->setSignOwner(MakeRequestOptions::getRequestParam('signOwner', 'boolean'))->setNumEntries(MakeRequestOptions::getRequestParam('numEntries', 'integer'))->setGetSummaries(MakeRequestOptions::getRequestParam('getSummaries', 'boolean'))->setRefreshInterval(MakeRequestOptions::getRequestParam('refreshInterval', 'integer'))->setNoCache(MakeRequestOptions::getRequestParam('bypassSpecCache', 'boolean'))->setOAuthServiceName(MakeRequestOptions::getRequestParam('OAUTH_SERVICE_NAME'))->setOAuthTokenName(MakeRequestOptions::getRequestParam('OAUTH_TOKEN_NAME'))->setOAuthRequestToken(MakeRequestOptions::getRequestParam('OAUTH_REQUEST_TOKEN'))->setOAuthRequestTokenSecret(MakeRequestOptions::getRequestParam('OAUTH_REQUEST_TOKEN_SECRET'))->setOAuthUseToken(MakeRequestOptions::getRequestParam('OAUTH_USE_TOKEN'))->setOAuthReceivedCallback(MakeRequestOptions::getRequestParam('OAUTH_RECEIVED_CALLBACK'))->setOAuthClientState(MakeRequestOptions::getRequestParam('oauthState'))->setSecurityTokenString(BasicSecurityToken::getTokenStringFromRequest());
     return $options;
 }
 /**
  *
  * @return SecurityToken
  */
 public function getSecurityToken()
 {
     // Support a configurable host name ('http_host' key) so that OAuth signatures don't fail in reverse-proxy type situations
     $scheme = !isset($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != "on" ? 'http' : 'https';
     $http_url = $scheme . '://' . (Config::get('http_host') ? Config::get('http_host') : $_SERVER['HTTP_HOST']) . $_SERVER['REQUEST_URI'];
     // see if we have an OAuth request
     $request = OAuthRequest::from_request(null, $http_url, null);
     $appUrl = $request->get_parameter('oauth_consumer_key');
     $userId = $request->get_parameter('xoauth_requestor_id');
     // from Consumer Request extension (2-legged OAuth)
     $signature = $request->get_parameter('oauth_signature');
     if ($appUrl && $signature) {
         //if ($appUrl && $signature && $userId) {
         // look up the user and perms for this oauth request
         $oauthLookupService = Config::get('oauth_lookup_service');
         $oauthLookupService = new $oauthLookupService();
         $token = $oauthLookupService->getSecurityToken($request, $appUrl, $userId, $this->getContentType());
         if ($token) {
             $token->setAuthenticationMode(AuthenticationMode::$OAUTH_CONSUMER_REQUEST);
             return $token;
         } else {
             return null;
             // invalid oauth request, or 3rd party doesn't have access to this user
         }
     }
     // else, not a valid oauth request, so don't bother
     // look for encrypted security token
     $token = BasicSecurityToken::getTokenStringFromRequest();
     if (empty($token)) {
         if (Config::get('allow_anonymous_token')) {
             // no security token, continue anonymously, remeber to check
             // for private profiles etc in your code so their not publicly
             // accessable to anoymous users! Anonymous == owner = viewer = appId = modId = 0
             // create token with 0 values, no gadget url, no domain and 0 duration
             $gadgetSigner = Config::get('security_token');
             return new $gadgetSigner(null, 0, SecurityToken::$ANONYMOUS, SecurityToken::$ANONYMOUS, 0, '', '', 0, Config::get('container_id'));
         } else {
             return null;
         }
     }
     $gadgetSigner = Config::get('security_token_signer');
     $gadgetSigner = new $gadgetSigner();
     return $gadgetSigner->createToken($token);
 }
 /**
  * Extracts the 'st' token from the GET or POST params and calls the
  * signer to validate the token
  *
  * @param SecurityTokenDecoder $signer the signer to use (configured in config.php)
  * @return SecurityToken An object representation of the token data.
  */
 public function extractAndValidateToken($signer)
 {
     if ($signer == null) {
         return null;
     }
     $token = BasicSecurityToken::getTokenStringFromRequest();
     return $this->validateToken($token, $signer);
 }
 /**
  * Peforms the actual http fetching of the data-pipelining requests, all social requests
  * are made to $_SERVER['HTTP_HOST'] (the virtual host name of this server) / (optional) web_prefix / social / rpc, and
  * the httpRequest's are made to $_SERVER['HTTP_HOST'] (the virtual host name of this server) / (optional) web_prefix / gadgets / makeRequest
  * both request types use the current security token ($_GET['st']) when performing the requests so they happen in the correct context
  *
  * @param array $requests
  * @param GadgetContext $context
  * @return array response
  */
 private static function performRequests($requests, GadgetContext $context)
 {
     $jsonRequests = array();
     $httpRequests = array();
     $decodedResponse = array();
     // Using the same gadget security token for all social & http requests so everything happens in the right context
     if (!BasicSecurityToken::getTokenStringFromRequest()) {
         throw new ExpressionException("No security token set, required for data-pipeling");
     }
     $securityToken = $_GET['st'];
     foreach ($requests as $request) {
         switch ($request['type']) {
             case 'os:DataRequest':
                 // Add to the social request batch
                 $id = $request['key'];
                 $method = $request['method'];
                 // remove our internal fields so we can use the remainder as params
                 unset($request['key']);
                 unset($request['method']);
                 unset($request['type']);
                 if (isset($request['fields'])) {
                     $request['fields'] = explode(',', $request['fields']);
                 }
                 $jsonRequests[] = array('method' => $method, 'id' => $id, 'params' => $request);
                 break;
             case 'os:HttpRequest':
                 $id = $request['key'];
                 $url = $request['href'];
                 $format = isset($request['format']) ? $request['format'] : 'json';
                 unset($request['key']);
                 unset($request['type']);
                 unset($request['href']);
                 $httpRequests[$url] = array('id' => $id, 'url' => $url, 'format' => $format, 'queryStr' => implode('&', $request));
                 break;
         }
     }
     if (count($jsonRequests)) {
         // perform social api requests
         $request = new RemoteContentRequest('http://' . $_SERVER['HTTP_HOST'] . Config::get('web_prefix') . '/rpc?st=' . urlencode($securityToken) . '&format=json', "Content-Type: application/json\n", json_encode($jsonRequests));
         $request->setMethod('POST');
         $remoteFetcherClass = Config::get('remote_content_fetcher');
         $remoteFetcher = new $remoteFetcherClass();
         $basicRemoteContent = new BasicRemoteContent($remoteFetcher);
         $response = $basicRemoteContent->fetch($request);
         $decodedResponse = json_decode($response->getResponseContent(), true);
     }
     if (count($httpRequests)) {
         $requestQueue = array();
         foreach ($httpRequests as $request) {
             $req = new RemoteContentRequest($_SERVER['HTTP_HOST'] . Config::get('web_prefix') . '/gadgets/makeRequest?url=' . urlencode($request['url']) . '&st=' . urlencode($securityToken) . (!empty($request['queryStr']) ? '&' . $request['queryStr'] : ''));
             $req->getOptions()->ignoreCache = $context->getIgnoreCache();
             $req->setNotSignedUri($request['url']);
             $requestQueue[] = $req;
         }
         $basicRemoteContent = new BasicRemoteContent();
         $resps = $basicRemoteContent->multiFetch($requestQueue);
         foreach ($resps as $response) {
             //FIXME: this isn't completely correct yet since this picks up the status code and headers
             // as they are returned by the makeRequest handler and not the ones from the original request
             $url = $response->getNotSignedUrl();
             $id = $httpRequests[$url]['id'];
             // strip out the UNPARSEABLE_CRUFT (see makeRequestHandler.php) on assigning the body
             $resp = json_decode(str_replace("throw 1; < don't be evil' >", '', $response->getResponseContent()), true);
             if (is_array($resp)) {
                 $statusCode = $response->getHttpCode();
                 $statusCodeMessage = $response->getHttpCodeMsg();
                 $headers = $response->getHeaders();
                 if (intval($statusCode) == 200) {
                     $content = $httpRequests[$url]['format'] == 'json' ? json_decode($resp[$url]['body'], true) : $resp[$url]['body'];
                     $toAdd = array('result' => array('content' => $content, 'status' => $statusCode, 'headers' => $headers));
                 } else {
                     $content = $resp[$url]['body'];
                     $toAdd = array('error' => array('code' => $statusCode, 'message' => $statusCodeMessage, 'result' => array('content' => $content, 'headers' => $headers)));
                 }
                 //$toAdd[$id] = array('id' => $id, 'result' => $httpRequests[$url]['format'] == 'json' ? json_decode($resp[$url]['body'], true) : $resp[$url]['body']);
                 $decodedResponse[] = array('id' => $id, 'result' => $toAdd);
             }
         }
     }
     return $decodedResponse;
 }
 /**
  * Function that handles the os: and osx: tags
  *
  * @param DOMNode $node
  * @return DOMNode or false
  */
 private function parseOsmlNode(DOMNode &$node)
 {
     $tagName = strtolower($node->tagName);
     if (!$this->checkIf($node)) {
         // If the OSML tag contains an if attribute and the expression evaluates to false
         // flag it for removal and don't process it
         return $node;
     }
     switch ($tagName) {
         /****** Control statements ******/
         case 'os:repeat':
             if (!$node->getAttribute('expression')) {
                 throw new ExpressionException("Invalid os:Repeat tag, missing expression attribute");
             }
             $expressions = array();
             preg_match_all('/(\\$\\{)(.*)(\\})/imsxU', $node->getAttribute('expression'), $expressions);
             $expression = $expressions[2][0];
             $expressionResult = ExpressionParser::evaluate($expression, $this->dataContext);
             if (!is_array($expressionResult)) {
                 throw new ExpressionException("Can't repeat on a singular var");
             }
             // Store the current 'Cur', index and count state, we might be in a nested repeat loop
             $previousCount = isset($this->dataContext['Context']['Count']) ? $this->dataContext['Context']['Count'] : null;
             $previousIndex = isset($this->dataContext['Context']['Index']) ? $this->dataContext['Context']['Index'] : null;
             $previousCur = $this->dataContext['Cur'];
             // Is a named var requested?
             $variableName = $node->getAttribute('var') ? trim($node->getAttribute('var')) : false;
             // For information on the loop context, see http://opensocial-resources.googlecode.com/svn/spec/0.9/OpenSocial-Templating.xml#rfc.section.10.1
             $this->dataContext['Context']['Count'] = count($expressionResult);
             foreach ($expressionResult as $index => $entry) {
                 if ($variableName) {
                     // this is cheating a little since we're not putting it on the top level scope, the variable resolver will check 'Cur' first though so myVar.Something will still resolve correctly
                     $this->dataContext['Cur'][$variableName] = $entry;
                 }
                 $this->dataContext['Cur'] = $entry;
                 $this->dataContext['Context']['Index'] = $index;
                 foreach ($node->childNodes as $childNode) {
                     $newNode = $childNode->cloneNode(true);
                     $newNode = $node->parentNode->insertBefore($newNode, $node);
                     $this->parseNode($newNode);
                 }
             }
             // Restore our previous data context state
             $this->dataContext['Cur'] = $previousCur;
             if ($previousCount) {
                 $this->dataContext['Context']['Count'] = $previousCount;
             } else {
                 unset($this->dataContext['Context']['Count']);
             }
             if ($previousIndex) {
                 $this->dataContext['Context']['Index'] = $previousIndex;
             } else {
                 unset($this->dataContext['Context']['Index']);
             }
             return $node;
             break;
         case 'os:if':
             $expressions = array();
             if (!$node->getAttribute('condition')) {
                 throw new ExpressionException("Invalid os:If tag, missing condition attribute");
             }
             preg_match_all('/(\\$\\{)(.*)(\\})/imsxU', $node->getAttribute('condition'), $expressions);
             if (!count($expressions[2])) {
                 throw new ExpressionException("Invalid os:If tag, missing condition expression");
             }
             $expression = $expressions[2][0];
             $expressionResult = ExpressionParser::evaluate($expression, $this->dataContext);
             if ($expressionResult) {
                 foreach ($node->childNodes as $childNode) {
                     $newNode = $childNode->cloneNode(true);
                     $this->parseNode($newNode);
                     $newNode = $node->parentNode->insertBefore($newNode, $node);
                 }
             }
             return $node;
             break;
             /****** OSML tags (os: name space) ******/
         /****** OSML tags (os: name space) ******/
         case 'os:name':
             $this->parseLibrary('os:Name', $node);
             return $node;
             break;
         case 'os:badge':
             $this->parseLibrary('os:Badge', $node);
             return $node;
             break;
         case 'os:peopleselector':
             $this->parseLibrary('os:PeopleSelector', $node);
             return $node;
             break;
         case 'os:html':
             if (!$node->getAttribute('code')) {
                 throw new ExpressionException("Invalid os:Html tag, missing code attribute");
             }
             preg_match_all('/(\\$\\{)(.*)(\\})/imsxU', $node->getAttribute('code'), $expressions);
             if (count($expressions[2])) {
                 $expression = $expressions[2][0];
                 $code = ExpressionParser::evaluate($expression, $this->dataContext);
             } else {
                 $code = $node->getAttribute('code');
             }
             $node->parentNode->insertBefore($node->ownerDocument->createTextNode($code), $node);
             return $node;
             break;
         case 'os:render':
             if (!($content = $node->getAttribute('content'))) {
                 throw new ExpressionException("os:Render missing attribute: content");
             }
             $content = $node->getAttribute('content');
             if (!isset($this->dataContext['_os_render_nodes'][$content])) {
                 throw new ExpressionException("os:Render, Unknown entry: " . htmlentities($content));
             }
             $nodes = $this->dataContext['_os_render_nodes'][$content];
             $ownerDocument = $node->ownerDocument;
             // Only parse the child nodes of the dom tree and not the (myapp:foo) top level element
             foreach ($nodes->childNodes as $childNode) {
                 $importedNode = $ownerDocument->importNode($childNode, true);
                 $importedNode = $node->parentNode->insertBefore($importedNode, $node);
                 $this->parseNode($importedNode);
             }
             return $node;
             break;
             /****** Extension - Tags ******/
         /****** Extension - Tags ******/
         case 'os:flash':
             // handle expressions
             $this->parseNodeAttributes($node);
             // read swf config from attributes
             $swfConfig = array('width' => '100px', 'height' => '100px', 'play' => 'immediate');
             foreach ($node->attributes as $attr) {
                 $swfConfig[$attr->name] = $attr->value;
             }
             // attach security token in the flash var
             $st = 'st=' . BasicSecurityToken::getTokenStringFromRequest();
             if (array_key_exists('flashvars', $swfConfig)) {
                 $swfConfig['flashvars'] = $swfConfig['flashvars'] . '&' . $st;
             } else {
                 $swfConfig['flashvars'] = $st;
             }
             // Restrict the content if sanitization is enabled
             $sanitizationEnabled = Config::get('sanitize_views');
             if ($sanitizationEnabled) {
                 $swfConfig['allowscriptaccess'] = 'never';
                 $swfConfig['swliveconnect'] = 'false';
                 $swfConfig['allownetworking'] = 'internal';
             }
             // Generate unique id for this swf
             $ALT_CONTENT_PREFIX = 'os_Flash_alt_';
             $altContentId = uniqid($ALT_CONTENT_PREFIX);
             // Create a div wrapper around the provided alternate content, and add the alternate content to the holder
             $altHolder = $node->ownerDocument->createElement('div');
             $altHolder->setAttribute('id', $altContentId);
             foreach ($node->childNodes as $childNode) {
                 $altHolder->appendChild($childNode);
             }
             $node->parentNode->insertBefore($altHolder, $node);
             // Create the call to swfobject in header
             $scriptCode = SwfConfig::buildSwfObjectCall($swfConfig, $altContentId);
             $scriptBlock = $node->ownerDocument->createElement('script');
             $scriptBlock->setAttribute('type', 'text/javascript');
             $node->parentNode->insertBefore($scriptBlock, $node);
             if ($swfConfig['play'] != 'immediate') {
                 // Add onclick handler to trigger call to swfobject
                 $scriptCode = "function {$altContentId}()\\{{$scriptCode};\\}";
                 $altHolder->setAttribute('onclick', "{$altContentId}()");
             }
             $scriptCodeNode = $node->ownerDocument->createTextNode($scriptCode);
             $scriptBlock->appendChild($scriptCodeNode);
             return $node;
             break;
         case 'os:var':
             // handle expressions
             $this->parseNodeAttributes($node);
             if (!($key = $node->getAttribute('key'))) {
                 throw new ExpressionException("os:Var missing attribute: key");
             }
             // either get value from attribute
             if (!($value = $node->getAttribute('value'))) {
                 $value = '';
             }
             // or from inner text of node
             if (!$value && $node->textContent) {
                 $value = $node->textContent;
             }
             // try to decode if the value is a valid json object
             $parsedValue = json_decode($value, true);
             if ($parsedValue) {
                 $value = $parsedValue;
             }
             $this->dataContext['Top'][$key] = $value;
             return $node;
             break;
         case 'osx:navigatetoapp':
             break;
         case 'osx:navigatetoperson':
             break;
     }
     return false;
 }