/*
 * call-seq:
 * conn.modify_ext(dn, mods, sctrls, cctrls)  => self
 *
 * Modify an entry with the DN, +dn+, and the attributes, +mods+. +mods+
 * should be either an array of LDAP#Mod objects or a hash of attribute/value
 * array pairs. +sctrls+ is an array of server controls, whilst +cctrls+ is
 * an array of client controls.
 */
VALUE
rb_ldap_conn_modify_ext_s (VALUE self, VALUE dn, VALUE attrs,
                           VALUE serverctrls, VALUE clientctrls)
{
  RB_LDAP_DATA *ldapdata;
  char *c_dn;
  LDAPMod **c_attrs;
  int i;
  LDAPControl **sctrls, **cctrls;

  switch (TYPE (attrs))
    {
    case T_HASH:
      attrs =
        rb_ldap_hash2mods (rb_mLDAP,
                           INT2NUM (LDAP_MOD_REPLACE | LDAP_MOD_BVALUES),
                           attrs);
      break;
    case T_ARRAY:
      break;
    default:
      rb_raise (rb_eTypeError, "must be a hash or an array");
    };

  GET_LDAP_DATA (self, ldapdata);
  c_dn = StringValueCStr (dn);
  c_attrs = ALLOC_N (LDAPMod *, RARRAY (attrs)->len + 1);
  sctrls = rb_ldap_get_controls (serverctrls);
  cctrls = rb_ldap_get_controls (clientctrls);

  for (i = 0; i < RARRAY (attrs)->len; i++)
    {
      VALUE mod = RARRAY (attrs)->ptr[i];
      RB_LDAPMOD_DATA *moddata;
      Check_Kind (mod, rb_cLDAP_Mod);
      GET_LDAPMOD_DATA (mod, moddata);
      c_attrs[i] = moddata->mod;
    };
  c_attrs[i] = NULL;

  ldapdata->err =
    ldap_modify_ext_s (ldapdata->ldap, c_dn, c_attrs, sctrls, cctrls);
  Check_LDAP_Result (ldapdata->err);

  return self;
}