entrywsi.c 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183
  1. /** BEGIN COPYRIGHT BLOCK
  2. * This Program is free software; you can redistribute it and/or modify it under
  3. * the terms of the GNU General Public License as published by the Free Software
  4. * Foundation; version 2 of the License.
  5. *
  6. * This Program is distributed in the hope that it will be useful, but WITHOUT
  7. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  8. * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  9. *
  10. * You should have received a copy of the GNU General Public License along with
  11. * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
  12. * Place, Suite 330, Boston, MA 02111-1307 USA.
  13. *
  14. * In addition, as a special exception, Red Hat, Inc. gives You the additional
  15. * right to link the code of this Program with code not covered under the GNU
  16. * General Public License ("Non-GPL Code") and to distribute linked combinations
  17. * including the two, subject to the limitations in this paragraph. Non-GPL Code
  18. * permitted under this exception must only link to the code of this Program
  19. * through those well defined interfaces identified in the file named EXCEPTION
  20. * found in the source code files (the "Approved Interfaces"). The files of
  21. * Non-GPL Code may instantiate templates or use macros or inline functions from
  22. * the Approved Interfaces without causing the resulting work to be covered by
  23. * the GNU General Public License. Only Red Hat, Inc. may make changes or
  24. * additions to the list of Approved Interfaces. You must obey the GNU General
  25. * Public License in all respects for all of the Program code and other code used
  26. * in conjunction with the Program except the Non-GPL Code covered by this
  27. * exception. If you modify this file, you may extend this exception to your
  28. * version of the file, but you are not obligated to do so. If you do not wish to
  29. * provide this exception without modification, you must delete this exception
  30. * statement from your version and license this file solely under the GPL without
  31. * exception.
  32. *
  33. *
  34. * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
  35. * Copyright (C) 2005 Red Hat, Inc.
  36. * All rights reserved.
  37. * END COPYRIGHT BLOCK **/
  38. /* entrywsi.c - routines for dealing with entries... With State Information */
  39. #include "slap.h"
  40. #include "slapi-plugin.h"
  41. static void resolve_attribute_state(Slapi_Entry *e, Slapi_Attr *a, int attribute_state);
  42. static int
  43. entry_present_value_to_deleted_value(Slapi_Attr *a, Slapi_Value *v)
  44. {
  45. Slapi_Value *r= valueset_remove_value(a, &a->a_present_values, v);
  46. if(r!=NULL)
  47. {
  48. slapi_valueset_add_value_ext(&a->a_deleted_values, r, SLAPI_VALUE_FLAG_PASSIN);
  49. }
  50. return LDAP_SUCCESS;
  51. }
  52. static int
  53. entry_present_value_to_zapped_value(Slapi_Attr *a, Slapi_Value *v)
  54. {
  55. if(v!=NULL)
  56. {
  57. Slapi_Value *r= valueset_remove_value(a, &a->a_present_values, v);
  58. if(r!=NULL)
  59. {
  60. slapi_value_free(&r);
  61. }
  62. }
  63. return LDAP_SUCCESS;
  64. }
  65. static int
  66. entry_deleted_value_to_present_value(Slapi_Attr *a, Slapi_Value *v)
  67. {
  68. Slapi_Value *r= valueset_remove_value(a, &a->a_deleted_values, v);
  69. if(r!=NULL)
  70. {
  71. slapi_valueset_add_value_ext(&a->a_present_values, r, SLAPI_VALUE_FLAG_PASSIN);
  72. }
  73. return LDAP_SUCCESS;
  74. }
  75. static int
  76. entry_deleted_value_to_zapped_value(Slapi_Attr *a, Slapi_Value *v)
  77. {
  78. if(v!=NULL)
  79. {
  80. Slapi_Value *r= valueset_remove_value(a, &a->a_deleted_values, v);
  81. if(r!=NULL)
  82. {
  83. slapi_value_free(&r);
  84. }
  85. }
  86. return LDAP_SUCCESS;
  87. }
  88. static int
  89. entry_present_attribute_to_deleted_attribute(Slapi_Entry *e, Slapi_Attr *a)
  90. {
  91. attrlist_remove(&e->e_attrs,a->a_type);
  92. attrlist_add(&e->e_deleted_attrs,a);
  93. return LDAP_SUCCESS;
  94. }
  95. static int
  96. entry_deleted_attribute_to_present_attribute(Slapi_Entry *e, Slapi_Attr *a)
  97. {
  98. attrlist_remove(&e->e_deleted_attrs,a->a_type);
  99. attrlist_add(&e->e_attrs,a);
  100. return LDAP_SUCCESS;
  101. }
  102. /*
  103. * Get the first deleted attribute.
  104. *
  105. * Return 0: Return the type and the CSN of the deleted attribute.
  106. * Return -1: There are no deleted attributes.
  107. */
  108. int
  109. entry_first_deleted_attribute( const Slapi_Entry *e, Slapi_Attr **a)
  110. {
  111. *a= e->e_deleted_attrs;
  112. return( *a ? 0 : -1 );
  113. }
  114. /*
  115. * Get the next deleted attribute.
  116. *
  117. * Return 0: the type and the CSN of the deleted attribute.
  118. * Return -1: no deleted attributes.
  119. */
  120. int
  121. entry_next_deleted_attribute( const Slapi_Entry *e, Slapi_Attr **a)
  122. {
  123. *a= (*a)->a_next;
  124. return( *a ? 0 : -1 );
  125. }
  126. const CSN *
  127. entry_get_maxcsn ( const Slapi_Entry *entry )
  128. {
  129. return entry->e_maxcsn;
  130. }
  131. void
  132. entry_set_maxcsn ( Slapi_Entry *entry, const CSN *csn )
  133. {
  134. if ( NULL == entry->e_maxcsn )
  135. {
  136. entry->e_maxcsn = csn_dup ( csn );
  137. }
  138. else if ( csn_compare ( entry->e_maxcsn, csn ) < 0 )
  139. {
  140. csn_init_by_csn ( entry->e_maxcsn, csn );
  141. }
  142. }
  143. /*
  144. * Get the DN CSN of an entry.
  145. */
  146. const CSN *
  147. entry_get_dncsn(const Slapi_Entry *entry)
  148. {
  149. return csnset_get_last_csn(entry->e_dncsnset);
  150. }
  151. /*
  152. * Get the DN CSN set of an entry.
  153. */
  154. const CSNSet *
  155. entry_get_dncsnset(const Slapi_Entry *entry)
  156. {
  157. return entry->e_dncsnset;
  158. }
  159. /*
  160. * Add a DN CSN to an entry.
  161. */
  162. int
  163. entry_add_dncsn(Slapi_Entry *entry, const CSN *csn)
  164. {
  165. PR_ASSERT(entry!=NULL);
  166. if(!csnset_contains(entry->e_dncsnset,csn))
  167. {
  168. csnset_add_csn(&entry->e_dncsnset, CSN_TYPE_VALUE_DISTINGUISHED, csn);
  169. }
  170. return 0;
  171. }
  172. /*
  173. * Add a DN CSN to an entry, but uses flags to control the behavior
  174. * Using the ENTRY_DNCSN_INCREASING flag makes sure the csnset is in
  175. * order of increasing csn. csnset_insert_csn may not be very fast, so
  176. * we may have to revisit this if it becomes a performance problem.
  177. * In most cases, storing the csn unsorted is ok since the server
  178. * usually makes sure the csn is already in order. However, when doing
  179. * a str2entry, the order is not preserved unless we sort it.
  180. */
  181. int
  182. entry_add_dncsn_ext(Slapi_Entry *entry, const CSN *csn, PRUint32 flags)
  183. {
  184. PR_ASSERT(entry!=NULL);
  185. if(!csnset_contains(entry->e_dncsnset,csn))
  186. {
  187. if (flags & ENTRY_DNCSN_INCREASING)
  188. {
  189. csnset_insert_csn(&entry->e_dncsnset, CSN_TYPE_VALUE_DISTINGUISHED, csn);
  190. }
  191. else
  192. {
  193. csnset_add_csn(&entry->e_dncsnset, CSN_TYPE_VALUE_DISTINGUISHED, csn);
  194. }
  195. }
  196. return 0;
  197. }
  198. /*
  199. * Set the CSN for all the present values on the entry.
  200. * This is only intended to be used for new entries
  201. * being added.
  202. */
  203. int
  204. entry_set_csn(Slapi_Entry *entry, const CSN *csn)
  205. {
  206. Slapi_Attr *a;
  207. PR_ASSERT(entry!=NULL);
  208. slapi_entry_first_attr( entry, &a );
  209. while(a!=NULL)
  210. {
  211. /*
  212. * JCM - it'd be more efficient if the str2entry code
  213. * set a flag on the attribute structure.
  214. */
  215. if(strcasecmp(a->a_type, SLAPI_ATTR_UNIQUEID)!=0)
  216. {
  217. attr_set_csn(a,csn);
  218. }
  219. slapi_entry_next_attr( entry, a, &a );
  220. }
  221. return 0;
  222. }
  223. /*
  224. * Set the Distinguished CSN for the RDN components of the entry.
  225. */
  226. void
  227. entry_add_rdn_csn(Slapi_Entry *e, const CSN *csn)
  228. {
  229. char *type;
  230. char *value;
  231. int index;
  232. const Slapi_DN *dn= slapi_entry_get_sdn_const(e);
  233. Slapi_RDN *rdn= slapi_rdn_new_sdn(dn);
  234. index= slapi_rdn_get_first(rdn, &type, &value);
  235. while(index!=-1)
  236. {
  237. Slapi_Attr *a= NULL;
  238. Slapi_Value *v= NULL;
  239. entry_attr_find_wsi(e, type, &a);
  240. if(a!=NULL)
  241. {
  242. struct berval bv;
  243. bv.bv_len= strlen(value);
  244. bv.bv_val= (void*)value;
  245. attr_value_find_wsi(a, &bv, &v);
  246. }
  247. if(v!=NULL)
  248. {
  249. value_update_csn(v,CSN_TYPE_VALUE_DISTINGUISHED,csn);
  250. }
  251. else
  252. {
  253. /* JCM RDN component isn't a present value - this is illegal. */
  254. }
  255. index= slapi_rdn_get_next(rdn, index, &type, &value);
  256. }
  257. slapi_rdn_free(&rdn);
  258. }
  259. CSN*
  260. entry_assign_operation_csn ( Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *parententry )
  261. {
  262. Slapi_Operation *op;
  263. const CSN *basecsn = NULL;
  264. const CSN *parententry_dncsn = NULL;
  265. CSN *opcsn = NULL;
  266. slapi_pblock_get ( pb, SLAPI_OPERATION, &op );
  267. /*
  268. * The replication pre-op would have set op->o_csngen_handler for
  269. * user requests that are against a replica.
  270. */
  271. if ( op->o_csngen_handler )
  272. {
  273. /*
  274. * Sync up the CSN generator so that the new csn is greater
  275. * than the entry's maxcsn and/or the parent's max dncsn.
  276. */
  277. if ( e )
  278. {
  279. basecsn = entry_get_maxcsn ( e );
  280. }
  281. if ( parententry )
  282. {
  283. parententry_dncsn = entry_get_dncsn ( parententry );
  284. if ( csn_compare ( parententry_dncsn, basecsn ) > 0 )
  285. {
  286. basecsn = parententry_dncsn;
  287. }
  288. }
  289. opcsn = op->o_csngen_handler ( pb, basecsn );
  290. if (NULL != opcsn)
  291. {
  292. operation_set_csn (op, opcsn);
  293. }
  294. }
  295. return opcsn;
  296. }
  297. /*
  298. * Purge state information from the entry older than csnUpTo
  299. *
  300. * if csnUpTo is NULL, get rid of all the CSN related info.
  301. * if csnUpTo is non-NULL, purge all info older than csnUpTo
  302. */
  303. void
  304. entry_purge_state_information(Slapi_Entry *e, const CSN *csnUpTo)
  305. {
  306. Slapi_Attr *a=NULL;
  307. PR_ASSERT(e!=NULL);
  308. for(a = e->e_attrs; NULL != a; a = a->a_next)
  309. {
  310. /*
  311. * we are passing in the entry so that we may be able to "optimize"
  312. * the csn related information and roll it up higher to the level
  313. * of entry
  314. */
  315. attr_purge_state_information(e, a, csnUpTo);
  316. }
  317. for(a = e->e_deleted_attrs; NULL != a; a = a->a_next)
  318. {
  319. /*
  320. * we are passing in the entry so that we may be able to "optimize"
  321. * the csn related information and roll it up higher to the level
  322. * of entry
  323. */
  324. attr_purge_state_information(e, a, csnUpTo);
  325. }
  326. }
  327. /*
  328. * Look for the attribute on the present and deleted attribute lists.
  329. */
  330. int
  331. entry_attr_find_wsi(Slapi_Entry *e, const char *type, Slapi_Attr **a)
  332. {
  333. int retVal= ATTRIBUTE_NOTFOUND;
  334. PR_ASSERT(e!=NULL);
  335. PR_ASSERT(type!=NULL);
  336. PR_ASSERT(a!=NULL);
  337. /* Look on the present attribute list */
  338. *a= attrlist_find(e->e_attrs,type);
  339. if(*a!=NULL)
  340. {
  341. /* The attribute is present */
  342. retVal= ATTRIBUTE_PRESENT;
  343. }
  344. else
  345. {
  346. /* Maybe the attribue was deleted... */
  347. *a= attrlist_find(e->e_deleted_attrs,type);
  348. if(*a!=NULL)
  349. {
  350. /* The attribute is deleted */
  351. retVal= ATTRIBUTE_DELETED;
  352. }
  353. else
  354. {
  355. /* The attribute was not found */
  356. retVal= ATTRIBUTE_NOTFOUND;
  357. }
  358. }
  359. return retVal;
  360. }
  361. /*
  362. * Add the attribute to the deleted attribute list.
  363. *
  364. * Consumes the attribute.
  365. */
  366. int
  367. entry_add_deleted_attribute_wsi(Slapi_Entry *e, Slapi_Attr *a)
  368. {
  369. PR_ASSERT( e!=NULL );
  370. PR_ASSERT( a!=NULL );
  371. attrlist_add(&e->e_deleted_attrs, a);
  372. return 0;
  373. }
  374. /*
  375. * Add the attribute to the present attribute list.
  376. *
  377. * Consumes the attribute.
  378. */
  379. int
  380. entry_add_present_attribute_wsi(Slapi_Entry *e, Slapi_Attr *a)
  381. {
  382. PR_ASSERT( e!=NULL );
  383. PR_ASSERT( a!=NULL );
  384. attrlist_add(&e->e_attrs, a);
  385. return 0;
  386. }
  387. /*
  388. * Add a list of values to the attribute, whilst maintaining state information.
  389. *
  390. * Preserves LDAP Information Model constraints,
  391. * returning an LDAP result code.
  392. */
  393. static int
  394. entry_add_present_values_wsi(Slapi_Entry *e, const char *type, struct berval **bervals, const CSN *csn, int urp, long flags)
  395. {
  396. int retVal= LDAP_SUCCESS;
  397. Slapi_Value **valuestoadd = NULL;
  398. valuearray_init_bervalarray(bervals,&valuestoadd); /* JCM SLOW FUNCTION */
  399. if(!valuearray_isempty(valuestoadd))
  400. {
  401. Slapi_Attr *a= NULL;
  402. long a_flags_orig;
  403. int attr_state= entry_attr_find_wsi(e, type, &a);
  404. if (ATTRIBUTE_NOTFOUND == attr_state)
  405. {
  406. /* Create a new attribute */
  407. a = slapi_attr_new();
  408. slapi_attr_init(a, type);
  409. attrlist_add(&e->e_attrs, a);
  410. }
  411. a_flags_orig = a->a_flags;
  412. a->a_flags |= flags;
  413. if(urp)
  414. {
  415. /*
  416. * Consolidate a->a_present_values and the pending values:
  417. * Delete the pending values from a->a_present_values
  418. * and transfer their csnsets to valuestoadd.
  419. */
  420. valueset_remove_valuearray (&a->a_present_values, a, valuestoadd,
  421. SLAPI_VALUE_FLAG_IGNOREERROR |
  422. SLAPI_VALUE_FLAG_PRESERVECSNSET, NULL);
  423. /*
  424. * Consolidate a->a_deleted_values and the pending values
  425. * similarly.
  426. */
  427. valueset_remove_valuearray (&a->a_deleted_values, a, valuestoadd,
  428. SLAPI_VALUE_FLAG_IGNOREERROR |
  429. SLAPI_VALUE_FLAG_PRESERVECSNSET, NULL);
  430. /* Append the pending values to a->a_present_values */
  431. valuearray_update_csn (valuestoadd,CSN_TYPE_VALUE_UPDATED,csn);
  432. valueset_add_valuearray_ext(&a->a_present_values, valuestoadd, SLAPI_VALUE_FLAG_PASSIN);
  433. slapi_ch_free ( (void **)&valuestoadd );
  434. /*
  435. * Now delete non-RDN values from a->a_present_values; and
  436. * restore possible RDN values from a->a_deleted_values
  437. */
  438. resolve_attribute_state(e, a, attr_state);
  439. retVal= LDAP_SUCCESS;
  440. }
  441. else
  442. {
  443. Slapi_Value **deletedvalues= NULL;
  444. switch(attr_state)
  445. {
  446. case ATTRIBUTE_PRESENT:
  447. /* The attribute is already on the present list */
  448. break;
  449. case ATTRIBUTE_DELETED:
  450. /* Move the deleted attribute onto the present list */
  451. entry_deleted_attribute_to_present_attribute(e, a);
  452. break;
  453. case ATTRIBUTE_NOTFOUND:
  454. /* No-op - attribute was initialized & added to entry above */
  455. break;
  456. }
  457. /* Check if any of the values to be added are on the deleted list */
  458. valueset_remove_valuearray(&a->a_deleted_values, a, valuestoadd, SLAPI_VALUE_FLAG_IGNOREERROR,&deletedvalues); /* JCM Check return code */
  459. if(deletedvalues!=NULL && deletedvalues[0]!=NULL)
  460. {
  461. /* Some of the values to be added were on the deleted list */
  462. Slapi_Value **v= NULL;
  463. Slapi_ValueSet vs;
  464. /* Add each deleted value to the present list */
  465. valuearray_update_csn(deletedvalues,CSN_TYPE_VALUE_UPDATED,csn);
  466. valueset_add_valuearray_ext(&a->a_present_values, deletedvalues, SLAPI_VALUE_FLAG_PASSIN);
  467. /* Remove the deleted values from the values to add */
  468. valueset_set_valuearray_passin(&vs,valuestoadd);
  469. valueset_remove_valuearray(&vs, a, deletedvalues, SLAPI_VALUE_FLAG_IGNOREERROR, &v);
  470. valuestoadd= valueset_get_valuearray(&vs);
  471. valuearray_free(&v);
  472. slapi_ch_free((void **)&deletedvalues);
  473. }
  474. valuearray_update_csn(valuestoadd,CSN_TYPE_VALUE_UPDATED,csn);
  475. retVal= attr_add_valuearray(a, valuestoadd, slapi_entry_get_dn_const(e));
  476. valuearray_free(&valuestoadd);
  477. }
  478. a->a_flags = a_flags_orig;
  479. }
  480. return(retVal);
  481. }
  482. /*
  483. * Delete a list of values from an attribute, whilst maintaining state information.
  484. *
  485. * Preserves LDAP Information Model constraints,
  486. * returning an LDAP result code.
  487. */
  488. static int
  489. entry_delete_present_values_wsi(Slapi_Entry *e, const char *type, struct berval **vals, const CSN *csn, int urp, int mod_op)
  490. {
  491. int retVal= LDAP_SUCCESS;
  492. Slapi_Attr *a= NULL;
  493. int attr_state= entry_attr_find_wsi(e, type, &a);
  494. if(attr_state==ATTRIBUTE_PRESENT || (attr_state==ATTRIBUTE_DELETED && urp))
  495. {
  496. /* The attribute is on the present list, or the deleted list and we're doing URP */
  497. if ( vals == NULL || vals[0] == NULL )
  498. {
  499. /* delete the entire attribute */
  500. LDAPDebug( LDAP_DEBUG_ARGS, "removing entire attribute %s\n", type, 0, 0 );
  501. attr_set_deletion_csn(a,csn);
  502. if(urp)
  503. {
  504. resolve_attribute_state(e, a, attr_state); /* ABSOLVED */
  505. }
  506. else
  507. {
  508. if(!slapi_attr_flag_is_set(a,SLAPI_ATTR_FLAG_SINGLE))
  509. {
  510. /* We don't maintain a deleted value list for single valued attributes */
  511. valueset_add_valueset(&a->a_deleted_values, &a->a_present_values); /* JCM Would be better to passin the valuestodelete */
  512. }
  513. slapi_valueset_done(&a->a_present_values);
  514. entry_present_attribute_to_deleted_attribute(e, a);
  515. }
  516. retVal= LDAP_SUCCESS; /* This Operation always succeeds when the attribute is Present */
  517. }
  518. else
  519. {
  520. /* delete some specific values */
  521. Slapi_Value **valuestodelete= NULL;
  522. valuearray_init_bervalarray(vals,&valuestodelete); /* JCM SLOW FUNCTION */
  523. if(urp)
  524. {
  525. Slapi_Value **valuesupdated= NULL;
  526. valueset_update_csn_for_valuearray(&a->a_present_values, a, valuestodelete, CSN_TYPE_VALUE_DELETED, csn, &valuesupdated);
  527. /* if we removed the last value, we need to mark the attribute as deleted
  528. the resolve_attribute_state() code will "resurrect" the attribute if
  529. there are present values with a later CSN - otherwise, even though
  530. the value will be updated with a VDCSN which is later than the VUCSN,
  531. the attribute will not be deleted */
  532. if(slapi_attr_flag_is_set(a,SLAPI_ATTR_FLAG_SINGLE) && valuesupdated &&
  533. *valuesupdated)
  534. {
  535. attr_set_deletion_csn(a,csn);
  536. }
  537. valuearray_free(&valuesupdated);
  538. valueset_update_csn_for_valuearray(&a->a_deleted_values, a, valuestodelete, CSN_TYPE_VALUE_DELETED, csn, &valuesupdated);
  539. valuearray_free(&valuesupdated);
  540. valuearray_update_csn(valuestodelete,CSN_TYPE_VALUE_DELETED,csn);
  541. valueset_add_valuearray_ext(&a->a_deleted_values, valuestodelete, SLAPI_VALUE_FLAG_PASSIN);
  542. /* all the elements in valuestodelete are passed;
  543. * should free valuestodelete only (don't call valuearray_free)
  544. * [622023] */
  545. slapi_ch_free((void **)&valuestodelete);
  546. resolve_attribute_state(e, a, attr_state);
  547. retVal= LDAP_SUCCESS;
  548. }
  549. else
  550. {
  551. Slapi_Value **deletedvalues= NULL;
  552. retVal= valueset_remove_valuearray(&a->a_present_values, a, valuestodelete, 0 /* Do Not Ignore Errors */,&deletedvalues);
  553. if(retVal==LDAP_SUCCESS && deletedvalues != NULL)
  554. {
  555. if(!slapi_attr_flag_is_set(a,SLAPI_ATTR_FLAG_SINGLE))
  556. {
  557. /* We don't maintain a deleted value list for single valued attributes */
  558. /* Add each deleted value to the deleted set */
  559. valuearray_update_csn(deletedvalues,CSN_TYPE_VALUE_DELETED,csn);
  560. valueset_add_valuearray_ext(&a->a_deleted_values, deletedvalues, SLAPI_VALUE_FLAG_PASSIN);
  561. slapi_ch_free((void **)&deletedvalues);
  562. }
  563. else {
  564. valuearray_free(&deletedvalues);
  565. }
  566. if(valueset_isempty(&a->a_present_values))
  567. {
  568. /* There are no present values, so move the
  569. * attribute to the deleted attribute list. */
  570. entry_present_attribute_to_deleted_attribute(e, a);
  571. }
  572. }
  573. else if (retVal != LDAP_SUCCESS)
  574. {
  575. /* Failed
  576. * - Duplicate value
  577. * - Value not found
  578. * - Operations error
  579. */
  580. if ( retVal==LDAP_OPERATIONS_ERROR )
  581. {
  582. LDAPDebug( LDAP_DEBUG_ANY, "Possible existing duplicate "
  583. "value for attribute type %s found in "
  584. "entry %s\n", a->a_type, slapi_entry_get_dn_const(e), 0 );
  585. }
  586. }
  587. valuearray_free(&valuestodelete);
  588. }
  589. }
  590. }
  591. else if (attr_state==ATTRIBUTE_DELETED)
  592. {
  593. retVal= LDAP_NO_SUCH_ATTRIBUTE;
  594. }
  595. else if (attr_state==ATTRIBUTE_NOTFOUND)
  596. {
  597. if (!urp)
  598. {
  599. /* Only warn if not urping */
  600. LDAPDebug( LDAP_DEBUG_ARGS, "could not find attribute %s\n", type, 0, 0 );
  601. }
  602. retVal= LDAP_NO_SUCH_ATTRIBUTE;
  603. if (LDAP_MOD_REPLACE == mod_op)
  604. {
  605. /* Create a new attribute and set the adcsn */
  606. Slapi_Attr *a = slapi_attr_new();
  607. slapi_attr_init(a, type);
  608. /* According to URP there should be an adcsn.
  609. * According to Tests, there should not
  610. * since the attribute never existed
  611. * Tests 26 and 27 of MMRepl state. */
  612. if (urp)
  613. attr_set_deletion_csn(a,csn);
  614. attrlist_add(&e->e_attrs, a);
  615. }
  616. }
  617. return( retVal );
  618. }
  619. /*
  620. * Replace all the values of an attribute with a list of attribute values.
  621. *
  622. * Preserves LDAP Information Model constraints,
  623. * returning an LDAP result code.
  624. */
  625. static int
  626. entry_replace_present_values_wsi(Slapi_Entry *e, const char *type, struct berval **vals, const CSN *csn, int urp)
  627. {
  628. /*
  629. * Remove all existing values.
  630. */
  631. entry_delete_present_values_wsi(e, type, NULL /* Delete all values */, csn, urp, LDAP_MOD_REPLACE);
  632. /*
  633. * Add the new values. If there are no new values,
  634. * slapi_entry_add_values() returns LDAP_SUCCESS and so the
  635. * attribute remains deleted (which is the correct outcome).
  636. */
  637. return( entry_add_present_values_wsi( e, type, vals, csn, urp, SLAPI_ATTR_FLAG_CMP_BITBYBIT ));
  638. }
  639. /*
  640. * Applies the modification to the entry whilst
  641. * maintaining state information.
  642. */
  643. int
  644. entry_apply_mod_wsi(Slapi_Entry *e, const LDAPMod *mod, const CSN *csn, int urp)
  645. {
  646. int retVal= LDAP_SUCCESS;
  647. int i;
  648. switch ( mod->mod_op & ~LDAP_MOD_BVALUES )
  649. {
  650. case LDAP_MOD_ADD:
  651. LDAPDebug( LDAP_DEBUG_ARGS, " add: %s\n", mod->mod_type, 0, 0 );
  652. retVal = entry_add_present_values_wsi( e, mod->mod_type, mod->mod_bvalues, csn, urp, 0 );
  653. break;
  654. case LDAP_MOD_DELETE:
  655. LDAPDebug( LDAP_DEBUG_ARGS, " delete: %s\n", mod->mod_type, 0, 0 );
  656. retVal = entry_delete_present_values_wsi( e, mod->mod_type, mod->mod_bvalues, csn, urp, mod->mod_op );
  657. break;
  658. case LDAP_MOD_REPLACE:
  659. LDAPDebug( LDAP_DEBUG_ARGS, " replace: %s\n", mod->mod_type, 0, 0 );
  660. retVal = entry_replace_present_values_wsi( e, mod->mod_type, mod->mod_bvalues, csn, urp );
  661. break;
  662. }
  663. for ( i = 0; mod->mod_bvalues != NULL && mod->mod_bvalues[i] != NULL; i++ )
  664. {
  665. LDAPDebug( LDAP_DEBUG_ARGS, " %s: %s\n", mod->mod_type, mod->mod_bvalues[i]->bv_val, 0 );
  666. }
  667. LDAPDebug( LDAP_DEBUG_ARGS, " -\n", 0, 0, 0 );
  668. return retVal;
  669. }
  670. /*
  671. * Applies the set of modifications to the entry whilst
  672. * maintaining state information.
  673. */
  674. int
  675. entry_apply_mods_wsi(Slapi_Entry *e, Slapi_Mods *smods, const CSN *csn, int urp)
  676. {
  677. int retVal= LDAP_SUCCESS;
  678. LDAPMod *mod;
  679. LDAPDebug( LDAP_DEBUG_TRACE, "=> entry_apply_mods_wsi\n", 0, 0, 0 );
  680. mod = slapi_mods_get_first_mod(smods);
  681. while(NULL!=mod && retVal==LDAP_SUCCESS)
  682. {
  683. if(csn!=NULL)
  684. {
  685. retVal= entry_apply_mod_wsi(e, mod, csn, urp);
  686. }
  687. else
  688. {
  689. retVal= entry_apply_mod(e, mod);
  690. }
  691. mod = slapi_mods_get_next_mod(smods);
  692. }
  693. LDAPDebug( LDAP_DEBUG_TRACE, "<= entry_apply_mods_wsi %d\n", retVal, 0, 0 );
  694. return retVal;
  695. }
  696. /*
  697. * This code implements a computed attribute called 'nscpEntryWSI'.
  698. * By specifically asking for this attribute the client will recieve
  699. * an LDIF dump of the entry with all its state information.
  700. *
  701. * JCM - Security... Only for the Directory Manager.
  702. */
  703. static const char *nscpEntryWSI= "nscpEntryWSI";
  704. /*
  705. */
  706. static int
  707. entry_compute_nscpentrywsi(computed_attr_context *c,char* type,Slapi_Entry *e,slapi_compute_output_t outputfn)
  708. {
  709. int rc = 0;
  710. if ( strcasecmp(type, nscpEntryWSI ) == 0)
  711. {
  712. /* If not, we return it as zero */
  713. char *es;
  714. char *s;
  715. char *p;
  716. int len;
  717. Slapi_Attr our_attr;
  718. slapi_attr_init(&our_attr, nscpEntryWSI);
  719. our_attr.a_flags = SLAPI_ATTR_FLAG_OPATTR;
  720. es= slapi_entry2str_with_options(e, &len, SLAPI_DUMP_STATEINFO | SLAPI_DUMP_UNIQUEID | SLAPI_DUMP_NOWRAP);
  721. s= es;
  722. p= ldif_getline( &s );
  723. while(p!=NULL)
  724. {
  725. Slapi_Value *v;
  726. char *t, *d;
  727. /* Strip out the Continuation Markers (JCM - I think that NOWRAP means we don't need to do this any more)*/
  728. for ( t = p, d = p; *t; t++ )
  729. {
  730. if ( *t != 0x01 )
  731. *d++ = *t;
  732. }
  733. *d = '\0';
  734. v= slapi_value_new_string(p);
  735. slapi_attr_add_value(&our_attr,v);
  736. slapi_value_free(&v);
  737. p= ldif_getline( &s );
  738. }
  739. slapi_ch_free((void**)&es);
  740. rc = (*outputfn) (c, &our_attr, e);
  741. attr_done(&our_attr);
  742. return (rc);
  743. }
  744. return -1; /* I see no ships */
  745. }
  746. int
  747. entry_computed_attr_init()
  748. {
  749. slapi_compute_add_evaluator(entry_compute_nscpentrywsi);
  750. return 0;
  751. }
  752. static void
  753. purge_attribute_state_multi_valued(const Slapi_Attr *a, Slapi_Value *v)
  754. {
  755. const CSN *vdcsn= value_get_csn(v,CSN_TYPE_VALUE_DELETED);
  756. const CSN *vucsn= value_get_csn(v,CSN_TYPE_VALUE_UPDATED);
  757. if(vdcsn && csn_compare(vdcsn,vucsn)<0)
  758. {
  759. value_remove_csn(v,CSN_TYPE_VALUE_DELETED);
  760. }
  761. }
  762. /*
  763. * utility function for value_distinguished_at_csn...
  764. */
  765. static const CSN *
  766. vdac_sniff_value(Slapi_ValueSet *vs, const Slapi_Value *v, const CSN *csn, const CSN *most_recent_mdcsn)
  767. {
  768. const CSN *mdcsn= value_get_csn(v,CSN_TYPE_VALUE_DISTINGUISHED);
  769. if(mdcsn!=NULL)
  770. {
  771. /* This value was/is distinguished... */
  772. if(csn_compare(csn,most_recent_mdcsn)<0)
  773. {
  774. /* ...and was distinguished before the point in time we're interested in... */
  775. int r= csn_compare(mdcsn,most_recent_mdcsn);
  776. if(r>0)
  777. {
  778. /* ...and is the most recent MDCSN we've seen thus far. */
  779. slapi_valueset_done(vs);
  780. slapi_valueset_add_value(vs,v);
  781. most_recent_mdcsn= mdcsn;
  782. }
  783. else if(r==0)
  784. {
  785. /* ...and is as recent as the last most recent MDCSN we've seen thus far. */
  786. /* Must have been a multi-valued RDN */
  787. slapi_valueset_add_value(vs,v);
  788. }
  789. }
  790. }
  791. return most_recent_mdcsn;
  792. }
  793. /*
  794. * utility function for value_distinguished_at_csn...
  795. */
  796. static const CSN *
  797. vdac_sniff_attribute(Slapi_ValueSet *vs, Slapi_Attr *a, const CSN *csn, const CSN *most_recent_mdcsn)
  798. {
  799. Slapi_Value *v;
  800. int i= slapi_attr_first_value( a, &v );
  801. while(i!=-1)
  802. {
  803. most_recent_mdcsn= vdac_sniff_value( vs, v, csn, most_recent_mdcsn );
  804. i= slapi_attr_next_value( a, i, &v );
  805. }
  806. i= attr_first_deleted_value( a, &v );
  807. while(i!=-1)
  808. {
  809. most_recent_mdcsn= vdac_sniff_value( vs, v, csn, most_recent_mdcsn );
  810. i= attr_next_deleted_value( a, i, &v );
  811. }
  812. return most_recent_mdcsn;
  813. }
  814. /*
  815. * utility function for value_distinguished_at_csn...
  816. *
  817. * Return the set of values that made up the RDN at or before the csn point.
  818. */
  819. static const CSN *
  820. distinguished_values_at_csn(const Slapi_Entry *e, const CSN *csn, Slapi_ValueSet *vs)
  821. {
  822. const CSN *most_recent_mdcsn= NULL;
  823. Slapi_Attr *a;
  824. int i= slapi_entry_first_attr( e, &a );
  825. while(i!=-1)
  826. {
  827. most_recent_mdcsn= vdac_sniff_attribute( vs, a, csn, most_recent_mdcsn);
  828. i= slapi_entry_next_attr( e, a, &a );
  829. }
  830. i= entry_first_deleted_attribute( e, &a );
  831. while(i!=-1)
  832. {
  833. most_recent_mdcsn= vdac_sniff_attribute( vs, a, csn, most_recent_mdcsn);
  834. i= entry_next_deleted_attribute( e, &a );
  835. }
  836. return most_recent_mdcsn;
  837. }
  838. /*
  839. * Work out if the value was distinguished at time csn.
  840. */
  841. static int
  842. value_distinguished_at_csn(const Slapi_Entry *e, const Slapi_Attr *original_attr, Slapi_Value *original_value, const CSN *csn)
  843. {
  844. int r= 0;
  845. const CSN *mdcsn= value_get_csn(original_value,CSN_TYPE_VALUE_DISTINGUISHED);
  846. if(mdcsn!=NULL)
  847. {
  848. /*
  849. * Oh bugger. This means that we have to work out what the RDN components
  850. * were at this point in time. This is non-trivial since we must walk
  851. * through all the present and deleted attributes and their present and
  852. * deleted values. Slow :-(
  853. */
  854. Slapi_ValueSet *vs= slapi_valueset_new();
  855. const CSN *most_recent_mdcsn= distinguished_values_at_csn(e, csn, vs);
  856. /*
  857. * We now know what the RDN components were at the point in time we're interested in.
  858. * And the question we need to answer is :-
  859. * 'Was the provided value one of those RDN components?'
  860. */
  861. if(most_recent_mdcsn!=NULL)
  862. {
  863. Slapi_Value *v;
  864. int i= slapi_valueset_first_value( vs, &v );
  865. while(i!=-1)
  866. {
  867. if(slapi_value_compare(original_attr, original_value, v)==0)
  868. {
  869. /* This value was distinguished at the time in question. */
  870. r= 1;
  871. i= -1;
  872. }
  873. else
  874. {
  875. i= slapi_valueset_next_value( vs, i, &v );
  876. }
  877. }
  878. }
  879. slapi_valueset_free(vs);
  880. }
  881. else
  882. {
  883. /* This value has never been distinguished */
  884. r= 0;
  885. }
  886. return r;
  887. }
  888. static void
  889. resolve_attribute_state_multi_valued(Slapi_Entry *e, Slapi_Attr *a, int attribute_state)
  890. {
  891. int i;
  892. const CSN *adcsn= attr_get_deletion_csn(a);
  893. Slapi_ValueSet *vs= valueset_dup(&a->a_present_values); /* JCM This is slow... but otherwise we end up iterating through a changing array */
  894. Slapi_Value *v;
  895. /* Loop over the present attribute values */
  896. i= slapi_valueset_first_value( vs, &v );
  897. while(v!=NULL)
  898. {
  899. const CSN *vdcsn;
  900. const CSN *vucsn;
  901. const CSN *deletedcsn;
  902. /* This call ensures that the value does not contain a deletion_csn
  903. * which is before the presence_csn or distinguished_csn of the value.
  904. */
  905. purge_attribute_state_multi_valued(a, v);
  906. vdcsn= value_get_csn(v, CSN_TYPE_VALUE_DELETED);
  907. vucsn= value_get_csn(v, CSN_TYPE_VALUE_UPDATED);
  908. deletedcsn= csn_max(vdcsn, adcsn);
  909. if(csn_compare(vucsn,deletedcsn)<0) /* check if the attribute or value was deleted after the value was last updated */
  910. {
  911. if(!value_distinguished_at_csn(e, a, v, deletedcsn))
  912. {
  913. entry_present_value_to_deleted_value(a,v);
  914. }
  915. }
  916. i= slapi_valueset_next_value( vs, i, &v );
  917. }
  918. slapi_valueset_free(vs);
  919. /* Loop over the deleted attribute values */
  920. vs= valueset_dup(&a->a_deleted_values); /* JCM This is slow... but otherwise we end up iterating through a changing array */
  921. i= slapi_valueset_first_value( vs, &v );
  922. while(v!=NULL)
  923. {
  924. const CSN *vdcsn;
  925. const CSN *vucsn;
  926. const CSN *deletedcsn;
  927. /* This call ensures that the value does not contain a deletion_csn which is before the presence_csn or distinguished_csn of the value. */
  928. purge_attribute_state_multi_valued(a, v);
  929. vdcsn= value_get_csn(v, CSN_TYPE_VALUE_DELETED);
  930. vucsn= value_get_csn(v, CSN_TYPE_VALUE_UPDATED);
  931. deletedcsn= csn_max(vdcsn, adcsn);
  932. if((csn_compare(vucsn,deletedcsn)>=0) || /* check if the attribute or value was deleted after the value was last updated */
  933. value_distinguished_at_csn(e, a, v, deletedcsn))
  934. {
  935. entry_deleted_value_to_present_value(a,v);
  936. }
  937. i= slapi_valueset_next_value( vs, i, &v );
  938. }
  939. slapi_valueset_free(vs);
  940. if(valueset_isempty(&a->a_present_values))
  941. {
  942. if(attribute_state==ATTRIBUTE_PRESENT)
  943. {
  944. entry_present_attribute_to_deleted_attribute(e, a);
  945. }
  946. }
  947. else
  948. {
  949. if(attribute_state==ATTRIBUTE_DELETED)
  950. {
  951. entry_deleted_attribute_to_present_attribute(e, a);
  952. }
  953. }
  954. }
  955. static void
  956. resolve_attribute_state_single_valued(Slapi_Entry *e, Slapi_Attr *a, int attribute_state)
  957. {
  958. Slapi_Value *current_value= NULL;
  959. Slapi_Value *pending_value= NULL;
  960. Slapi_Value *new_value= NULL;
  961. const CSN *current_value_vucsn;
  962. const CSN *pending_value_vucsn;
  963. const CSN *adcsn;
  964. int i;
  965. /*
  966. * this call makes sure that the attribute does not have a pending_value
  967. * or deletion_csn which is before the current_value.
  968. */
  969. i= slapi_attr_first_value(a,&current_value);
  970. if(i!=-1)
  971. {
  972. slapi_attr_next_value(a,i,&new_value);
  973. }
  974. attr_first_deleted_value(a,&pending_value);
  975. /* purge_attribute_state_single_valued */
  976. adcsn= attr_get_deletion_csn(a);
  977. current_value_vucsn= value_get_csn(current_value, CSN_TYPE_VALUE_UPDATED);
  978. pending_value_vucsn= value_get_csn(pending_value, CSN_TYPE_VALUE_UPDATED);
  979. if((pending_value!=NULL && (csn_compare(adcsn, pending_value_vucsn)<0)) ||
  980. (pending_value==NULL && (csn_compare(adcsn, current_value_vucsn)<0)))
  981. {
  982. attr_set_deletion_csn(a,NULL);
  983. adcsn= NULL;
  984. }
  985. if(new_value==NULL)
  986. {
  987. /* check if the pending value should become the current value */
  988. if(pending_value!=NULL)
  989. {
  990. if(!value_distinguished_at_csn(e,a,current_value,pending_value_vucsn))
  991. {
  992. /* attribute.current_value = attribute.pending_value; */
  993. /* attribute.pending_value = NULL; */
  994. entry_present_value_to_zapped_value(a,current_value);
  995. entry_deleted_value_to_present_value(a,pending_value);
  996. current_value= pending_value;
  997. pending_value= NULL;
  998. current_value_vucsn= pending_value_vucsn;
  999. pending_value_vucsn= NULL;
  1000. }
  1001. }
  1002. /* check if the current value should be deleted */
  1003. if(current_value!=NULL)
  1004. {
  1005. if(csn_compare(adcsn,current_value_vucsn)>0) /* check if the attribute was deleted after the value was last updated */
  1006. {
  1007. if(!value_distinguished_at_csn(e,a,current_value,current_value_vucsn))
  1008. {
  1009. entry_present_value_to_zapped_value(a,current_value);
  1010. current_value= NULL;
  1011. current_value_vucsn= NULL;
  1012. }
  1013. }
  1014. }
  1015. }
  1016. else /* addition of a new value */
  1017. {
  1018. const CSN *new_value_vucsn= value_get_csn(new_value,CSN_TYPE_VALUE_UPDATED);
  1019. if(csn_compare(new_value_vucsn,current_value_vucsn)<0)
  1020. {
  1021. /*
  1022. * if the new value was distinguished at the time the current value was added
  1023. * then the new value should become current
  1024. */
  1025. if(value_distinguished_at_csn(e,a,new_value,current_value_vucsn))
  1026. {
  1027. /* attribute.pending_value = attribute.current_value */
  1028. /* attribute.current_value = new_value */
  1029. if(pending_value==NULL)
  1030. {
  1031. entry_present_value_to_deleted_value(a,current_value);
  1032. }
  1033. else
  1034. {
  1035. entry_present_value_to_zapped_value(a,current_value);
  1036. }
  1037. pending_value= current_value;
  1038. current_value= new_value;
  1039. new_value= NULL;
  1040. pending_value_vucsn= current_value_vucsn;
  1041. current_value_vucsn= new_value_vucsn;
  1042. }
  1043. else
  1044. {
  1045. /* new_value= NULL */
  1046. entry_present_value_to_zapped_value(a, new_value);
  1047. new_value= NULL;
  1048. }
  1049. }
  1050. else /* new value is after the current value */
  1051. {
  1052. if(!value_distinguished_at_csn(e, a, current_value, new_value_vucsn))
  1053. {
  1054. /* attribute.current_value = new_value */
  1055. entry_present_value_to_zapped_value(a, current_value);
  1056. current_value= new_value;
  1057. new_value= NULL;
  1058. current_value_vucsn= new_value_vucsn;
  1059. }
  1060. else /* value is distinguished - check if we should replace the current pending value */
  1061. {
  1062. if(csn_compare(new_value_vucsn, pending_value_vucsn)>0)
  1063. {
  1064. /* attribute.pending_value = new_value */
  1065. entry_deleted_value_to_zapped_value(a,pending_value);
  1066. entry_present_value_to_deleted_value(a,new_value);
  1067. pending_value= new_value;
  1068. new_value= NULL;
  1069. pending_value_vucsn= new_value_vucsn;
  1070. }
  1071. }
  1072. }
  1073. }
  1074. /*
  1075. * This call ensures that the attribute does not have a pending_value
  1076. * or a deletion_csn that is earlier than the current_value.
  1077. */
  1078. /* purge_attribute_state_single_valued */
  1079. if((pending_value!=NULL && (csn_compare(adcsn, pending_value_vucsn)<0)) ||
  1080. (pending_value==NULL && (csn_compare(adcsn, current_value_vucsn)<0)))
  1081. {
  1082. attr_set_deletion_csn(a,NULL);
  1083. adcsn= NULL;
  1084. }
  1085. /* set attribute state */
  1086. if(current_value==NULL)
  1087. {
  1088. if(attribute_state==ATTRIBUTE_PRESENT)
  1089. {
  1090. entry_present_attribute_to_deleted_attribute(e, a);
  1091. }
  1092. }
  1093. else
  1094. {
  1095. if(attribute_state==ATTRIBUTE_DELETED)
  1096. {
  1097. entry_deleted_attribute_to_present_attribute(e, a);
  1098. }
  1099. }
  1100. }
  1101. static void
  1102. resolve_attribute_state(Slapi_Entry *e, Slapi_Attr *a, int attribute_state)
  1103. {
  1104. if(slapi_attr_flag_is_set(a,SLAPI_ATTR_FLAG_SINGLE))
  1105. {
  1106. resolve_attribute_state_single_valued(e,a,attribute_state);
  1107. }
  1108. else
  1109. {
  1110. resolve_attribute_state_multi_valued(e,a,attribute_state);
  1111. }
  1112. }