Skip to content

Commit c0e3429

Browse files
committed
Implement ldap_modify_batch.
1 parent 0eff717 commit c0e3429

File tree

4 files changed

+588
-0
lines changed

4 files changed

+588
-0
lines changed

ext/ldap/ldap.c

Lines changed: 365 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,15 @@ PHP_MINIT_FUNCTION(ldap)
152152
REGISTER_LONG_CONSTANT("LDAP_DEREF_FINDING", LDAP_DEREF_FINDING, CONST_PERSISTENT | CONST_CS);
153153
REGISTER_LONG_CONSTANT("LDAP_DEREF_ALWAYS", LDAP_DEREF_ALWAYS, CONST_PERSISTENT | CONST_CS);
154154

155+
/* Constants to be used with ldap_modify_batch() */
156+
REGISTER_LONG_CONSTANT("LDAP_MODIFY_BATCH_ADD", LDAP_MODIFY_BATCH_ADD, CONST_PERSISTENT | CONST_CS);
157+
REGISTER_LONG_CONSTANT("LDAP_MODIFY_BATCH_REMOVE", LDAP_MODIFY_BATCH_REMOVE, CONST_PERSISTENT | CONST_CS);
158+
REGISTER_LONG_CONSTANT("LDAP_MODIFY_BATCH_REMOVE_ALL", LDAP_MODIFY_BATCH_REMOVE_ALL, CONST_PERSISTENT | CONST_CS);
159+
REGISTER_LONG_CONSTANT("LDAP_MODIFY_BATCH_REPLACE", LDAP_MODIFY_BATCH_REPLACE, CONST_PERSISTENT | CONST_CS);
160+
REGISTER_STRING_CONSTANT("LDAP_MODIFY_BATCH_ATTRIB", LDAP_MODIFY_BATCH_ATTRIB, CONST_PERSISTENT | CONST_CS);
161+
REGISTER_STRING_CONSTANT("LDAP_MODIFY_BATCH_MODTYPE", LDAP_MODIFY_BATCH_MODTYPE, CONST_PERSISTENT | CONST_CS);
162+
REGISTER_STRING_CONSTANT("LDAP_MODIFY_BATCH_VALUES", LDAP_MODIFY_BATCH_VALUES, CONST_PERSISTENT | CONST_CS);
163+
155164
#if (LDAP_API_VERSION > 2000) || HAVE_NSLDAP || HAVE_ORALDAP_10
156165
/* LDAP options */
157166
REGISTER_LONG_CONSTANT("LDAP_OPT_DEREF", LDAP_OPT_DEREF, CONST_PERSISTENT | CONST_CS);
@@ -1432,6 +1441,355 @@ PHP_FUNCTION(ldap_delete)
14321441
}
14331442
/* }}} */
14341443

