@@ -927,6 +927,42 @@ static mongoc_uri_t *php_phongo_make_uri(const char *uri_string, bson_t *options
927927 return uri ;
928928} /* }}} */
929929
930+ static const char * php_phongo_bson_type_to_string (bson_type_t type ) /* {{{ */
931+ {
932+ switch (type ) {
933+ case BSON_TYPE_EOD : return "EOD" ;
934+ case BSON_TYPE_DOUBLE : return "double" ;
935+ case BSON_TYPE_UTF8 : return "string" ;
936+ case BSON_TYPE_DOCUMENT : return "document" ;
937+ case BSON_TYPE_ARRAY : return "array" ;
938+ case BSON_TYPE_BINARY : return "Binary" ;
939+ case BSON_TYPE_UNDEFINED : return "undefined" ;
940+ case BSON_TYPE_OID : return "ObjectID" ;
941+ case BSON_TYPE_BOOL : return "boolean" ;
942+ case BSON_TYPE_DATE_TIME : return "UTCDateTime" ;
943+ case BSON_TYPE_NULL : return "null" ;
944+ case BSON_TYPE_REGEX : return "Regex" ;
945+ case BSON_TYPE_DBPOINTER : return "DBPointer" ;
946+ case BSON_TYPE_CODE : return "Javascript" ;
947+ case BSON_TYPE_SYMBOL : return "symbol" ;
948+ case BSON_TYPE_CODEWSCOPE : return "Javascript with scope" ;
949+ case BSON_TYPE_INT32 : return "32-bit integer" ;
950+ case BSON_TYPE_TIMESTAMP : return "Timestamp" ;
951+ case BSON_TYPE_INT64 : return "64-bit integer" ;
952+ case BSON_TYPE_DECIMAL128 : return "Decimal128" ;
953+ case BSON_TYPE_MAXKEY : return "MaxKey" ;
954+ case BSON_TYPE_MINKEY : return "MinKey" ;
955+ default : return "unknown" ;
956+ }
957+ } /* }}} */
958+
959+ #define PHONGO_URI_INVALID_TYPE (iter , expected ) \
960+ phongo_throw_exception(PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC, \
961+ "Expected %s for \"%s\" URI option, %s given", \
962+ (expected), \
963+ bson_iter_key(&(iter)), \
964+ php_phongo_bson_type_to_string(bson_iter_type(&(iter))))
965+
930966static bool php_phongo_apply_options_to_uri (mongoc_uri_t * uri , bson_t * options TSRMLS_DC ) /* {{{ */
931967{
932968 bson_iter_t iter ;
@@ -949,12 +985,14 @@ static bool php_phongo_apply_options_to_uri(mongoc_uri_t *uri, bson_t *options T
949985 !strcasecmp (key , MONGOC_URI_SAFE ) ||
950986 !strcasecmp (key , MONGOC_URI_SLAVEOK ) ||
951987 !strcasecmp (key , MONGOC_URI_W ) ||
952- !strcasecmp (key , MONGOC_URI_WTIMEOUTMS ) ||
953- !strcasecmp (key , MONGOC_URI_APPNAME )) {
988+ !strcasecmp (key , MONGOC_URI_WTIMEOUTMS )) {
954989 continue ;
955990 }
956991
957992 if (mongoc_uri_option_is_bool (key )) {
993+ /* The option's type is not validated because bson_iter_as_bool() is
994+ * used to cast the value to a boolean. Validation may be introduced
995+ * in PHPC-990. */
958996 if (!mongoc_uri_set_option_as_bool (uri , key , bson_iter_as_bool (& iter ))) {
959997 phongo_throw_exception (PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC , "Failed to parse \"%s\" URI option" , key );
960998 return false;
@@ -963,7 +1001,12 @@ static bool php_phongo_apply_options_to_uri(mongoc_uri_t *uri, bson_t *options T
9631001 continue ;
9641002 }
9651003
966- if (mongoc_uri_option_is_int32 (key ) && BSON_ITER_HOLDS_INT32 (& iter )) {
1004+ if (mongoc_uri_option_is_int32 (key )) {
1005+ if (!BSON_ITER_HOLDS_INT32 (& iter )) {
1006+ PHONGO_URI_INVALID_TYPE (iter , "32-bit integer" );
1007+ return false;
1008+ }
1009+
9671010 if (!mongoc_uri_set_option_as_int32 (uri , key , bson_iter_int32 (& iter ))) {
9681011 phongo_throw_exception (PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC , "Failed to parse \"%s\" URI option" , key );
9691012 return false;
@@ -972,60 +1015,93 @@ static bool php_phongo_apply_options_to_uri(mongoc_uri_t *uri, bson_t *options T
9721015 continue ;
9731016 }
9741017
975- if (mongoc_uri_option_is_utf8 (key ) && BSON_ITER_HOLDS_UTF8 (& iter )) {
1018+ if (mongoc_uri_option_is_utf8 (key )) {
1019+ if (!BSON_ITER_HOLDS_UTF8 (& iter )) {
1020+ PHONGO_URI_INVALID_TYPE (iter , "string" );
1021+ return false;
1022+ }
1023+
9761024 if (!mongoc_uri_set_option_as_utf8 (uri , key , bson_iter_utf8 (& iter , NULL ))) {
977- phongo_throw_exception (PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC , "Failed to parse \"%s\" URI option" , key );
1025+ /* Assignment uses mongoc_uri_set_appname() for the "appname"
1026+ * option, which validates length in addition to UTF-8 encoding.
1027+ * For BC, we report the invalid string to the user. */
1028+ if (!strcasecmp (key , MONGOC_URI_APPNAME )) {
1029+ phongo_throw_exception (PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC , "Invalid appname value: '%s'" , bson_iter_utf8 (& iter , NULL ));
1030+ } else {
1031+ phongo_throw_exception (PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC , "Failed to parse \"%s\" URI option" , key );
1032+ }
9781033 return false;
9791034 }
9801035
9811036 continue ;
9821037 }
9831038
984- if (BSON_ITER_HOLDS_UTF8 (& iter )) {
985- const char * value = bson_iter_utf8 (& iter , NULL );
986-
987- if (!strcasecmp (key , "username" )) {
988- if (!mongoc_uri_set_username (uri , value )) {
989- phongo_throw_exception (PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC , "Failed to parse \"%s\" URI option" , key );
990- return false;
991- }
1039+ if (!strcasecmp (key , "username" )) {
1040+ if (!BSON_ITER_HOLDS_UTF8 (& iter )) {
1041+ PHONGO_URI_INVALID_TYPE (iter , "string" );
1042+ return false;
1043+ }
9921044
993- continue ;
1045+ if (!mongoc_uri_set_username (uri , bson_iter_utf8 (& iter , NULL ))) {
1046+ phongo_throw_exception (PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC , "Failed to parse \"%s\" URI option" , key );
1047+ return false;
9941048 }
9951049
996- if (!strcasecmp (key , "password" )) {
997- if (!mongoc_uri_set_password (uri , value )) {
998- phongo_throw_exception (PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC , "Failed to parse \"%s\" URI option" , key );
999- return false;
1000- }
1050+ continue ;
1051+ }
10011052
1002- continue ;
1053+ if (!strcasecmp (key , "password" )) {
1054+ if (!BSON_ITER_HOLDS_UTF8 (& iter )) {
1055+ PHONGO_URI_INVALID_TYPE (iter , "string" );
1056+ return false;
10031057 }
10041058
1005- if (!strcasecmp (key , MONGOC_URI_AUTHMECHANISM )) {
1006- if (!mongoc_uri_set_auth_mechanism (uri , value )) {
1007- phongo_throw_exception (PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC , "Failed to parse \"%s\" URI option" , key );
1008- return false;
1009- }
1059+ if (!mongoc_uri_set_password (uri , bson_iter_utf8 (& iter , NULL ))) {
1060+ phongo_throw_exception (PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC , "Failed to parse \"%s\" URI option" , key );
1061+ return false;
1062+ }
10101063
1011- continue ;
1064+ continue ;
1065+ }
1066+
1067+ if (!strcasecmp (key , MONGOC_URI_AUTHMECHANISM )) {
1068+ if (!BSON_ITER_HOLDS_UTF8 (& iter )) {
1069+ PHONGO_URI_INVALID_TYPE (iter , "string" );
1070+ return false;
10121071 }
10131072
1014- if (!strcasecmp (key , MONGOC_URI_AUTHSOURCE )) {
1015- if (!mongoc_uri_set_auth_source (uri , value )) {
1016- phongo_throw_exception (PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC , "Failed to parse \"%s\" URI option" , key );
1017- return false;
1018- }
1073+ if (!mongoc_uri_set_auth_mechanism (uri , bson_iter_utf8 (& iter , NULL ))) {
1074+ phongo_throw_exception (PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC , "Failed to parse \"%s\" URI option" , key );
1075+ return false;
1076+ }
10191077
1020- continue ;
1078+ continue ;
1079+ }
1080+
1081+ if (!strcasecmp (key , MONGOC_URI_AUTHSOURCE )) {
1082+ if (!BSON_ITER_HOLDS_UTF8 (& iter )) {
1083+ PHONGO_URI_INVALID_TYPE (iter , "string" );
1084+ return false;
10211085 }
1086+
1087+ if (!mongoc_uri_set_auth_source (uri , bson_iter_utf8 (& iter , NULL ))) {
1088+ phongo_throw_exception (PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC , "Failed to parse \"%s\" URI option" , key );
1089+ return false;
1090+ }
1091+
1092+ continue ;
10221093 }
10231094
1024- if (BSON_ITER_HOLDS_DOCUMENT ( & iter ) && !strcasecmp (key , MONGOC_URI_AUTHMECHANISMPROPERTIES )) {
1095+ if (!strcasecmp (key , MONGOC_URI_AUTHMECHANISMPROPERTIES )) {
10251096 bson_t properties ;
10261097 uint32_t len ;
10271098 const uint8_t * data ;
10281099
1100+ if (!BSON_ITER_HOLDS_DOCUMENT (& iter )) {
1101+ PHONGO_URI_INVALID_TYPE (iter , "array or object" );
1102+ return false;
1103+ }
1104+
10291105 bson_iter_document (& iter , & len , & data );
10301106
10311107 if (!bson_init_static (& properties , data , len )) {
@@ -1062,16 +1138,21 @@ static bool php_phongo_apply_rc_options_to_uri(mongoc_uri_t *uri, bson_t *option
10621138 return true;
10631139 }
10641140
1065- if (!bson_iter_init_find_case (& iter , options , "readconcernlevel" )) {
1141+ if (!bson_iter_init_find_case (& iter , options , MONGOC_URI_READCONCERNLEVEL )) {
10661142 return true;
10671143 }
10681144
10691145 new_rc = mongoc_read_concern_copy (old_rc );
10701146
1071- if (bson_iter_init_find_case (& iter , options , "readconcernlevel" ) && BSON_ITER_HOLDS_UTF8 (& iter )) {
1072- const char * str = bson_iter_utf8 (& iter , NULL );
1147+ if (bson_iter_init_find_case (& iter , options , MONGOC_URI_READCONCERNLEVEL )) {
1148+ if (!BSON_ITER_HOLDS_UTF8 (& iter )) {
1149+ PHONGO_URI_INVALID_TYPE (iter , "string" );
1150+ mongoc_read_concern_destroy (new_rc );
10731151
1074- mongoc_read_concern_set_level (new_rc , str );
1152+ return false;
1153+ }
1154+
1155+ mongoc_read_concern_set_level (new_rc , bson_iter_utf8 (& iter , NULL ));
10751156 }
10761157
10771158 mongoc_uri_set_read_concern (uri , new_rc );
@@ -1107,12 +1188,30 @@ static bool php_phongo_apply_rp_options_to_uri(mongoc_uri_t *uri, bson_t *option
11071188
11081189 new_rp = mongoc_read_prefs_copy (old_rp );
11091190
1110- if (bson_iter_init_find_case (& iter , options , MONGOC_URI_SLAVEOK ) && BSON_ITER_HOLDS_BOOL (& iter ) && bson_iter_bool (& iter )) {
1111- mongoc_read_prefs_set_mode (new_rp , MONGOC_READ_SECONDARY_PREFERRED );
1191+ if (bson_iter_init_find_case (& iter , options , MONGOC_URI_SLAVEOK )) {
1192+ if (!BSON_ITER_HOLDS_BOOL (& iter )) {
1193+ PHONGO_URI_INVALID_TYPE (iter , "boolean" );
1194+ mongoc_read_prefs_destroy (new_rp );
1195+
1196+ return false;
1197+ }
1198+
1199+ if (bson_iter_bool (& iter )) {
1200+ mongoc_read_prefs_set_mode (new_rp , MONGOC_READ_SECONDARY_PREFERRED );
1201+ }
11121202 }
11131203
1114- if (bson_iter_init_find_case (& iter , options , MONGOC_URI_READPREFERENCE ) && BSON_ITER_HOLDS_UTF8 (& iter )) {
1115- const char * str = bson_iter_utf8 (& iter , NULL );
1204+ if (bson_iter_init_find_case (& iter , options , MONGOC_URI_READPREFERENCE )) {
1205+ const char * str ;
1206+
1207+ if (!BSON_ITER_HOLDS_UTF8 (& iter )) {
1208+ PHONGO_URI_INVALID_TYPE (iter , "string" );
1209+ mongoc_read_prefs_destroy (new_rp );
1210+
1211+ return false;
1212+ }
1213+
1214+ str = bson_iter_utf8 (& iter , NULL );
11161215
11171216 if (0 == strcasecmp ("primary" , str )) {
11181217 mongoc_read_prefs_set_mode (new_rp , MONGOC_READ_PRIMARY );
@@ -1125,18 +1224,25 @@ static bool php_phongo_apply_rp_options_to_uri(mongoc_uri_t *uri, bson_t *option
11251224 } else if (0 == strcasecmp ("nearest" , str )) {
11261225 mongoc_read_prefs_set_mode (new_rp , MONGOC_READ_NEAREST );
11271226 } else {
1128- phongo_throw_exception (PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC , "Unsupported readPreference value: '%s'" , str );
1227+ phongo_throw_exception (PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC , "Unsupported %s value: '%s'" , bson_iter_key ( & iter ) , str );
11291228 mongoc_read_prefs_destroy (new_rp );
11301229
11311230 return false;
11321231 }
11331232 }
11341233
1135- if (bson_iter_init_find_case (& iter , options , MONGOC_URI_READPREFERENCETAGS ) && BSON_ITER_HOLDS_ARRAY ( & iter ) ) {
1234+ if (bson_iter_init_find_case (& iter , options , MONGOC_URI_READPREFERENCETAGS )) {
11361235 bson_t tags ;
11371236 uint32_t len ;
11381237 const uint8_t * data ;
11391238
1239+ if (!BSON_ITER_HOLDS_ARRAY (& iter )) {
1240+ PHONGO_URI_INVALID_TYPE (iter , "array" );
1241+ mongoc_read_prefs_destroy (new_rp );
1242+
1243+ return false;
1244+ }
1245+
11401246 bson_iter_array (& iter , & len , & data );
11411247
11421248 if (!bson_init_static (& tags , data , len )) {
@@ -1166,8 +1272,17 @@ static bool php_phongo_apply_rp_options_to_uri(mongoc_uri_t *uri, bson_t *option
11661272
11671273 /* Handle maxStalenessSeconds, and make sure it is not combined with primary
11681274 * readPreference */
1169- if (bson_iter_init_find_case (& iter , options , MONGOC_URI_MAXSTALENESSSECONDS ) && BSON_ITER_HOLDS_INT (& iter )) {
1170- int64_t max_staleness_seconds = bson_iter_as_int64 (& iter );
1275+ if (bson_iter_init_find_case (& iter , options , MONGOC_URI_MAXSTALENESSSECONDS )) {
1276+ int64_t max_staleness_seconds ;
1277+
1278+ if (!BSON_ITER_HOLDS_INT (& iter )) {
1279+ PHONGO_URI_INVALID_TYPE (iter , "integer" );
1280+ mongoc_read_prefs_destroy (new_rp );
1281+
1282+ return false;
1283+ }
1284+
1285+ max_staleness_seconds = bson_iter_as_int64 (& iter );
11711286
11721287 if (max_staleness_seconds != MONGOC_NO_MAX_STALENESS ) {
11731288
@@ -1240,15 +1355,36 @@ static bool php_phongo_apply_wc_options_to_uri(mongoc_uri_t *uri, bson_t *option
12401355
12411356 new_wc = mongoc_write_concern_copy (old_wc );
12421357
1243- if (bson_iter_init_find_case (& iter , options , MONGOC_URI_SAFE ) && BSON_ITER_HOLDS_BOOL (& iter )) {
1358+ if (bson_iter_init_find_case (& iter , options , MONGOC_URI_SAFE )) {
1359+ if (!BSON_ITER_HOLDS_BOOL (& iter )) {
1360+ PHONGO_URI_INVALID_TYPE (iter , "boolean" );
1361+ mongoc_write_concern_destroy (new_wc );
1362+
1363+ return false;
1364+ }
1365+
12441366 mongoc_write_concern_set_w (new_wc , bson_iter_bool (& iter ) ? 1 : MONGOC_WRITE_CONCERN_W_UNACKNOWLEDGED );
12451367 }
12461368
1247- if (bson_iter_init_find_case (& iter , options , MONGOC_URI_WTIMEOUTMS ) && BSON_ITER_HOLDS_INT32 (& iter )) {
1369+ if (bson_iter_init_find_case (& iter , options , MONGOC_URI_WTIMEOUTMS )) {
1370+ if (!BSON_ITER_HOLDS_INT32 (& iter )) {
1371+ PHONGO_URI_INVALID_TYPE (iter , "32-bit integer" );
1372+ mongoc_write_concern_destroy (new_wc );
1373+
1374+ return false;
1375+ }
1376+
12481377 wtimeoutms = bson_iter_int32 (& iter );
12491378 }
12501379
1251- if (bson_iter_init_find_case (& iter , options , MONGOC_URI_JOURNAL ) && BSON_ITER_HOLDS_BOOL (& iter )) {
1380+ if (bson_iter_init_find_case (& iter , options , MONGOC_URI_JOURNAL )) {
1381+ if (!BSON_ITER_HOLDS_BOOL (& iter )) {
1382+ PHONGO_URI_INVALID_TYPE (iter , "boolean" );
1383+ mongoc_write_concern_destroy (new_wc );
1384+
1385+ return false;
1386+ }
1387+
12521388 mongoc_write_concern_set_journal (new_wc , bson_iter_bool (& iter ));
12531389 }
12541390
@@ -1280,6 +1416,11 @@ static bool php_phongo_apply_wc_options_to_uri(mongoc_uri_t *uri, bson_t *option
12801416 } else {
12811417 mongoc_write_concern_set_wtag (new_wc , str );
12821418 }
1419+ } else {
1420+ PHONGO_URI_INVALID_TYPE (iter , "32-bit integer or string" );
1421+ mongoc_write_concern_destroy (new_wc );
1422+
1423+ return false;
12831424 }
12841425 }
12851426
@@ -1770,7 +1911,6 @@ void phongo_manager_init(php_phongo_manager_t *manager, const char *uri_string,
17701911 size_t hash_len = 0 ;
17711912 bson_t bson_options = BSON_INITIALIZER ;
17721913 mongoc_uri_t * uri = NULL ;
1773- bson_iter_t iter ;
17741914#ifdef MONGOC_ENABLE_SSL
17751915 mongoc_ssl_opt_t * ssl_opt = NULL ;
17761916#endif
@@ -1807,15 +1947,6 @@ void phongo_manager_init(php_phongo_manager_t *manager, const char *uri_string,
18071947 goto cleanup ;
18081948 }
18091949
1810- if (bson_iter_init_find_case (& iter , & bson_options , MONGOC_URI_APPNAME ) && BSON_ITER_HOLDS_UTF8 (& iter )) {
1811- const char * str = bson_iter_utf8 (& iter , NULL );
1812-
1813- if (!mongoc_uri_set_appname (uri , str )) {
1814- phongo_throw_exception (PHONGO_ERROR_INVALID_ARGUMENT TSRMLS_CC , "Invalid appname value: '%s'" , str );
1815- goto cleanup ;
1816- }
1817- }
1818-
18191950#ifdef MONGOC_ENABLE_SSL
18201951 /* Construct SSL options even if SSL is not enabled so that exceptions can
18211952 * be thrown for unsupported driver options. */
0 commit comments