| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056 |
- /** BEGIN COPYRIGHT BLOCK
- * This Program is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License as published by the Free Software
- * Foundation; version 2 of the License.
- *
- * This Program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
- * Place, Suite 330, Boston, MA 02111-1307 USA.
- *
- * In addition, as a special exception, Red Hat, Inc. gives You the additional
- * right to link the code of this Program with code not covered under the GNU
- * General Public License ("Non-GPL Code") and to distribute linked combinations
- * including the two, subject to the limitations in this paragraph. Non-GPL Code
- * permitted under this exception must only link to the code of this Program
- * through those well defined interfaces identified in the file named EXCEPTION
- * found in the source code files (the "Approved Interfaces"). The files of
- * Non-GPL Code may instantiate templates or use macros or inline functions from
- * the Approved Interfaces without causing the resulting work to be covered by
- * the GNU General Public License. Only Red Hat, Inc. may make changes or
- * additions to the list of Approved Interfaces. You must obey the GNU General
- * Public License in all respects for all of the Program code and other code used
- * in conjunction with the Program except the Non-GPL Code covered by this
- * exception. If you modify this file, you may extend this exception to your
- * version of the file, but you are not obligated to do so. If you do not wish to
- * provide this exception without modification, you must delete this exception
- * statement from your version and license this file solely under the GPL without
- * exception.
- *
- *
- * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
- * Copyright (C) 2005 Red Hat, Inc.
- * All rights reserved.
- * END COPYRIGHT BLOCK **/
- #ifdef HAVE_CONFIG_H
- # include <config.h>
- #endif
- /* schema.c - routines to enforce schema definitions */
- #include <stdio.h>
- #include <string.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <prio.h>
- #include <plstr.h>
- #include <plhash.h>
- #include "slap.h"
- typedef struct sizedbuffer
- {
- char *buffer;
- size_t size;
- } sizedbuffer;
- typedef char *(*schema_strstr_fn_t)( const char *big, const char *little);
- /*
- * The schema_oc_kind_strings array is indexed by oc_kind values, i.e.,
- * OC_KIND_STRUCTURAL (0), OC_KIND_AUXILIARY (1), or OC_KIND_ABSTRACT (2).
- * The leading and trailing spaces are intentional.
- */
- #define SCHEMA_OC_KIND_COUNT 3
- static char *schema_oc_kind_strings_with_spaces[] = {
- " STRUCTURAL ",
- " AUXILIARY ",
- " ABSTRACT ",
- };
- /* constant strings (used in a few places) */
- static const char *schema_obsolete_with_spaces = " OBSOLETE ";
- static const char *schema_collective_with_spaces = " COLLECTIVE ";
- static const char *schema_nousermod_with_spaces = " NO-USER-MODIFICATION ";
- /* user defined origin array */
- static char *schema_user_defined_origin[] = {
- "user defined",
- NULL
- };
- /*
- * pschemadse is based on the general implementation in dse
- */
- static struct dse *pschemadse= NULL;
- static void oc_add_nolock(struct objclass *newoc);
- static int oc_delete_nolock (char *ocname);
- static int oc_replace_nolock(const char *ocname, struct objclass *newoc);
- static int oc_check_required(Slapi_PBlock *, Slapi_Entry *,struct objclass *);
- static int oc_check_allowed_sv(Slapi_PBlock *, Slapi_Entry *e, const char *type, struct objclass **oclist );
- static char **read_dollar_values ( char *vals);
- static int schema_delete_objectclasses ( Slapi_Entry *entryBefore,
- LDAPMod *mod, char *errorbuf, size_t errorbufsize,
- int schema_ds4x_compat );
- static int schema_delete_attributes ( Slapi_Entry *entryBefore,
- LDAPMod *mod, char *errorbuf, size_t errorbufsize);
- static int schema_add_attribute ( Slapi_PBlock *pb, LDAPMod *mod,
- char *errorbuf, size_t errorbufsize, int schema_ds4x_compat );
- static int schema_add_objectclass ( Slapi_PBlock *pb, LDAPMod *mod,
- char *errorbuf, size_t errorbufsize, int schema_ds4x_compat );
- static int schema_replace_attributes ( Slapi_PBlock *pb, LDAPMod *mod,
- char *errorbuf, size_t errorbufsize );
- static int schema_replace_objectclasses ( Slapi_PBlock *pb, LDAPMod *mod,
- char *errorbuf, size_t errorbufsize );
- static int read_oc_ldif ( const char *input, struct objclass **oc,
- char *errorbuf, size_t errorbufsize, PRUint32 flags, int is_user_defined,
- int schema_ds4x_compat );
- static int schema_check_name(char *name, PRBool isAttribute, char *errorbuf,
- size_t errorbufsize );
- static int schema_check_oid(const char *name, const char *oid,
- PRBool isAttribute, char *errorbuf, size_t errorbufsize);
- static int has_smart_referral( Slapi_Entry *e );
- static int isExtensibleObjectclass(const char *objectclass);
- static int strip_oc_options ( struct objclass *poc );
- static char *stripOption (char *attr);
- static int read_at_ldif(const char *input, struct asyntaxinfo **asipp,
- char *errorbuf, size_t errorbufsize, PRUint32 flags,
- int is_user_defined, int schema_ds4x_compat, int is_remote);
- static char **parse_qdlist(const char *s, int *n, int strip_options);
- static void free_qdlist(char **vals, int n);
- static char **parse_qdescrs(const char *s, int *n);
- static char **parse_qdstrings(const char *s, int *n);
- static int get_flag_keyword( const char *keyword, int flag_value,
- const char **inputp, schema_strstr_fn_t strstr_fn );
- static char *get_tagged_oid( const char *tag, const char **inputp,
- schema_strstr_fn_t strstr_fn );
- static int put_tagged_oid( char *outp, const char *tag, const char *oid,
- const char *suffix, int enquote );
- static void strcat_oids( char *buf, char *prefix, char **oids,
- int schema_ds4x_compat );
- static size_t strcat_qdlist( char *buf, char *prefix, char **qdlist );
- static size_t strlen_null_ok(const char *s);
- static int strcpy_count( char *dst, const char *src );
- static int element_is_user_defined( char * const * origins );
- static char **parse_origin_list( const char *schema_value, int *num_originsp,
- char **default_list );
- static int refresh_user_defined_schema(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode, char *returntext, void *arg);
- static int schema_check_oc_attrs ( struct objclass *poc, char *errorbuf,
- size_t errorbufsize, int stripOptions );
- static struct objclass *oc_find_nolock( const char *ocname_or_oid );
- static struct objclass *oc_find_oid_nolock( const char *ocoid );
- static void oc_free( struct objclass **ocp );
- static PRBool oc_equal( struct objclass *oc1, struct objclass *oc2 );
- static PRBool attr_syntax_equal( struct asyntaxinfo *asi1,
- struct asyntaxinfo *asi2 );
- static int schema_strcmp( const char *s1, const char *s2 );
- static int schema_strcmp_array( char **sa1, char **sa2,
- const char *ignorestr );
- static PRBool schema_type_is_interesting( const char *type );
- static void schema_create_errormsg( char *errorbuf, size_t errorbufsize,
- const char *prefix, const char *name, const char *fmt, ... )
- #ifdef __GNUC__
- __attribute__ ((format (printf, 5, 6)));
- #else
- ;
- #endif
- /* Some utility functions for dealing with a dynamic buffer */
- static struct sizedbuffer *sizedbuffer_construct(size_t size);
- static void sizedbuffer_destroy(struct sizedbuffer *p);
- static void sizedbuffer_allocate(struct sizedbuffer *p, size_t sizeneeded);
- /*
- * Constant strings that we pass to schema_create_errormsg().
- */
- static const char *schema_errprefix_oc = "object class %s: ";
- static const char *schema_errprefix_at = "attribute type %s: ";
- static const char *schema_errprefix_generic = "%s: ";
- /*
- * A "cached" copy of the "ignore trailing spaces" config. setting.
- * This is set during initialization only (server restart required for
- * changes to take effect). We do things this way to avoid lock/unlock
- * mutex sequences inside performance critical code.
- */
- static int schema_ignore_trailing_spaces =
- SLAPD_DEFAULT_SCHEMA_IGNORE_TRAILING_SPACES;
- /* R/W lock used to serialize access to the schema DSE */
- static PRRWLock *schema_dse_lock = NULL;
- /*
- * The schema_dse_mandatory_init_callonce structure is used by NSPR to ensure
- * that schema_dse_mandatory_init() is called at most once.
- */
- static PRCallOnceType schema_dse_mandatory_init_callonce = { 0, 0, 0 };
- /* Essential initialization. Returns PRSuccess if successful */
- static PRStatus
- schema_dse_mandatory_init( void )
- {
- if ( NULL == ( schema_dse_lock = PR_NewRWLock( PR_RWLOCK_RANK_NONE,
- "schema DSE rwlock" ))) {
- slapi_log_error( SLAPI_LOG_FATAL, "schema_dse_mandatory_init",
- "PR_NewRWLock() for schema DSE lock failed\n" );
- return PR_FAILURE;
- }
- schema_ignore_trailing_spaces = config_get_schema_ignore_trailing_spaces();
- return PR_SUCCESS;
- }
- static void
- schema_dse_lock_read( void )
- {
- if ( NULL != schema_dse_lock ||
- PR_SUCCESS == PR_CallOnce( &schema_dse_mandatory_init_callonce,
- schema_dse_mandatory_init )) {
- PR_RWLock_Rlock( schema_dse_lock );
- }
- }
- static void
- schema_dse_lock_write( void )
- {
- if ( NULL != schema_dse_lock ||
- PR_SUCCESS == PR_CallOnce( &schema_dse_mandatory_init_callonce,
- schema_dse_mandatory_init )) {
- PR_RWLock_Wlock( schema_dse_lock );
- }
- }
- static void
- schema_dse_unlock( void )
- {
- if ( schema_dse_lock != NULL ) {
- PR_RWLock_Unlock( schema_dse_lock );
- }
- }
- static int
- dont_allow_that(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode, char *returntext, void *arg)
- {
- *returncode = LDAP_UNWILLING_TO_PERFORM;
- return SLAPI_DSE_CALLBACK_ERROR;
- }
- #if 0
- /*
- * hashNocaseString - used for case insensitive hash lookups
- */
- static PLHashNumber
- hashNocaseString(const void *key)
- {
- PLHashNumber h = 0;
- const unsigned char *s;
- for (s = key; *s; s++)
- h = (h >> 28) ^ (h << 4) ^ (tolower(*s));
- return h;
- }
- #endif
- static const char *
- skipWS(const char *s)
- {
- while (s && isascii(*s) && isspace(*s) )
- ++s;
- if ((isascii(*s)) == 0) {
- return NULL;
- }
- return s;
- }
- /*
- * like strchr() but strings within single quotes are skipped.
- */
- static char *
- strchr_skip_quoted_strings( char *s, int c )
- {
- int in_quote = 0;
- while ( *s != '\0' ) {
- if ( *s == '\'' ) {
- in_quote = 1 - in_quote; /* toggle */
- } else if ( !in_quote && *s == c ) {
- return s;
- }
- ++s;
- }
- return( NULL );
- }
- /**
- * parses a string containing a qdescrs or qdstrings (as described by
- * RFC 2252, section 4.1) into an array of strings; the second parameter
- * will hold the actual number of strings in the array. The returned array
- * is NULL terminated.
- *
- * This function can handle qdescrs or qdstrings because the only
- * difference between the two is that fewer characters are allowed in
- * a qdescr (our parsing code does not check anyway) and we want to
- * strip attribute options when parsing qdescrs (indicated by a non-zero
- * strip_options parameter).
- */
- static char **
- parse_qdlist(const char *s, int *n, int strip_options)
- {
- char **retval = 0;
- char *work = 0;
- char *start = 0, *end = 0;
- int num = 0;
- int in_quote = 0;
- if (n)
- *n = 0;
- if (!s || !*s || !n) {
- return retval;
- }
- /* make a working copy of the given string */
- work = slapi_ch_strdup(s);
- /* count the number of qdescr items in the string e.g. just count
- the number of spaces */
- /* for a single qdescr, the terminal character will be the final
- single quote; for a qdesclist, the terminal will be the close
- parenthesis */
- end = strrchr(work, '\'');
- if ((start = strchr_skip_quoted_strings(work, '(')) != NULL)
- end = strchr_skip_quoted_strings(work, ')');
- else
- start = strchr(work, '\'');
- if (!end) /* already nulled out */
- end = work + strlen(work);
- if (start) {
- num = 1;
- /* first pass: count number of items and zero out non useful tokens */
- for (; *start && (start != end); ++start) {
- if (*start == '\'' ) {
- in_quote = 1 - in_quote; /* toggle */
- *start = 0;
- } else if ( !in_quote && ((*start == ' ') || (*start == '(') ||
- (*start == ')'))) {
- if (*start == ' ') {
- num++;
- }
- *start = 0;
- }
- }
- *start = 0;
- /* allocate retval; num will be >= actual number of items */
- retval = (char**)slapi_ch_calloc(num+1, sizeof(char *));
- /* second pass: copy strings into the return value and set the
- actual number of items returned */
- start = work;
- while (start != end) {
- /* skip over nulls */
- while (!*start && (start != end))
- ++start;
- if (start == end)
- break;
- retval[*n] = slapi_ch_strdup(start);
- /*
- * A qdescr list may contain attribute options; we just strip
- * them here. In the future, we may want to support them or do
- * something really fancy with them
- */
- if ( strip_options ) {
- stripOption(retval[*n]);
- }
- (*n)++;
- start += strlen(start);
- }
- PR_ASSERT( *n <= num ); /* sanity check */
- retval[*n] = NULL;
- } else {
- /* syntax error - no start and/or end delimiters */
- }
- /* free the working string */
- slapi_ch_free((void **)&work);
- return retval;
- }
- static void
- free_qdlist(char **vals, int n)
- {
- int ii;
- for (ii = 0; ii < n; ++ii)
- slapi_ch_free((void **)&(vals[ii]));
- slapi_ch_free((void **)&vals);
- }
- /**
- * parses a string containing a qdescrs (as described by RFC 2252, section 4.1)
- * into an array of strings; the second parameter will hold the actual number
- * of strings in the array. The returned array is NULL terminated.
- */
- static char **
- parse_qdescrs(const char *s, int *n)
- {
- return parse_qdlist( s, n, 1 /* strip attribute options */ );
- }
- /*
- * Parses a string containing a qdstrings (see RFC 2252, section 4.1) into
- * an array of strings; the second parameter will hold the actual number
- * of strings in the array.
- */
- static char **
- parse_qdstrings(const char *s, int *n)
- {
- return parse_qdlist( s, n, 0 /* DO NOT strip attribute options */ );
- }
- /*
- * slapi_entry_schema_check - check that entry e conforms to the schema
- * required by its object class(es). returns 0 if so, non-zero otherwise.
- * [ the pblock is only used to check if this is a replicated operation.
- * you may pass in NULL if this isn't part of an operation. ]
- */
- int
- slapi_entry_schema_check( Slapi_PBlock *pb, Slapi_Entry *e )
- {
- struct objclass **oclist;
- struct objclass *oc;
- const char *ocname;
- Slapi_Attr *a, *aoc;
- Slapi_Value *v;
- int ret = 0;
- int schemacheck = config_get_schemacheck();
- int is_replicated_operation = 0;
- int is_extensible_object = 0;
- int i, oc_count = 0;
- int unknown_class = 0;
- char errtext[ BUFSIZ ];
- PRUint32 schema_flags = 0;
- /* smart referrals are not allowed in Directory Lite */
- if ( config_is_slapd_lite() ) {
- if ( has_smart_referral(e) ) {
- return 1;
- }
- }
-
- /*
- * say the schema checked out ok if we're not checking schema at
- * all, or if this is a replication update.
- */
- if (pb != NULL) {
- slapi_pblock_get(pb, SLAPI_IS_REPLICATED_OPERATION, &is_replicated_operation);
- slapi_pblock_get(pb, SLAPI_SCHEMA_FLAGS, &schema_flags);
- }
- if ( schemacheck == 0 || is_replicated_operation ) {
- return( 0 );
- }
- /* find the object class attribute - could error out here */
- if ( (aoc = attrlist_find( e->e_attrs, "objectclass" )) == NULL ) {
- char ebuf[ BUFSIZ ];
- LDAPDebug( LDAP_DEBUG_ANY,
- "Entry \"%s\" required attribute \"objectclass\" missing\n",
- escape_string( slapi_entry_get_dn_const(e), ebuf ), 0, 0 );
- if (pb) {
- PR_snprintf( errtext, sizeof( errtext ),
- "missing required attribute \"objectclass\"\n" );
- slapi_pblock_set( pb, SLAPI_PB_RESULT_TEXT, errtext );
- }
- return( 1 );
- }
- /*
- * Create an array of pointers to the objclass definitions.
- */
- i= slapi_attr_first_value(aoc,&v);
- while (i != -1) {
- oc_count++;
- i= slapi_attr_next_value(aoc,i,&v);
- }
- oclist = (struct objclass**)
- slapi_ch_malloc((oc_count+1)*sizeof(struct objclass*));
- /*
- * Need the read lock to create the oc array and while we use it.
- */
- if (!(schema_flags & DSE_SCHEMA_LOCKED))
- oc_lock_read();
- oc_count = 0;
- for (i= slapi_attr_first_value(aoc,&v); i != -1;
- i= slapi_attr_next_value(aoc,i,&v)) {
- ocname = slapi_value_get_string(v);
- if ( isExtensibleObjectclass( ocname )) {
- /*
- * if the entry is an extensibleObject, just check to see if
- * the required attributes for whatever other objectclasses the
- * entry might be are present. All other attributes are allowed
- */
- is_extensible_object = 1;
- continue;
- }
- if ((oc = oc_find_nolock( ocname )) != NULL ) {
- oclist[oc_count++] = oc;
- } else {
- /* we don't know about the oc; return an appropriate error message */
- char ebuf[ BUFSIZ ];
- char ebuf2[ BUFSIZ ];
- size_t ocname_len = ( ocname == NULL ) ? 0 : strlen( ocname );
- const char *extra_msg = "";
- if ( ocname_len > 0 && isspace( ocname[ ocname_len-1 ] )) {
- if ( ocname_len > 1 && isspace( ocname[ ocname_len-2 ] )) {
- extra_msg = " (remove the trailing spaces)";
- } else {
- extra_msg = " (remove the trailing space)";
- }
- }
- LDAPDebug( LDAP_DEBUG_ANY,
- "Entry \"%s\" has unknown object class \"%s\"%s\n",
- escape_string( slapi_entry_get_dn_const(e), ebuf ),
- escape_string(ocname, ebuf2), extra_msg );
- if (pb) {
- PR_snprintf( errtext, sizeof( errtext ),
- "unknown object class \"%s\"%s\n",
- escape_string(ocname, ebuf2), extra_msg );
- slapi_pblock_set( pb, SLAPI_PB_RESULT_TEXT, errtext );
- }
- unknown_class = 1;
- }
- }
- oclist[oc_count] = NULL;
- if (unknown_class) {
- /* failure */
- ret = 1;
- goto out;
- }
- /*
- * go through all the checking so we can log everything
- * wrong with the entry. some day, we might want to return
- * this information to the client as an error message.
- */
- /*
- * check that the entry has required attrs for each oc
- */
- for (i = 0; oclist[i] != NULL; i++) {
- if ( oc_check_required( pb, e, oclist[i] ) != 0 ) {
- ret = 1;
- goto out;
- }
- }
- /*
- * check that each attr in the entry is allowed by some oc,
- * and that single-valued attrs only have one value
- */
- {
- Slapi_Attr *prevattr;
- i = slapi_entry_first_attr(e, &a);
- while (-1 != i && 0 == ret)
- {
- if (is_extensible_object == 0 &&
- unknown_class == 0 &&
- !slapi_attr_flag_is_set(a, SLAPI_ATTR_FLAG_OPATTR))
- {
- char *attrtype;
- slapi_attr_get_type(a, &attrtype);
- if (oc_check_allowed_sv(pb, e, attrtype, oclist) != 0)
- {
- ret = 1;
- }
- }
-
- if ( slapi_attr_flag_is_set( a, SLAPI_ATTR_FLAG_SINGLE ) ) {
- if (slapi_valueset_count(&a->a_present_values) > 1)
- {
- char ebuf[ BUFSIZ ];
- LDAPDebug( LDAP_DEBUG_ANY,
- "Entry \"%s\" single-valued attribute \"%s\" has multiple values\n",
- escape_string( slapi_entry_get_dn_const(e), ebuf ),
- a->a_type, 0 );
- if (pb) {
- PR_snprintf( errtext, sizeof( errtext ),
- "single-valued attribute \"%s\" has multiple values\n",
- a->a_type );
- slapi_pblock_set( pb, SLAPI_PB_RESULT_TEXT, errtext );
- }
- ret = 1;
- }
- }
- prevattr = a;
- i = slapi_entry_next_attr(e, prevattr, &a);
- }
- }
- out:
- /* Done with the oc array so can release the lock */
- if (!(schema_flags & DSE_SCHEMA_LOCKED))
- oc_unlock();
- slapi_ch_free((void**)&oclist);
- return( ret );
- }
- /*
- * The caller must obtain a read lock first by calling oc_lock_read().
- */
- static int
- oc_check_required( Slapi_PBlock *pb, Slapi_Entry *e, struct objclass *oc )
- {
- int i;
- int rc = 0; /* success, by default */
- Slapi_Attr *a;
- if (oc == NULL || oc->oc_required == NULL || oc->oc_required[0] == NULL) {
- return 0; /* success, as none required */
- }
- /* for each required attribute */
- for ( i = 0; oc->oc_required[i] != NULL; i++ ) {
- /* see if it's in the entry */
- for ( a = e->e_attrs; a != NULL; a = a->a_next ) {
- if ( slapi_attr_type_cmp( oc->oc_required[i], a->a_type,
- SLAPI_TYPE_CMP_SUBTYPE ) == 0 ) {
- break;
- }
- }
-
- /* not there => schema violation */
- if ( a == NULL ) {
- char errtext[ BUFSIZ ];
- char ebuf[ BUFSIZ ];
- LDAPDebug( LDAP_DEBUG_ANY,
- "Entry \"%s\" missing attribute \"%s\" required"
- " by object class \"%s\"\n",
- escape_string( slapi_entry_get_dn_const(e), ebuf ),
- oc->oc_required[i], oc->oc_name);
- if (pb) {
- PR_snprintf( errtext, sizeof( errtext ),
- "missing attribute \"%s\" required"
- " by object class \"%s\"\n",
- oc->oc_required[i], oc->oc_name );
- slapi_pblock_set( pb, SLAPI_PB_RESULT_TEXT, errtext );
- }
- rc = 1; /* failure */
- }
- }
-
- return rc;
- }
- /*
- * The caller must obtain a read lock first by calling oc_lock_read().
- */
- static int
- oc_check_allowed_sv(Slapi_PBlock *pb, Slapi_Entry *e, const char *type, struct objclass **oclist )
- {
- struct objclass *oc;
- int i, j;
- int rc = 1; /* failure */
-
- /* always allow objectclass and entryid attributes */
- /* MFW XXX THESE SHORTCUTS SHOULD NOT BE NECESSARY BUT THEY MASK
- * MFW XXX OTHER BUGS IN THE SERVER.
- */
- if ( slapi_attr_type_cmp( type, "objectclass", SLAPI_TYPE_CMP_EXACT ) == 0 ) {
- return( 0 );
- } else if ( slapi_attr_type_cmp( type, "entryid", SLAPI_TYPE_CMP_EXACT ) == 0 ) {
- return( 0 );
- }
-
- /* check that the type appears as req or opt in at least one oc */
- for (i = 0; rc != 0 && oclist[i] != NULL; i++) {
- oc = oclist[i];
- /* does it require the type? */
- for ( j = 0; oc->oc_required && oc->oc_required[j] != NULL; j++ ) {
- if ( slapi_attr_type_cmp( oc->oc_required[j],
- type, SLAPI_TYPE_CMP_SUBTYPE ) == 0 ) {
- rc = 0;
- break;
- }
- }
-
- if ( 0 != rc ) {
- /* does it allow the type? */
- for ( j = 0; oc->oc_allowed && oc->oc_allowed[j] != NULL; j++ ) {
- if ( slapi_attr_type_cmp( oc->oc_allowed[j],
- type, SLAPI_TYPE_CMP_SUBTYPE ) == 0 ||
- strcmp( oc->oc_allowed[j],"*" ) == 0 ) {
- rc = 0;
- break;
- }
- }
- /* maybe the next oc allows it */
- }
- }
- if ( 0 != rc ) {
- char errtext[ BUFSIZ ];
- char ebuf[ BUFSIZ ];
- char ebuf2[ BUFSIZ ];
- LDAPDebug( LDAP_DEBUG_ANY,
- "Entry \"%s\" -- attribute \"%s\" not allowed\n",
- escape_string( slapi_entry_get_dn_const(e), ebuf ),
- escape_string( type, ebuf2 ),
- 0);
- if (pb) {
- PR_snprintf( errtext, sizeof( errtext ),
- "attribute \"%s\" not allowed\n",
- escape_string( type, ebuf2 ) );
- slapi_pblock_set( pb, SLAPI_PB_RESULT_TEXT, errtext );
- }
- }
-
- return rc;
- }
- /*
- * oc_find_name() will return a strdup'd string or NULL if the objectclass
- * could not be found.
- */
- char *
- oc_find_name( const char *name_or_oid )
- {
- struct objclass *oc;
- char *ocname = NULL;
- oc_lock_read();
- if ( NULL != ( oc = oc_find_nolock( name_or_oid ))) {
- ocname = slapi_ch_strdup( oc->oc_name );
- }
- oc_unlock();
- return ocname;
- }
- /*
- * oc_find_nolock will return a pointer to the objectclass which has the
- * same name OR oid.
- * NULL is returned if no match is found or `name_or_oid' is NULL.
- */
- static struct objclass *
- oc_find_nolock( const char *ocname_or_oid )
- {
- struct objclass *oc;
- if ( NULL != ocname_or_oid ) {
- if ( !schema_ignore_trailing_spaces ) {
- for ( oc = g_get_global_oc_nolock(); oc != NULL; oc = oc->oc_next ) {
- if ( ( strcasecmp( oc->oc_name, ocname_or_oid ) == 0 )
- || ( oc->oc_oid &&
- strcasecmp( oc->oc_oid, ocname_or_oid ) == 0 )) {
- return( oc );
- }
- }
- } else {
- const char *p;
- size_t len;
- /*
- * Ignore trailing spaces when comparing object class names.
- */
- for ( p = ocname_or_oid, len = 0; (*p != '\0') && (*p != ' ');
- p++, len++ ) {
- ; /* NULL */
- }
- for ( oc = g_get_global_oc_nolock(); oc != NULL; oc = oc->oc_next ) {
- if ( ( (strncasecmp( oc->oc_name, ocname_or_oid, len ) == 0)
- && (len == strlen(oc->oc_name)) )
- ||
- ( oc->oc_oid &&
- ( strncasecmp( oc->oc_oid, ocname_or_oid, len ) == 0)
- && (len == strlen(oc->oc_oid)) ) ) {
- return( oc );
- }
- }
- }
- }
- return( NULL );
- }
- /*
- * oc_find_oid_nolock will return a pointer to the objectclass which has
- * the same oid.
- * NULL is returned if no match is found or `ocoid' is NULL.
- */
- static struct objclass *
- oc_find_oid_nolock( const char *ocoid )
- {
- struct objclass *oc;
- if ( NULL != ocoid ) {
- for ( oc = g_get_global_oc_nolock(); oc != NULL; oc = oc->oc_next ) {
- if ( ( oc->oc_oid &&
- ( strcasecmp( oc->oc_oid, ocoid ) == 0)) ){
- return( oc );
- }
- }
- }
- return( NULL );
- }
- /*
- We need to keep the objectclasses in the same order as defined in the ldif files. If not
- SUP dependencies will break. When the user redefines an existing objectclass this code
- makes sure it is put back in the same order it was read to from the ldif file. It also
- verifies that the entries oc_superior value preceeds it in the chain. If not it will not
- allow the entry to be added. This makes sure that the ldif will be written back correctly.
- */
- static int
- oc_replace_nolock(const char *ocname, struct objclass *newoc) {
- struct objclass *oc, *pnext;
- int rc = LDAP_SUCCESS;
- PRBool saw_sup=PR_FALSE;
-
- oc = g_get_global_oc_nolock();
-
- if(newoc->oc_superior == NULL)
- {
- saw_sup=PR_TRUE;
- }
- /* don't check SUP dependency for first one because it always/should be top */
- if (strcasecmp (oc->oc_name, ocname) == 0) {
- newoc->oc_next=oc->oc_next;
- g_set_global_oc_nolock ( newoc );
- oc_free( &oc );
- } else {
- for (pnext = oc ; pnext != NULL;
- oc = pnext, pnext = pnext->oc_next) {
- if((pnext->oc_name != NULL) && (newoc->oc_superior != NULL)) {
- if(strcasecmp( pnext->oc_name, newoc->oc_superior) == 0)
- {
- saw_sup=PR_TRUE;
- }
- }
- if (strcasecmp ( pnext->oc_name, ocname ) == 0) {
- if(saw_sup)
- {
- oc->oc_next=newoc;
- newoc->oc_next=pnext->oc_next;
- oc_free( &pnext );
- break;
-
- } else
- {
- rc = LDAP_TYPE_OR_VALUE_EXISTS;
- break;
- }
-
- }
- }
- }
- return rc;
- }
- static int
- oc_delete_nolock (char *ocname)
- {
- struct objclass *oc, *pnext;
- int rc = 0; /* failure */
- oc = g_get_global_oc_nolock();
-
- /* special case if we're removing the first oc */
- if (strcasecmp (oc->oc_name, ocname) == 0) {
- g_set_global_oc_nolock ( oc->oc_next );
- oc_free( &oc );
- rc = 1;
- } else {
- for (pnext = oc->oc_next ; pnext != NULL;
- oc = pnext, pnext = pnext->oc_next) {
- if (strcasecmp ( pnext->oc_name, ocname ) == 0) {
- oc->oc_next = pnext->oc_next;
- oc_free( &pnext );
- rc = 1;
- break;
- }
- }
- }
- return rc;
- }
- static void
- oc_delete_all_nolock( void )
- {
- struct objclass *oc, *pnext;
- oc = g_get_global_oc_nolock();
- for (pnext = oc->oc_next; oc;
- oc = pnext, pnext = oc?oc->oc_next:NULL) {
- oc_free( &oc );
- }
- g_set_global_oc_nolock ( NULL );
- }
- /*
- * Compare two objectclass definitions for equality. Return PR_TRUE if
- * they are equivalent and PR_FALSE if not.
- *
- * The oc_required and oc_allowed arrays are ignored.
- * The string "user defined" is ignored within the origins array.
- * The following flags are ignored:
- * OC_FLAG_STANDARD_OC
- * OC_FLAG_USER_OC
- * OC_FLAG_REDEFINED_OC
- */
- static PRBool
- oc_equal( struct objclass *oc1, struct objclass *oc2 )
- {
- PRUint8 flagmask;
- if ( schema_strcmp( oc1->oc_name, oc2->oc_name ) != 0
- || schema_strcmp( oc1->oc_desc, oc2->oc_desc ) != 0
- || schema_strcmp( oc1->oc_oid, oc2->oc_oid ) != 0
- || schema_strcmp( oc1->oc_superior, oc2->oc_superior ) != 0 ) {
- return PR_FALSE;
- }
- flagmask = ~(OC_FLAG_STANDARD_OC | OC_FLAG_USER_OC | OC_FLAG_REDEFINED_OC);
- if ( oc1->oc_kind != oc2->oc_kind
- || ( oc1->oc_flags & flagmask ) != ( oc2->oc_flags & flagmask )) {
- return PR_FALSE;
- }
- if ( schema_strcmp_array( oc1->oc_orig_required, oc2->oc_orig_required,
- NULL ) != 0
- || schema_strcmp_array( oc1->oc_orig_allowed, oc2->oc_orig_allowed,
- NULL ) != 0
- || schema_strcmp_array( oc1->oc_origin, oc2->oc_origin,
- schema_user_defined_origin[0] ) != 0 ) {
- return PR_FALSE;
- }
- return PR_TRUE;
- }
- #ifdef OC_DEBUG
- static int
- oc_print( struct objclass *oc )
- {
- int i;
- printf( "object class %s\n", oc->oc_name );
- if ( oc->oc_required != NULL ) {
- printf( "\trequires %s", oc->oc_required[0] );
- for ( i = 1; oc->oc_required[i] != NULL; i++ ) {
- printf( ",%s", oc->oc_required[i] );
- }
- printf( "\n" );
- }
- if ( oc->oc_allowed != NULL ) {
- printf( "\tallows %s", oc->oc_allowed[0] );
- for ( i = 1; oc->oc_allowed[i] != NULL; i++ ) {
- printf( ",%s", oc->oc_allowed[i] );
- }
- printf( "\n" );
- }
- return 0;
- }
- #endif
- /*
- * Compare two attrsyntax definitions for equality. Return PR_TRUE if
- * they are equivalent and PR_FALSE if not.
- *
- * The string "user defined" is ignored within the origins array.
- * The following flags are ignored:
- * SLAPI_ATTR_FLAG_STD_ATTR
- * SLAPI_ATTR_FLAG_NOLOCKING
- * SLAPI_ATTR_FLAG_OVERRIDE
- */
- static PRBool
- attr_syntax_equal( struct asyntaxinfo *asi1, struct asyntaxinfo *asi2 )
- {
- unsigned long flagmask;
- flagmask = ~( SLAPI_ATTR_FLAG_STD_ATTR | SLAPI_ATTR_FLAG_NOLOCKING
- | SLAPI_ATTR_FLAG_OVERRIDE );
- if ( schema_strcmp( asi1->asi_oid, asi2->asi_oid ) != 0
- || schema_strcmp( asi1->asi_name, asi2->asi_name ) != 0
- || schema_strcmp( asi1->asi_desc, asi2->asi_desc ) != 0
- || schema_strcmp( asi1->asi_superior, asi2->asi_superior ) != 0
- || schema_strcmp( asi1->asi_mr_equality, asi2->asi_mr_equality )
- != 0
- || schema_strcmp( asi1->asi_mr_ordering, asi2->asi_mr_ordering )
- != 0
- || schema_strcmp( asi1->asi_mr_substring,
- asi2->asi_mr_substring ) != 0 ) {
- return PR_FALSE;
- }
- if ( schema_strcmp_array( asi1->asi_aliases, asi2->asi_aliases, NULL ) != 0
- || schema_strcmp_array( asi1->asi_origin, asi2->asi_origin,
- schema_user_defined_origin[0] ) != 0
- || asi1->asi_plugin != asi2->asi_plugin
- || ( asi1->asi_flags & flagmask ) !=
- ( asi2->asi_flags & flagmask )
- || asi1->asi_syntaxlength != asi2->asi_syntaxlength ) {
- return PR_FALSE;
- }
- return PR_TRUE;
- }
- /*
- * Like strcmp(), but a NULL string pointer is treated as equivalent to
- * another NULL one and NULL is treated as "less than" all non-NULL values.
- */
- static int
- schema_strcmp( const char *s1, const char *s2 )
- {
- if ( s1 == NULL ) {
- if ( s2 == NULL ) {
- return 0; /* equal */
- }
- return -1; /* s1 < s2 */
- }
- if ( s2 == NULL ) {
- return 1; /* s1 > s2 */
- }
- return strcmp( s1, s2 );
- }
- /*
- * Invoke strcmp() on each string in an array. If one array has fewer elements
- * than the other, it is treated as "less than" the other. Two NULL or
- * empty arrays (or one NULL and one empty) are considered to be equivalent.
- *
- * If ignorestr is non-NULL, occurrences of that string are ignored.
- */
- static int
- schema_strcmp_array( char **sa1, char **sa2, const char *ignorestr )
- {
- int i1, i2, rc;
- if ( sa1 == NULL || *sa1 == NULL ) {
- if ( sa2 == NULL || *sa2 == NULL ) {
- return 0; /* equal */
- }
- return -1; /* sa1 < sa2 */
- }
- if ( sa2 == NULL || *sa2 == NULL ) {
- return 1; /* sa1 > sa2 */
- }
- rc = 0;
- i1 = i2 = 0;
- while ( sa1[i1] != NULL && sa2[i2] != NULL ) {
- if ( NULL != ignorestr ) {
- if ( 0 == strcmp( sa1[i1], ignorestr )) {
- ++i1;
- continue;
- }
- if ( 0 == strcmp( sa2[i2], ignorestr )) {
- ++i2;
- continue;
- }
- }
- rc = strcmp( sa1[i1], sa2[i2] );
- ++i1;
- ++i2;
- }
- if ( rc == 0 ) { /* all matched so far */
- /* get rid of trailing ignored strings (if any) */
- if ( NULL != ignorestr ) {
- if ( sa1[i1] != NULL && 0 == strcmp( sa1[i1], ignorestr )) {
- ++i1;
- }
- if ( sa2[i2] != NULL && 0 == strcmp( sa2[i2], ignorestr )) {
- ++i2;
- }
- }
- /* check for differing array lengths */
- if ( sa2[i2] != NULL ) {
- rc = -1; /* sa1 < sa2 -- fewer elements */
- } else if ( sa1[i1] != NULL ) {
- rc = 1; /* sa1 > sa2 -- more elements */
- }
- }
- return rc;
- }
- struct attr_enum_wrapper {
- Slapi_Attr **attrs;
- int enquote_sup_oc;
- struct sizedbuffer *psbAttrTypes;
- int user_defined_only;
- int schema_ds4x_compat;
- };
- static int
- schema_attr_enum_callback(struct asyntaxinfo *asip, void *arg)
- {
- struct attr_enum_wrapper *aew = (struct attr_enum_wrapper *)arg;
- int aliaslen = 0;
- struct berval val;
- struct berval *vals[2] = {0, 0};
- const char *attr_desc, *syntaxoid;
- char *outp, syntaxlengthbuf[ 128 ];
- int i;
- vals[0] = &val;
- if (!asip) {
- LDAPDebug(LDAP_DEBUG_ANY,
- "Error: no attribute types in schema_attr_enum_callback\n",
- 0, 0, 0);
- return ATTR_SYNTAX_ENUM_NEXT;
- }
- if (aew->user_defined_only &&
- (asip->asi_flags & SLAPI_ATTR_FLAG_STD_ATTR)) {
- return ATTR_SYNTAX_ENUM_NEXT; /* not user defined */
- }
- if ( aew->schema_ds4x_compat ) {
- attr_desc = ( asip->asi_flags & SLAPI_ATTR_FLAG_STD_ATTR)
- ? ATTR_STANDARD_STRING : ATTR_USERDEF_STRING;
- } else {
- attr_desc = asip->asi_desc;
- }
- if ( asip->asi_aliases != NULL ) {
- for ( i = 0; asip->asi_aliases[i] != NULL; ++i ) {
- aliaslen += strlen( asip->asi_aliases[i] );
- }
- }
- syntaxoid = plugin_syntax2oid(asip->asi_plugin);
- if ( !aew->schema_ds4x_compat &&
- asip->asi_syntaxlength != SLAPI_SYNTAXLENGTH_NONE ) {
- /* sprintf() is safe because syntaxlengthbuf is large enough */
- sprintf( syntaxlengthbuf, "{%d}", asip->asi_syntaxlength );
- } else {
- *syntaxlengthbuf = '\0';
- }
- /*
- * XXX: 256 is a magic number... it must be big enough to account for
- * all of the fixed sized items we output.
- */
- sizedbuffer_allocate(aew->psbAttrTypes,256+strlen(asip->asi_oid)+
- strlen(asip->asi_name) +
- aliaslen + strlen_null_ok(attr_desc) +
- strlen(syntaxoid) +
- strlen_null_ok(asip->asi_superior) +
- strlen_null_ok(asip->asi_mr_equality) +
- strlen_null_ok(asip->asi_mr_ordering) +
- strlen_null_ok(asip->asi_mr_substring) +
- strcat_qdlist( NULL, "X-ORIGIN", asip->asi_origin ));
- /*
- * Overall strategy is to maintain a pointer to the next location in
- * the output buffer so we can do simple strcpy's, sprintf's, etc.
- * That pointer is `outp'. Each item that is output includes a trailing
- * space, so there is no need to include a leading one in the next item.
- */
- outp = aew->psbAttrTypes->buffer;
- outp += sprintf(outp, "( %s NAME ", asip->asi_oid);
- if ( asip->asi_aliases == NULL || asip->asi_aliases[0] == NULL ) {
- /* only one name */
- outp += sprintf(outp, "'%s' ", asip->asi_name);
- } else {
- /* several names */
- outp += sprintf(outp, "( '%s' ", asip->asi_name);
- for ( i = 0; asip->asi_aliases[i] != NULL; ++i ) {
- outp += sprintf(outp, "'%s' ", asip->asi_aliases[i]);
- }
- outp += strcpy_count(outp, ") ");
- }
- /* DESC is optional */
- if (attr_desc && *attr_desc) {
- outp += sprintf( outp, "DESC '%s'", attr_desc );
- }
- if ( !aew->schema_ds4x_compat &&
- ( asip->asi_flags & SLAPI_ATTR_FLAG_OBSOLETE )) {
- outp += strcpy_count( outp, schema_obsolete_with_spaces );
- } else {
- outp += strcpy_count( outp, " " );
- }
- if ( !aew->schema_ds4x_compat ) {
- outp += put_tagged_oid( outp, "SUP ",
- asip->asi_superior, NULL, aew->enquote_sup_oc );
- outp += put_tagged_oid( outp, "EQUALITY ",
- asip->asi_mr_equality, NULL, aew->enquote_sup_oc );
- outp += put_tagged_oid( outp, "ORDERING ",
- asip->asi_mr_ordering, NULL, aew->enquote_sup_oc );
- outp += put_tagged_oid( outp, "SUBSTR ",
- asip->asi_mr_substring, NULL, aew->enquote_sup_oc );
- }
- outp += put_tagged_oid( outp, "SYNTAX ", syntaxoid, syntaxlengthbuf,
- aew->enquote_sup_oc );
- if (asip->asi_flags & SLAPI_ATTR_FLAG_SINGLE) {
- outp += strcpy_count(outp, "SINGLE-VALUE ");
- }
- if ( !aew->schema_ds4x_compat ) {
- if (asip->asi_flags & SLAPI_ATTR_FLAG_COLLECTIVE ) {
- outp += strcpy_count( outp, 1 + schema_collective_with_spaces );
- }
- if (asip->asi_flags & SLAPI_ATTR_FLAG_NOUSERMOD ) {
- outp += strcpy_count( outp, 1 + schema_nousermod_with_spaces );
- }
- if (asip->asi_flags & SLAPI_ATTR_FLAG_OPATTR) {
- outp += strcpy_count(outp, "USAGE directoryOperation ");
- }
- outp += strcat_qdlist( outp, "X-ORIGIN", asip->asi_origin );
- }
- outp += strcpy_count(outp, ")");
- val.bv_val = aew->psbAttrTypes->buffer;
- val.bv_len = outp - aew->psbAttrTypes->buffer;
- attrlist_merge(aew->attrs, "attributetypes", vals);
- return ATTR_SYNTAX_ENUM_NEXT;
- }
- struct syntax_enum_wrapper {
- Slapi_Attr **attrs;
- struct sizedbuffer *psbSyntaxDescription;
- };
- static int
- schema_syntax_enum_callback(char **names, Slapi_PluginDesc *plugindesc,
- void *arg)
- {
- struct syntax_enum_wrapper *sew = (struct syntax_enum_wrapper *)arg;
- char *oid, *desc;
- int i;
- struct berval val;
- struct berval *vals[2] = {0, 0};
- vals[0] = &val;
- oid = NULL;
- if ( names != NULL ) {
- for ( i = 0; names[i] != NULL; ++i ) {
- if ( isdigit( names[i][0] )) {
- oid = names[i];
- break;
- }
- }
- }
- if ( oid == NULL ) { /* must have an OID */
- LDAPDebug(LDAP_DEBUG_ANY, "Error: no OID found in"
- " schema_syntax_enum_callback for syntax %s\n",
- ( names == NULL ) ? "unknown" : names[0], 0, 0);
- return 1;
- }
- desc = names[0]; /* by convention, the first name is the "official" one */
- /*
- * RFC 2252 section 4.3.3 Syntax Description says:
- *
- * The following BNF may be used to associate a short description with a
- * syntax OBJECT IDENTIFIER. Implementors should note that future
- * versions of this document may expand this definition to include
- * additional terms. Terms whose identifier begins with "X-" are
- * reserved for private experiments, and MUST be followed by a
- * <qdstrings>.
- *
- * SyntaxDescription = "(" whsp
- * numericoid whsp
- * [ "DESC" qdstring ]
- * whsp ")"
- *
- * And section 5.3.1 ldapSyntaxes says:
- *
- * Servers MAY use this attribute to list the syntaxes which are
- * implemented. Each value corresponds to one syntax.
- *
- * ( 1.3.6.1.4.1.1466.101.120.16 NAME 'ldapSyntaxes'
- * EQUALITY objectIdentifierFirstComponentMatch
- * SYNTAX 1.3.6.1.4.1.1466.115.121.1.54 USAGE directoryOperation )
- */
- if ( desc == NULL ) {
- /* allocate enough room for "( )" and '\0' at end */
- sizedbuffer_allocate(sew->psbSyntaxDescription, strlen(oid) + 5);
- sprintf(sew->psbSyntaxDescription->buffer, "( %s )", oid );
- } else {
- /* allocate enough room for "( ) DESC '' " and '\0' at end */
- sizedbuffer_allocate(sew->psbSyntaxDescription,
- strlen(oid) + strlen(desc) + 13);
- sprintf(sew->psbSyntaxDescription->buffer, "( %s DESC '%s' )",
- oid, desc );
- }
- val.bv_val = sew->psbSyntaxDescription->buffer;
- val.bv_len = strlen(sew->psbSyntaxDescription->buffer);
- attrlist_merge(sew->attrs, "ldapSyntaxes", vals);
- return 1;
- }
- struct listargs{
- char **attrs;
- unsigned long flag;
- };
- static int
- schema_list_attributes_callback(struct asyntaxinfo *asi, void *arg)
- {
- struct listargs *aew = (struct listargs *)arg;
- if (!asi) {
- LDAPDebug(LDAP_DEBUG_ANY, "Error: no attribute types in schema_list_attributes_callback\n",
- 0, 0, 0);
- return ATTR_SYNTAX_ENUM_NEXT;
- }
- if (aew->flag && (asi->asi_flags & aew->flag)) {
- charray_add(&aew->attrs, slapi_ch_strdup(asi->asi_name));
- if (NULL != asi->asi_aliases) {
- int i;
- for ( i = 0; asi->asi_aliases[i] != NULL; ++i ) {
- charray_add(&aew->attrs,
- slapi_ch_strdup(asi->asi_aliases[i]));
- }
- }
- }
- return ATTR_SYNTAX_ENUM_NEXT;
- }
- /* Return the list of attributes names matching attribute flags */
- char **
- slapi_schema_list_attribute_names(unsigned long flag)
- {
- struct listargs aew;
- memset(&aew,0,sizeof(struct listargs));
- aew.flag=flag;
- attr_syntax_enumerate_attrs(schema_list_attributes_callback, &aew,
- PR_FALSE);
- return aew.attrs;
- }
- /*
- * returntext is always at least SLAPI_DSE_RETURNTEXT_SIZE bytes in size.
- */
- int
- read_schema_dse(
- Slapi_PBlock *pb,
- Slapi_Entry *pschema_info_e,
- Slapi_Entry *entryAfter,
- int *returncode,
- char *returntext /* not used */,
- void *arg /* not used */ )
- {
- struct berval val;
- struct berval *vals[2];
- int i;
- struct objclass *oc;
- struct matchingRuleList *mrl=NULL;
- struct sizedbuffer *psbObjectClasses= sizedbuffer_construct(BUFSIZ);
- struct sizedbuffer *psbAttrTypes= sizedbuffer_construct(BUFSIZ);
- struct sizedbuffer *psbMatchingRule= sizedbuffer_construct(BUFSIZ);
- struct sizedbuffer *psbSyntaxDescription = sizedbuffer_construct(BUFSIZ);
- struct attr_enum_wrapper aew;
- struct syntax_enum_wrapper sew;
- int enquote_sup_oc = config_get_enquote_sup_oc();
- PRUint32 schema_flags = 0;
- int user_defined_only = 0;
- char **allowed, **required;
- char *mr_desc, *mr_name, *oc_description;
- int schema_ds4x_compat = config_get_ds4_compatible_schema();
- const CSN *csn;
- vals[0] = &val;
- vals[1] = NULL;
- slapi_pblock_get(pb, SLAPI_SCHEMA_FLAGS, (void*)&schema_flags);
- user_defined_only = (schema_flags & DSE_SCHEMA_USER_DEFINED_ONLY) ? 1 : 0;
-
- attrlist_delete (&pschema_info_e->e_attrs, "objectclasses");
- attrlist_delete (&pschema_info_e->e_attrs, "attributetypes");
- attrlist_delete (&pschema_info_e->e_attrs, "matchingRules");
- attrlist_delete (&pschema_info_e->e_attrs, "ldapSyntaxes");
- /*
- attrlist_delete (&pschema_info_e->e_attrs, "matchingRuleUse");
- */
- schema_dse_lock_read();
- /* return the objectclasses */
- oc_lock_read();
- for (oc = g_get_global_oc_nolock(); oc != NULL; oc = oc->oc_next)
- {
- size_t size= 0;
- int need_extra_space = 1;
- if (user_defined_only &&
- !(oc->oc_flags & OC_FLAG_USER_OC ||
- oc->oc_flags & OC_FLAG_REDEFINED_OC)) {
- continue;
- }
- /*
- * XXX: 256 is a magic number... it must be large enough to fit
- * all of the fixed size items including description (DESC),
- * kind (STRUCTURAL, AUXILIARY, or ABSTRACT), and the OBSOLETE flag.
- */
- if ( schema_ds4x_compat ) {
- oc_description = (oc->oc_flags & OC_FLAG_STANDARD_OC) ?
- OC_STANDARD_STRING : OC_USERDEF_STRING;
- } else {
- oc_description = oc->oc_desc;
- }
-
- size= 256+strlen_null_ok(oc->oc_oid) + strlen(oc->oc_name) +
- strlen_null_ok(oc_description) +
- strcat_qdlist( NULL, "X-ORIGIN", oc->oc_origin );
- required = schema_ds4x_compat ? oc->oc_required : oc->oc_orig_required;
- if (required && required[0]) {
- for (i = 0 ; required[i]; i++)
- size+= 16 + strlen(required[i]);
- }
- allowed = schema_ds4x_compat ? oc->oc_allowed : oc->oc_orig_allowed;
- if (allowed && allowed[0]) {
- for (i = 0 ; allowed[i]; i++)
- size+= 16 + strlen(allowed[i]);
- }
- sizedbuffer_allocate(psbObjectClasses,size);
- /* put the OID and the NAME */
- sprintf (psbObjectClasses->buffer,
- "( %s NAME '%s'",
- (oc->oc_oid) ? oc->oc_oid : "",
- oc->oc_name);
- /* The DESC (description) is OPTIONAL */
- if (oc_description) {
- strcat(psbObjectClasses->buffer, " DESC '");
- /* We want to list an empty description
- * element if it was defined that way. */
- if (*oc_description) {
- strcat(psbObjectClasses->buffer, oc_description);
- }
- strcat(psbObjectClasses->buffer, "'");
- need_extra_space = 1;
- }
- /* put the OBSOLETE keyword */
- if (!schema_ds4x_compat && (oc->oc_flags & OC_FLAG_OBSOLETE)) {
- strcat(psbObjectClasses->buffer, schema_obsolete_with_spaces);
- need_extra_space = 0;
- }
- /* put the SUP superior objectclass */
- if (0 != strcasecmp(oc->oc_name, "top")) { /* top has no SUP */
- /* some AUXILIARY AND ABSTRACT objectclasses may not have a SUP either */
- /* for compatability, every objectclass other than top must have a SUP */
- if (schema_ds4x_compat || (oc->oc_superior && *oc->oc_superior)) {
- if (need_extra_space) {
- strcat(psbObjectClasses->buffer, " ");
- }
- strcat(psbObjectClasses->buffer, "SUP ");
- strcat(psbObjectClasses->buffer, (enquote_sup_oc ? "'" : ""));
- strcat(psbObjectClasses->buffer,
- ((oc->oc_superior && *oc->oc_superior) ?
- oc->oc_superior : "top"));
- strcat(psbObjectClasses->buffer, (enquote_sup_oc ? "'" : ""));
- need_extra_space = 1;
- }
- }
- /* put the kind of objectclass */
- if (schema_ds4x_compat) {
- if (need_extra_space) {
- strcat(psbObjectClasses->buffer, " ");
- }
- } else {
- strcat(psbObjectClasses->buffer, schema_oc_kind_strings_with_spaces[oc->oc_kind]);
- }
- strcat_oids( psbObjectClasses->buffer, "MUST", required,
- schema_ds4x_compat );
- strcat_oids( psbObjectClasses->buffer, "MAY", allowed,
- schema_ds4x_compat );
- if ( !schema_ds4x_compat ) {
- strcat_qdlist( psbObjectClasses->buffer, "X-ORIGIN", oc->oc_origin );
- }
- strcat( psbObjectClasses->buffer, ")");
- val.bv_val = psbObjectClasses->buffer;
- val.bv_len = strlen (psbObjectClasses->buffer);
- attrlist_merge (&pschema_info_e->e_attrs, "objectclasses", vals);
- }
- oc_unlock();
- /* now return the attrs */
- aew.attrs = &pschema_info_e->e_attrs;
- aew.enquote_sup_oc = enquote_sup_oc;
- aew.psbAttrTypes = psbAttrTypes;
- aew.user_defined_only = user_defined_only;
- aew.schema_ds4x_compat = schema_ds4x_compat;
- attr_syntax_enumerate_attrs(schema_attr_enum_callback, &aew, PR_FALSE);
- /* return the set of matching rules we support */
- for (mrl = g_get_global_mrl(); !user_defined_only && mrl != NULL; mrl = mrl->mrl_next) {
- mr_name = mrl->mr_entry->mr_name ? mrl->mr_entry->mr_name : "";
- mr_desc = mrl->mr_entry->mr_desc ? mrl->mr_entry->mr_desc : "";
- sizedbuffer_allocate(psbMatchingRule,128+
- strlen_null_ok(mrl->mr_entry->mr_oid) +
- strlen(mr_name)+ strlen(mr_desc)+
- strlen_null_ok(mrl->mr_entry->mr_syntax));
- if ( schema_ds4x_compat ) {
- sprintf(psbMatchingRule->buffer,
- "( %s NAME '%s' DESC '%s' SYNTAX %s%s%s )",
- (mrl->mr_entry->mr_oid ? mrl->mr_entry->mr_oid : ""),
- mr_name, mr_desc,
- enquote_sup_oc ? "'" : "",
- mrl->mr_entry->mr_syntax ? mrl->mr_entry->mr_syntax : "" ,
- enquote_sup_oc ? "'" : "");
- } else if ( NULL != mrl->mr_entry->mr_oid &&
- NULL != mrl->mr_entry->mr_syntax ) {
- char *p;
- sprintf(psbMatchingRule->buffer, "( %s ", mrl->mr_entry->mr_oid );
- p = psbMatchingRule->buffer + strlen(psbMatchingRule->buffer);
- if ( *mr_name != '\0' ) {
- sprintf(p, "NAME '%s' ", mr_name );
- p += strlen(p);
- }
- if ( *mr_desc != '\0' ) {
- sprintf(p, "DESC '%s' ", mr_desc );
- p += strlen(p);
- }
- sprintf(p, "SYNTAX %s )", mrl->mr_entry->mr_syntax );
- }
- val.bv_val = psbMatchingRule->buffer;
- val.bv_len = strlen (psbMatchingRule->buffer);
- attrlist_merge (&pschema_info_e->e_attrs, "matchingRules", vals);
- }
- if ( !schema_ds4x_compat && !user_defined_only ) {
- /* return the set of syntaxes we support */
- sew.attrs = &pschema_info_e->e_attrs;
- sew.psbSyntaxDescription = psbSyntaxDescription;
- plugin_syntax_enumerate(schema_syntax_enum_callback, &sew);
- }
- csn = g_get_global_schema_csn();
- if (NULL != csn) {
- char csn_str[CSN_STRSIZE + 1];
- csn_as_string(csn, PR_FALSE, csn_str);
- slapi_entry_attr_delete(pschema_info_e, "nsschemacsn");
- slapi_entry_add_string(pschema_info_e, "nsschemacsn", csn_str);
- }
- schema_dse_unlock();
- sizedbuffer_destroy(psbObjectClasses);
- sizedbuffer_destroy(psbAttrTypes);
- sizedbuffer_destroy(psbMatchingRule);
- sizedbuffer_destroy(psbSyntaxDescription);
- *returncode= LDAP_SUCCESS;
- return SLAPI_DSE_CALLBACK_OK;
- }
- /* helper for deleting mods (we do not want to be applied) from the mods array */
- static void
- mod_free(LDAPMod *mod)
- {
- ber_bvecfree(mod->mod_bvalues);
- slapi_ch_free((void**)&(mod->mod_type));
- slapi_ch_free((void**)&mod);
- }
- /*
- * modify_schema_dse: called by do_modify() when target is cn=schema
- *
- * Add/Delete attributes and objectclasses from the schema
- * Supported mod_ops are LDAP_MOD_DELETE and LDAP_MOD_ADD
- *
- * Note that the in-memory DSE Slapi_Entry object does NOT hold the
- * attributeTypes and objectClasses attributes -- it only holds
- * non-schema related attributes such as aci.
- *
- * returntext is always at least SLAPI_DSE_RETURNTEXT_SIZE bytes in size.
- */
- int
- modify_schema_dse (Slapi_PBlock *pb, Slapi_Entry *entryBefore, Slapi_Entry *entryAfter, int *returncode, char *returntext, void *arg)
- {
- int i, rc= SLAPI_DSE_CALLBACK_OK; /* default is to apply changes to the DSE */
- char *schema_dse_attr_name;
- LDAPMod **mods = NULL;
- int num_mods = 0; /* count the number of mods */
- int schema_ds4x_compat = config_get_ds4_compatible_schema();
- int reapply_mods = 0;
- int is_replicated_operation = 0;
- slapi_pblock_get( pb, SLAPI_MODIFY_MODS, &mods );
- slapi_pblock_get( pb, SLAPI_IS_REPLICATED_OPERATION, &is_replicated_operation);
- schema_dse_lock_write();
- /*
- * Process each modification. Stop as soon as we hit an error.
- *
- * XXXmcs: known bugs: we don't operate on a copy of the schema, so it
- * is possible for some schema changes to be made but not all of them.
- * True for DS 4.x as well, although it tried to keep going even after
- * an error was detected (which was very wrong).
- */
- for (i = 0; rc == SLAPI_DSE_CALLBACK_OK && mods[i]; i++) {
- schema_dse_attr_name = (char *) mods[i]->mod_type;
- num_mods++; /* incr the number of mods */
- /*
- * skip attribute types that we do not recognize (the DSE code will
- * handle them).
- */
- if ( !schema_type_is_interesting( schema_dse_attr_name )) {
- continue;
- }
- /*
- * Delete an objectclass or attribute
- */
- if (SLAPI_IS_MOD_DELETE(mods[i]->mod_op)) {
- if (strcasecmp (mods[i]->mod_type, "objectclasses") == 0) {
- *returncode = schema_delete_objectclasses (entryBefore, mods[i],
- returntext, SLAPI_DSE_RETURNTEXT_SIZE, schema_ds4x_compat );
- }
- else if (strcasecmp (mods[i]->mod_type, "attributetypes") == 0) {
- *returncode = schema_delete_attributes (entryBefore, mods[i],
- returntext, SLAPI_DSE_RETURNTEXT_SIZE );
- }
- else {
- *returncode= LDAP_NO_SUCH_ATTRIBUTE;
- schema_create_errormsg( returntext, SLAPI_DSE_RETURNTEXT_SIZE,
- schema_errprefix_generic, mods[i]->mod_type,
- "Only object classes and attribute types may be deleted" );
- }
- if ( LDAP_SUCCESS != *returncode ) {
- rc= SLAPI_DSE_CALLBACK_ERROR;
- } else {
- reapply_mods = 1;
- }
- }
-
- /*
- * Replace an objectclass,attribute, or schema CSN
- */
- else if (SLAPI_IS_MOD_REPLACE(mods[i]->mod_op)) {
- int replace_allowed = 0;
- slapdFrontendConfig_t *slapdFrontendConfig;
- slapdFrontendConfig = getFrontendConfig();
- CFG_LOCK_READ( slapdFrontendConfig );
- if ( 0 == strcasecmp( slapdFrontendConfig->schemareplace,
- CONFIG_SCHEMAREPLACE_STR_ON )) {
- replace_allowed = 1;
- } else if ( 0 == strcasecmp( slapdFrontendConfig->schemareplace,
- CONFIG_SCHEMAREPLACE_STR_REPLICATION_ONLY )) {
- replace_allowed = is_replicated_operation;
- }
- CFG_UNLOCK_READ( slapdFrontendConfig );
- if ( !replace_allowed ) {
- *returncode= LDAP_UNWILLING_TO_PERFORM;
- schema_create_errormsg( returntext, SLAPI_DSE_RETURNTEXT_SIZE,
- schema_errprefix_generic, mods[i]->mod_type,
- "Replace is not allowed on the subschema subentry" );
- rc = SLAPI_DSE_CALLBACK_ERROR;
- } else {
- if (strcasecmp (mods[i]->mod_type, "attributetypes") == 0) {
- /*
- * Replace all attribute types
- */
- *returncode = schema_replace_attributes( pb, mods[i], returntext,
- SLAPI_DSE_RETURNTEXT_SIZE );
- } else if (strcasecmp (mods[i]->mod_type, "objectclasses") == 0) {
- /*
- * Replace all objectclasses
- */
- *returncode = schema_replace_objectclasses( pb, mods[i],
- returntext, SLAPI_DSE_RETURNTEXT_SIZE );
- } else if (strcasecmp (mods[i]->mod_type, "nsschemacsn") == 0) {
- if (is_replicated_operation) {
- /* Update the schema CSN */
- if (mods[i]->mod_bvalues && mods[i]->mod_bvalues[0] &&
- mods[i]->mod_bvalues[0]->bv_val &&
- mods[i]->mod_bvalues[0]->bv_len > 0) {
- char new_csn_string[CSN_STRSIZE + 1];
- CSN *new_schema_csn;
- memcpy(new_csn_string, mods[i]->mod_bvalues[0]->bv_val,
- mods[i]->mod_bvalues[0]->bv_len);
- new_csn_string[mods[i]->mod_bvalues[0]->bv_len] = '\0';
- new_schema_csn = csn_new_by_string(new_csn_string);
- if (NULL != new_schema_csn) {
- g_set_global_schema_csn(new_schema_csn); /* csn is consumed */
- }
- }
- }
- } else {
- *returncode= LDAP_UNWILLING_TO_PERFORM; /* XXXmcs: best error? */
- schema_create_errormsg( returntext, SLAPI_DSE_RETURNTEXT_SIZE,
- schema_errprefix_generic, mods[i]->mod_type,
- "Only object classes and attribute types may be replaced" );
- }
- }
- if ( LDAP_SUCCESS != *returncode ) {
- rc= SLAPI_DSE_CALLBACK_ERROR;
- } else {
- reapply_mods = 1; /* we have at least some modifications we need to reapply */
- }
- }
-
- /*
- * Add an objectclass or attribute
- */
- else if (SLAPI_IS_MOD_ADD(mods[i]->mod_op)) {
- if (strcasecmp (mods[i]->mod_type, "attributetypes") == 0) {
- /*
- * Add a new attribute
- */
- *returncode = schema_add_attribute ( pb, mods[i], returntext,
- SLAPI_DSE_RETURNTEXT_SIZE, schema_ds4x_compat );
- }
- else if (strcasecmp (mods[i]->mod_type, "objectclasses") == 0) {
- /*
- * Add a new objectclass
- */
- *returncode = schema_add_objectclass ( pb, mods[i], returntext,
- SLAPI_DSE_RETURNTEXT_SIZE, schema_ds4x_compat );
- }
- else {
- if ( schema_ds4x_compat ) {
- *returncode= LDAP_NO_SUCH_ATTRIBUTE;
- } else {
- *returncode= LDAP_UNWILLING_TO_PERFORM; /* XXXmcs: best error? */
- }
- schema_create_errormsg( returntext, SLAPI_DSE_RETURNTEXT_SIZE,
- schema_errprefix_generic, mods[i]->mod_type,
- "Only object classes and attribute types may be added" );
- }
- if ( LDAP_SUCCESS != *returncode ) {
- rc= SLAPI_DSE_CALLBACK_ERROR;
- } else {
- reapply_mods = 1; /* we have at least some modifications we need to reapply */
- }
- }
- /*
- ** No value was specified to modify, the user probably tried
- ** to delete all attributetypes or all objectclasses, which
- ** isn't allowed
- */
- if (!mods[i]->mod_vals.modv_strvals)
- {
- if ( schema_ds4x_compat ) {
- *returncode= LDAP_INVALID_SYNTAX;
- } else {
- *returncode= LDAP_UNWILLING_TO_PERFORM; /* XXXmcs: best error? */
- }
- schema_create_errormsg( returntext, SLAPI_DSE_RETURNTEXT_SIZE,
- schema_errprefix_generic, mods[i]->mod_type,
- "No target attribute type or object class specified" );
- rc= SLAPI_DSE_CALLBACK_ERROR;
- }
- }
- if(rc==SLAPI_DSE_CALLBACK_OK && reapply_mods)
- {
- CSN *new_schema_csn;
- int newindex = 0; /* mods array index */
- /* tell the "unholy" dse_modify code to reapply the mods and use
- that result instead of the initial result; we must remove the attributes
- we manage in this code from the mods
- */
- slapi_pblock_set(pb, SLAPI_DSE_REAPPLY_MODS, (void *)&reapply_mods);
- /* because we are reapplying the mods, we want the entryAfter to
- look just like the entryBefore, except that "our" attributes
- will have been removed
- */
- /* delete the mods from the mods array */
- for (i = 0; i < num_mods ; i++) {
- const char *attrname = mods[i]->mod_type;
- /* delete this attr from the entry */
- slapi_entry_attr_delete(entryAfter, attrname);
- if ( schema_type_is_interesting( attrname )) {
- mod_free(mods[i]);
- mods[i] = NULL;
- } else {
- /* add the original value of the attr back to the entry after */
- Slapi_Attr *origattr = NULL;
- Slapi_ValueSet *origvalues = NULL;
- slapi_entry_attr_find(entryBefore, attrname, &origattr);
- if (NULL != origattr) {
- slapi_attr_get_valueset(origattr, &origvalues);
- if (NULL != origvalues) {
- slapi_entry_add_valueset(entryAfter, attrname, origvalues);
- slapi_valueset_free(origvalues);
- }
- }
- mods[newindex++] = mods[i];
- }
- }
- mods[newindex] = NULL;
- /*
- * Since we successfully updated the schema, we need to generate
- * a new schema CSN for non-replicated operations.
- */
- /* XXXmcs: I wonder if we should update the schema CSN even when no
- * attribute types or OCs were changed? That way, an administrator
- * could force schema replication to occur by submitting a modify
- * operation that did not really do anything, such as:
- *
- * dn:cn=schema
- * changetype:modify
- * replace:cn
- * cn:schema
- */
- if (!is_replicated_operation)
- {
- new_schema_csn = csn_new();
- if (NULL != new_schema_csn) {
- char csn_str[CSN_STRSIZE + 1];
- csn_set_replicaid(new_schema_csn, 0);
- csn_set_time(new_schema_csn, current_time());
- g_set_global_schema_csn(new_schema_csn);
- slapi_entry_attr_delete(entryBefore, "nsschemacsn");
- csn_as_string(new_schema_csn, PR_FALSE, csn_str);
- slapi_entry_add_string(entryBefore, "nsschemacsn", csn_str);
- }
- }
- }
- schema_dse_unlock();
- return rc;
- }
- CSN *
- dup_global_schema_csn()
- {
- CSN *schema_csn;
- schema_dse_lock_read();
- schema_csn = csn_dup ( g_get_global_schema_csn() );
- schema_dse_unlock();
- return schema_csn;
- }
- /*
- * Remove all attribute types and objectclasses from the entry and
- * then add back the user defined ones based on the contents of the
- * schema hash tables.
- *
- * Returns SLAPI_DSE_CALLBACK_OK is all goes well.
- *
- * returntext is always at least SLAPI_DSE_RETURNTEXT_SIZE bytes in size.
- */
- static int
- refresh_user_defined_schema( Slapi_PBlock *pb, Slapi_Entry *pschema_info_e, Slapi_Entry *entryAfter, int *returncode, char *returntext, void *arg /* not used */ )
- {
- int rc;
- Slapi_PBlock *mypbptr = pb;
- Slapi_PBlock mypb;
- const CSN *schema_csn;
- PRUint32 schema_flags = DSE_SCHEMA_USER_DEFINED_ONLY;
- pblock_init(&mypb);
- slapi_entry_attr_delete( pschema_info_e, "objectclasses");
- slapi_entry_attr_delete( pschema_info_e, "attributetypes");
- /* for write callbacks, no pb is supplied, so use our own */
- if (!mypbptr) {
- mypbptr = &mypb;
- }
- slapi_pblock_set(mypbptr, SLAPI_SCHEMA_FLAGS, &schema_flags);
- rc = read_schema_dse(mypbptr, pschema_info_e, NULL, returncode, returntext, NULL);
- schema_csn = g_get_global_schema_csn();
- if (NULL != schema_csn) {
- char csn_str[CSN_STRSIZE + 1];
- slapi_entry_attr_delete(pschema_info_e, "nsschemacsn");
- csn_as_string(schema_csn, PR_FALSE, csn_str);
- slapi_entry_add_string(pschema_info_e, "nsschemacsn", csn_str);
- }
- pblock_done(&mypb);
- return rc;
- }
- /* oc_add_nolock
- * Add the objectClass newoc to the global list of objectclasses
- */
- static void
- oc_add_nolock(struct objclass *newoc)
- {
- struct objclass *poc;
- poc = g_get_global_oc_nolock();
- if ( NULL == poc ) {
- g_set_global_oc_nolock(newoc);
- } else {
- for ( ; (poc != NULL) && (poc->oc_next != NULL); poc = poc->oc_next) {
- ;
- }
- poc->oc_next = newoc;
- newoc->oc_next = NULL;
- }
- }
-
- static char **read_dollar_values ( char *vals) {
- int i,k;
- char **retVal;
- static const char *charsToRemove = " ()";
-
- /* get rid of all the parens and spaces */
- for ( i = 0, k = 0; vals[i]; i++) {
- if (!strchr(charsToRemove, vals[i])) {
- vals[k++] = vals[i];
- }
- }
- vals[k] = '\0';
- retVal = slapi_str2charray (vals, "$");
- return retVal;
- }
- /*
- * Delete one or more objectClasses from our internal data structure.
- *
- * Return an LDAP error code (LDAP_SUCCESS if all goes well).
- * If an error occurs, explanatory text is copied into 'errorbuf'.
- *
- * This function should not send an LDAP result; that is the caller's
- * responsibility.
- */
- static int
- schema_delete_objectclasses( Slapi_Entry *entryBefore, LDAPMod *mod,
- char *errorbuf, size_t errorbufsize, int schema_ds4x_compat )
- {
- int i;
- int rc = LDAP_SUCCESS; /* optimistic */
- struct objclass *poc, *poc2, *delete_oc = NULL;
-
- if ( NULL == mod->mod_bvalues ) {
- schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_oc,
- NULL, "Cannot remove all schema object classes" );
- return LDAP_UNWILLING_TO_PERFORM;
- }
- for (i = 0; mod->mod_bvalues[i]; i++) {
- if ( LDAP_SUCCESS != ( rc = read_oc_ldif (
- (const char *)mod->mod_bvalues[i]->bv_val, &delete_oc,
- errorbuf, errorbufsize, 0, 0, schema_ds4x_compat))) {
- return rc;
- }
- oc_lock_write();
- if ((poc = oc_find_nolock(delete_oc->oc_name)) != NULL) {
- /* check to see if any objectclasses inherit from this oc */
- for (poc2 = g_get_global_oc_nolock(); poc2 != NULL; poc2 = poc2->oc_next) {
- if (poc2->oc_superior &&
- (strcasecmp (poc2->oc_superior, delete_oc->oc_name) == 0)) {
- schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_oc,
- delete_oc->oc_name, "Cannot delete an object class"
- " which has child object classes" );
- rc = LDAP_UNWILLING_TO_PERFORM;
- goto unlock_and_return;
- }
- }
- if ( (poc->oc_flags & OC_FLAG_STANDARD_OC) == 0) {
- oc_delete_nolock (poc->oc_name);
- }
-
- else {
- schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_oc,
- delete_oc->oc_name, "Cannot delete a standard object class" );
- rc = LDAP_UNWILLING_TO_PERFORM;
- goto unlock_and_return;
- }
- }
- else {
- schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_oc,
- delete_oc->oc_name, "Is unknown. Cannot delete." );
- rc = schema_ds4x_compat ? LDAP_NO_SUCH_OBJECT : LDAP_NO_SUCH_ATTRIBUTE;
- goto unlock_and_return;
- }
- oc_free( &delete_oc );
- oc_unlock();
- }
- return rc;
- unlock_and_return:
- oc_free( &delete_oc );
- oc_unlock();
- return rc;
- }
- static int
- schema_return(int rc,struct sizedbuffer * psb1,struct sizedbuffer *psb2,struct sizedbuffer *psb3,struct sizedbuffer *psb4)
- {
- sizedbuffer_destroy(psb1);
- sizedbuffer_destroy(psb2);
- sizedbuffer_destroy(psb3);
- sizedbuffer_destroy(psb4);
- return rc;
- }
- /*
- * Delete one or more attributeTypes from our internal data structure.
- *
- * Return an LDAP error code (LDAP_SUCCESS if all goes well).
- * If an error occurs, explanatory text is copied into 'errorbuf'.
- *
- * This function should not send an LDAP result; that is the caller's
- * responsibility.
- */
- static int
- schema_delete_attributes ( Slapi_Entry *entryBefore, LDAPMod *mod,
- char *errorbuf, size_t errorbufsize)
- {
- char *attr_ldif, *oc_list_type = "";
- asyntaxinfo *a;
- struct objclass *oc = NULL;
- int i, k, attr_in_use_by_an_oc = 0;
- struct sizedbuffer *psbAttrName= sizedbuffer_construct(BUFSIZ);
- struct sizedbuffer *psbAttrOid= sizedbuffer_construct(BUFSIZ);
- struct sizedbuffer *psbAttrSyntax= sizedbuffer_construct(BUFSIZ);
- if (NULL == mod->mod_bvalues) {
- schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_at,
- NULL, "Cannot remove all schema attribute types" );
- return schema_return(LDAP_UNWILLING_TO_PERFORM,psbAttrOid,psbAttrName,
- psbAttrSyntax,NULL);
- }
- for (i = 0; mod->mod_bvalues[i]; i++) {
- attr_ldif =(char *) mod->mod_bvalues[i]->bv_val;
- /* normalize the attr ldif */
- for ( k = 0; attr_ldif[k]; k++) {
- if (attr_ldif[k] == '\'' ||
- attr_ldif[k] == '(' ||
- attr_ldif[k] == ')' ) {
- attr_ldif[k] = ' ';
- }
- attr_ldif[k] = tolower (attr_ldif[k]);
- }
-
- sizedbuffer_allocate(psbAttrName,strlen(attr_ldif));
- sizedbuffer_allocate(psbAttrOid,strlen(attr_ldif));
- sizedbuffer_allocate(psbAttrSyntax,strlen(attr_ldif));
-
- sscanf (attr_ldif, "%s name %s syntax %s",
- psbAttrOid->buffer, psbAttrName->buffer, psbAttrSyntax->buffer);
- if ((a = attr_syntax_get_by_name ( psbAttrName->buffer)) != NULL ) {
- /* only modify attrs which were user defined */
- if (a->asi_flags & SLAPI_ATTR_FLAG_STD_ATTR) {
- schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_at,
- psbAttrName->buffer,
- "Cannot delete a standard attribute type" );
- attr_syntax_return( a );
- return schema_return(LDAP_UNWILLING_TO_PERFORM,psbAttrOid,psbAttrName,
- psbAttrSyntax,NULL);
- }
- /* Do not allow deletion if referenced by an object class. */
- oc_lock_read();
- attr_in_use_by_an_oc = 0;
- for ( oc = g_get_global_oc_nolock(); oc != NULL; oc = oc->oc_next ) {
- if (NULL != oc->oc_required) {
- for ( k = 0; oc->oc_required[k] != NULL; k++ ) {
- if ( 0 == slapi_attr_type_cmp( oc->oc_required[k], a->asi_name,
- SLAPI_TYPE_CMP_EXACT )) {
- oc_list_type = "MUST";
- attr_in_use_by_an_oc = 1;
- break;
- }
- }
- }
- if (!attr_in_use_by_an_oc && NULL != oc->oc_allowed) {
- for ( k = 0; oc->oc_allowed[k] != NULL; k++ ) {
- if ( 0 == slapi_attr_type_cmp( oc->oc_allowed[k], a->asi_name,
- SLAPI_TYPE_CMP_EXACT )) {
- oc_list_type = "MAY";
- attr_in_use_by_an_oc = 1;
- break;
- }
- }
- }
- if (attr_in_use_by_an_oc) {
- schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_at,
- psbAttrName->buffer, "Is included in the %s list for object class %s. Cannot delete.",
- oc_list_type, oc->oc_name );
- break;
- }
- }
- oc_unlock();
- if (attr_in_use_by_an_oc) {
- attr_syntax_return( a );
- return schema_return(LDAP_UNWILLING_TO_PERFORM,psbAttrOid,psbAttrName,
- psbAttrSyntax,NULL);
- }
- /* Delete it. */
- attr_syntax_delete( a );
- attr_syntax_return( a );
- }
- else {
- /* unknown attribute */
- schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_at,
- psbAttrName->buffer, "Is unknown. Cannot delete." );
- return schema_return(LDAP_NO_SUCH_ATTRIBUTE,psbAttrOid,psbAttrName,
- psbAttrSyntax,NULL);
- }
- }
- return schema_return(LDAP_SUCCESS,psbAttrOid,psbAttrName,psbAttrSyntax,
- NULL);
- }
- static int
- schema_add_attribute ( Slapi_PBlock *pb, LDAPMod *mod, char *errorbuf,
- size_t errorbufsize, int schema_ds4x_compat )
- {
- int i;
- char *attr_ldif;
- /* LPXXX: Eventually, we should not allocate the buffers in read_at_ldif
- * for each attribute, but use the same buffer for all.
- * This is not done yet, so it's useless to allocate buffers for nothing.
- */
- /* struct sizedbuffer *psbAttrName= sizedbuffer_construct(BUFSIZ); */
- /* struct sizedbuffer *psbAttrOid= sizedbuffer_construct(BUFSIZ); */
- /* struct sizedbuffer *psbAttrDesc= sizedbuffer_construct(BUFSIZ); */
- /* struct sizedbuffer *psbAttrSyntax= sizedbuffer_construct(BUFSIZ); */
- int status = 0;
-
- for (i = 0; LDAP_SUCCESS == status && mod->mod_bvalues[i]; i++) {
- PRUint32 nolock = 0; /* lock global resources during normal operation */
- attr_ldif = (char *) mod->mod_bvalues[i]->bv_val;
- status = read_at_ldif(attr_ldif, NULL, errorbuf, errorbufsize,
- nolock, 1 /* user defined */, schema_ds4x_compat, 1);
- if ( LDAP_SUCCESS != status ) {
- break; /* stop on first error */
- }
- }
- /* free everything */
- /* sizedbuffer_destroy(psbAttrOid); */
- /* sizedbuffer_destroy(psbAttrName); */
- /* sizedbuffer_destroy(psbAttrDesc); */
- /* sizedbuffer_destroy(psbAttrSyntax); */
- return status;
- }
- /*
- * Returns an LDAP error code (LDAP_SUCCESS if all goes well)
- */
- static int
- add_oc_internal(struct objclass *pnew_oc, char *errorbuf, size_t errorbufsize,
- int schema_ds4x_compat, PRUint32 flags )
- {
- struct objclass *oldoc_by_name, *oldoc_by_oid, *psup_oc = NULL;
- int redefined_oc = 0, rc=0;
- asyntaxinfo *pasyntaxinfo = 0;
- if (!(flags & DSE_SCHEMA_LOCKED))
- oc_lock_write();
- oldoc_by_name = oc_find_nolock (pnew_oc->oc_name);
- oldoc_by_oid = oc_find_nolock (pnew_oc->oc_oid);
- /* Check to see if the objectclass name and the objectclass oid are already
- * in use by an existing objectclass. If an existing objectclass is already
- * using the name or oid, the name and the oid should map to the same objectclass.
- * Otherwise, return an error.
- */
- if ( oldoc_by_name != oldoc_by_oid ) {
- schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_oc,
- pnew_oc->oc_name, "The name does not match the OID \"%s\". "
- "Another object class is already using the name or OID.",
- pnew_oc->oc_oid);
- rc = LDAP_TYPE_OR_VALUE_EXISTS;
- }
- /*
- * Set a flag so we know if we are updating an existing OC definition.
- */
- if ( !rc ) {
- if ( NULL != oldoc_by_name ) {
- redefined_oc = 1;
- } else {
- /*
- * If we are not updating an existing OC, check that the new
- * oid is not already in use.
- */
- if ( NULL != oldoc_by_oid ) {
- schema_create_errormsg( errorbuf, errorbufsize,
- schema_errprefix_oc, pnew_oc->oc_name,
- "The OID \"%s\" is already used by the object class \"%s\"",
- pnew_oc->oc_oid, oldoc_by_oid->oc_name);
- rc = LDAP_TYPE_OR_VALUE_EXISTS;
- }
- }
- }
- /* check to see if the superior oc exists */
- if (!rc && pnew_oc->oc_superior &&
- ((psup_oc = oc_find_nolock (pnew_oc->oc_superior)) == NULL)) {
- schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_oc,
- pnew_oc->oc_name, "Superior object class \"%s\" does not exist",
- pnew_oc->oc_superior);
- rc = LDAP_TYPE_OR_VALUE_EXISTS;
- }
-
- /* inherit the attributes from the superior oc */
- if (!rc && psup_oc ) {
- if ( psup_oc->oc_required ) {
- charray_merge( &pnew_oc->oc_required, psup_oc->oc_required, 1 );
- }
- if ( psup_oc->oc_allowed ) {
- charray_merge ( &pnew_oc->oc_allowed, psup_oc->oc_allowed, 1 );
- }
- }
- /* check to see if the oid is already in use by an attribute */
- if (!rc && (pasyntaxinfo = attr_syntax_get_by_oid(pnew_oc->oc_oid))) {
- schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_oc,
- pnew_oc->oc_name,
- "The OID \"%s\" is also used by the attribute type \"%s\"",
- pnew_oc->oc_oid, pasyntaxinfo->asi_name);
- rc = LDAP_TYPE_OR_VALUE_EXISTS;
- attr_syntax_return( pasyntaxinfo );
- }
-
- /* check to see if the objectclass name is valid */
- if (!rc && !(flags & DSE_SCHEMA_NO_CHECK) &&
- schema_check_name ( pnew_oc->oc_name, PR_FALSE, errorbuf, errorbufsize )
- == 0 ) {
- rc = schema_ds4x_compat ? LDAP_OPERATIONS_ERROR : LDAP_INVALID_SYNTAX;
- }
- /* check to see if the oid is valid */
- if (!rc && !(flags & DSE_SCHEMA_NO_CHECK))
- {
- struct sizedbuffer *psbOcOid, *psbOcName;
-
- psbOcName = sizedbuffer_construct(strlen(pnew_oc->oc_name) + 1);
- psbOcOid = sizedbuffer_construct(strlen(pnew_oc->oc_oid) + 1);
- strcpy(psbOcName->buffer, pnew_oc->oc_name);
- strcpy(psbOcOid->buffer, pnew_oc->oc_oid);
- if (!schema_check_oid ( psbOcName->buffer, psbOcOid->buffer, PR_FALSE,
- errorbuf, errorbufsize))
- rc = schema_ds4x_compat ? LDAP_OPERATIONS_ERROR : LDAP_INVALID_SYNTAX;
- sizedbuffer_destroy(psbOcName);
- sizedbuffer_destroy(psbOcOid);
- }
- /* check to see if the oc's attributes are valid */
- if (!rc && !(flags & DSE_SCHEMA_NO_CHECK) &&
- schema_check_oc_attrs ( pnew_oc, errorbuf, errorbufsize,
- 0 /* don't strip options */ ) == 0 ) {
- rc = schema_ds4x_compat ? LDAP_OPERATIONS_ERROR : LDAP_INVALID_SYNTAX;
- }
- /* insert new objectclass exactly where the old one one in the linked list*/
- if ( !rc && redefined_oc ) {
- pnew_oc->oc_flags |= OC_FLAG_REDEFINED_OC;
- rc=oc_replace_nolock( pnew_oc->oc_name, pnew_oc);
- }
- if (!rc && !redefined_oc ) {
- oc_add_nolock(pnew_oc);
- }
- if (!rc && redefined_oc ) {
- oc_update_inheritance_nolock( pnew_oc );
- }
- if (!(flags & DSE_SCHEMA_LOCKED))
- oc_unlock();
- return rc;
- }
- /*
- * Process a replace modify suboperation for attributetypes.
- *
- * XXXmcs: At present, readonly (bundled) schema definitions can't be
- * removed. If that is attempted, we just keep them without generating
- * an error.
- *
- * Our algorithm is:
- *
- * Clear the "keep" flags on the all existing attr. definitions.
- *
- * For each replacement value:
- * If the value exactly matches an existing schema definition,
- * set that definition's keep flag.
- *
- * Else if the OID in the replacement value matches an existing
- * definition, delete the old definition and add the new one. Set
- * the keep flag on the newly added definition.
- *
- * Else add the new definition. Set the keep flag on the newly
- * added definition.
- *
- * For each definition that is not flagged keep, delete.
- *
- * Clear all remaining "keep" flags.
- *
- * Note that replace was not supported at all before iDS 5.0.
- */
- static int
- schema_replace_attributes ( Slapi_PBlock *pb, LDAPMod *mod, char *errorbuf,
- size_t errorbufsize )
- {
- int i, rc = LDAP_SUCCESS;
- struct asyntaxinfo *newasip, *oldasip;
- if ( NULL == mod->mod_bvalues ) {
- schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_at,
- NULL, "Cannot remove all schema attribute types" );
- return LDAP_UNWILLING_TO_PERFORM;
- }
- /* clear all of the "keep" flags */
- attr_syntax_all_clear_flag( SLAPI_ATTR_FLAG_KEEP );
- for ( i = 0; mod->mod_bvalues[i] != NULL; ++i ) {
- if ( LDAP_SUCCESS != ( rc = read_at_ldif( mod->mod_bvalues[i]->bv_val,
- &newasip, errorbuf, errorbufsize, 0, 1, 0, 0 ))) {
- goto clean_up_and_return;
- }
- /*
- * Check for a match with an existing type and
- * handle the various cases.
- */
- if ( NULL == ( oldasip =
- attr_syntax_get_by_oid( newasip->asi_oid ))) {
- /* new attribute type */
- LDAPDebug( LDAP_DEBUG_TRACE, "schema_replace_attributes:"
- " new type %s (OID %s)\n",
- newasip->asi_name, newasip->asi_oid, 0 );
- } else {
- /* the name matches -- check the rest */
- if ( attr_syntax_equal( newasip, oldasip )) {
- /* unchanged attribute type -- just mark it as one to keep */
- oldasip->asi_flags |= SLAPI_ATTR_FLAG_KEEP;
- attr_syntax_free( newasip );
- newasip = NULL;
- } else {
- /* modified attribute type */
- LDAPDebug( LDAP_DEBUG_TRACE, "schema_replace_attributes:"
- " replacing type %s (OID %s)\n",
- newasip->asi_name, newasip->asi_oid, 0 );
- /* flag for deletion */
- attr_syntax_delete( oldasip );
- }
- attr_syntax_return( oldasip );
- }
- if ( NULL != newasip ) { /* add new or replacement definition */
- rc = attr_syntax_add( newasip );
- if ( LDAP_SUCCESS != rc ) {
- schema_create_errormsg( errorbuf, errorbufsize,
- schema_errprefix_at, newasip->asi_name,
- "Could not be added (OID is \"%s\")",
- newasip->asi_oid );
- attr_syntax_free( newasip );
- goto clean_up_and_return;
- }
- newasip->asi_flags |= SLAPI_ATTR_FLAG_KEEP;
- }
- }
- /*
- * Delete all of the definitions that are not marked "keep" or "standard".
- *
- * XXXmcs: we should consider reporting an error if any read only types
- * remain....
- */
- attr_syntax_delete_all_not_flagged( SLAPI_ATTR_FLAG_KEEP
- | SLAPI_ATTR_FLAG_STD_ATTR );
- clean_up_and_return:
- /* clear all of the "keep" flags */
- attr_syntax_all_clear_flag( SLAPI_ATTR_FLAG_KEEP );
- return rc;
- }
- static int
- schema_add_objectclass ( Slapi_PBlock *pb, LDAPMod *mod, char *errorbuf,
- size_t errorbufsize, int schema_ds4x_compat )
- {
- struct objclass *pnew_oc;
- char *newoc_ldif;
- int j, rc=0;
- for (j = 0; mod->mod_bvalues[j]; j++) {
- newoc_ldif = (char *) mod->mod_bvalues[j]->bv_val;
- if ( LDAP_SUCCESS != (rc = read_oc_ldif ( newoc_ldif, &pnew_oc,
- errorbuf, errorbufsize, 0, 1 /* user defined */,
- schema_ds4x_compat))) {
- return rc;
- }
- if ( LDAP_SUCCESS != (rc = add_oc_internal(pnew_oc, errorbuf,
- errorbufsize, schema_ds4x_compat, 0/* no restriction */))) {
- oc_free( &pnew_oc );
- return rc;
- }
- normalize_oc();
- }
- return LDAP_SUCCESS;
- }
- /*
- * Process a replace modify suboperation for objectclasses.
- *
- * XXXmcs: At present, readonly (bundled) schema definitions can't be
- * removed. If that is attempted, we just keep them without generating
- * an error.
- *
- * Our algorithm is:
- *
- * Lock the global objectclass linked list.
- *
- * Create a new empty (temporary) linked list, initially empty.
- *
- * For each replacement value:
- * If the value exactly matches an existing schema definition,
- * move the existing definition from the current global list to the
- * temporary list
- *
- * Else if the OID in the replacement value matches an existing
- * definition, delete the old definition from the current global
- * list and add the new one to the temporary list.
- *
- * Else add the new definition to the temporary list.
- *
- * Delete all definitions that remain on the current global list.
- *
- * Make the temporary list the current global list.
- *
- * Note that since the objectclass definitions are stored in a linked list,
- * this algorithm is O(N * M) where N is the number of existing objectclass
- * definitions and M is the number of replacement definitions.
- * XXXmcs: Yuck. We should use a hash table for the OC definitions.
- *
- * Note that replace was not supported at all by DS versions prior to 5.0
- */
- static int
- schema_replace_objectclasses ( Slapi_PBlock *pb, LDAPMod *mod, char *errorbuf,
- size_t errorbufsize )
- {
- struct objclass *newocp, *curlisthead, *prevocp, *tmpocp;
- struct objclass *newlisthead = NULL, *newlistend = NULL;
- int i, rc = LDAP_SUCCESS;
- if ( NULL == mod->mod_bvalues ) {
- schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_oc,
- NULL, "Cannot remove all schema object classes" );
- return LDAP_UNWILLING_TO_PERFORM;
- }
- oc_lock_write();
- curlisthead = g_get_global_oc_nolock();
- for ( i = 0; mod->mod_bvalues[i] != NULL; ++i ) {
- struct objclass *addocp = NULL;
- if ( LDAP_SUCCESS != ( rc = read_oc_ldif( mod->mod_bvalues[i]->bv_val,
- &newocp, errorbuf, errorbufsize, DSE_SCHEMA_NO_GLOCK,
- 1 /* user defined */, 0 /* no DS 4.x compat issues */ ))) {
- rc = LDAP_INVALID_SYNTAX;
- goto clean_up_and_return;
- }
- prevocp = NULL;
- for ( tmpocp = curlisthead; tmpocp != NULL; tmpocp = tmpocp->oc_next ) {
- if ( 0 == strcasecmp( tmpocp->oc_oid, newocp->oc_oid ) ) {
- /* the names match -- remove from the current list */
- if ( tmpocp == curlisthead ) {
- curlisthead = tmpocp->oc_next;
- /* The global oc list is scanned in read_oc_ldif above,
- if there are multiple objectclasses to be updated.
- Needs to maintain the list dynamically. */
- g_set_global_oc_nolock( curlisthead );
- } else {
- prevocp->oc_next = tmpocp->oc_next;
- }
- tmpocp->oc_next = NULL;
- /* check for a full match */
- if ( oc_equal( tmpocp, newocp )) {
- /* no changes: keep existing definition and discard new */
- oc_free( &newocp );
- addocp = tmpocp;
- } else {
- /* some differences: discard old and keep the new one */
- oc_free( &tmpocp );
- LDAPDebug( LDAP_DEBUG_TRACE, "schema_replace_objectclasses:"
- " replacing object class %s (OID %s)\n",
- newocp->oc_name, newocp->oc_oid, 0 );
- addocp = newocp;
- }
- break; /* we found it -- exit the loop */
- }
- prevocp = tmpocp;
- }
- if ( NULL == addocp ) {
- LDAPDebug( LDAP_DEBUG_TRACE, "schema_replace_objectclasses:"
- " new object class %s (OID %s)\n",
- newocp->oc_name, newocp->oc_oid, 0 );
- addocp = newocp;
- }
- /* add the objectclass to the end of the new list */
- if ( NULL != addocp ) {
- if ( NULL == newlisthead ) {
- newlisthead = addocp;
- } else {
- newlistend->oc_next = addocp;
- }
- newlistend = addocp;
- }
- }
- clean_up_and_return:
- if ( LDAP_SUCCESS == rc ) {
- /*
- * Delete all remaining OCs that are on the old list AND are not
- * "standard" classes.
- */
- struct objclass *nextocp;
- prevocp = NULL;
- for ( tmpocp = curlisthead; tmpocp != NULL; tmpocp = nextocp ) {
- if ( 0 == ( tmpocp->oc_flags & OC_FLAG_STANDARD_OC )) {
- /* not a standard definition -- remove it */
- if ( tmpocp == curlisthead ) {
- curlisthead = tmpocp->oc_next;
- } else {
- prevocp->oc_next = tmpocp->oc_next;
- }
- nextocp = tmpocp->oc_next;
- oc_free( &tmpocp );
- } else {
- /*
- * XXXmcs: we could generate an error, but for now we do not.
- */
- nextocp = tmpocp->oc_next;
- prevocp = tmpocp;
- #if 0
- schema_create_errormsg( errorbuf, errorbufsize,
- schema_errprefix_oc, tmpocp->oc_name,
- "Cannot delete a standard object class" );
- rc = LDAP_UNWILLING_TO_PERFORM;
- break;
- #endif
- }
- }
- }
- /*
- * Combine the two lists by adding the new list to the end of the old
- * one.
- */
- if ( NULL != curlisthead ) {
- for ( tmpocp = curlisthead; tmpocp->oc_next != NULL;
- tmpocp = tmpocp->oc_next ) {
- ;/*NULL*/
- }
- tmpocp->oc_next = newlisthead;
- newlisthead = curlisthead;
- }
- /*
- * Install the new list as the global one, replacing the old one.
- */
- g_set_global_oc_nolock( newlisthead );
- oc_unlock();
- return rc;
- }
- /*
- * read_oc_ldif_return
- * Free all the memory that read_oc_ldif() allocated, and return the retVal
- *
- * It's nice to do all the freeing in one spot, as read_oc_ldif() returns sideways
- */
- static int
- read_oc_ldif_return( int retVal,
- char *oid,
- struct sizedbuffer *name,
- char *sup,
- char **origins,
- int num_origins,
- char *desc )
- {
- slapi_ch_free((void **)&oid);
- sizedbuffer_destroy( name );
- slapi_ch_free((void **)&sup);
- free_qdlist( origins, num_origins );
- slapi_ch_free((void **)&desc);
- return retVal;
- }
- /*
- * read_oc_ldif
- * Read the value of the objectclasses attribute in cn=schema, convert it
- * into an objectclass struct.
- *
- * Arguments:
- *
- * input : value of objectclasses attribute to read
- * oc : pointer write the objectclass to
- * errorbuf : buffer to write any errors to
- * is_user_defined : if non-zero, force objectclass to be user defined
- * schema_flags : Any or none of the following bits could be set
- * DSE_SCHEMA_NO_CHECK -- schema won't be checked
- * DSE_SCHEMA_NO_GLOCK -- don't lock global resources
- * DSE_SCHEMA_LOCKED -- already locked with
- * reload_schemafile_lock;
- * no further lock needed
- * schema_ds4x_compat: if non-zero, act like Netscape DS 4.x
- *
- * Returns: an LDAP error code
- *
- * LDAP_SUCCESS if the objectclass was sucessfully read, the new
- * objectclass will be written to oc
- *
- * All others: there was an error, an error message will
- * be written to errorbuf
- */
- static int
- read_oc_ldif ( const char *input, struct objclass **oc, char *errorbuf,
- size_t errorbufsize, PRUint32 schema_flags, int is_user_defined,
- int schema_ds4x_compat )
- {
- int i, j, num_origins;
- const char *pstart, *nextinput;
- struct objclass *pnew_oc, *psup_oc;
- char **RequiredAttrsArray, **AllowedAttrsArray;
- char **OrigRequiredAttrsArray, **OrigAllowedAttrsArray;
- char **oc_origins;
- char *pend, *pOcOid, *pOcSup, *pOcDesc;
- struct sizedbuffer *psbOcName= sizedbuffer_construct(BUFSIZ);
- PRUint8 kind, flags;
- int invalid_syntax_error;
- schema_strstr_fn_t keyword_strstr_fn;
- /*
- * From RFC 2252 section 4.4:
- *
- * ObjectClassDescription = "(" whsp
- * numericoid whsp ; ObjectClass identifier
- * [ "NAME" qdescrs ]
- * [ "DESC" qdstring ]
- * [ "OBSOLETE" whsp ]
- * [ "SUP" oids ] ; Superior ObjectClasses
- * [ ( "ABSTRACT" / "STRUCTURAL" / "AUXILIARY" ) whsp ]
- * ; default structural
- * [ "MUST" oids ] ; AttributeTypes
- * [ "MAY" oids ] ; AttributeTypes
- * whsp ")"
- *
- * XXXmcs: Our parsing technique is poor. In (Netscape) DS 4.12 and earlier
- * releases, parsing was mostly done by looking anywhere within the input
- * string for various keywords such as "MUST". But if, for example, a
- * description contains the word "must", the parser would take assume that
- * the tokens following the word were attribute types or OIDs. Bad news.
- *
- * In iDS 5.0 and later, we parse in order left to right and advance a
- * pointer as we consume the input string (the nextinput variable). We
- * also use a case-insensitive search when looking for keywords such as
- * DESC. But the parser will still be fooled by sequences like:
- *
- * ( 1.2.3.4 NAME 'testOC' MUST ( DESC cn ) )
- *
- * Someday soon we will need to write a real parser.
- *
- * Compatibility notes: if schema_ds4x_compat is set, we:
- * 1. always parse from the beginning of the string
- * 2. use a case-insensitive compare when looking for keywords, e.g., MUST
- */
- if ( schema_ds4x_compat ) {
- keyword_strstr_fn = PL_strcasestr;
- invalid_syntax_error = LDAP_OPERATIONS_ERROR;
- } else {
- keyword_strstr_fn = PL_strstr;
- invalid_syntax_error = LDAP_INVALID_SYNTAX;
- }
- flags = 0;
- num_origins = 0;
- oc_origins = NULL;
- pOcOid = pOcSup = pOcDesc = NULL;
- if ( NULL == input || '\0' == input[0] ) {
-
- schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_oc, NULL,
- "One or more values are required for the objectClasses attribute" );
- LDAPDebug ( LDAP_DEBUG_ANY, "NULL args passed to read_oc_ldif\n",0,0,0);
- return read_oc_ldif_return( LDAP_OPERATIONS_ERROR, pOcOid, psbOcName,
- pOcSup, oc_origins, num_origins, pOcDesc );
- }
- nextinput = input;
- /* look for the OID */
- if ( NULL == ( pOcOid = get_tagged_oid( "(", &nextinput,
- keyword_strstr_fn ))) {
- schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_oc,
- input, "Value is malformed. It must include a \"(\"");
- return read_oc_ldif_return( invalid_syntax_error, pOcOid, psbOcName,
- pOcSup, oc_origins, num_origins, pOcDesc );
- }
- if ( schema_ds4x_compat || ( strcasecmp(pOcOid, "NAME") == 0))
- nextinput = input;
- /* look for the NAME */
- if ( (pstart = (*keyword_strstr_fn)(nextinput, "NAME '")) != NULL ) {
- pstart += 6;
- sizedbuffer_allocate(psbOcName,strlen(pstart));
- if ( sscanf ( pstart, "%s", psbOcName->buffer ) > 0 ) {
- /* strip the trailing single quote */
- if ( psbOcName->buffer[strlen(psbOcName->buffer)-1] == '\'' ) {
- psbOcName->buffer[strlen(psbOcName->buffer)-1] = '\0';
- nextinput = pstart + strlen(psbOcName->buffer) + 1;
- } else {
- schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_oc,
- input, "Value is malformed. It must include a single quote around"
- " the name" );
- return read_oc_ldif_return( invalid_syntax_error, pOcOid, psbOcName,
- pOcSup, oc_origins, num_origins, pOcDesc );
- }
- }
- }
- else {
- schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_oc,
- input, "Value is malformed. It must include a \"NAME '\"");
- return read_oc_ldif_return( invalid_syntax_error, pOcOid, psbOcName,
- pOcSup, oc_origins, num_origins, pOcDesc );
- }
- /*
- ** if the objectclass ldif doesn't have an OID, we'll make the oid
- ** ocname-oid
- */
- if ( strcasecmp ( pOcOid, "NAME" ) == 0 ) {
- slapi_ch_free_string( &pOcOid );
- pOcOid = slapi_ch_smprintf("%s-oid", psbOcName->buffer );
- }
- if ( schema_ds4x_compat ) nextinput = input;
- /* look for an optional DESCription */
- if ( (pstart = (*keyword_strstr_fn) ( nextinput, " DESC '")) != NULL ) {
- pstart += 7;
- if (( pend = strchr( pstart, '\'' )) == NULL ) {
- pend = (char *)(pstart + strlen(pstart));
- }
- pOcDesc = slapi_ch_malloc( pend - pstart + 1 );
- memcpy( pOcDesc, pstart, pend - pstart );
- pOcDesc[ pend - pstart ] = '\0';
- nextinput = pend + 1;
- }
- if ( schema_ds4x_compat ) nextinput = input;
- /* look for the optional OBSOLETE marker */
- flags |= get_flag_keyword( schema_obsolete_with_spaces,
- OC_FLAG_OBSOLETE, &nextinput, keyword_strstr_fn );
- if (!(schema_flags & DSE_SCHEMA_NO_GLOCK)) {
- oc_lock_read(); /* needed because we access the superior oc */
- }
- if ( schema_ds4x_compat ) nextinput = input;
- /*
- * Look for the superior objectclass. We first look for a parenthesized
- * list and if not found we look for a simple OID.
- *
- * XXXmcs: Since we do not yet support multiple superior objectclasses, we
- * just grab the first OID in a parenthesized list.
- */
- if ( NULL == ( pOcSup = get_tagged_oid( " SUP (", &nextinput,
- keyword_strstr_fn ))) {
- pOcSup = get_tagged_oid( " SUP ", &nextinput, keyword_strstr_fn );
- }
- psup_oc = oc_find_nolock ( pOcSup );
- if ( schema_ds4x_compat ) nextinput = input;
- /* look for the optional kind (ABSTRACT, STRUCTURAL, AUXILIARY) */
- for ( i = 0; i < SCHEMA_OC_KIND_COUNT; ++i ) {
- if ( NULL != ( pstart = (*keyword_strstr_fn)( nextinput,
- schema_oc_kind_strings_with_spaces[i] ))) {
- kind = i;
- nextinput = pstart + strlen( schema_oc_kind_strings_with_spaces[i] ) - 1;
- break;
- }
- }
- if ( i >= SCHEMA_OC_KIND_COUNT ) { /* not found */
- if ( NULL != psup_oc && OC_KIND_ABSTRACT != psup_oc->oc_kind ) {
- /* inherit kind from superior class if not ABSTRACT */
- kind = psup_oc->oc_kind;
- } else {
- /* according to RFC 2252, the default is structural */
- kind = OC_KIND_STRUCTURAL;
- }
- }
- if ( schema_ds4x_compat ) nextinput = input;
- /* look for required attributes (MUST) */
- if ( (pstart = (*keyword_strstr_fn) (nextinput, " MUST ")) != NULL ) {
- char *pRequiredAttrs;
- int saw_open_paren = 0;
- pstart += 6;
- pstart = skipWS( pstart ); /* skip past any extra white space */
- if ( *pstart == '(' ) {
- saw_open_paren = 1;
- ++pstart;
- }
- pRequiredAttrs = slapi_ch_strdup ( pstart );
- if ( saw_open_paren && (pend = strchr (pRequiredAttrs, ')')) != NULL ) {
- *pend = '\0';
- } else if ((pend = strchr (pRequiredAttrs, ' ' )) != NULL ) {
- *pend = '\0';
- } else {
- pend = pRequiredAttrs + strlen(pRequiredAttrs); /* at end of string */
- }
- nextinput = pstart + ( pend - pRequiredAttrs );
- RequiredAttrsArray = read_dollar_values (pRequiredAttrs);
- slapi_ch_free((void**)&pRequiredAttrs);
- } else {
- RequiredAttrsArray = (char **) slapi_ch_malloc (1 * sizeof(char *)) ;
- RequiredAttrsArray[0] = NULL;
- }
- if ( schema_ds4x_compat ) nextinput = input;
- /* look for allowed attributes (MAY) */
- if ( (pstart = (*keyword_strstr_fn) (nextinput, " MAY ")) != NULL ) {
- char *pAllowedAttrs;
- int saw_open_paren = 0;
- pstart += 5;
- pstart = skipWS( pstart ); /* skip past any extra white space */
- if ( *pstart == '(' ) {
- saw_open_paren = 1;
- ++pstart;
- }
- pAllowedAttrs = slapi_ch_strdup ( pstart );
- if ( saw_open_paren && (pend = strchr (pAllowedAttrs, ')')) != NULL ) {
- *pend = '\0';
- } else if ((pend = strchr (pAllowedAttrs, ' ' )) != NULL ) {
- *pend = '\0';
- } else {
- pend = pAllowedAttrs + strlen(pAllowedAttrs); /* at end of string */
- }
- nextinput = pstart + ( pend - pAllowedAttrs );
- AllowedAttrsArray = read_dollar_values (pAllowedAttrs);
- slapi_ch_free((void**)&pAllowedAttrs);
- } else {
- AllowedAttrsArray = (char **) slapi_ch_malloc (1 * sizeof(char *)) ;
- AllowedAttrsArray[0] = NULL;
- }
- if ( schema_ds4x_compat ) nextinput = input;
- /* look for X-ORIGIN list */
- oc_origins = parse_origin_list( nextinput, &num_origins,
- schema_user_defined_origin );
- /* set remaining flags */
- if ( element_is_user_defined( oc_origins )) {
- flags |= OC_FLAG_USER_OC;
- } else if ( is_user_defined ) {
- flags |= OC_FLAG_USER_OC;
- /* add missing user defined origin string */
- charray_add( &oc_origins, slapi_ch_strdup( schema_user_defined_origin[0] ));
- ++num_origins;
- } else {
- flags |= OC_FLAG_STANDARD_OC;
- }
- /* generate OrigRequiredAttrsArray and OrigAllowedAttrsArray */
- if (psup_oc) {
- int found_it;
- OrigRequiredAttrsArray = (char **) slapi_ch_malloc (1 * sizeof(char *)) ;
- OrigRequiredAttrsArray[0] = NULL;
- OrigAllowedAttrsArray = (char **) slapi_ch_malloc (1 * sizeof(char *)) ;
- OrigAllowedAttrsArray[0] = NULL;
- if (psup_oc->oc_required) {
- for (i = 0; RequiredAttrsArray[i]; i++) {
- for (j = 0, found_it = 0; psup_oc->oc_required[j]; j++) {
- if (strcasecmp (psup_oc->oc_required[j], RequiredAttrsArray[i]) == 0) {
- found_it = 1;
- }
- }
- if (!found_it) {
- charray_add (&OrigRequiredAttrsArray, slapi_ch_strdup ( RequiredAttrsArray[i] ) );
- }
- }
- }
- if (psup_oc->oc_allowed) {
- for (i = 0; AllowedAttrsArray[i]; i++) {
- for (j = 0, found_it=0; psup_oc->oc_allowed[j]; j++) {
- if (strcasecmp (psup_oc->oc_allowed[j], AllowedAttrsArray[i]) == 0) {
- found_it = 1;
- }
- }
- if (!found_it) {
- charray_add (&OrigAllowedAttrsArray, slapi_ch_strdup (AllowedAttrsArray[i]) );
- }
- }
- }
- }
- else {
- /* if no parent oc */
- OrigRequiredAttrsArray = charray_dup ( RequiredAttrsArray );
- OrigAllowedAttrsArray = charray_dup ( AllowedAttrsArray );
- }
- if (!(schema_flags & DSE_SCHEMA_NO_GLOCK)) {
- oc_unlock(); /* we are done accessing superior oc (psup_oc) */
- }
- /* finally -- create new objclass structure */
- pnew_oc = (struct objclass *) slapi_ch_malloc (1 * sizeof (struct objclass));
- pnew_oc->oc_name = slapi_ch_strdup ( psbOcName->buffer );
- pnew_oc->oc_superior = pOcSup;
- pOcSup = NULL; /* don't free this later */
- pnew_oc->oc_oid = pOcOid;
- pOcOid = NULL; /* don't free this later */
- pnew_oc->oc_desc = pOcDesc;
- pOcDesc = NULL; /* don't free this later */
- pnew_oc->oc_required = RequiredAttrsArray;
- pnew_oc->oc_allowed = AllowedAttrsArray;
- pnew_oc->oc_orig_required = OrigRequiredAttrsArray;
- pnew_oc->oc_orig_allowed = OrigAllowedAttrsArray;
- pnew_oc->oc_origin = cool_charray_dup( oc_origins );
- pnew_oc->oc_next = NULL;
- pnew_oc->oc_flags = flags;
- pnew_oc->oc_kind = kind;
- *oc = pnew_oc;
- return read_oc_ldif_return( LDAP_SUCCESS, pOcOid, psbOcName, pOcSup,
- oc_origins, num_origins, pOcDesc );
- }
- static void
- oc_free( struct objclass **ocp )
- {
- struct objclass *oc;
- if ( NULL != ocp && NULL != *ocp ) {
- oc = *ocp;
- slapi_ch_free( (void **)&oc->oc_name );
- slapi_ch_free( (void **)&oc->oc_desc );
- slapi_ch_free( (void **)&oc->oc_oid );
- slapi_ch_free( (void **)&oc->oc_superior );
- charray_free( oc->oc_required );
- charray_free( oc->oc_allowed );
- charray_free( oc->oc_orig_required );
- charray_free( oc->oc_orig_allowed );
- cool_charray_free( oc->oc_origin );
- slapi_ch_free( (void **)&oc );
- *ocp = NULL;
- }
- }
- struct supargs{
- char *sup, *oid;
- unsigned long rc;
- };
- static int
- at_sup_dependency_callback(struct asyntaxinfo *asi, void *arg)
- {
- struct supargs *aew = (struct supargs *)arg;
- int rc=ATTR_SYNTAX_ENUM_NEXT;
-
- if (!asi) {
- LDAPDebug(LDAP_DEBUG_ANY, "Error: no attribute types in at_schema_attributes_callback\n",
- 0, 0, 0);
- }
- else
- {
- if (strcasecmp (asi->asi_oid, aew->oid ) == 0) {
- rc=ATTR_SYNTAX_ENUM_STOP;
- } else {
- if(asi->asi_name != NULL) {
- if (strcasecmp (asi->asi_name, aew->sup ) == 0) {
- aew->rc=0;
- } else if (asi->asi_aliases) {
- int i = 0;
- /* Loop through aliases to see if any match */
- for (i=0; asi->asi_aliases[i] != NULL; i++) {
- if (strcasecmp (asi->asi_aliases[i], aew->sup ) == 0) {
- aew->rc=0;
- }
- }
- }
- }
- }
- }
- return rc;
- }
- /* walks down attribute types and makes sure that the superior value is found in an attribute type
- preceeding in the hash table. I have concerns about collisions messing with the order here but this
- may be the best we can do.
- */
- static int
- slapi_check_at_sup_dependency(char *sup, char *oid)
- {
- struct supargs aew;
-
- memset(&aew,0,sizeof(struct supargs));
- aew.rc=LDAP_TYPE_OR_VALUE_EXISTS;
- aew.sup=sup;
- aew.oid=oid;
- attr_syntax_enumerate_attrs(at_sup_dependency_callback, &aew, PR_FALSE);
- return aew.rc;
- }
- /*
- * if asipp is NULL, the attribute type is added to the global set of schema.
- * if asipp is not NULL, the AT is not added but *asipp is set. When you are
- * finished with *asipp, use attr_syntax_free() to dispose of it.
- *
- * schema_flags: Any or none of the following bits could be set
- * DSE_SCHEMA_NO_CHECK -- schema won't be checked
- * DSE_SCHEMA_NO_GLOCK -- locking of global resources is turned off;
- * this saves time during initialization since
- * the server operates in single threaded mode
- * at that time or in reload_schemafile_lock.
- * DSE_SCHEMA_LOCKED -- already locked with reload_schemafile_lock;
- * no further lock needed
- *
- * if is_user_defined is true, force attribute type to be user defined.
- *
- * returns an LDAP error code (LDAP_SUCCESS if all goes well)
- */
- static int
- read_at_ldif(const char *input, struct asyntaxinfo **asipp, char *errorbuf,
- size_t errorbufsize, PRUint32 schema_flags, int is_user_defined,
- int schema_ds4x_compat, int is_remote)
- {
- char *pStart, *pEnd;
- char *pOid, *pSyntax, *pSuperior, *pMREquality, *pMROrdering, *pMRSubstring;
- const char *nextinput;
- struct sizedbuffer *psbAttrName= sizedbuffer_construct(BUFSIZ);
- struct sizedbuffer *psbAttrDesc= sizedbuffer_construct(BUFSIZ);
- int status = 0;
- int syntaxlength;
- char **attr_names = NULL;
- char *first_attr_name = NULL;
- char **attr_origins = NULL;
- int num_names = 0;
- int num_origins = 0;
- unsigned long flags = SLAPI_ATTR_FLAG_OVERRIDE;
- const char *ss = 0;
- struct asyntaxinfo *tmpasip;
- int invalid_syntax_error;
- schema_strstr_fn_t keyword_strstr_fn;
- /*
- * From RFC 2252 section 4.2:
- *
- * AttributeTypeDescription = "(" whsp
- * numericoid whsp ; AttributeType identifier
- * [ "NAME" qdescrs ] ; name used in AttributeType
- * [ "DESC" qdstring ] ; description
- * [ "OBSOLETE" whsp ]
- * [ "SUP" woid ] ; derived from this other
- * ; AttributeType
- * [ "EQUALITY" woid ; Matching Rule name
- * [ "ORDERING" woid ; Matching Rule name
- * [ "SUBSTR" woid ] ; Matching Rule name
- * [ "SYNTAX" whsp noidlen whsp ] ; see section 4.3
- * [ "SINGLE-VALUE" whsp ] ; default multi-valued
- * [ "COLLECTIVE" whsp ] ; default not collective
- * [ "NO-USER-MODIFICATION" whsp ]; default user modifiable
- * [ "USAGE" whsp AttributeUsage ]; default userApplications
- * whsp ")"
- *
- * AttributeUsage =
- * "userApplications" /
- * "directoryOperation" /
- * "distributedOperation" / ; DSA-shared
- * "dSAOperation" ; DSA-specific, value depends on server
- *
- * XXXmcs: Our parsing technique is poor. In (Netscape) DS 4.12 and earlier
- * releases, parsing was mostly done by looking anywhere within the input
- * string for various keywords such as "EQUALITY". But if, for example, a
- * description contains the word "equality", the parser would take assume
- * that the token following the word was a matching rule. Bad news.
- *
- * In iDS 5.0 and later, we parse in order left to right and advance a
- * pointer as we consume the input string (the nextinput variable). We
- * also use a case-insensitive search when looking for keywords such as
- * DESC. This is still less than ideal.
- *
- * Someday soon we will need to write a real parser.
- *
- * Compatibility notes: if schema_ds4x_compat is set, we:
- * 1. always parse from the beginning of the string
- * 2. use a case-insensitive compare when looking for keywords, e.g., DESC
- */
- if ( schema_ds4x_compat ) {
- keyword_strstr_fn = PL_strcasestr;
- invalid_syntax_error = LDAP_OPERATIONS_ERROR;
- } else {
- keyword_strstr_fn = PL_strstr;
- invalid_syntax_error = LDAP_INVALID_SYNTAX;
- }
- if (schema_flags & DSE_SCHEMA_NO_GLOCK)
- flags |= SLAPI_ATTR_FLAG_NOLOCKING;
- psbAttrName->buffer[0] = '\0';
- psbAttrDesc->buffer[0] = '\0';
- pOid = pSyntax = pSuperior = NULL;
- pMREquality = pMROrdering = pMRSubstring = NULL;
- syntaxlength = SLAPI_SYNTAXLENGTH_NONE;
- nextinput = input;
- /* get the OID */
- pOid = get_tagged_oid( "(", &nextinput, keyword_strstr_fn );
- if (NULL == pOid) {
- schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_at,
- input, "Missing or invalid OID" );
- status = invalid_syntax_error;
- goto done;
- }
- if ( schema_ds4x_compat || (strcasecmp(pOid, "NAME") == 0))
- nextinput = input;
- /* look for the NAME (single or list of names) */
- if ( (pStart = (*keyword_strstr_fn) ( nextinput, "NAME ")) != NULL ) {
- pStart += 5;
- sizedbuffer_allocate(psbAttrName,strlen(pStart)+1);
- strcpy ( psbAttrName->buffer, pStart);
- if (*pStart == '(')
- pEnd = strchr(psbAttrName->buffer, ')');
- else
- pEnd = strchr(psbAttrName->buffer+1, '\'');
- if (pEnd)
- *(pEnd+1) = 0;
- nextinput = pStart + strlen(psbAttrName->buffer) + 1;
- attr_names = parse_qdescrs(psbAttrName->buffer, &num_names);
- if ( NULL != attr_names ) {
- first_attr_name = attr_names[0];
- } else { /* NAME followed by nothing violates syntax */
- schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_at,
- input, "Missing or invalid attribute name" );
- status = invalid_syntax_error;
- goto done;
- }
- }
- if ( schema_ds4x_compat ) nextinput = input;
- /*
- * if the attribute ldif doesn't have an OID, we'll make the oid
- * attrname-oid
- */
- if ( (strcasecmp ( pOid, "NAME" ) == 0) && (first_attr_name)) {
- slapi_ch_free_string( &pOid );
- pOid = slapi_ch_smprintf("%s-oid", first_attr_name );
- }
- /* look for the optional DESCription */
- if ( (pStart = (*keyword_strstr_fn) ( nextinput, "DESC '")) != NULL ) {
- pStart += 6;
- sizedbuffer_allocate(psbAttrDesc,strlen(pStart));
- strcpy ( psbAttrDesc->buffer, pStart);
- if ( (pEnd = strchr (psbAttrDesc->buffer, '\'' )) != NULL ){
- *pEnd ='\0';
- }
- nextinput = pStart + strlen(psbAttrDesc->buffer) + 1;
- }
- if ( schema_ds4x_compat ) nextinput = input;
- /* look for the optional OBSOLETE marker */
- flags |= get_flag_keyword( schema_obsolete_with_spaces,
- SLAPI_ATTR_FLAG_OBSOLETE, &nextinput, keyword_strstr_fn );
- if ( schema_ds4x_compat ) nextinput = input;
- /* look for the optional SUPerior type */
- pSuperior = get_tagged_oid( "SUP ", &nextinput, keyword_strstr_fn );
- if ( schema_ds4x_compat ) nextinput = input;
- /* look for the optional matching rules */
- pMREquality = get_tagged_oid( "EQUALITY ", &nextinput, keyword_strstr_fn );
- if ( schema_ds4x_compat ) nextinput = input;
- pMROrdering = get_tagged_oid( "ORDERING ", &nextinput, keyword_strstr_fn );
- if ( schema_ds4x_compat ) nextinput = input;
- pMRSubstring = get_tagged_oid( "SUBSTR ", &nextinput, keyword_strstr_fn );
- if ( schema_ds4x_compat ) nextinput = input;
- /* look for the optional SYNTAX */
- if ( NULL != ( pSyntax = get_tagged_oid( "SYNTAX ", &nextinput,
- keyword_strstr_fn ))) {
- /*
- * Check for an optional {LEN}, which if present indicates a
- * suggested maximum size for values of this attribute type.
- *
- * XXXmcs: we do not enforce length restrictions, but we do read
- * and include them in the subschemasubentry.
- */
- if ( (pEnd = strchr ( pSyntax, '{')) != NULL /* balance } */ ) {
- *pEnd = '\0';
- syntaxlength = atoi( pEnd + 1 );
- }
- }
- if ( schema_ds4x_compat ) nextinput = input;
- /* look for the optional SINGLE-VALUE marker */
- flags |= get_flag_keyword( " SINGLE-VALUE ",
- SLAPI_ATTR_FLAG_SINGLE, &nextinput, keyword_strstr_fn );
- if ( schema_ds4x_compat ) nextinput = input;
- /* look for the optional COLLECTIVE marker */
- flags |= get_flag_keyword( schema_collective_with_spaces,
- SLAPI_ATTR_FLAG_COLLECTIVE, &nextinput, keyword_strstr_fn );
- if ( schema_ds4x_compat ) nextinput = input;
- /* look for the optional NO-USER-MODIFICATION marker */
- flags |= get_flag_keyword( schema_nousermod_with_spaces,
- SLAPI_ATTR_FLAG_NOUSERMOD, &nextinput, keyword_strstr_fn );
- if ( schema_ds4x_compat ) nextinput = input;
- /* look for the optional USAGE */
- if (NULL != (ss = (*keyword_strstr_fn)(nextinput, " USAGE "))) {
- ss += 7;
- ss = skipWS(ss);
- if (ss) {
- if ( !PL_strncmp(ss, "directoryOperation",
- strlen("directoryOperation"))) {
- flags |= SLAPI_ATTR_FLAG_OPATTR;
- }
- if ( NULL == ( nextinput = strchr( ss, ' ' ))) {
- nextinput = ss + strlen(ss);
- }
- }
- }
- if ( schema_ds4x_compat ) nextinput = input;
- /* X-ORIGIN list */
- attr_origins = parse_origin_list( nextinput, &num_origins,
- schema_user_defined_origin );
- /* Do some sanity checking to make sure everything was read correctly */
-
- if (NULL == pOid) {
- schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_at,
- first_attr_name, "Missing OID" );
- status = invalid_syntax_error;
- }
- if (!status && (!attr_names || !num_names)) {
- schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_at,
- first_attr_name,
- "Missing name (OID is \"%s\")", pOid );
- status = invalid_syntax_error;
- }
- if (!status && (NULL != pSuperior)) {
- struct asyntaxinfo *asi_parent;
-
- asi_parent = attr_syntax_get_by_name(pSuperior);
- /* if we find no match then server won't start or add the attribute type */
- if (asi_parent == NULL) {
- LDAPDebug (LDAP_DEBUG_PARSE,
- "Cannot find parent attribute type \"%s\"\n",pSuperior,
- NULL,NULL);
- schema_create_errormsg( errorbuf, errorbufsize,
- schema_errprefix_at, first_attr_name,
- "Missing parent attribute syntax OID");
- status = invalid_syntax_error;
- /* We only want to use the parent syntax if a SYNTAX
- * wasn't explicitly specified for this attribute. */
- } else if (NULL == pSyntax) {
- char *pso = plugin_syntax2oid(asi_parent->asi_plugin);
-
- if (pso) {
- slapi_ch_free ((void **)&pSyntax);
- pSyntax = slapi_ch_strdup(pso);
- LDAPDebug (LDAP_DEBUG_TRACE,
- "Inheriting syntax %s from parent type %s\n",
- pSyntax, pSuperior,NULL);
- } else {
- schema_create_errormsg( errorbuf, errorbufsize,
- schema_errprefix_at, first_attr_name,
- "Missing parent attribute syntax OID");
- status = invalid_syntax_error;
- }
- attr_syntax_return( asi_parent );
- }
- }
- /*
- if we are remote (via modify_schema_dse) then check sup dependencies. Locally
- was done in if statement above
- */
-
- if(!status)
- {
- if(is_remote && (pSuperior != NULL))
- {
- status=slapi_check_at_sup_dependency(pSuperior, pOid);
- }
- if(LDAP_SUCCESS != status) {
- schema_create_errormsg( errorbuf, errorbufsize,
- schema_errprefix_at, first_attr_name,
- "Missing parent attribute syntax OID");
- status = LDAP_TYPE_OR_VALUE_EXISTS;
- }
- }
- if (!status && (NULL == pSyntax)) {
- schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_at,
- first_attr_name, "Missing attribute syntax OID");
- status = invalid_syntax_error;
- }
-
- if (!status && (plugin_syntax_find ( pSyntax ) == NULL) ) {
- schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_at,
- first_attr_name, "Unknown attribute syntax OID \"%s\"",
- pSyntax );
- status = invalid_syntax_error;
- }
- if (!status) {
- struct objclass *poc;
- /* check to make sure that the OID isn't being used by an objectclass */
- if (!(schema_flags & DSE_SCHEMA_LOCKED))
- oc_lock_read();
- poc = oc_find_oid_nolock( pOid );
- if ( poc != NULL) {
- schema_create_errormsg( errorbuf, errorbufsize,
- schema_errprefix_at, first_attr_name,
- "The OID \"%s\" is also used by the object class \"%s\"",
- pOid, poc->oc_name);
- status = LDAP_TYPE_OR_VALUE_EXISTS;
- }
- if (!(schema_flags & DSE_SCHEMA_LOCKED))
- oc_unlock();
- }
- if (!status && !element_is_user_defined( attr_origins )) {
- if ( is_user_defined ) {
- /* add missing user defined origin string */
- charray_add( &attr_origins,
- slapi_ch_strdup( schema_user_defined_origin[0] ));
- ++num_origins;
- } else {
- flags |= SLAPI_ATTR_FLAG_STD_ATTR;
- }
- }
- if (!(schema_flags & DSE_SCHEMA_NO_CHECK) && !status) {
- int ii;
- /* check to see if the attribute name is valid */
- for (ii = 0; !status && (ii < num_names); ++ii) {
- if ( schema_check_name(attr_names[ii], PR_TRUE, errorbuf,
- errorbufsize) == 0 ) {
- status = invalid_syntax_error;
- }
- else if (!(flags & SLAPI_ATTR_FLAG_OVERRIDE) &&
- attr_syntax_exists(attr_names[ii])) {
- schema_create_errormsg( errorbuf, errorbufsize,
- schema_errprefix_at, attr_names[ii],
- "Could not be added because it already exists" );
- status = LDAP_TYPE_OR_VALUE_EXISTS;
- }
- }
- }
- if (!(schema_flags & DSE_SCHEMA_NO_CHECK) && !status) {
- if ( schema_check_oid ( first_attr_name, pOid, PR_TRUE, errorbuf,
- errorbufsize ) == 0 ) {
- status = invalid_syntax_error;
- }
- }
- if (!status) {
- struct asyntaxinfo *tmpasi;
- if (!(flags & SLAPI_ATTR_FLAG_OVERRIDE) &&
- ( NULL != ( tmpasi = attr_syntax_get_by_oid(pOid)))) {
- schema_create_errormsg( errorbuf, errorbufsize,
- schema_errprefix_at, first_attr_name,
- "Could not be added because the OID \"%s\" is already in use",
- pOid);
- status = LDAP_TYPE_OR_VALUE_EXISTS;
- attr_syntax_return( tmpasi );
- }
- }
-
- if (!status) {
- status = attr_syntax_create( pOid, attr_names, num_names,
- *psbAttrDesc->buffer == '\0' ? NULL : psbAttrDesc->buffer,
- pSuperior,
- pMREquality, pMROrdering, pMRSubstring, attr_origins,
- pSyntax, syntaxlength, flags, &tmpasip );
- }
- if (!status) {
- if ( NULL != asipp ) {
- *asipp = tmpasip; /* just return it */
- } else { /* add the new attribute to the global store */
- status = attr_syntax_add( tmpasip );
- if ( LDAP_SUCCESS != status ) {
- if ( 0 != (flags & SLAPI_ATTR_FLAG_OVERRIDE) &&
- LDAP_TYPE_OR_VALUE_EXISTS == status ) {
- /*
- * This can only occur if the name and OID don't match the
- * attribute we are trying to override (all other cases of
- * "type or value exists" were trapped above).
- */
- schema_create_errormsg( errorbuf, errorbufsize,
- schema_errprefix_at, first_attr_name,
- "Does not match the OID \"%s\". Another attribute"
- " type is already using the name or OID.", pOid);
- } else {
- schema_create_errormsg( errorbuf, errorbufsize,
- schema_errprefix_at, first_attr_name,
- "Could not be added (OID is \"%s\")", pOid );
- }
- attr_syntax_free( tmpasip );
- }
- }
- }
- done:
- /* free everything */
- free_qdlist(attr_names, num_names);
- free_qdlist(attr_origins, num_origins);
- sizedbuffer_destroy(psbAttrName);
- sizedbuffer_destroy(psbAttrDesc);
- slapi_ch_free((void **)&pOid);
- slapi_ch_free((void **)&pSuperior);
- slapi_ch_free((void **)&pMREquality);
- slapi_ch_free((void **)&pMROrdering);
- slapi_ch_free((void **)&pMRSubstring);
- slapi_ch_free((void **)&pSyntax);
- return status;
- }
- /*
- * schema_check_oc_attrs:
- * Check to see if the required and allowed attributes are valid attributes
- *
- * arguments: poc : pointer to the objectclass to check
- * errorbuf : buffer to write any error messages to
- * stripOptions: 1 if you want to silently strip any options
- * 0 if options should cause an error
- *
- * Returns:
- *
- * 0 if there's a unknown attribute, and errorbuf will contain an
- * error message.
- *
- * 1 if everything is ok
- *
- * Note: no locking of poc is needed because poc is always a newly allocated
- * objclass struct (this function is only called by add_oc_internal).
- */
- static int
- schema_check_oc_attrs ( struct objclass *poc,
- char *errorbuf, size_t errorbufsize,
- int stripOptions )
- {
- int i;
- if ( errorbuf == NULL || poc == NULL || poc->oc_name == NULL) {
- /* error */
- LDAPDebug (LDAP_DEBUG_PARSE,
- "Null args passed to schema_check_oc_attrs\n",
- NULL, NULL, NULL);
- return -1;
- }
- /* remove any options like ;binary from the oc's attributes */
- if ( strip_oc_options( poc ) && !stripOptions) {
- /* there were options present, this oc should be rejected */
- schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_oc,
- poc->oc_name, "Contains attribute options. "
- "Attribute options, such as \";binary\" are not allowed in "
- "object class definitions." );
- return 0;
- }
-
- for ( i = 0; poc->oc_allowed && poc->oc_allowed[i]; i++ ) {
- if ( attr_syntax_exists ( poc->oc_allowed[i] ) == 0 ) {
- schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_oc,
- poc->oc_name, "Unknown allowed attribute type \"%s\"",
- poc->oc_allowed[i]);
- return 0;
- }
- }
-
-
- for ( i = 0; poc->oc_required && poc->oc_required[i]; i++ ) {
- if ( attr_syntax_exists ( poc->oc_required[i] ) == 0 ) {
- schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_oc,
- poc->oc_name, "Unknown required attribute type \"%s\"",
- poc->oc_required[i]);
- return 0;
- }
- }
- return 1;
- }
- /*
- * schema_check_name:
- * Check if the attribute or objectclass name is valid. Names can only contain
- * characters, digits, and hyphens. In addition, names must begin with
- * a character. If the nsslapd-attribute-name-exceptions attribute in cn=config
- * is true, then we also allow underscores.
- *
- * XXX We're also supposed to allow semicolons, but we already use them to deal
- * with attribute options XXX
- *
- * returns 1 if the attribute has a legal name
- * 0 if not
- *
- * If the attribute name is invalid, an error message will be written to msg
- */
- static int
- schema_check_name(char *name, PRBool isAttribute, char *errorbuf,
- size_t errorbufsize )
- {
- int i;
- /* allowed characters */
- static char allowed[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890-";
- /* additional characters to allow if allow_exceptions is true */
- static char allowedExceptions[] = "_";
- int allow_exceptions = config_get_attrname_exceptions();
-
- if ( name == NULL || errorbuf == NULL) {
- /* this is bad */
- return 0;
- }
- /* attribute names must begin with a letter */
- if ( (isascii (name[0]) == 0) || (isalpha (name[0]) == 0)) {
- if ( (strlen(name) + 80) < BUFSIZ ) {
- schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_at,
- name, "The name is invalid. Names must begin with a letter" );
- }
- else {
- schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_at,
- name, "The name is invalid, and probably too long. "
- "Names must begin with a letter" );
- }
- return 0;
- }
- for (i = 1; name[i]; i++ ) {
- if ( (NULL == strchr( allowed, name[i] )) &&
- (!allow_exceptions ||
- (NULL == strchr(allowedExceptions, name[i])) ) ) {
- if ( (strlen(name) + 80) < BUFSIZ ) {
- schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_at,
- name, "The name contains the invalid character \"%c\"", name[i] );
- }
- else {
- schema_create_errormsg( errorbuf, errorbufsize, schema_errprefix_at,
- name, "The name contains the invalid character \"%c\". The name"
- " is also probably too long.", name[i] );
- }
- return 0;
- }
- }
- return 1;
- }
- /*
- * schema_check_oid:
- * Check if the oid is valid.
- *
- * returns 1 if the attribute has a legal oid
- * 0 if not
- *
- * If the oid is invalid, an error message will be written to errorbuf
- *
- * Oids can either have the form <attr/oc name>-oid or
- * start and end with a digit, and contain only digits and periods
- */
- static int
- schema_check_oid( const char *name, const char *oid, PRBool isAttribute,
- char *errorbuf, size_t errorbufsize ) {
- int i = 0, length_oid = 0, rc = 0;
- char *namePlusOid = NULL;
- if ( name == NULL || oid == NULL) {
- /* this is bad */
- LDAPDebug (LDAP_DEBUG_ANY, "NULL passed to schema_check_oid\n",0,0,0);
- return 0;
- }
-
- /* check to see if the OID is <name>-oid */
- namePlusOid = slapi_ch_smprintf("%s-oid", name );
- rc = strcasecmp( oid, namePlusOid );
- slapi_ch_free( (void **) &namePlusOid );
- if ( 0 == rc ) {
- return 1;
- }
- /* If not, the OID must begin and end with a digit, and contain only
- digits and dots */
-
- /* check to see that it begins and ends with a digit */
- length_oid = strlen(oid);
- if ( !isdigit(oid[0]) ||
- !isdigit(oid[length_oid-1]) ) {
- schema_create_errormsg( errorbuf, errorbufsize,
- isAttribute ? schema_errprefix_at : schema_errprefix_oc,
- name,
- "The OID \"%s\" must begin and end with a digit, or be \"%s-oid\"",
- oid, name );
- return 0;
- }
- /* check to see that it contains only digits and dots */
- for ( i = 0; i < length_oid; i++ ) {
- if ( !isdigit(oid[i]) && oid[i] != '.' ){
- schema_create_errormsg( errorbuf, errorbufsize,
- isAttribute ? schema_errprefix_at : schema_errprefix_oc,
- name,
- "The OID \"%s\" contains an invalid character: \"%c\"; the"
- " OID must contain only digits and periods, or be \"%s-oid\"",
- oid, oid[i], name );
- return 0;
- }
- }
- /* The oid is OK if we're here */
- return 1;
-
- }
- /*
- * Some utility functions for dealing with a dynamically
- * allocated buffer.
- */
- static struct sizedbuffer *sizedbuffer_construct(size_t size)
- {
- struct sizedbuffer *p= (struct sizedbuffer *)slapi_ch_malloc(sizeof(struct sizedbuffer));
- p->size= size;
- if(size>0)
- {
- p->buffer= (char*)slapi_ch_malloc(size);
- p->buffer[0]= '\0';
- }
- else
- {
- p->buffer= NULL;
- }
- return p;
- }
- static void sizedbuffer_destroy(struct sizedbuffer *p)
- {
- if(p!=NULL)
- {
- slapi_ch_free((void**)&p->buffer);
- }
- slapi_ch_free((void**)&p);
- }
- static void sizedbuffer_allocate(struct sizedbuffer *p, size_t sizeneeded)
- {
- if(p!=NULL)
- {
- if(sizeneeded>p->size)
- {
- if(p->buffer!=NULL)
- {
- slapi_ch_free((void**)&p->buffer);
- }
- p->buffer= (char*)slapi_ch_malloc(sizeneeded);
- p->buffer[0]= '\0';
- p->size= sizeneeded;
- }
- }
- }
- /*
- * has_smart_referral: returns 1 if the entry contains a ref attribute,
- * or a referral objectclass.
- *
- * Returns 0 If not.
- */
-
- static int
- has_smart_referral( Slapi_Entry *e ) {
-
- Slapi_Attr *aoc;
- char ebuf[BUFSIZ];
- /* Look for the ref attribute */
- if ( (aoc = attrlist_find( e->e_attrs, "ref" )) != NULL ) {
- LDAPDebug ( LDAP_DEBUG_ANY, "Entry \"%s\" contains a ref attrbute. Smart referrals are disabled in Directory Lite.\n", escape_string(slapi_entry_get_dn_const(e), ebuf),0,0 );
- return 1;
- }
-
- /* Look for the referral objectclass */
- if ( (aoc = attrlist_find( e->e_attrs, "objectclass" )) != NULL ) {
- Slapi_Value target, *found;
- slapi_value_init(&target);
- slapi_value_set_string(&target,"referral");
- found= slapi_valueset_find(aoc, &aoc->a_present_values, &target);
- value_done(&target);
- if(found!=NULL)
- {
- LDAPDebug ( LDAP_DEBUG_ANY, "Entry \"%s\" is a referral object class. Smart referrals are disabled in Directory Lite.\n", escape_string(slapi_entry_get_dn_const(e), ebuf),0,0 );
- return 1;
- }
- }
-
- /* No smart referral here */
- return 0;
- }
- /*
- * Check if the object class is extensible
- */
- static int isExtensibleObjectclass(const char *objectclass)
- {
- if ( strcasecmp( objectclass, "extensibleobject" ) == 0 ) {
- return( 1 );
- }
- /* The Easter Egg is based on a special object class */
- if ( strcasecmp( objectclass, EGG_OBJECT_CLASS ) == 0 ) {
- return( 1 );
- }
- return 0;
- }
- /*
- * strip_oc_options: strip any attribute options from the objectclass'
- * attributes (remove things like ;binary from the attrs)
- *
- * argument: pointer to an objectclass, attributes will have their
- * options removed in place
- *
- * returns: number of options removed
- *
- * Note: no locking of poc is needed because poc is always a newly allocated
- * objclass struct (this function is only called by schema_check_oc_attrs,
- * which is only called by add_oc_internal).
- */
- static int
- strip_oc_options( struct objclass *poc ) {
- int i, numRemoved = 0;
- char *mod = NULL;
- for ( i = 0; poc->oc_allowed && poc->oc_allowed[i]; i++ ) {
- if ( (mod = stripOption( poc->oc_allowed[i] )) != NULL ){
- LDAPDebug (LDAP_DEBUG_ANY,
- "Removed option \"%s\" from allowed attribute type "
- "\"%s\" in object class \"%s\".\n",
- mod, poc->oc_allowed[i], poc->oc_name );
- numRemoved++;
- }
- }
-
- for ( i = 0; poc->oc_required && poc->oc_required[i]; i++ ) {
- if ( (mod = stripOption( poc->oc_required[i] )) != NULL ){
- LDAPDebug (LDAP_DEBUG_ANY,
- "Removed option \"%s\" from required attribute type "
- "\"%s\" in object class \"%s\".\n",
- mod, poc->oc_required[i], poc->oc_name );
- numRemoved++;
- }
- }
- return numRemoved;
- }
- /*
- * stripOption:
- * removes options such as ";binary" from attribute names
- *
- * argument: pointer to an attribute name, such as "userCertificate;binary"
- *
- * returns: pointer to the option, such as "binary"
- * NULL if there's no option
- *
- */
- static char *
- stripOption(char *attr) {
- char *pSemiColon = strchr( attr, ';' );
- if (pSemiColon) {
- *pSemiColon = '\0';
- }
- return pSemiColon ? pSemiColon + 1 : NULL;
- }
- /*
- * load_schema_dse: called by dse_read_file() when target is cn=schema
- *
- * Initialize attributes and objectclasses from the schema
- *
- * Note that this function removes all values for `attributetypes'
- * and `objectclasses' attributes from the entry `e'.
- *
- * returntext is always at least SLAPI_DSE_RETURNTEXT_SIZE bytes in size.
- */
- int
- load_schema_dse(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *ignored,
- int *returncode, char *returntext, void *arg)
- {
- Slapi_Attr *attr = 0;
- int primary_file = 0; /* this is the primary (writeable) schema file */
- int schema_ds4x_compat = config_get_ds4_compatible_schema();
- PRUint32 flags = *(PRUint32 *)arg;
- flags |= DSE_SCHEMA_NO_GLOCK; /* don't lock global resources
- during initialization */
- *returncode = 0;
- /*
- * Note: there is no need to call schema_lock_write() here because this
- * function is only called during server startup.
- */
- slapi_pblock_get( pb, SLAPI_DSE_IS_PRIMARY_FILE, &primary_file );
- if (!slapi_entry_attr_find(e, "attributetypes", &attr) && attr)
- {
- /* enumerate the values in attr */
- Slapi_Value *v = 0;
- int index = 0;
- for (index = slapi_attr_first_value(attr, &v);
- v && (index != -1);
- index = slapi_attr_next_value(attr, index, &v))
- {
- const char *s = slapi_value_get_string(v);
- if (!s)
- continue;
- if (flags & DSE_SCHEMA_NO_LOAD)
- {
- struct asyntaxinfo *tmpasip = NULL;
- if ((*returncode = read_at_ldif(s, &tmpasip, returntext,
- SLAPI_DSE_RETURNTEXT_SIZE, flags,
- primary_file /* force user defined? */,
- schema_ds4x_compat, 0)) != 0)
- break;
- attr_syntax_free( tmpasip ); /* trash it */
- }
- else
- {
- if ((*returncode = read_at_ldif(s, NULL, returntext,
- SLAPI_DSE_RETURNTEXT_SIZE, flags,
- primary_file /* force user defined? */,
- schema_ds4x_compat, 0)) != 0)
- break;
- }
- }
- slapi_entry_attr_delete(e, "attributetypes");
- }
- if (*returncode)
- return SLAPI_DSE_CALLBACK_ERROR;
- if (!slapi_entry_attr_find(e, "objectclasses", &attr) && attr)
- {
- /* enumerate the values in attr */
- Slapi_Value *v = 0;
- int index = 0;
- for (index = slapi_attr_first_value(attr, &v);
- v && (index != -1);
- index = slapi_attr_next_value(attr, index, &v))
- {
- struct objclass *oc = 0;
- const char *s = slapi_value_get_string(v);
- if (!s)
- continue;
- if ( LDAP_SUCCESS != (*returncode = read_oc_ldif(s, &oc, returntext,
- SLAPI_DSE_RETURNTEXT_SIZE, flags,
- primary_file /* force user defined? */,
- schema_ds4x_compat))) {
- break;
- }
- if (flags & DSE_SCHEMA_NO_LOAD)
- {
- /* we don't load the objectclase; free it */
- oc_free( &oc );
- }
- else
- {
- if ( LDAP_SUCCESS !=
- (*returncode = add_oc_internal(oc, returntext,
- SLAPI_DSE_RETURNTEXT_SIZE, schema_ds4x_compat,
- flags))) {
- oc_free( &oc );
- break;
- }
- }
- }
- slapi_entry_attr_delete(e, "objectclasses");
- }
- /* Set the schema CSN */
- if (!(flags & DSE_SCHEMA_NO_LOAD) &&
- !slapi_entry_attr_find(e, "nsschemacsn", &attr) && attr)
- {
- Slapi_Value *v = NULL;
- slapi_attr_first_value(attr, &v);
- if (NULL != v) {
- const char *s = slapi_value_get_string(v);
- if (NULL != s) {
- CSN *csn = csn_new_by_string(s);
- g_set_global_schema_csn(csn);
- }
- }
- }
- return (*returncode == LDAP_SUCCESS) ? SLAPI_DSE_CALLBACK_OK
- : SLAPI_DSE_CALLBACK_ERROR;
- }
- /*
- * Try to initialize the schema from the LDIF file. Read
- * the file and convert it to the avl tree of DSEs. If the
- * file doesn't exist, we try to create it and put a minimal
- * schema entry into it.
- *
- * Returns 1 for OK, 0 for Fail.
- *
- * schema_flags:
- * DSE_SCHEMA_NO_LOAD -- schema won't get loaded
- * DSE_SCHEMA_NO_CHECK -- schema won't be checked
- * DSE_SCHEMA_NO_BACKEND -- don't add as backend
- * DSE_SCHEMA_LOCKED -- already locked; no further lock needed
- */
- static int
- init_schema_dse_ext(char *schemadir, Slapi_Backend *be,
- struct dse **local_pschemadse, PRUint32 schema_flags)
- {
- int rc= 1; /* OK */
- char *userschemafile = 0;
- char *userschematmpfile = 0;
- char **filelist = 0;
- char *myschemadir = NULL;
- Slapi_DN schema;
- if (NULL == local_pschemadse)
- {
- return 0; /* cannot proceed; return failure */
- }
- slapi_sdn_init_dn_byref(&schema,"cn=schema");
- /* get schemadir if not given */
- if (NULL == schemadir)
- {
- myschemadir = config_get_schemadir();
- if (NULL == myschemadir)
- {
- return 0; /* cannot proceed; return failure */
- }
- }
- else
- {
- myschemadir = schemadir;
- }
- filelist = get_priority_filelist(myschemadir, ".*ldif$");
- if (!filelist || !*filelist)
- {
- slapi_log_error(SLAPI_LOG_FATAL, "schema",
- "No schema files were found in the directory %s\n", myschemadir);
- free_filelist(filelist);
- rc = 0;
- }
- else
- {
- /* figure out the last file in the list; it is the user schema */
- int ii = 0;
- while (filelist[ii]) ++ii;
- userschemafile = filelist[ii-1];
- userschematmpfile = slapi_ch_smprintf("%s.tmp", userschemafile);
- }
- if(rc)
- {
- *local_pschemadse = dse_new_with_filelist(userschemafile,
- userschematmpfile, NULL, NULL, myschemadir, filelist);
- }
- PR_ASSERT(*local_pschemadse);
- if ((rc = (*local_pschemadse != NULL)) != 0)
- {
- /* pass schema_flags as arguments */
- dse_register_callback(*local_pschemadse,
- DSE_OPERATION_READ, DSE_FLAG_PREOP, &schema,
- LDAP_SCOPE_BASE, NULL,
- load_schema_dse, (void *)&schema_flags);
- }
- slapi_ch_free_string(&userschematmpfile);
- if (NULL == schemadir)
- slapi_ch_free_string(&myschemadir); /* allocated in this function */
- if(rc)
- {
- char errorbuf[SLAPI_DSE_RETURNTEXT_SIZE] = {0};
- int dont_write = 1;
- int merge = 1;
- int dont_dup_check = 1;
- Slapi_PBlock pb;
- memset(&pb, 0, sizeof(pb));
- /* don't write out the file when reading */
- slapi_pblock_set(&pb, SLAPI_DSE_DONT_WRITE_WHEN_ADDING, (void*)&dont_write);
- /* duplicate entries are allowed */
- slapi_pblock_set(&pb, SLAPI_DSE_MERGE_WHEN_ADDING, (void*)&merge);
- /* use the non duplicate checking str2entry */
- slapi_pblock_set(&pb, SLAPI_DSE_DONT_CHECK_DUPS, (void*)&dont_dup_check);
- /* borrow the task flag space */
- slapi_pblock_set(&pb, SLAPI_SCHEMA_FLAGS, (void*)&schema_flags);
- /* add the objectclass attribute so we can do some basic schema
- checking during initialization; this will be overridden when
- its "real" definition is read from the schema conf files */
- if (schema_flags & DSE_SCHEMA_NO_LOAD)
- {
- struct asyntaxinfo *tmpasip = NULL;
- rc = read_at_ldif("attributeTypes: ( 2.5.4.0 NAME 'objectClass' "
- "DESC 'Standard schema for LDAP' SYNTAX "
- "1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'RFC 2252' )",
- &tmpasip, errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,
- DSE_SCHEMA_NO_GLOCK|schema_flags, 0, 0, 0);
- attr_syntax_free( tmpasip ); /* trash it */
- }
- else
- {
- rc = read_at_ldif("attributeTypes: ( 2.5.4.0 NAME 'objectClass' "
- "DESC 'Standard schema for LDAP' SYNTAX "
- "1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'RFC 2252' )",
- NULL, errorbuf, SLAPI_DSE_RETURNTEXT_SIZE,
- DSE_SCHEMA_NO_GLOCK|schema_flags, 0, 0, 0);
- }
- if (rc)
- {
- slapi_log_error(SLAPI_LOG_FATAL, "schema", "Could not add"
- " attribute type \"objectClass\" to the schema: %s\n",
- errorbuf);
- }
- rc = dse_read_file(*local_pschemadse, &pb);
- }
- if (rc && !(schema_flags & DSE_SCHEMA_NO_BACKEND))
- {
- /* make sure the schema is normalized */
- if (schema_flags & DSE_SCHEMA_LOCKED)
- normalize_oc_nolock();
- else
- normalize_oc();
- /* register callbacks */
- dse_register_callback(*local_pschemadse, SLAPI_OPERATION_SEARCH,
- DSE_FLAG_PREOP,&schema, LDAP_SCOPE_BASE,
- NULL, read_schema_dse, NULL);
- dse_register_callback(*local_pschemadse, SLAPI_OPERATION_MODIFY,
- DSE_FLAG_PREOP,&schema, LDAP_SCOPE_BASE,
- NULL, modify_schema_dse, NULL);
- dse_register_callback(*local_pschemadse, SLAPI_OPERATION_DELETE,
- DSE_FLAG_PREOP, &schema, LDAP_SCOPE_BASE,
- NULL,dont_allow_that,NULL);
- dse_register_callback(*local_pschemadse, DSE_OPERATION_WRITE,
- DSE_FLAG_PREOP, &schema, LDAP_SCOPE_BASE,
- NULL, refresh_user_defined_schema, NULL);
- if (rc) {
- if (NULL == be) { /* be is not given. select it */
- be = slapi_be_select_by_instance_name( DSE_SCHEMA );
- }
- if (NULL == be) { /* first time */
- /* add as a backend */
- be = be_new_internal(*local_pschemadse, "DSE", DSE_SCHEMA);
- be_addsuffix(be, &schema);
- } else { /* schema file reload */
- struct slapdplugin *backend_plugin = NULL;
- be_replace_dse_internal(be, *local_pschemadse);
- /* ldbm has some internal attributes to be added */
- backend_plugin = plugin_get_by_name("ldbm database");
- if (backend_plugin) {
- if (backend_plugin->plg_add_schema) {
- (backend_plugin->plg_add_schema)( NULL );
- } else {
- slapi_log_error( SLAPI_LOG_FATAL, "init_schema_dse",
- "backend has not set internal schema\n" );
- }
- }
- }
- }
- }
- slapi_sdn_done(&schema);
- return rc;
- }
- int
- init_schema_dse(const char *configdir)
- {
- char *schemadir = config_get_schemadir();
- int rc = 0;
- if (NULL == schemadir)
- {
- schemadir = slapi_ch_smprintf("%s/%s", configdir, SCHEMA_SUBDIR_NAME);
- }
- rc = init_schema_dse_ext(schemadir, NULL, &pschemadse, 0);
- slapi_ch_free_string(&schemadir);
- return rc;
- }
- /*
- * Look for `keyword' within `*inputp' and return the flag_value if found
- * (zero if returned if not found).
- *
- * If the keyword is found, `*inputp' is set to point just beyond the end of
- * the keyword. If the keyword is not found, `*inputp' is not changed.
- *
- * The `strstr_fn' function pointer is used to search for `keyword', e.g., it
- * could be PL_strcasestr().
- *
- * The string passed in `keyword' MUST include a trailing space, e.g.,
- *
- * flag |= get_flag_keyword( " COLLECTIVE ", SLAPI_ATTR_FLAG_COLLECTIVE,
- * &input, PL_strcasestr );
- *
- */
- static int
- get_flag_keyword( const char *keyword, int flag_value, const char **inputp,
- schema_strstr_fn_t strstr_fn )
- {
- const char *kw;
- PR_ASSERT( NULL != inputp );
- PR_ASSERT( NULL != *inputp );
- PR_ASSERT( ' ' == keyword[ strlen( keyword ) - 1 ] );
- if ( NULL == strstr_fn ) {
- strstr_fn = PL_strcasestr;
- }
- kw = (*strstr_fn)( *inputp, keyword );
- if ( NULL == kw ) {
- flag_value = 0; /* not found -- return no value */
- } else {
- *inputp = kw + strlen( keyword ) - 1; /* advance input */
- }
- return flag_value;
- }
- /*
- * Look for `tag' within `*inputp' and return the OID string following `tag'.
- * If the OID has single quotes around it they are removed (they are allowed
- * for compatibility with DS 3.x and 4.x).
- *
- * If the tag is found, `*inputp' is set to point just beyond the end of
- * the OID that was extracted and returned. If the tag is not found,
- * `*inputp' is not changed.
- *
- * The `strstr_fn' function pointer is used to search for `tag', e.g., it
- * could be PL_strcasestr().
- *
- * The string passed in `tag' SHOULD generally include a trailing space, e.g.,
- *
- * pSuperior = get_tagged_oid( "SUP ", &input, PL_strcasestr );
- *
- * The exception to this is when the tag contains '(' as a trailing character.
- * This is used to process lists of oids, such as the following:
- *
- * SUP (inetOrgPerson $ testUser)
- *
- * A malloc'd string is returned if `tag; is found and NULL if not.
- */
- static char *
- get_tagged_oid( const char *tag, const char **inputp,
- schema_strstr_fn_t strstr_fn )
- {
- const char *startp, *endp;
- char *oid;
- PR_ASSERT( NULL != inputp );
- PR_ASSERT( NULL != *inputp );
- PR_ASSERT( NULL != tag );
- PR_ASSERT( '\0' != tag[ 0 ] );
- if('(' !=tag[0])
- PR_ASSERT((' ' == tag[ strlen( tag ) - 1 ]) || ('(' == tag[ strlen( tag ) - 1 ]));
- if ( NULL == strstr_fn ) {
- strstr_fn = PL_strcasestr;
- }
- oid = NULL;
- if ( NULL != ( startp = (*strstr_fn)( *inputp, tag ))) {
- startp += strlen( tag );
- /* skip past any extra white space */
- if ( NULL == ( startp = skipWS( startp ))) {
- return( NULL );
- }
- /* skip past the leading single quote, if present */
- if ( *startp == '\'' ) {
- ++startp;
- /* skip past any extra white space */
- startp = skipWS( startp );
- }
- /* locate the end of the OID */
- if ((NULL != ( endp = strchr( startp, ' '))) ||
- (NULL != (endp = strchr( startp, ')'))) ) {
- if ( '\'' == *(endp-1) && endp > startp ) {
- --endp; /* ignore trailing quote */
- }
- } else {
- endp = startp + strlen( startp ); /* remainder of input */
- }
- oid = slapi_ch_malloc( endp - startp + 1 );
- memcpy( oid, startp, endp - startp );
- oid[ endp - startp ] = '\0';
- *inputp = endp;
- }
- return( oid );
- }
- /*
- * sprintf to `outp' the contents of `tag' followed by `oid' followed by a
- * trailing space. If enquote is non-zero, single quotes are included
- * around the `oid' string. If `suffix' is not NULL, it is output directly
- * after the `oid' (before the trailing space).
- * Note that `tag' should typically include a trailing space, e.g.,
- *
- * outp += put_tagged_oid( outp, "SUP ", "1.2.3.4", NULL, enquote_oids );
- *
- * Returns the number of bytes copied to `outp' or 0 if `oid' is NULL.
- */
- static int
- put_tagged_oid( char *outp, const char *tag, const char *oid,
- const char *suffix, int enquote )
- {
- int count = 0;
- if ( NULL == suffix ) {
- suffix = "";
- }
- if ( NULL != oid ) {
- if ( enquote ) {
- count = sprintf( outp, "%s'%s%s' ", tag, oid, suffix );
- } else {
- count = sprintf( outp, "%s%s%s ", tag, oid, suffix );
- }
- }
- return( count );
- }
- /*
- * Add to `buf' a string of the form:
- *
- * prefix SPACE ( oid1 $ oid2 ... ) SPACE
- * OR
- * prefix SPACE oid SPACE
- *
- * The part after <prefix> matches the `oids' definition
- * from RFC 2252 section 4.1.
- *
- * If oids is NULL or an empty array, `buf' is not touched.
- */
- static void
- strcat_oids( char *buf, char *prefix, char **oids, int schema_ds4x_compat )
- {
- char *p;
- int i;
-
- if ( NULL != oids && NULL != oids[0] ) {
- p = buf + strlen(buf); /* skip past existing content */
- if ( NULL == oids[1] && !schema_ds4x_compat ) {
- sprintf( p, "%s %s ", prefix, oids[0] ); /* just one oid */
- } else {
- sprintf( p, "%s ( ", prefix ); /* oidlist */
- for ( i = 0; oids[i] != NULL; ++i ) {
- if ( i > 0 ) {
- strcat( p, " $ " );
- }
- strcat( p, oids[i] );
- }
- strcat( p, " ) " );
- }
- }
- }
- /*
- * Add to `buf' a string of the form:
- *
- * prefix SPACE ( 's1' 's2' ... ) SPACE
- * OR
- * prefix SPACE 's1' SPACE
- *
- * The part after <prefix> matches the qdescs definition
- * from RFC 2252 section 4.1.
- *
- * A count of the number of bytes added to buf or needed is returned.
- *
- * If buf is NULL, no copying is done but the number of bytes needed
- * is calculated and returned. This is useful if you need to allocate
- * space before calling this function will a buffer.
- *
- * If qdlist is NULL or an empty array, `buf' is not touched and zero
- * is returned.
- */
- static size_t
- strcat_qdlist( char *buf, char *prefix, char **qdlist )
- {
- int i;
- char *start, *p;
- size_t len = 0;
- if ( NULL != qdlist && NULL != qdlist[0] ) {
- if ( NULL == buf ) { /* calculate length only */
- len += strlen( prefix );
- if ( NULL != qdlist[1] ) {
- len += 4; /* surrounding spaces and '(' and ')' */
- }
- for ( i = 0; NULL != qdlist[i]; ++i ) {
- len += 3; /* leading space and quote marks */
- len += strlen(qdlist[i]);
- }
- ++len; /* trailing space */
- } else {
- p = start = buf + strlen(buf); /* skip past existing content */
- if ( NULL == qdlist[1] ) { /* just one string */
- p += sprintf( p, "%s '%s' ", prefix, qdlist[0] );
- } else { /* a list of strings */
- p += sprintf( p, "%s (", prefix );
- for ( i = 0; qdlist[i] != NULL; ++i ) {
- p += sprintf( p, " '%s'", qdlist[i] );
- }
- *p++ = ' ';
- *p++ = ')';
- *p++ = ' ';
- *p = '\0';
- }
- len = p - start;
- }
- }
- return( len );
- }
- /*
- * Just like strlen() except that 0 is returned if `s' is NULL.
- */
- static size_t
- strlen_null_ok(const char *s)
- {
- if ( NULL == s ) {
- return( 0 );
- }
- return( strlen( s ));
- }
- /*
- * Like strcpy() except a count of the number of bytes copied is returned.
- */
- static int
- strcpy_count( char *dst, const char *src )
- {
- char *p;
- p = dst;
- while ( *src != '\0' ) {
- *p++ = *src++;
- }
- *p = '\0';
- return( p - dst );
- }
- /*
- * Look for an X-ORIGIN list in `schema_value' and return a
- * qdstrings array (or NULL if no list is present). *num_originsp is
- * set to a count of the number of origin strings returned.
- *
- * If no X-ORIGIN list is found and `default_list' is non-NULL, a copy
- * of default list is returned.
- */
- static char **
- parse_origin_list( const char *schema_value, int *num_originsp,
- char **default_list )
- {
- char *start, *end, *origin_tmp;
- char **origins = NULL;
- if (( start = PL_strstr ( schema_value, "X-ORIGIN ")) != NULL ) {
- start += 9;
- origin_tmp = slapi_ch_strdup( start );
- if ( *start == '(' ) {
- end = strchr( origin_tmp, ')' );
- } else {
- end = strchr( origin_tmp + 1, '\'' );
- }
- if (end) {
- *(end+1) = 0;
- }
- origins = parse_qdstrings( origin_tmp, num_originsp );
- slapi_ch_free( (void **)&origin_tmp );
- } else {
- origins = NULL;
- *num_originsp = 0;
- }
- if ( NULL == origins && NULL != default_list ) {
- int i;
- for ( i = 0; default_list[i] != NULL; ++i ) {
- ;
- }
- *num_originsp = i;
- origins = (char **)slapi_ch_malloc( (i+1) * sizeof(char *));
- for ( i = 0; default_list[i] != NULL; ++i ) {
- origins[i] = slapi_ch_strdup( default_list[i] );
- }
- origins[i] = NULL;
- }
- if ( origins == NULL || origins[0] == NULL ) {
- LDAPDebug( LDAP_DEBUG_ANY, "no origin (%s)\n", schema_value, 0, 0 );
- }
- return origins;
- }
- /*
- * Determine based on origin whether a schema element is standard or
- * user-defined. Algorithm: perform a case insensitive search for
- * "user defined". If found, return 1 (user defined); if not, return 0.
- */
- static int
- element_is_user_defined( char * const *origins )
- {
- int i;
- if ( origins != NULL ) {
- for ( i = 0; origins[i] != NULL; ++i ) {
- if ( 0 == strcasecmp( schema_user_defined_origin[0], origins[i] )) {
- return 1;
- }
- }
- }
- return 0;
- }
- /*
- * Return PR_TRUE if the attribute type named 'type' is one of those that
- * we handle directly in this file (in the scheme DSE callbacks).
- * Other types are handled by the generic DSE code in dse.c.
- */
- /* subschema DSE attribute types we handle within the DSE callback */
- static char *schema_interesting_attr_types[] = {
- "dITStructureRules",
- "nameForms",
- "dITContentRules",
- "objectClasses",
- "attributeTypes",
- "matchingRules",
- "matchingRuleUse",
- "ldapSyntaxes",
- "nsschemacsn",
- NULL
- };
- static PRBool
- schema_type_is_interesting( const char *type )
- {
- int i;
- for ( i = 0; schema_interesting_attr_types[i] != NULL; ++i ) {
- if ( 0 == strcasecmp( type, schema_interesting_attr_types[i] )) {
- return PR_TRUE;
- }
- }
- return PR_FALSE;
- }
- static void
- schema_create_errormsg(
- char *errorbuf,
- size_t errorbufsize,
- const char *prefix,
- const char *name,
- const char *fmt,
- ...
- )
- {
- if ( NULL != errorbuf ) {
- va_list ap;
- int rc = 0;
- va_start( ap, fmt );
- if ( NULL != name ) {
- rc = PR_snprintf( errorbuf, errorbufsize, prefix, name );
- }
- /* ok to cast here because rc is positive */
- if ( (rc >= 0) && ((size_t)rc < errorbufsize) ) {
- (void)PR_vsnprintf( errorbuf + rc, errorbufsize - rc, fmt, ap );
- }
- va_end( ap );
- }
- }
- /*
- * va_locate_oc_val finds an objectclass within the array of values in va.
- * First oc_name is used, falling back to oc_oid. oc_oid can be NULL.
- * oc_name and oc_oid should be official names (no trailing spaces). But
- * trailing spaces within the va are ignored if appropriate.
- *
- * Returns >=0 if found (index into va) and -1 if not found.
- */
- static int
- va_locate_oc_val( Slapi_Value **va, const char *oc_name, const char *oc_oid )
- {
- int i;
- const char *strval;
- if ( NULL == va || oc_name == NULL ) { /* nothing to look for */
- return -1;
- }
- if ( !schema_ignore_trailing_spaces ) {
- for ( i = 0; va[i] != NULL; i++ ) {
- strval = slapi_value_get_string(va[i]);
- if ( NULL != strval ) {
- if ( 0 == strcasecmp(strval, oc_name)) {
- return i;
- }
-
- if ( NULL != oc_oid
- && 0 == strcasecmp( strval, oc_oid )) {
- return i;
- }
- }
- }
- } else {
- /*
- * Ignore trailing spaces when comparing object class names.
- */
- size_t len;
- const char *p;
- for ( i = 0; va[i] != NULL; i++ ) {
- strval = slapi_value_get_string(va[i]);
- if ( NULL != strval ) {
- for ( p = strval, len = 0; (*p != '\0') && (*p != ' ');
- p++, len++ ) {
- ; /* NULL */
- }
- if ( 0 == strncasecmp(oc_name, strval, len )
- && ( len == strlen(oc_name))) {
- return i;
- }
-
- if ( NULL != oc_oid
- && ( 0 == strncasecmp( oc_oid, strval, len ))
- && ( len == strlen(oc_oid))) {
- return i;
- }
- }
- }
- }
- return -1; /* not found */
- }
- /*
- * va_expand_one_oc is used to add missing superclass values to the
- * objectclass attribute when an entry is added or modified.
- *
- * missing values are always added to the end of the 'vap' array.
- *
- * Note: calls to this function MUST be bracketed by lock()/unlock(), i.e.,
- *
- * oc_lock_read();
- * va_expand_one_oc( b, o );
- * oc_unlock();
- */
- static void
- va_expand_one_oc( const char *dn, Slapi_Value ***vap, const char *ocs )
- {
- struct objclass *this_oc, *sup_oc;
- int p,i;
- Slapi_Value **newva;
- char ebuf[BUFSIZ];
- this_oc = oc_find_nolock( ocs );
-
- if ( this_oc == NULL ) {
- return; /* skip unknown object classes */
- }
-
- if ( this_oc->oc_superior == NULL ) {
- return; /* no superior */
- }
- sup_oc = oc_find_nolock( this_oc->oc_superior );
- if ( sup_oc == NULL ) {
- return; /* superior is unknown -- ignore */
- }
- p = va_locate_oc_val( *vap, sup_oc->oc_name, sup_oc->oc_oid );
- if ( p != -1 ) {
- return; /* value already present -- done! */
- }
-
- /* parent was not found. add to the end */
- for ( i = 0; (*vap)[i] != NULL; i++ ) {
- ;
- }
-
- /* prevent loops: stop if more than 1000 OC values are present */
- if ( i > 1000 ) {
- return;
- }
-
- newva = (Slapi_Value **)slapi_ch_realloc( (char *)*vap,
- ( i + 2 )*sizeof(Slapi_Value **));
-
- newva[i] = slapi_value_new_string(sup_oc->oc_name);
- newva[i+1] = NULL;
-
- *vap = newva;
- LDAPDebug( LDAP_DEBUG_TRACE,
- "Entry \"%s\": added missing objectClass value %s\n",
- escape_string( dn, ebuf ), sup_oc->oc_name, 0 );
- }
- /*
- * Expand the objectClass values in 'e' to take superior classes into account.
- * All missing superior classes are added to the objectClass attribute, as
- * is 'top' if it is missing.
- */
- void
- slapi_schema_expand_objectclasses( Slapi_Entry *e )
- {
- Slapi_Attr *sa;
- Slapi_Value **va;
- const char *dn = slapi_entry_get_dn_const( e );
- int i;
- if ( 0 != slapi_entry_attr_find( e, SLAPI_ATTR_OBJECTCLASS, &sa )) {
- return; /* no OC values -- nothing to do */
- }
- va = attr_get_present_values( sa );
- if ( va == NULL || va[0] == NULL ) {
- return; /* no OC values -- nothing to do */
- }
- oc_lock_read();
- /*
- * This loop relies on the fact that bv_expand_one_oc()
- * always adds to the end
- */
- for ( i = 0; va[i] != NULL; ++i ) {
- if ( NULL != slapi_value_get_string(va[i]) ) {
- va_expand_one_oc( dn, &va, slapi_value_get_string(va[i]) );
- }
- }
-
- /* top must always be present */
- va_expand_one_oc( dn, &va, "top" );
- /*
- * Reset the present values in the set because we may have realloc'd it.
- * Note that this is the counterpart to the attr_get_present_values()
- * call we made above... nothing new has been allocated, but sa holds
- * a pointer to the original (pre realloc) va.
- */
- sa->a_present_values.va = va;
- oc_unlock();
- }
- void
- schema_expand_objectclasses_nolock( Slapi_Entry *e )
- {
- Slapi_Attr *sa;
- Slapi_Value **va;
- const char *dn = slapi_entry_get_dn_const( e );
- int i;
- if ( 0 != slapi_entry_attr_find( e, SLAPI_ATTR_OBJECTCLASS, &sa )) {
- return; /* no OC values -- nothing to do */
- }
- va = attr_get_present_values( sa );
- if ( va == NULL || va[0] == NULL ) {
- return; /* no OC values -- nothing to do */
- }
- /*
- * This loop relies on the fact that bv_expand_one_oc()
- * always adds to the end
- */
- for ( i = 0; va[i] != NULL; ++i ) {
- if ( NULL != slapi_value_get_string(va[i]) ) {
- va_expand_one_oc( dn, &va, slapi_value_get_string(va[i]) );
- }
- }
-
- /* top must always be present */
- va_expand_one_oc( dn, &va, "top" );
- /*
- * Reset the present values in the set because we may have realloc'd it.
- * Note that this is the counterpart to the attr_get_present_values()
- * call we made above... nothing new has been allocated, but sa holds
- * a pointer to the original (pre realloc) va.
- */
- sa->a_present_values.va = va;
- }
- /* lock to protect both objectclass and schema_dse */
- static void
- reload_schemafile_lock()
- {
- oc_lock_write();
- schema_dse_lock_write();
- }
- static void
- reload_schemafile_unlock()
- {
- schema_dse_unlock();
- oc_unlock();
- }
- /* API to validate the schema files */
- int
- slapi_validate_schema_files(char *schemadir)
- {
- struct dse *my_pschemadse = NULL;
- int rc = init_schema_dse_ext(schemadir, NULL, &my_pschemadse,
- DSE_SCHEMA_NO_LOAD | DSE_SCHEMA_NO_BACKEND);
- if (rc) {
- dse_destroy(my_pschemadse);
- return LDAP_SUCCESS;
- } else {
- slapi_log_error( SLAPI_LOG_FATAL, "schema_reload",
- "schema file validation failed\n" );
- return LDAP_OBJECT_CLASS_VIOLATION;
- }
- }
- /*
- * API to reload the schema files.
- * Rule: this function is called when slapi_validate_schema_files is passed.
- * Schema checking is skipped in this function.
- */
- int
- slapi_reload_schema_files(char *schemadir)
- {
- int rc = LDAP_SUCCESS;
- struct dse *my_pschemadse = NULL;
- /* get be to lock */
- Slapi_Backend *be = slapi_be_select_by_instance_name( DSE_SCHEMA );
- if (NULL == be)
- {
- slapi_log_error( SLAPI_LOG_FATAL, "schema_reload",
- "schema file reload failed\n" );
- return LDAP_LOCAL_ERROR;
- }
- slapi_be_Wlock(be); /* be lock must be outer of schemafile lock */
- reload_schemafile_lock();
- attr_syntax_delete_all();
- oc_delete_all_nolock();
- rc = init_schema_dse_ext(schemadir, be, &my_pschemadse,
- DSE_SCHEMA_NO_CHECK | DSE_SCHEMA_LOCKED);
- if (rc) {
- dse_destroy(pschemadse);
- pschemadse = my_pschemadse;
- reload_schemafile_unlock();
- slapi_be_Unlock(be);
- return LDAP_SUCCESS;
- } else {
- reload_schemafile_unlock();
- slapi_be_Unlock(be);
- slapi_log_error( SLAPI_LOG_FATAL, "schema_reload",
- "schema file reload failed\n" );
- return LDAP_LOCAL_ERROR;
- }
- }
- /*
- * slapi_schema_list_objectclass_attributes:
- * Return the list of attributes belonging to the objectclass
- *
- * The caller is responsible to free the returned list with charray_free.
- * flags: one of them or both:
- * SLAPI_OC_FLAG_REQUIRED
- * SLAPI_OC_FLAG_ALLOWED
- */
- char **
- slapi_schema_list_objectclass_attributes(const char *ocname_or_oid,
- PRUint32 flags)
- {
- struct objclass *oc = NULL;
- char **attrs = NULL;
- PRUint32 mask = SLAPI_OC_FLAG_REQUIRED | SLAPI_OC_FLAG_ALLOWED;
- if (!flags) {
- return attrs;
- }
-
- oc_lock_read();
- oc = oc_find_nolock(ocname_or_oid);
- if (oc) {
- switch (flags & mask) {
- case SLAPI_OC_FLAG_REQUIRED:
- attrs = charray_dup(oc->oc_required);
- break;
- case SLAPI_OC_FLAG_ALLOWED:
- attrs = charray_dup(oc->oc_allowed);
- break;
- case SLAPI_OC_FLAG_REQUIRED|SLAPI_OC_FLAG_ALLOWED:
- attrs = charray_dup(oc->oc_required);
- charray_merge(&attrs, oc->oc_allowed, 1/*copy_strs*/);
- break;
- default:
- slapi_log_error( SLAPI_LOG_FATAL, "list objectclass attributes",
- "flag 0x%x not supported\n", flags );
- break;
- }
- }
- oc_unlock();
- return attrs;
- }
- /*
- * slapi_schema_get_superior_name:
- * Return the name of the superior objectclass
- *
- * The caller is responsible to free the returned name
- */
- char *
- slapi_schema_get_superior_name(const char *ocname_or_oid)
- {
- struct objclass *oc = NULL;
- char *superior = NULL;
- oc_lock_read();
- oc = oc_find_nolock(ocname_or_oid);
- if (oc) {
- superior = slapi_ch_strdup(oc->oc_superior);
- }
- oc_unlock();
- return superior;
- }
|