diff --git a/src/Jackalope/Transport/Jackrabbit/Client.php b/src/Jackalope/Transport/Jackrabbit/Client.php index 33c1572a..ab69621c 100644 --- a/src/Jackalope/Transport/Jackrabbit/Client.php +++ b/src/Jackalope/Transport/Jackrabbit/Client.php @@ -41,6 +41,7 @@ use Jackalope\Lock\Lock; use Jackalope\FactoryInterface; use PHPCR\Util\ValueConverter; +use PHPCR\ValueFormatException; /** * Connection to one Jackrabbit server. @@ -1197,6 +1198,13 @@ private function storeProperty(Property $property) $path = $property->getPath(); $typeid = $property->getType(); $nativeValue = $property->getValueForStorage(); + if ($typeid === PropertyType::STRING) { + foreach ((array) $nativeValue as $string) { + if (!$this->isStringValid($string)) { + throw new ValueFormatException('Invalid character found in property "'.$property->getName().'". Are you passing a valid string?'); + } + } + } $value = $this->propertyToJsopString($property); if (!$value) { @@ -1211,6 +1219,23 @@ private function storeProperty(Property $property) } } + /** + * Checks for occurrence of invalid UTF characters, that can not occur in valid XML document. + * If occurrence is found, returns false, otherwise true. + * Invalid characters were taken from this list: http://en.wikipedia.org/wiki/Valid_characters_in_XML#XML_1.0 + * + * Uses regexp mentioned here: http://stackoverflow.com/a/961504 + * + * @param $string string value + * @return bool true if string is OK, false otherwise. + */ + protected function isStringValid($string) + { + $regex = '/[^\x{9}\x{a}\x{d}\x{20}-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}]+/u'; + + return (preg_match($regex, $string, $matches) === 0); + } + /** * {@inheritDoc} */ diff --git a/tests/Jackalope/Transport/Jackrabbit/ClientTest.php b/tests/Jackalope/Transport/Jackrabbit/ClientTest.php index e60f2bf3..5143d0f5 100644 --- a/tests/Jackalope/Transport/Jackrabbit/ClientTest.php +++ b/tests/Jackalope/Transport/Jackrabbit/ClientTest.php @@ -541,6 +541,43 @@ public function deleteNodesProvider() ), ); } + + /** + * @dataProvider provideTestOutOfRangeCharacters + */ + public function testOutOfRangeCharacterOccurrence($string, $isValid) + { + if (false === $isValid) { + $this->setExpectedException('PHPCR\ValueFormatException', 'Invalid character found in property "test". Are you passing a valid string?'); + } + + $root = $this->session->getNode('/'); + $article = $root->addNode('article'); + $article->setProperty('test', $string); + $this->session->save(); + } + + public function provideTestOutOfRangeCharacters() + { + return array( + array('This is valid too!'.$this->translateCharFromCode('\u0009'), true), + array('This is valid', true), + array($this->translateCharFromCode('\uD7FF'), true), + array('This is on the edge, but valid too.'. $this->translateCharFromCode('\uFFFD'), true), + array($this->translateCharFromCode('\u10000'), true), + array($this->translateCharFromCode('\u10FFFF'), true), + array($this->translateCharFromCode('\u0001'), false), + array($this->translateCharFromCode('\u0002'), false), + array($this->translateCharFromCode('\u0003'), false), + array($this->translateCharFromCode('\u0008'), false), + array($this->translateCharFromCode('\uFFFF'), false), + ); + } + + private function translateCharFromCode($char) + { + return json_decode('"'.$char.'"'); + } } class falseCredentialsMock implements \PHPCR\CredentialsInterface @@ -606,5 +643,4 @@ public function getJsopBody() { return $this->jsopBody; } - }