1444+
/* {{{ _ldap_str_equal_to_const
1445+
*/
1446+
static int _ldap_str_equal_to_const(const char *str, uint str_len, const char *cstr)
1447+
{
1448+
int i;
1449+
1450+
if (strlen(cstr) != str_len)
1451+
return 0;
1452+
1453+
for (i = 0; i < str_len; ++i) {
1454+
if (str[i] != cstr[i]) {
1455+
return 0;
1456+
}
1457+
}
1458+
1459+
return 1;
1460+
}
1461+
/* }}} */
1462+
1463+
/* {{{ _ldap_strlen_max
1464+
*/
1465+
static int _ldap_strlen_max(const char *str, uint max_len)
1466+
{
1467+
int i;
1468+
1469+
for (i = 0; i < max_len; ++i) {
1470+
if (str[i] == '\0') {
1471+
return i;
1472+
}
1473+
}
1474+
1475+
return max_len;
1476+
}
1477+
/* }}} */
1478+
1479+
/* {{{ _ldap_hash_fetch
1480+
*/
1481+
static void _ldap_hash_fetch(zval *hashTbl, const char *key, zval **out)
1482+
{
1483+
zval **fetched;
1484+
if (zend_hash_find(Z_ARRVAL_P(hashTbl), key, strlen(key)+1, (void **) &fetched) == SUCCESS) {
1485+
*out = *fetched;
1486+
}
1487+
else {
1488+
*out = NULL;
1489+
}
1490+
}
1491+
/* }}} */
1492+
1493+
/* {{{ proto bool ldap_modify_batch(resource link, string dn, array modifs)
1494+
Perform multiple modifications as part of one operation */
1495+
PHP_FUNCTION(ldap_modify_batch)
1496+
{
1497+
ldap_linkdata *ld;
1498+
zval *link, *mods, *mod, *modinfo, *modval;
1499+
zval *attrib, *modtype, *vals;
1500+
zval **fetched;
1501+
char *dn;
1502+
int dn_len;
1503+
int i, j, k;
1504+
int num_mods, num_modprops, num_modvals;
1505+
LDAPMod **ldap_mods;
1506+
uint oper;
1507+
1508+
/*
1509+
$mods = array(
1510+
array(
1511+
"attrib" => "unicodePwd",
1512+
"modtype" => LDAP_MODIFY_BATCH_REMOVE,
1513+
"values" => array($oldpw)
1514+
),
1515+
array(
1516+
"attrib" => "unicodePwd",
1517+
"modtype" => LDAP_MODIFY_BATCH_ADD,
1518+
"values" => array($newpw)
1519+
),
1520+
array(
1521+
"attrib" => "userPrincipalName",
1522+
"modtype" => LDAP_MODIFY_BATCH_REPLACE,
1523+
"values" => array("janitor@corp.contoso.com")
1524+
),
1525+
array(
1526+
"attrib" => "userCert",
1527+
"modtype" => LDAP_MODIFY_BATCH_REMOVE_ALL
1528+
)
1529+
);
1530+
*/
1531+
1532+
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsa", &link, &dn, &dn_len, &mods) != SUCCESS) {
1533+
return;
1534+
}
1535+
1536+
ZEND_FETCH_RESOURCE(ld, ldap_linkdata *, &link, -1, "ldap link", le_link);
1537+
1538+
/* perform validation */
1539+
{
1540+
char *modkey;
1541+
uint modkeylen;
1542+
long modtype;
1543+
1544+
/* to store the wrongly-typed keys */
1545+
ulong tmpUlong;
1546+
1547+
/* make sure the DN contains no NUL bytes */
1548+
if (_ldap_strlen_max(dn, dn_len) != dn_len) {
1549+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "DN must not contain NUL bytes");
1550+
RETURN_FALSE;
1551+
}
1552+
1553+
/* make sure the top level is a normal array */
1554+
zend_hash_internal_pointer_reset(Z_ARRVAL_P(mods));
1555+
if (zend_hash_get_current_key_type(Z_ARRVAL_P(mods)) != HASH_KEY_IS_LONG) {
1556+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Modifications array must not be string-indexed");
1557+
RETURN_FALSE;
1558+
}
1559+
1560+
num_mods = zend_hash_num_elements(Z_ARRVAL_P(mods));
1561+
1562+
for (i = 0; i < num_mods; i++) {
1563+
/* is the numbering consecutive? */
1564+
if (zend_hash_index_find(Z_ARRVAL_P(mods), i, (void **) &fetched) != SUCCESS) {
1565+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Modifications array must have consecutive indices 0, 1, ...");
1566+
RETURN_FALSE;
1567+
}
1568+
mod = *fetched;
1569+
1570+
/* is it an array? */
1571+
if (Z_TYPE_P(mod) != IS_ARRAY) {
1572+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Each entry of modifications array must be an array itself");
1573+
RETURN_FALSE;
1574+
}
1575+
1576+
/* for the modification hashtable... */
1577+
zend_hash_internal_pointer_reset(Z_ARRVAL_P(mod));
1578+
num_modprops = zend_hash_num_elements(Z_ARRVAL_P(mod));
1579+
1580+
for (j = 0; j < num_modprops; j++) {
1581+
/* are the keys strings? */
1582+
if (zend_hash_get_current_key_ex(Z_ARRVAL_P(mod), &modkey, &modkeylen, &tmpUlong, 0, NULL) != HASH_KEY_IS_STRING) {
1583+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Each entry of modifications array must be string-indexed");
1584+
RETURN_FALSE;
1585+
}
1586+
1587+
/* modkeylen includes the terminating NUL byte; remove that */
1588+
--modkeylen;
1589+
1590+
/* is this a valid entry? */
1591+
if (
1592+
!_ldap_str_equal_to_const(modkey, modkeylen, LDAP_MODIFY_BATCH_ATTRIB) &&
1593+
!_ldap_str_equal_to_const(modkey, modkeylen, LDAP_MODIFY_BATCH_MODTYPE) &&
1594+
!_ldap_str_equal_to_const(modkey, modkeylen, LDAP_MODIFY_BATCH_VALUES)
1595+
) {
1596+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "The only allowed keys in entries of the modifications array are '" LDAP_MODIFY_BATCH_ATTRIB "', '" LDAP_MODIFY_BATCH_MODTYPE "' and '" LDAP_MODIFY_BATCH_VALUES "'");
1597+
RETURN_FALSE;
1598+
}
1599+
1600+
zend_hash_get_current_data(Z_ARRVAL_P(mod), (void **) &fetched);
1601+
modinfo = *fetched;
1602+
1603+
/* does the value type match the key? */
1604+
if (_ldap_str_equal_to_const(modkey, modkeylen, LDAP_MODIFY_BATCH_ATTRIB)) {
1605+
if (Z_TYPE_P(modinfo) != IS_STRING) {
1606+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "A '" LDAP_MODIFY_BATCH_ATTRIB "' value must be a string");
1607+
RETURN_FALSE;
1608+
}
1609+
1610+
if (Z_STRLEN_P(modinfo) != _ldap_strlen_max(Z_STRVAL_P(modinfo), Z_STRLEN_P(modinfo))) {
1611+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "A '" LDAP_MODIFY_BATCH_ATTRIB "' value must not contain NUL bytes");
1612+
RETURN_FALSE;
1613+
}
1614+
}
1615+
else if (_ldap_str_equal_to_const(modkey, modkeylen, LDAP_MODIFY_BATCH_MODTYPE)) {
1616+
if (Z_TYPE_P(modinfo) != IS_LONG) {
1617+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "A '" LDAP_MODIFY_BATCH_MODTYPE "' value must be a long");
1618+
RETURN_FALSE;
1619+
}
1620+
1621+
/* is the value in range? */
1622+
modtype = Z_LVAL_P(modinfo);
1623+
if (
1624+
modtype != LDAP_MODIFY_BATCH_ADD &&
1625+
modtype != LDAP_MODIFY_BATCH_REMOVE &&
1626+
modtype != LDAP_MODIFY_BATCH_REPLACE &&
1627+
modtype != LDAP_MODIFY_BATCH_REMOVE_ALL
1628+
) {
1629+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "The '" LDAP_MODIFY_BATCH_MODTYPE "' value must match one of the LDAP_MODIFY_BATCH_* constants");
1630+
RETURN_FALSE;
1631+
}
1632+
1633+
/* if it's REMOVE_ALL, there must not be a values array; otherwise, there must */
1634+
if (modtype == LDAP_MODIFY_BATCH_REMOVE_ALL) {
1635+
if (zend_hash_exists(Z_ARRVAL_P(mod), LDAP_MODIFY_BATCH_VALUES, strlen(LDAP_MODIFY_BATCH_VALUES) + 1)) {
1636+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "If '" LDAP_MODIFY_BATCH_MODTYPE "' is LDAP_MODIFY_BATCH_REMOVE_ALL, a '" LDAP_MODIFY_BATCH_VALUES "' array must not be provided");
1637+
RETURN_FALSE;
1638+
}
1639+
}
1640+
else {
1641+
if (!zend_hash_exists(Z_ARRVAL_P(mod), LDAP_MODIFY_BATCH_VALUES, strlen(LDAP_MODIFY_BATCH_VALUES) + 1)) {
1642+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "If '" LDAP_MODIFY_BATCH_MODTYPE "' is not LDAP_MODIFY_BATCH_REMOVE_ALL, a '" LDAP_MODIFY_BATCH_VALUES "' array must be provided");
1643+
RETURN_FALSE;
1644+
}
1645+
}
1646+
}
1647+
else if (_ldap_str_equal_to_const(modkey, modkeylen, LDAP_MODIFY_BATCH_VALUES)) {
1648+
if (Z_TYPE_P(modinfo) != IS_ARRAY) {
1649+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "A '" LDAP_MODIFY_BATCH_VALUES "' value must be an array");
1650+
RETURN_FALSE;
1651+
}
1652+
1653+
/* is the array not empty? */
1654+
zend_hash_internal_pointer_reset(Z_ARRVAL_P(modinfo));
1655+
num_modvals = zend_hash_num_elements(Z_ARRVAL_P(modinfo));
1656+
if (num_modvals == 0) {
1657+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "A '" LDAP_MODIFY_BATCH_VALUES "' array must have at least one element");
1658+
RETURN_FALSE;
1659+
}
1660+
1661+
/* are its keys integers? */
1662+
if (zend_hash_get_current_key_type(Z_ARRVAL_P(modinfo)) != HASH_KEY_IS_LONG) {
1663+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "A '" LDAP_MODIFY_BATCH_VALUES "' array must not be string-indexed");
1664+
RETURN_FALSE;
1665+
}
1666+
1667+
/* are the keys consecutive? */
1668+
for (k = 0; k < num_modvals; k++) {
1669+
if (zend_hash_index_find(Z_ARRVAL_P(modinfo), k, (void **) &fetched) != SUCCESS) {
1670+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "A '" LDAP_MODIFY_BATCH_VALUES "' array must have consecutive indices 0, 1, ...");
1671+
RETURN_FALSE;
1672+
}
1673+
modval = *fetched;
1674+
1675+
/* is the data element a string? */
1676+
if (Z_TYPE_P(modval) != IS_STRING) {
1677+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Each element of a '" LDAP_MODIFY_BATCH_VALUES "' array must be a string");
1678+
RETURN_FALSE;
1679+
}
1680+
}
1681+
}
1682+
1683+
zend_hash_move_forward(Z_ARRVAL_P(mod));
1684+
}
1685+
}
1686+
}
1687+
/* validation was successful */
1688+
1689+
/* allocate array of modifications */
1690+
ldap_mods = safe_emalloc((num_mods+1), sizeof(LDAPMod *), 0);
1691+
1692+
/* for each modification */
1693+
for (i = 0; i < num_mods; i++) {
1694+
/* allocate the modification struct */
1695+
ldap_mods[i] = safe_emalloc(1, sizeof(LDAPMod), 0);
1696+
1697+
/* fetch the relevant data */
1698+
zend_hash_index_find(Z_ARRVAL_P(mods), i, (void **) &fetched);
1699+
mod = *fetched;
1700+
1701+
_ldap_hash_fetch(mod, LDAP_MODIFY_BATCH_ATTRIB, &attrib);
1702+
_ldap_hash_fetch(mod, LDAP_MODIFY_BATCH_MODTYPE, &modtype);
1703+
_ldap_hash_fetch(mod, LDAP_MODIFY_BATCH_VALUES, &vals);
1704+
1705+
/* map the modification type */
1706+
switch (Z_LVAL_P(modtype)) {
1707+
case LDAP_MODIFY_BATCH_ADD:
1708+
oper = LDAP_MOD_ADD;
1709+
break;
1710+
case LDAP_MODIFY_BATCH_REMOVE:
1711+
case LDAP_MODIFY_BATCH_REMOVE_ALL:
1712+
oper = LDAP_MOD_DELETE;
1713+
break;
1714+
case LDAP_MODIFY_BATCH_REPLACE:
1715+
oper = LDAP_MOD_REPLACE;
1716+
break;
1717+
default:
1718+
php_error_docref(NULL TSRMLS_CC, E_ERROR, "Unknown and uncaught modification type.");
1719+
RETURN_FALSE;
1720+
}
1721+
1722+
/* fill in the basic info */
1723+
ldap_mods[i]->mod_op = oper | LDAP_MOD_BVALUES;
1724+
ldap_mods[i]->mod_type = estrndup(Z_STRVAL_P(attrib), Z_STRLEN_P(attrib));
1725+
1726+
if (Z_LVAL_P(modtype) == LDAP_MODIFY_BATCH_REMOVE_ALL) {
1727+
/* no values */
1728+
ldap_mods[i]->mod_bvalues = NULL;
1729+
}
1730+
else {
1731+
/* allocate space for the values as part of this modification */
1732+
num_modvals = zend_hash_num_elements(Z_ARRVAL_P(vals));
1733+
ldap_mods[i]->mod_bvalues = safe_emalloc((num_modvals+1), sizeof(struct berval *), 0);
1734+
1735+
/* for each value */
1736+
for (j = 0; j < num_modvals; j++) {
1737+
/* fetch it */
1738+
zend_hash_index_find(Z_ARRVAL_P(vals), j, (void **) &fetched);
1739+
modval = *fetched;
1740+
1741+
/* allocate the data struct */
1742+
ldap_mods[i]->mod_bvalues[j] = safe_emalloc(1, sizeof(struct berval), 0);
1743+
1744+
/* fill it */
1745+
ldap_mods[i]->mod_bvalues[j]->bv_len = Z_STRLEN_P(modval);
1746+
ldap_mods[i]->mod_bvalues[j]->bv_val = estrndup(Z_STRVAL_P(modval), Z_STRLEN_P(modval));
1747+
}
1748+
1749+
/* NULL-terminate values */
1750+
ldap_mods[i]->mod_bvalues[num_modvals] = NULL;
1751+
}
1752+
}
1753+
1754+
/* NULL-terminate modifications */
1755+
ldap_mods[num_mods] = NULL;
1756+
1757+
/* perform (finally) */
1758+
if ((i = ldap_modify_ext_s(ld->link, dn, ldap_mods, NULL, NULL)) != LDAP_SUCCESS) {
1759+
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Batch Modify: %s", ldap_err2string(i));
1760+
RETVAL_FALSE;
1761+
} else RETVAL_TRUE;
1762+
1763+
/* clean up */
1764+
{
1765+
for (i = 0; i < num_mods; i++) {
1766+
/* attribute */
1767+
efree(ldap_mods[i]->mod_type);
1768+
1769+
if (ldap_mods[i]->mod_bvalues != NULL) {
1770+
/* each BER value */
1771+
for (j = 0; ldap_mods[i]->mod_bvalues[j] != NULL; j++) {
1772+
/* free the data bytes */
1773+
efree(ldap_mods[i]->mod_bvalues[j]->bv_val);
1774+
1775+
/* free the bvalue struct */
1776+
efree(ldap_mods[i]->mod_bvalues[j]);
1777+
}
1778+
1779+
/* the BER value array */
1780+
efree(ldap_mods[i]->mod_bvalues);
1781+
}
1782+
1783+
/* the modification */
1784+
efree(ldap_mods[i]);
1785+
}
1786+
1787+
/* the modifications array */
1788+
efree(ldap_mods);
1789+
}
1790+
}
1791+
/* }}} */
1792+
14351793
/* {{{ proto int ldap_errno(resource link)
14361794
Get the current ldap error number */
14371795
PHP_FUNCTION(ldap_errno)
@@ -2516,6 +2874,12 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_modify, 0, 0, 3)
25162874
ZEND_ARG_INFO(0, entry)
25172875
ZEND_END_ARG_INFO()
25182876

2877+
ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_modify_batch, 0, 0, 3)
2878+
ZEND_ARG_INFO(0, link_identifier)
2879+
ZEND_ARG_INFO(0, dn)
2880+
ZEND_ARG_ARRAY_INFO(0, modifications_info, 0)
2881+
ZEND_END_ARG_INFO()
2882+
25192883
ZEND_BEGIN_ARG_INFO_EX(arginfo_ldap_mod_add, 0, 0, 3)
25202884
ZEND_ARG_INFO(0, link_identifier)
25212885
ZEND_ARG_INFO(0, dn)
@@ -2669,6 +3033,7 @@ const zend_function_entry ldap_functions[] = {
26693033
PHP_FE(ldap_dn2ufn, arginfo_ldap_dn2ufn)
26703034
PHP_FE(ldap_add, arginfo_ldap_add)
26713035
PHP_FE(ldap_delete, arginfo_ldap_delete)
3036+
PHP_FE(ldap_modify_batch, arginfo_ldap_modify_batch)
26723037
PHP_FALIAS(ldap_modify, ldap_mod_replace, arginfo_ldap_modify)
26733038

26743039
/* additional functions for attribute based modifications, Gerrit Thomson */

0 commit comments

Comments
 (0)