Skip to content

Commit 2cd3e4f

Browse files
committed
Abandon strtok_r() in favour of a hand rolled algorithm
strtok_r() can't handle empty elements :-/
1 parent bb31cd4 commit 2cd3e4f

File tree

2 files changed

+120
-19
lines changed

2 files changed

+120
-19
lines changed

src/bson.c

Lines changed: 54 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1289,7 +1289,7 @@ static bool php_phongo_bson_state_parse_type(zval* options, const char* name, ph
12891289
return retval;
12901290
} /* }}} */
12911291

1292-
static void field_path_map_element_set_info(php_phongo_field_path_map_element* element, php_phongo_bson_typemap_types type, zend_class_entry* ce TSRMLS_DC)
1292+
static void field_path_map_element_set_info(php_phongo_field_path_map_element* element, php_phongo_bson_typemap_types type, zend_class_entry* ce)
12931293
{
12941294
element->node_type = type;
12951295
element->node_ce = ce;
@@ -1316,44 +1316,69 @@ static php_phongo_field_path_map_element* field_path_map_element_alloc(void)
13161316
return tmp;
13171317
}
13181318

1319+
static void field_path_map_element_dtor(php_phongo_field_path_map_element* element)
1320+
{
1321+
php_phongo_field_path_free(element->entry);
1322+
efree(element);
1323+
}
1324+
13191325
bool php_phongo_bson_state_add_field_path(php_phongo_bson_typemap* map, char* field_path_original, php_phongo_bson_typemap_types type, zend_class_entry* ce TSRMLS_DC)
13201326
{
1321-
char* saveptr;
1322-
char* field_path_str = estrdup(field_path_original);
1323-
const char* element;
1327+
char* ptr = NULL;
1328+
char* segment_end = NULL;
13241329
php_phongo_field_path_map_element* field_path_map_element;
13251330

1331+
if (field_path_original[0] == '.') {
1332+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "A 'fieldPaths' key may not start with a '.'");
1333+
return false;
1334+
}
1335+
1336+
if (field_path_original[strlen(field_path_original) - 1] == '.') {
1337+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "A 'fieldPaths' key may not end with a '.'");
1338+
return false;
1339+
}
1340+
13261341
field_path_map_element = field_path_map_element_alloc();
1342+
ptr = field_path_original;
13271343

13281344
/* Loop over all the segments. A segment is delimited by a "." */
1329-
element = php_strtok_r(field_path_str, ".", &saveptr);
1330-
while (element) {
1331-
php_phongo_field_path_push(field_path_map_element->entry, element, PHONGO_FIELD_PATH_ITEM_NONE);
1332-
element = php_strtok_r(NULL, ".", &saveptr);
1345+
while ((segment_end = strchr(ptr, '.')) != NULL) {
1346+
char* tmp = NULL;
1347+
1348+
/* Bail out if we have an empty segment */
1349+
if (ptr == segment_end) {
1350+
field_path_map_element_dtor(field_path_map_element);
1351+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "A 'fieldPaths' key may not have an empty segment");
1352+
return false;
1353+
}
1354+
1355+
tmp = calloc(1, segment_end - ptr + 1);
1356+
memcpy(tmp, ptr, segment_end - ptr);
1357+
php_phongo_field_path_push(field_path_map_element->entry, tmp, PHONGO_FIELD_PATH_ITEM_NONE);
1358+
free(tmp);
1359+
1360+
ptr = segment_end + 1;
13331361
}
13341362

1335-
efree(field_path_str);
1363+
/* Add the last (or single) element */
1364+
php_phongo_field_path_push(field_path_map_element->entry, ptr, PHONGO_FIELD_PATH_ITEM_NONE);
13361365

1337-
field_path_map_element_set_info(field_path_map_element, type, ce TSRMLS_CC);
1366+
field_path_map_element_set_info(field_path_map_element, type, ce);
13381367
map_add_field_path_element(map, field_path_map_element);
13391368

13401369
return true;
13411370
}
13421371

