entrywsi.c 33 KB

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