/** * New node is found. * @return bool */ public function nodeOpened(NMacroNode $node) { $this->used = TRUE; $node->isEmpty = FALSE; $node->openingCode = NPhpWriter::using($node) ->write('<?php if (NCacheMacro::createCache($netteCacheStorage, %var, $_g->caches, %node.array?)) { ?>', NStrings::random() ); }
/** * Process all {macros} and <tags/>. * @param string * @return string */ public function parse($s) { if (!NStrings::checkEncoding($s)) { throw new NLatteException('Template is not valid UTF-8 stream.'); } $s = str_replace("\r\n", "\n", $s); $this->templateId = NStrings::random(); $this->input = & $s; $this->offset = 0; $this->output = ''; $this->htmlNodes = $this->macroNodes = array(); foreach ($this->macroHandlers as $handler) { $handler->initialize($this); } $len = strlen($s); try { while ($this->offset < $len) { $matches = $this->{"context".$this->context[0]}(); if (!$matches) { // EOF break; } elseif (!empty($matches['comment'])) { // {* *} } elseif (!empty($matches['macro'])) { // {macro} list($macroName, $macroArgs, $macroModifiers) = $this->parseMacro($matches['macro']); $isRightmost = $this->offset >= $len || $this->input[$this->offset] === "\n"; $this->writeMacro($macroName, $macroArgs, $macroModifiers, $isRightmost); } else { // common behaviour $this->output .= $matches[0]; } } } catch (NLatteException $e) { if (!$e->sourceLine) { $e->sourceLine = $this->getLine(); } throw $e; } $this->output .= substr($this->input, $this->offset); foreach ($this->htmlNodes as $node) { if (!empty($node->attrs)) { throw new NLatteException("Missing end tag </$node->name> for macro-attribute " . self::N_PREFIX . implode(' and ' . self::N_PREFIX, array_keys($node->attrs)) . ".", 0, $this->getLine()); } } $prologs = $epilogs = ''; foreach ($this->macroHandlers as $handler) { $res = $handler->finalize(); $handlerName = get_class($handler); $prologs .= empty($res[0]) ? '' : "<?php\n// prolog $handlerName\n$res[0]\n?>"; $epilogs = (empty($res[1]) ? '' : "<?php\n// epilog $handlerName\n$res[1]\n?>") . $epilogs; } $this->output = ($prologs ? $prologs . "<?php\n//\n// main template\n//\n?>\n" : '') . $this->output . $epilogs; if ($this->macroNodes) { throw new NLatteException("There are unclosed macros.", 0, $this->getLine()); } return $this->output; }
/** * @return void */ public function __destruct() { if (self::$fixIE && isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE ') !== FALSE && in_array($this->code, array(400, 403, 404, 405, 406, 408, 409, 410, 500, 501, 505), TRUE) && $this->getHeader('Content-Type', 'text/html') === 'text/html' ) { echo NStrings::random(2e3, " \t\r\n"); // sends invisible garbage for IE self::$fixIE = FALSE; } }
/** * Returns session namespace provided to pass temporary data between redirects. * @return NSessionSection */ public function getFlashSession() { if (empty($this->params[self::FLASH_KEY])) { $this->params[self::FLASH_KEY] = NStrings::random(4); } return $this->getSession('Nette.Application.Flash/' . $this->params[self::FLASH_KEY]); }
/** * Returns encoded message. * @return string */ public function generateMessage() { $output = ''; $boundary = '--------' . NStrings::random(); foreach ($this->headers as $name => $value) { $output .= $name . ': ' . $this->getEncodedHeader($name); if ($this->parts && $name === 'Content-Type') { $output .= ';' . self::EOL . "\tboundary=\"$boundary\""; } $output .= self::EOL; } $output .= self::EOL; $body = (string) $this->body; if ($body !== '') { switch ($this->getEncoding()) { case self::ENCODING_QUOTED_PRINTABLE: $output .= function_exists('quoted_printable_encode') ? quoted_printable_encode($body) : self::encodeQuotedPrintable($body); break; case self::ENCODING_BASE64: $output .= rtrim(chunk_split(base64_encode($body), self::LINE_LENGTH, self::EOL)); break; case self::ENCODING_7BIT: $body = preg_replace('#[\x80-\xFF]+#', '', $body); // break intentionally omitted case self::ENCODING_8BIT: $body = str_replace(array("\x00", "\r"), '', $body); $body = str_replace("\n", self::EOL, $body); $output .= $body; break; default: throw new InvalidStateException('Unknown encoding.'); } } if ($this->parts) { if (substr($output, -strlen(self::EOL)) !== self::EOL) { $output .= self::EOL; } foreach ($this->parts as $part) { $output .= '--' . $boundary . self::EOL . $part->generateMessage() . self::EOL; } $output .= '--' . $boundary.'--'; } return $output; }
/** * Cross-Site Request Forgery (CSRF) form protection. * @param string * @param int * @return void */ public function addProtection($message = NULL, $timeout = NULL) { $session = $this->getSession()->getSection('Nette.Forms.Form/CSRF'); $key = "key$timeout"; if (isset($session->$key)) { $token = $session->$key; } else { $session->$key = $token = NStrings::random(); } $session->setExpiration($timeout, $key); $this[self::PROTECTOR_ID] = new NHiddenField($token); $this[self::PROTECTOR_ID]->addRule(self::PROTECTION, $message, $token); }
/** * Starts and initializes session data. * @throws InvalidStateException * @return void */ public function start() { if (self::$started) { return; } $this->configure($this->options); NDebugger::tryError(); session_start(); if (NDebugger::catchError($e)) { @session_write_close(); // this is needed throw new InvalidStateException('session_start(): ' . $e->getMessage(), 0, $e); } self::$started = TRUE; /* structure: __NF: Counter, BrowserKey, Data, Meta, Time DATA: section->variable = data META: section->variable = Timestamp, Browser, Version */ unset($_SESSION['__NT'], $_SESSION['__NS'], $_SESSION['__NM']); // old unused structures // initialize structures $nf = & $_SESSION['__NF']; if (empty($nf)) { // new session $nf = array('C' => 0); } else { $nf['C']++; } // session regenerate every 30 minutes $nfTime = & $nf['Time']; $time = time(); if ($time - $nfTime > self::REGENERATE_INTERVAL) { $nfTime = $time; $this->regenerationNeeded = TRUE; } // browser closing detection $browserKey = $this->request->getCookie('nette-browser'); if (!$browserKey) { $browserKey = NStrings::random(); } $browserClosed = !isset($nf['B']) || $nf['B'] !== $browserKey; $nf['B'] = $browserKey; // resend cookie $this->sendCookie(); // process meta metadata if (isset($nf['META'])) { $now = time(); // expire section variables foreach ($nf['META'] as $section => $metadata) { if (is_array($metadata)) { foreach ($metadata as $variable => $value) { if ((!empty($value['B']) && $browserClosed) || (!empty($value['T']) && $now > $value['T']) // whenBrowserIsClosed || Time || ($variable !== '' && is_object($nf['DATA'][$section][$variable]) && (isset($value['V']) ? $value['V'] : NULL) // Version !== NClassReflection::from($nf['DATA'][$section][$variable])->getAnnotation('serializationVersion')) ) { if ($variable === '') { // expire whole section unset($nf['META'][$section], $nf['DATA'][$section]); continue 2; } unset($nf['META'][$section][$variable], $nf['DATA'][$section][$variable]); } } } } } if ($this->regenerationNeeded) { session_regenerate_id(TRUE); $this->regenerationNeeded = FALSE; } register_shutdown_function(array($this, 'clean')); }
/** * Stores current request to session. * @param mixed optional expiration time * @return string key */ public function storeRequest($expiration = '+ 10 minutes') { $session = $this->session->getSection('Nette.Application/requests'); do { $key = NStrings::random(5); } while (isset($session[$key])); $session[$key] = end($this->requests); $session->setExpiration($expiration, $key); return $key; }
/** * Starts and initializes session data. * @throws InvalidStateException * @return void */ public function start() { if (self::$started) { return; } $this->configure($this->options); $id = & $_COOKIE[session_name()]; if (!is_string($id) || !preg_match('#^[0-9a-zA-Z,-]{22,128}\z#i', $id)) { unset($_COOKIE[session_name()]); } set_error_handler(create_function('$severity, $message', 'extract($GLOBALS[0]['.array_push($GLOBALS[0], array('error'=>& $error)).'-1], EXTR_REFS); // session_start returns FALSE on failure since PHP 5.3.0. if (($severity & error_reporting()) === $severity) { $error = $message; restore_error_handler(); } ')); session_start(); if (!$error) { restore_error_handler(); } $this->response->removeDuplicateCookies(); if ($error && !session_id()) { @session_write_close(); // this is needed throw new InvalidStateException("session_start(): $error"); } self::$started = TRUE; /* structure: __NF: Counter, BrowserKey, Data, Meta, Time DATA: section->variable = data META: section->variable = Timestamp, Browser, Version */ unset($_SESSION['__NT'], $_SESSION['__NS'], $_SESSION['__NM']); // old unused structures // initialize structures $nf = & $_SESSION['__NF']; @$nf['C']++; // regenerate empty session if (empty($nf['Time'])) { $nf['Time'] = time(); $this->regenerated = TRUE; } // browser closing detection $browserKey = $this->request->getCookie('nette-browser'); if (!$browserKey) { $browserKey = NStrings::random(); } $browserClosed = !isset($nf['B']) || $nf['B'] !== $browserKey; $nf['B'] = $browserKey; // resend cookie $this->sendCookie(); // process meta metadata if (isset($nf['META'])) { $now = time(); // expire section variables foreach ($nf['META'] as $section => $metadata) { if (is_array($metadata)) { foreach ($metadata as $variable => $value) { if ((!empty($value['B']) && $browserClosed) || (!empty($value['T']) && $now > $value['T']) // whenBrowserIsClosed || Time || (isset($nf['DATA'][$section][$variable]) && is_object($nf['DATA'][$section][$variable]) && (isset($value['V']) ? $value['V'] : NULL) // Version != NClassReflection::from($nf['DATA'][$section][$variable])->getAnnotation('serializationVersion')) // intentionally != ) { if ($variable === '') { // expire whole section unset($nf['META'][$section], $nf['DATA'][$section]); continue 2; } unset($nf['META'][$section][$variable], $nf['DATA'][$section][$variable]); } } } } } if ($this->regenerated) { $this->regenerated = FALSE; $this->regenerateId(); } register_shutdown_function(array($this, 'clean')); }
/** * Generates code for macro <tag n:attr> to the output. * @param string * @return void */ public function writeAttrsMacro($code, NHtmlNode $htmlNode) { $attrs = $htmlNode->macroAttrs; $left = $right = array(); $attrCode = ''; foreach ($this->macros as $name => $foo) { $attrName = NMacroNode::PREFIX_INNER . "-$name"; if (isset($attrs[$attrName])) { if ($htmlNode->closing) { $left[] = array("/$name", '', NMacroNode::PREFIX_INNER); } else { array_unshift($right, array($name, $attrs[$attrName], NMacroNode::PREFIX_INNER)); } unset($attrs[$attrName]); } } foreach (array_reverse($this->macros) as $name => $foo) { $attrName = NMacroNode::PREFIX_TAG . "-$name"; if (isset($attrs[$attrName])) { $left[] = array($name, $attrs[$attrName], NMacroNode::PREFIX_TAG); array_unshift($right, array("/$name", '', NMacroNode::PREFIX_TAG)); unset($attrs[$attrName]); } } foreach ($this->macros as $name => $foo) { if (isset($attrs[$name])) { if ($htmlNode->closing) { $right[] = array("/$name", '', NULL); } else { array_unshift($left, array($name, $attrs[$name], NULL)); } unset($attrs[$name]); } } if ($attrs) { throw new NCompileException("Unknown macro-attribute " . NParser::N_PREFIX . implode(' and ' . NParser::N_PREFIX, array_keys($attrs))); } if (!$htmlNode->closing) { $htmlNode->attrCode = & $this->attrCodes[$uniq = ' n:' . NStrings::random()]; $code = substr_replace($code, $uniq, ($tmp=strrpos($code, '/>')) ? $tmp : strrpos($code, '>'), 0); } foreach ($left as $item) { $node = $this->writeMacro($item[0], $item[1], NULL, NULL, $htmlNode, $item[2]); if ($node->closing || $node->isEmpty) { $htmlNode->attrCode .= $node->attrCode; if ($node->isEmpty) { unset($htmlNode->macroAttrs[$node->name]); } } } $this->output .= $code; foreach ($right as $item) { $node = $this->writeMacro($item[0], $item[1], NULL, NULL, $htmlNode); if ($node->closing) { $htmlNode->attrCode .= $node->attrCode; } } if ($right && substr($this->output, -2) === '?>') { $this->output .= "\n"; } }
/** @return string */ private function getRandomId() { return '<' . NStrings::random() . '@' . (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : (isset($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : 'localhost')) . '>'; }