1343-
static void field_path_map_element_dtor(php_phongo_field_path_map_element* element)
1344-
{
1345-
php_phongo_field_path_free(element->entry);
1346-
efree(element);
1347-
}
1348-
13491372
void php_phongo_bson_typemap_dtor(php_phongo_bson_typemap* map)
13501373
{
13511374
size_t i;
13521375

1353-
for (i = 0; i < map->field_paths.size; i++) {
1354-
field_path_map_element_dtor(map->field_paths.map[i]);
1376+
if (map->field_paths.map) {
1377+
for (i = 0; i < map->field_paths.size; i++) {
1378+
field_path_map_element_dtor(map->field_paths.map[i]);
1379+
}
1380+
efree(map->field_paths.map);
13551381
}
1356-
efree(map->field_paths.map);
13571382

13581383
map->field_paths.map = NULL;
13591384
}
@@ -1394,6 +1419,11 @@ bool php_phongo_bson_state_parse_fieldpaths(zval* typemap, php_phongo_bson_typem
13941419
return false;
13951420
}
13961421

1422+
if (strcmp(ZSTR_VAL(string_key), "") == 0) {
1423+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "The 'fieldPaths' element may not be an empty string");
1424+
return false;
1425+
}
1426+
13971427
if (!php_phongo_bson_state_parse_type(fieldpaths, ZSTR_VAL(string_key), &map_type, &map_ce TSRMLS_CC)) {
13981428
return false;
13991429
}
@@ -1425,6 +1455,11 @@ bool php_phongo_bson_state_parse_fieldpaths(zval* typemap, php_phongo_bson_typem
14251455
return false;
14261456
}
14271457

1458+
if (strcmp(string_key, "") == 0) {
1459+
phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, "The 'fieldPaths' element may not be an empty string");
1460+
return false;
1461+
}
1462+
14281463
if (!php_phongo_bson_state_parse_type(fieldpaths, string_key, &map_type, &map_ce TSRMLS_CC)) {
14291464
return false;
14301465
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
--TEST--
2+
Cursor::setTypeMap(): invalid fieldPaths keys
3+
--SKIPIF--
4+
<?php require __DIR__ . "/../utils/basic-skipif.inc"; ?>
5+
<?php NEEDS('STANDALONE'); CLEANUP(STANDALONE); ?>
6+
--FILE--
7+
<?php
8+
9+
require_once __DIR__ . "/../utils/basic.inc";
10+
11+
class MyDocument extends ArrayObject implements MongoDB\BSON\Unserializable
12+
{
13+
function bsonUnserialize(array $data)
14+
{
15+
parent::__construct($data, ArrayObject::ARRAY_AS_PROPS);
16+
}
17+
}
18+
19+
$fieldPaths = [
20+
['' => 'MyDocument'],
21+
['.foo' => 'MyDocument'],
22+
['...' => 'MyDocument'],
23+
['foo.' => 'MyDocument'],
24+
['foo..bar' => 'MyDocument'],
25+
];
26+
27+
$manager = new MongoDB\Driver\Manager(STANDALONE);
28+
$cursor = $manager->executeQuery(NS, new MongoDB\Driver\Query([]));
29+
30+
foreach ($fieldPaths as $fieldPath) {
31+
$typeMap = ['fieldPaths' => $fieldPath];
32+
33+
printf("Test typeMap: %s\n", json_encode($typeMap));
34+
35+
echo throws(function() use ($cursor, $typeMap) {
36+
$cursor->setTypeMap($typeMap);
37+
}, 'MongoDB\Driver\Exception\InvalidArgumentException'), "\n";
38+
39+
echo "\n";
40+
}
41+
42+
?>
43+
===DONE===
44+
<?php exit(0); ?>
45+
--EXPECT--
46+
Test typeMap: {"fieldPaths":{"":"MyDocument"}}
47+
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
48+
The 'fieldPaths' element may not be an empty string
49+
50+
Test typeMap: {"fieldPaths":{".foo":"MyDocument"}}
51+
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
52+
A 'fieldPaths' key may not start with a '.'
53+
54+
Test typeMap: {"fieldPaths":{"...":"MyDocument"}}
55+
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
56+
A 'fieldPaths' key may not start with a '.'
57+
58+
Test typeMap: {"fieldPaths":{"foo.":"MyDocument"}}
59+
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
60+
A 'fieldPaths' key may not end with a '.'
61+
62+
Test typeMap: {"fieldPaths":{"foo..bar":"MyDocument"}}
63+
OK: Got MongoDB\Driver\Exception\InvalidArgumentException
64+
A 'fieldPaths' key may not have an empty segment
65+
66+
===DONE===

0 commit comments

Comments
 (0)