Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F14089249
D20974.id49974.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
31 KB
Referenced Files
None
Subscribers
None
D20974.id49974.diff
View Options
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -15,14 +15,6 @@
'AASTTree' => 'parser/aast/api/AASTTree.php',
'AbstractDirectedGraph' => 'utils/AbstractDirectedGraph.php',
'AbstractDirectedGraphTestCase' => 'utils/__tests__/AbstractDirectedGraphTestCase.php',
- 'AphrontHTTPHeaderParser' => 'aphront/headerparser/AphrontHTTPHeaderParser.php',
- 'AphrontHTTPHeaderParserTestCase' => 'aphront/headerparser/__tests__/AphrontHTTPHeaderParserTestCase.php',
- 'AphrontMultipartParser' => 'aphront/multipartparser/AphrontMultipartParser.php',
- 'AphrontMultipartParserTestCase' => 'aphront/multipartparser/__tests__/AphrontMultipartParserTestCase.php',
- 'AphrontMultipartPart' => 'aphront/multipartparser/AphrontMultipartPart.php',
- 'AphrontRequestStream' => 'aphront/requeststream/AphrontRequestStream.php',
- 'AphrontScopedUnguardedWriteCapability' => 'aphront/writeguard/AphrontScopedUnguardedWriteCapability.php',
- 'AphrontWriteGuard' => 'aphront/writeguard/AphrontWriteGuard.php',
'BaseHTTPFuture' => 'future/http/BaseHTTPFuture.php',
'CaseInsensitiveArray' => 'utils/CaseInsensitiveArray.php',
'CaseInsensitiveArrayTestCase' => 'utils/__tests__/CaseInsensitiveArrayTestCase.php',
@@ -529,14 +521,6 @@
'AASTTree' => 'Phobject',
'AbstractDirectedGraph' => 'Phobject',
'AbstractDirectedGraphTestCase' => 'PhutilTestCase',
- 'AphrontHTTPHeaderParser' => 'Phobject',
- 'AphrontHTTPHeaderParserTestCase' => 'PhutilTestCase',
- 'AphrontMultipartParser' => 'Phobject',
- 'AphrontMultipartParserTestCase' => 'PhutilTestCase',
- 'AphrontMultipartPart' => 'Phobject',
- 'AphrontRequestStream' => 'Phobject',
- 'AphrontScopedUnguardedWriteCapability' => 'Phobject',
- 'AphrontWriteGuard' => 'Phobject',
'BaseHTTPFuture' => 'Future',
'CaseInsensitiveArray' => 'PhutilArray',
'CaseInsensitiveArrayTestCase' => 'PhutilTestCase',
diff --git a/src/aphront/headerparser/AphrontHTTPHeaderParser.php b/src/aphront/headerparser/AphrontHTTPHeaderParser.php
deleted file mode 100644
--- a/src/aphront/headerparser/AphrontHTTPHeaderParser.php
+++ /dev/null
@@ -1,150 +0,0 @@
-<?php
-
-final class AphrontHTTPHeaderParser extends Phobject {
-
- private $name;
- private $content;
- private $pairs;
-
- public function parseRawHeader($raw_header) {
- $this->name = null;
- $this->content = null;
-
- $parts = explode(':', $raw_header, 2);
- $this->name = trim($parts[0]);
- if (count($parts) > 1) {
- $this->content = trim($parts[1]);
- }
-
- $this->pairs = null;
-
- return $this;
- }
-
- public function getHeaderName() {
- $this->requireParse();
- return $this->name;
- }
-
- public function getHeaderContent() {
- $this->requireParse();
- return $this->content;
- }
-
- public function getHeaderContentAsPairs() {
- $content = $this->getHeaderContent();
-
-
- $state = 'prekey';
- $length = strlen($content);
-
- $pair_name = null;
- $pair_value = null;
-
- $pairs = array();
- $ii = 0;
- while ($ii < $length) {
- $c = $content[$ii];
-
- switch ($state) {
- case 'prekey';
- // We're eating space in front of a key.
- if ($c == ' ') {
- $ii++;
- break;
- }
- $pair_name = '';
- $state = 'key';
- break;
- case 'key';
- // We're parsing a key name until we find "=" or ";".
- if ($c == ';') {
- $state = 'done';
- break;
- }
-
- if ($c == '=') {
- $ii++;
- $state = 'value';
- break;
- }
-
- $ii++;
- $pair_name .= $c;
- break;
- case 'value':
- // We found an "=", so now figure out if the value is quoted
- // or not.
- if ($c == '"') {
- $ii++;
- $state = 'quoted';
- break;
- }
- $state = 'unquoted';
- break;
- case 'quoted':
- // We're in a quoted string, parse until we find the closing quote.
- if ($c == '"') {
- $ii++;
- $state = 'done';
- break;
- }
-
- $ii++;
- $pair_value .= $c;
- break;
- case 'unquoted':
- // We're in an unquoted string, parse until we find a space or a
- // semicolon.
- if ($c == ' ' || $c == ';') {
- $state = 'done';
- break;
- }
- $ii++;
- $pair_value .= $c;
- break;
- case 'done':
- // We parsed something, so eat any trailing whitespace and semicolons
- // and look for a new value.
- if ($c == ' ' || $c == ';') {
- $ii++;
- break;
- }
-
- $pairs[] = array(
- $pair_name,
- $pair_value,
- );
-
- $pair_name = null;
- $pair_value = null;
-
- $state = 'prekey';
- break;
- }
- }
-
- if ($state == 'quoted') {
- throw new Exception(
- pht(
- 'Header has unterminated double quote for key "%s".',
- $pair_name));
- }
-
- if ($pair_name !== null) {
- $pairs[] = array(
- $pair_name,
- $pair_value,
- );
- }
-
- return $pairs;
- }
-
- private function requireParse() {
- if ($this->name === null) {
- throw new PhutilInvalidStateException('parseRawHeader');
- }
- }
-
-}
diff --git a/src/aphront/headerparser/__tests__/AphrontHTTPHeaderParserTestCase.php b/src/aphront/headerparser/__tests__/AphrontHTTPHeaderParserTestCase.php
deleted file mode 100644
--- a/src/aphront/headerparser/__tests__/AphrontHTTPHeaderParserTestCase.php
+++ /dev/null
@@ -1,108 +0,0 @@
-<?php
-
-final class AphrontHTTPHeaderParserTestCase extends PhutilTestCase {
-
- public function testHeaderParser() {
- $cases = array(
- array(
- 'Key: x; y; z',
- 'Key',
- 'x; y; z',
- array(
- array('x', null),
- array('y', null),
- array('z', null),
- ),
- ),
- array(
- 'Content-Disposition: form-data; name="label"',
- 'Content-Disposition',
- 'form-data; name="label"',
- array(
- array('form-data', null),
- array('name', 'label'),
- ),
- ),
- array(
- 'Content-Type: multipart/form-data; charset=utf-8',
- 'Content-Type',
- 'multipart/form-data; charset=utf-8',
- array(
- array('multipart/form-data', null),
- array('charset', 'utf-8'),
- ),
- ),
- array(
- 'Content-Type: application/octet-stream; charset="ut',
- 'Content-Type',
- 'application/octet-stream; charset="ut',
- false,
- ),
- array(
- 'Content-Type: multipart/form-data; boundary=ABCDEFG',
- 'Content-Type',
- 'multipart/form-data; boundary=ABCDEFG',
- array(
- array('multipart/form-data', null),
- array('boundary', 'ABCDEFG'),
- ),
- ),
- array(
- 'Content-Type: multipart/form-data; boundary="ABCDEFG"',
- 'Content-Type',
- 'multipart/form-data; boundary="ABCDEFG"',
- array(
- array('multipart/form-data', null),
- array('boundary', 'ABCDEFG'),
- ),
- ),
- );
-
- foreach ($cases as $case) {
- $input = $case[0];
- $expect_name = $case[1];
- $expect_content = $case[2];
-
- $parser = id(new AphrontHTTPHeaderParser())
- ->parseRawHeader($input);
-
- $actual_name = $parser->getHeaderName();
- $actual_content = $parser->getHeaderContent();
-
- $this->assertEqual(
- $expect_name,
- $actual_name,
- pht('Header name for: %s', $input));
-
- $this->assertEqual(
- $expect_content,
- $actual_content,
- pht('Header content for: %s', $input));
-
- if (isset($case[3])) {
- $expect_pairs = $case[3];
-
- $caught = null;
- try {
- $actual_pairs = $parser->getHeaderContentAsPairs();
- } catch (Exception $ex) {
- $caught = $ex;
- }
-
- if ($expect_pairs === false) {
- $this->assertEqual(
- true,
- ($caught instanceof Exception),
- pht('Expect exception for header pairs of: %s', $input));
- } else {
- $this->assertEqual(
- $expect_pairs,
- $actual_pairs,
- pht('Header pairs for: %s', $input));
- }
- }
- }
- }
-
-
-}
diff --git a/src/aphront/multipartparser/AphrontMultipartParser.php b/src/aphront/multipartparser/AphrontMultipartParser.php
deleted file mode 100644
--- a/src/aphront/multipartparser/AphrontMultipartParser.php
+++ /dev/null
@@ -1,249 +0,0 @@
-<?php
-
-final class AphrontMultipartParser extends Phobject {
-
- private $contentType;
- private $boundary;
-
- private $buffer;
- private $body;
- private $state;
-
- private $part;
- private $parts;
-
- public function setContentType($content_type) {
- $this->contentType = $content_type;
- return $this;
- }
-
- public function getContentType() {
- return $this->contentType;
- }
-
- public function beginParse() {
- $content_type = $this->getContentType();
- if ($content_type === null) {
- throw new PhutilInvalidStateException('setContentType');
- }
-
- if (!preg_match('(^multipart/form-data)', $content_type)) {
- throw new Exception(
- pht(
- 'Expected "multipart/form-data" content type when executing a '.
- 'multipart body read.'));
- }
-
- $type_parts = preg_split('(\s*;\s*)', $content_type);
- $boundary = null;
- foreach ($type_parts as $type_part) {
- $matches = null;
- if (preg_match('(^boundary=(.*))', $type_part, $matches)) {
- $boundary = $matches[1];
- break;
- }
- }
-
- if ($boundary === null) {
- throw new Exception(
- pht('Received "multipart/form-data" request with no "boundary".'));
- }
-
- $this->parts = array();
- $this->part = null;
-
- $this->buffer = '';
- $this->boundary = $boundary;
-
- // We're looking for a (usually empty) body before the first boundary.
- $this->state = 'bodynewline';
- }
-
- public function continueParse($bytes) {
- $this->buffer .= $bytes;
-
- $continue = true;
- while ($continue) {
- switch ($this->state) {
- case 'endboundary':
- // We've just parsed a boundary. Next, we expect either "--" (which
- // indicates we've reached the end of the parts) or "\r\n" (which
- // indicates we should read the headers for the next part).
-
- if (strlen($this->buffer) < 2) {
- // We don't have enough bytes yet, so wait for more.
- $continue = false;
- break;
- }
-
- if (!strncmp($this->buffer, '--', 2)) {
- // This is "--" after a boundary, so we're done. We'll read the
- // rest of the body (the "epilogue") and discard it.
- $this->buffer = substr($this->buffer, 2);
- $this->state = 'epilogue';
-
- $this->part = null;
- break;
- }
-
- if (!strncmp($this->buffer, "\r\n", 2)) {
- // This is "\r\n" after a boundary, so we're going to going to
- // read the headers for a part.
- $this->buffer = substr($this->buffer, 2);
- $this->state = 'header';
-
- // Create the object to hold the part we're about to read.
- $part = new AphrontMultipartPart();
- $this->parts[] = $part;
- $this->part = $part;
- break;
- }
-
- throw new Exception(
- pht('Expected "\r\n" or "--" after multipart data boundary.'));
- case 'header':
- // We've just parsed a boundary, followed by "\r\n". We are going
- // to read the headers for this part. They are in the form of HTTP
- // headers and terminated by "\r\n". The section is terminated by
- // a line with no header on it.
-
- if (strlen($this->buffer) < 2) {
- // We don't have enough data to find a "\r\n", so wait for more.
- $continue = false;
- break;
- }
-
- if (!strncmp("\r\n", $this->buffer, 2)) {
- // This line immediately began "\r\n", so we're done with parsing
- // headers. Start parsing the body.
- $this->buffer = substr($this->buffer, 2);
- $this->state = 'body';
- break;
- }
-
- // This is an actual header, so look for the end of it.
- $header_len = strpos($this->buffer, "\r\n");
- if ($header_len === false) {
- // We don't have a full header yet, so wait for more data.
- $continue = false;
- break;
- }
-
- $header_buf = substr($this->buffer, 0, $header_len);
- $this->part->appendRawHeader($header_buf);
-
- $this->buffer = substr($this->buffer, $header_len + 2);
- break;
- case 'body':
- // We've parsed a boundary and headers, and are parsing the data for
- // this part. The data is terminated by "\r\n--", then the boundary.
-
- // We'll look for "\r\n", then switch to the "bodynewline" state if
- // we find it.
-
- $marker = "\r";
- $marker_pos = strpos($this->buffer, $marker);
-
- if ($marker_pos === false) {
- // There's no "\r" anywhere in the buffer, so we can just read it
- // as provided. Then, since we read all the data, we're done until
- // we get more.
-
- // Note that if we're in the preamble, we won't have a "part"
- // object and will just discard the data.
- if ($this->part) {
- $this->part->appendData($this->buffer);
- }
- $this->buffer = '';
- $continue = false;
- break;
- }
-
- if ($marker_pos > 0) {
- // If there are bytes before the "\r",
- if ($this->part) {
- $this->part->appendData(substr($this->buffer, 0, $marker_pos));
- }
- $this->buffer = substr($this->buffer, $marker_pos);
- }
-
- $expect = "\r\n";
- $expect_len = strlen($expect);
- if (strlen($this->buffer) < $expect_len) {
- // We don't have enough bytes yet to know if this is "\r\n"
- // or not.
- $continue = false;
- break;
- }
-
- if (strncmp($this->buffer, $expect, $expect_len)) {
- // The next two bytes aren't "\r\n", so eat them and go looking
- // for more newlines.
- if ($this->part) {
- $this->part->appendData(substr($this->buffer, 0, $expect_len));
- }
- $this->buffer = substr($this->buffer, $expect_len);
- break;
- }
-
- // Eat the "\r\n".
- $this->buffer = substr($this->buffer, $expect_len);
- $this->state = 'bodynewline';
- break;
- case 'bodynewline':
- // We've parsed a newline in a body, or we just started parsing the
- // request. In either case, we're looking for "--", then the boundary.
- // If we find it, this section is done. If we don't, we consume the
- // bytes and move on.
-
- $expect = '--'.$this->boundary;
- $expect_len = strlen($expect);
-
- if (strlen($this->buffer) < $expect_len) {
- // We don't have enough bytes yet, so wait for more.
- $continue = false;
- break;
- }
-
- if (strncmp($this->buffer, $expect, $expect_len)) {
- // This wasn't the boundary, so return to the "body" state and
- // consume it. (But first, we need to append the "\r\n" which we
- // ate earlier.)
- if ($this->part) {
- $this->part->appendData("\r\n");
- }
- $this->state = 'body';
- break;
- }
-
- // This is the boundary, so toss it and move on.
- $this->buffer = substr($this->buffer, $expect_len);
- $this->state = 'endboundary';
- break;
- case 'epilogue':
- // We just discard any epilogue.
- $this->buffer = '';
- $continue = false;
- break;
- default:
- throw new Exception(
- pht(
- 'Unknown parser state "%s".\n',
- $this->state));
- }
- }
- }
-
- public function endParse() {
- if ($this->state !== 'epilogue') {
- throw new Exception(
- pht(
- 'Expected "multipart/form-data" parse to end '.
- 'in state "epilogue".'));
- }
-
- return $this->parts;
- }
-
-
-}
diff --git a/src/aphront/multipartparser/AphrontMultipartPart.php b/src/aphront/multipartparser/AphrontMultipartPart.php
deleted file mode 100644
--- a/src/aphront/multipartparser/AphrontMultipartPart.php
+++ /dev/null
@@ -1,96 +0,0 @@
-<?php
-
-final class AphrontMultipartPart extends Phobject {
-
- private $headers = array();
- private $value = '';
-
- private $name;
- private $filename;
- private $tempFile;
- private $byteSize = 0;
-
- public function appendRawHeader($bytes) {
- $parser = id(new AphrontHTTPHeaderParser())
- ->parseRawHeader($bytes);
-
- $header_name = $parser->getHeaderName();
-
- $this->headers[] = array(
- $header_name,
- $parser->getHeaderContent(),
- );
-
- if (strtolower($header_name) === 'content-disposition') {
- $pairs = $parser->getHeaderContentAsPairs();
- foreach ($pairs as $pair) {
- list($key, $value) = $pair;
- switch ($key) {
- case 'filename':
- $this->filename = $value;
- break;
- case 'name':
- $this->name = $value;
- break;
- }
- }
- }
-
- return $this;
- }
-
- public function appendData($bytes) {
- $this->byteSize += strlen($bytes);
-
- if ($this->isVariable()) {
- $this->value .= $bytes;
- } else {
- if (!$this->tempFile) {
- $this->tempFile = new TempFile(getmypid().'.upload');
- }
- Filesystem::appendFile($this->tempFile, $bytes);
- }
-
- return $this;
- }
-
- public function isVariable() {
- return ($this->filename === null);
- }
-
- public function getName() {
- return $this->name;
- }
-
- public function getVariableValue() {
- if (!$this->isVariable()) {
- throw new Exception(pht('This part is not a variable!'));
- }
-
- return $this->value;
- }
-
- public function getPHPFileDictionary() {
- if (!$this->tempFile) {
- $this->appendData('');
- }
-
- $mime_type = 'application/octet-stream';
- foreach ($this->headers as $header) {
- list($name, $value) = $header;
- if (strtolower($name) == 'content-type') {
- $mime_type = $value;
- break;
- }
- }
-
- return array(
- 'name' => $this->filename,
- 'type' => $mime_type,
- 'tmp_name' => (string)$this->tempFile,
- 'error' => 0,
- 'size' => $this->byteSize,
- );
- }
-
-}
diff --git a/src/aphront/multipartparser/__tests__/AphrontMultipartParserTestCase.php b/src/aphront/multipartparser/__tests__/AphrontMultipartParserTestCase.php
deleted file mode 100644
--- a/src/aphront/multipartparser/__tests__/AphrontMultipartParserTestCase.php
+++ /dev/null
@@ -1,45 +0,0 @@
-<?php
-
-final class AphrontMultipartParserTestCase extends PhutilTestCase {
-
- public function testParser() {
- $map = array(
- array(
- 'data' => 'simple.txt',
- 'variables' => array(
- array('a', 'b'),
- ),
- ),
- );
-
- $data_dir = dirname(__FILE__).'/data/';
- foreach ($map as $test_case) {
- $data = Filesystem::readFile($data_dir.$test_case['data']);
- $data = str_replace("\n", "\r\n", $data);
-
- $parser = id(new AphrontMultipartParser())
- ->setContentType('multipart/form-data; boundary=ABCDEFG');
- $parser->beginParse();
- $parser->continueParse($data);
- $parts = $parser->endParse();
-
- $variables = array();
- foreach ($parts as $part) {
- if (!$part->isVariable()) {
- continue;
- }
-
- $variables[] = array(
- $part->getName(),
- $part->getVariableValue(),
- );
- }
-
- $expect_variables = idx($test_case, 'variables', array());
- $this->assertEqual($expect_variables, $variables);
- }
- }
-
-
-
-}
diff --git a/src/aphront/multipartparser/__tests__/data/simple.txt b/src/aphront/multipartparser/__tests__/data/simple.txt
deleted file mode 100644
--- a/src/aphront/multipartparser/__tests__/data/simple.txt
+++ /dev/null
@@ -1,5 +0,0 @@
---ABCDEFG
-Content-Disposition: form-data; name="a"
-
-b
---ABCDEFG--
diff --git a/src/aphront/requeststream/AphrontRequestStream.php b/src/aphront/requeststream/AphrontRequestStream.php
deleted file mode 100644
--- a/src/aphront/requeststream/AphrontRequestStream.php
+++ /dev/null
@@ -1,92 +0,0 @@
-<?php
-
-final class AphrontRequestStream extends Phobject {
-
- private $encoding;
- private $stream;
- private $closed;
- private $iterator;
-
- public function setEncoding($encoding) {
- $this->encoding = $encoding;
- return $this;
- }
-
- public function getEncoding() {
- return $this->encoding;
- }
-
- public function getIterator() {
- if (!$this->iterator) {
- $this->iterator = new PhutilStreamIterator($this->getStream());
- }
- return $this->iterator;
- }
-
- public function readData() {
- if (!$this->iterator) {
- $iterator = $this->getIterator();
- $iterator->rewind();
- } else {
- $iterator = $this->getIterator();
- }
-
- if (!$iterator->valid()) {
- return null;
- }
-
- $data = $iterator->current();
- $iterator->next();
-
- return $data;
- }
-
- private function getStream() {
- if (!$this->stream) {
- $this->stream = $this->newStream();
- }
-
- return $this->stream;
- }
-
- private function newStream() {
- $stream = fopen('php://input', 'rb');
- if (!$stream) {
- throw new Exception(
- pht(
- 'Failed to open stream "%s" for reading.',
- 'php://input'));
- }
-
- $encoding = $this->getEncoding();
- if ($encoding === 'gzip') {
- // This parameter is magic. Values 0-15 express a time/memory tradeoff,
- // but the largest value (15) corresponds to only 32KB of memory and
- // data encoded with a smaller window size than the one we pass can not
- // be decompressed. Always pass the maximum window size.
-
- // Additionally, you can add 16 (to enable gzip) or 32 (to enable both
- // gzip and zlib). Add 32 to support both.
- $zlib_window = 15 + 32;
-
- $ok = stream_filter_append(
- $stream,
- 'zlib.inflate',
- STREAM_FILTER_READ,
- array(
- 'window' => $zlib_window,
- ));
- if (!$ok) {
- throw new Exception(
- pht(
- 'Failed to append filter "%s" to input stream while processing '.
- 'a request with "%s" encoding.',
- 'zlib.inflate',
- $encoding));
- }
- }
-
- return $stream;
- }
-
-}
diff --git a/src/aphront/writeguard/AphrontScopedUnguardedWriteCapability.php b/src/aphront/writeguard/AphrontScopedUnguardedWriteCapability.php
deleted file mode 100644
--- a/src/aphront/writeguard/AphrontScopedUnguardedWriteCapability.php
+++ /dev/null
@@ -1,9 +0,0 @@
-<?php
-
-final class AphrontScopedUnguardedWriteCapability extends Phobject {
-
- public function __destruct() {
- AphrontWriteGuard::endUnguardedWrites();
- }
-
-}
diff --git a/src/aphront/writeguard/AphrontWriteGuard.php b/src/aphront/writeguard/AphrontWriteGuard.php
deleted file mode 100644
--- a/src/aphront/writeguard/AphrontWriteGuard.php
+++ /dev/null
@@ -1,267 +0,0 @@
-<?php
-
-/**
- * Guard writes against CSRF. The Aphront structure takes care of most of this
- * for you, you just need to call:
- *
- * AphrontWriteGuard::willWrite();
- *
- * ...before executing a write against any new kind of storage engine. MySQL
- * databases and the default file storage engines are already covered, but if
- * you introduce new types of datastores make sure their writes are guarded. If
- * you don't guard writes and make a mistake doing CSRF checks in a controller,
- * a CSRF vulnerability can escape undetected.
- *
- * If you need to execute writes on a page which doesn't have CSRF tokens (for
- * example, because you need to do logging), you can temporarily disable the
- * write guard by calling:
- *
- * AphrontWriteGuard::beginUnguardedWrites();
- * do_logging_write();
- * AphrontWriteGuard::endUnguardedWrites();
- *
- * This is dangerous, because it disables the backup layer of CSRF protection
- * this class provides. You should need this only very, very rarely.
- *
- * @task protect Protecting Writes
- * @task disable Disabling Protection
- * @task manage Managing Write Guards
- * @task internal Internals
- */
-final class AphrontWriteGuard extends Phobject {
-
- private static $instance;
- private static $allowUnguardedWrites = false;
-
- private $callback;
- private $allowDepth = 0;
-
-
-/* -( Managing Write Guards )---------------------------------------------- */
-
-
- /**
- * Construct a new write guard for a request. Only one write guard may be
- * active at a time. You must explicitly call @{method:dispose} when you are
- * done with a write guard:
- *
- * $guard = new AphrontWriteGuard($callback);
- * // ...
- * $guard->dispose();
- *
- * Normally, you do not need to manage guards yourself -- the Aphront stack
- * handles it for you.
- *
- * This class accepts a callback, which will be invoked when a write is
- * attempted. The callback should validate the presence of a CSRF token in
- * the request, or abort the request (e.g., by throwing an exception) if a
- * valid token isn't present.
- *
- * @param callable CSRF callback.
- * @return this
- * @task manage
- */
- public function __construct($callback) {
- if (self::$instance) {
- throw new Exception(
- pht(
- 'An %s already exists. Dispose of the previous guard '.
- 'before creating a new one.',
- __CLASS__));
- }
- if (self::$allowUnguardedWrites) {
- throw new Exception(
- pht(
- 'An %s is being created in a context which permits '.
- 'unguarded writes unconditionally. This is not allowed and '.
- 'indicates a serious error.',
- __CLASS__));
- }
- $this->callback = $callback;
- self::$instance = $this;
- }
-
-
- /**
- * Dispose of the active write guard. You must call this method when you are
- * done with a write guard. You do not normally need to call this yourself.
- *
- * @return void
- * @task manage
- */
- public function dispose() {
- if (!self::$instance) {
- throw new Exception(pht(
- 'Attempting to dispose of write guard, but no write guard is active!'));
- }
-
- if ($this->allowDepth > 0) {
- throw new Exception(
- pht(
- 'Imbalanced %s: more %s calls than %s calls.',
- __CLASS__,
- 'beginUnguardedWrites()',
- 'endUnguardedWrites()'));
- }
- self::$instance = null;
- }
-
-
- /**
- * Determine if there is an active write guard.
- *
- * @return bool
- * @task manage
- */
- public static function isGuardActive() {
- return (bool)self::$instance;
- }
-
- /**
- * Return on instance of AphrontWriteGuard if it's active, or null
- *
- * @return AphrontWriteGuard|null
- */
- public static function getInstance() {
- return self::$instance;
- }
-
-
-/* -( Protecting Writes )-------------------------------------------------- */
-
-
- /**
- * Declare intention to perform a write, validating that writes are allowed.
- * You should call this method before executing a write whenever you implement
- * a new storage engine where information can be permanently kept.
- *
- * Writes are permitted if:
- *
- * - The request has valid CSRF tokens.
- * - Unguarded writes have been temporarily enabled by a call to
- * @{method:beginUnguardedWrites}.
- * - All write guarding has been disabled with
- * @{method:allowDangerousUnguardedWrites}.
- *
- * If none of these conditions are true, this method will throw and prevent
- * the write.
- *
- * @return void
- * @task protect
- */
- public static function willWrite() {
- if (!self::$instance) {
- if (!self::$allowUnguardedWrites) {
- throw new Exception(
- pht(
- 'Unguarded write! There must be an active %s to perform writes.',
- __CLASS__));
- } else {
- // Unguarded writes are being allowed unconditionally.
- return;
- }
- }
-
- $instance = self::$instance;
- if ($instance->allowDepth == 0) {
- call_user_func($instance->callback);
- }
- }
-
-
-/* -( Disabling Write Protection )----------------------------------------- */
-
-
- /**
- * Enter a scope which permits unguarded writes. This works like
- * @{method:beginUnguardedWrites} but returns an object which will end
- * the unguarded write scope when its __destruct() method is called. This
- * is useful to more easily handle exceptions correctly in unguarded write
- * blocks:
- *
- * // Restores the guard even if do_logging() throws.
- * function unguarded_scope() {
- * $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
- * do_logging();
- * }
- *
- * @return AphrontScopedUnguardedWriteCapability Object which ends unguarded
- * writes when it leaves scope.
- * @task disable
- */
- public static function beginScopedUnguardedWrites() {
- self::beginUnguardedWrites();
- return new AphrontScopedUnguardedWriteCapability();
- }
-
-
- /**
- * Begin a block which permits unguarded writes. You should use this very
- * sparingly, and only for things like logging where CSRF is not a concern.
- *
- * You must pair every call to @{method:beginUnguardedWrites} with a call to
- * @{method:endUnguardedWrites}:
- *
- * AphrontWriteGuard::beginUnguardedWrites();
- * do_logging();
- * AphrontWriteGuard::endUnguardedWrites();
- *
- * @return void
- * @task disable
- */
- public static function beginUnguardedWrites() {
- if (!self::$instance) {
- return;
- }
- self::$instance->allowDepth++;
- }
-
- /**
- * Declare that you have finished performing unguarded writes. You must
- * call this exactly once for each call to @{method:beginUnguardedWrites}.
- *
- * @return void
- * @task disable
- */
- public static function endUnguardedWrites() {
- if (!self::$instance) {
- return;
- }
- if (self::$instance->allowDepth <= 0) {
- throw new Exception(
- pht(
- 'Imbalanced %s: more %s calls than %s calls.',
- __CLASS__,
- 'endUnguardedWrites()',
- 'beginUnguardedWrites()'));
- }
- self::$instance->allowDepth--;
- }
-
-
- /**
- * Allow execution of unguarded writes. This is ONLY appropriate for use in
- * script contexts or other contexts where you are guaranteed to never be
- * vulnerable to CSRF concerns. Calling this method is EXTREMELY DANGEROUS
- * if you do not understand the consequences.
- *
- * If you need to perform unguarded writes on an otherwise guarded workflow
- * which is vulnerable to CSRF, use @{method:beginUnguardedWrites}.
- *
- * @return void
- * @task disable
- */
- public static function allowDangerousUnguardedWrites($allow) {
- if (self::$instance) {
- throw new Exception(
- pht(
- 'You can not unconditionally disable %s by calling %s while a write '.
- 'guard is active. Use %s to temporarily allow unguarded writes.',
- __CLASS__,
- __FUNCTION__.'()',
- 'beginUnguardedWrites()'));
- }
- self::$allowUnguardedWrites = true;
- }
-
-}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Nov 25, 9:13 AM (20 h, 44 m)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6785537
Default Alt Text
D20974.id49974.diff (31 KB)
Attached To
Mode
D20974: Move lingering "Aphront" classes out of libphutil
Attached
Detach File
Event Timeline
Log In to Comment